我有一堆旧的 F77 源代码(通常在 x86_64
和 gfortran -std=legacy
上编译)。
它在形式上包含了相当多的功能:
double complex function f(x, y, i)
double precision x, y
integer i
f = cmplx(x, y) * i
return
end
我需要从一些 C++ 代码调用这些函数(通常在 x86_64
和 g++
上编译)。
它使用默认的 Fortran
KIND=8
:extern "C" { std::complex<double> f_(double *x, double *y, int *i); }
当我强制执行默认的 Fortran
KIND=4
时它起作用了使用-freal-8-real-4
选项:extern "C" { std::complex<float> f_(float *x, float *y, int *i); }
当我强制执行默认的 Fortran
KIND=16
时它起作用了使用-freal-8-real-16
选项(在 C++ 中为#include <quadmath.h>
):extern "C" { __complex128 f_(__float128 *x, __float128 *y, int *i); }
令我惊讶的是,在这种情况下,它似乎也适用于(返回值在
*z
中):extern "C" { void f_(__complex128 *z, __float128 *x, __float128 *y, int *i); }
以上两个原型(prototype)中哪个是(更多?)合适的?
我的问题是我无法让它与我想要的默认 Fortran 一起工作
KIND=10
使用-freal-8-real-10
选项。在 Fortran 内部,kind
,precision
,range
和sizeof
直接对应于 C++long double
的返回值.所以,我尝试了:extern "C" { std::complex<long double> f_(long double *x, long double *y, int *i); } extern "C" { void f_(std::complex<long double> *z, long double *x, long double *y, int *i); } extern "C" { void f_(long double *x, long double *y, int *i, std::complex<long double> *z); }
但我根本无法让它工作。
也许我需要为
gfortran
添加一些特殊标志和/或g++
调用以使 C++ 检索 FortranKIND=10
复杂的值(value)? 注意:我认为我不能使用-ff2c
.
更新(2020.08.04):我已经能够欺骗 C++ 编译器,使其似乎为任何 Fortran 生成正确的代码 KIND=4,8,10
.诀窍是使用 ISO C99 _Complex
在 C++ 中(注意:此技巧仅对 KIND=10
是必需的,但它实际上也适用于 KIND=4,8
):
#include <complex.h>
#define C99KIND long double /* it can be "float", "double" or "long double" */
extern "C" { C99KIND _Complex f_(C99KIND *x, C99KIND *y, int *i); }
请注意,在 C++ 中,您不能使用例如long double complex
不过还好long double _Complex
还是可以的。
ISO C99 的可用性 _Complex
在 C++ 中是相当有限的。例如,使用 -std=c++11
(或更新)甚至是最基本的 creal*
和 cimag*
功能消失。
因此,最好的办法是立即将返回值复制到一些标准的 C++ 模板化复杂变量中,例如使用类似的东西(注意:f_
返回 C99KIND _Complex
):
std::complex<C99KIND> z = f_(&x, &y, &i);
最佳答案
如果您想正确完成这项工作,请学习如何实际使用 Fortran 的种类类型参数并将您的 Fortran 代码正确移植到 REAL(10)
。是的,我知道 10
不可移植;然而,我们正在讨论特定的 Fortran 处理器。
考虑一下,
function f(x, y, i) result(r) bind(c, name='f')
use iso_c_binding, only : ep => c_long_double
implicit none
complex(ep) r
real(ep), intent(in), value :: x, y
integer, intent(in), value :: i
r = cmplx(x, y, ep) * i
end function f
而且,由于我不使用 C++,但您应该能够根据需要更新 C
#include <complex.h>
#include <stdio.h>
#include <stdlib.h>
long double complex f(long double, long double, int);
int
main(void)
{
int i;
long double x, y;
long double complex z;
i = 42;
x = 1;
y = 2;
printf("%.10Lf %.10Lf\n", x, y);
z = f(x, y, i);
x = creall(z);
y = cimagl(z);
printf("%.10Lf %.10Lf\n", x, y);
return 0;
}
% gfortran -c a.f90
% gcc -o z b.c a.o -lm
% ./z
1.0000000000 2.0000000000
42.0000000000 84.0000000000
OP 声明他不会为他/她的 Fortran 代码的正确移植而烦恼,因此,必须使用编译器选项来神奇地(是的,这很神奇)进行移植。这是一个例子
% cat a.f90
double complex function f(x, y, i)
implicit none
double precision :: x, y
integer i
f = cmplx(x, y) * i ! cmplx my not do what you expect
end function f
% cat b.c
#include <complex.h>
#include <stdio.h>
#include <stdlib.h>
long double complex f_(long double *, long double *, int *);
int
main(void)
{
int i;
long double x, y;
long double complex z;
i = 42;
x = 1;
y = 2;
printf("%.10Lf %.10Lf\n", x, y);
z = f_(&x, &y, &i);
x = creall(z);
y = cimagl(z);
printf("%.10Lf %.10Lf\n", x, y);
return 0;
}
% gfcx -c -freal-8-real-10 a.f90
% gcc -o z b.c a.o -lm
% ./z
1.0000000000 2.0000000000
42.0000000000 84.0000000000
不妨选择三重奏。下面是第二个示例中与上述文件 a.f90 一起使用的 C++ 代码。
#include <iostream>
#include <complex>
#include <cmath>
extern "C" { std::complex<long double> f_(long double *, long double *, int *); }
int main()
{
std::complex<long double> z;
long double x, y;
int i;
i = 42;
x = 1;
y = 2;
z = f_(&x, &y, &i);
std::cout << z << '\n';
}
关于c++ - 调用从 C++ 返回 KIND=10 复数值的 Fortran 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63230415/