我经常需要从我的 C++ 代码中调用一些 Fortran 例程。就我而言,C header 始终可用并包含诸如
之类的签名double fFortran(int* a, int* b, double* someArray, int* sizeOfThatArray)
我的问题是:是否可以编写通用的 C++14 包装器 fortranCall
(可能使用模板元编程)
在必要时获取地址,然后调用 fortran 函数
像这样
double someArray[2] = {1, 4};
double result = fortranCall(fFortran, 4, 5, someArray,
sizeof(someArray) / sizeof(someArray[0]));
应该等同于
double someArray[2] = {1, 4};
int sizeOfSomeArray = sizeof(someArray) / sizeof(someArray[0]);
int a = 4;
int b = 5;
double result = fFortran(&a, &b, someArray, &sizeOfSomeArray);
我认为正确的解决方案涉及参数包,但我不知道如何迭代一个参数包并在需要的地方引用。
最佳答案
对于这个答案,我将做出以下假设:
- FORTRAN 函数的参数都作为指针传递
- 指针地址将从传递给
fortranCall
函数的参数中获取。 - 数组指针参数后面总是跟着一个指向数组大小的指针
- 我们希望保留参数的顺序。
调用示例:
// So, given function signature
double fFortran(int* a, int* b, double* someArray, int* sizeOfThatArray);
// we would like to call with:
fortranCall(fFortran, 4, 5, someArray);
// Likewise, given
fFortranTwoArrays(double* arrayA, int* size_of_A, double* arrayB, int* size_of_B);
// we would like to call with
fortranCall(fFortranTwoArrays, someArray, some_other_Array);
以下程序将进行如上所示的调用:
#include <tuple>
#include <type_traits>
// Functions to call eventually
double fFortran(int* a, int* b, double* someArray, int* sizeOfThatArray)
{
return 0.0;
}
double fFortranTwoArrays(double* arrayA, int* size_of_A, double* arrayB, int* size_of_B)
{
return 0.0;
}
// If T is an array
// then make a std::tuple with two parameters
// pointer to first of T and
// pointer to extent of T
template<
typename T,
typename std::enable_if <
std::is_array<T>{},
int
>::type Extent = std::extent<T>::value,
typename Ptr = typename std::decay<T>::type
>
auto make_my_tuple(T& t)
{
static auto extent = Extent;
Ptr ptr = &t[0];
return std::make_tuple(ptr, &extent);
}
// If T is not an array
// then make a std::tuple with a single parameter
// pointer to T
template<typename T,
typename std::enable_if <
!std::is_array<T>{},
int
>::type = 0
>
auto make_my_tuple(T& t)
{
return std::make_tuple(&t);
}
template<typename F, typename... Targs>
auto fortranCall(F& f, Targs&& ... args)
{
// Make a single tuple with all the parameters.
auto parameters = std::tuple_cat(make_my_tuple(args)...);
// Arrays were each expanded to
// two pointer parameters(location and size).
// Other parameters will pass as a single pointer
return std::apply(f,parameters);
}
int main()
{
double someArray[2] = {1, 4};
double result = fortranCall(fFortran, 4, 5, someArray);
double some_other_Array[] = {6,7,8,9,10};
auto result2 = fortranCall(fFortranTwoArrays, someArray, some_other_Array);
}
std::apply 是 C++17。如果你想让它在 C++14 中工作,请使用来自 https://en.cppreference.com/w/cpp/utility/apply 的示例实现
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tuple>>::value>{});
}
并使用 Martin Moene ( https://github.com/martinmoene/invoke-lite ) 向后移植的调用
关于c++ - 如何编写用于在 C++14 中调用 Fortran 函数的通用包装器(按引用调用 --> 按值调用),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55765162/