[英]Calling Fortran functions which return KIND=10 complex values from C++
我有一堆舊的 F77 源代碼(通常使用gfortran -std=legacy
在x86_64
上編譯)。 它包含相當多的形式功能:
double complex function f(x, y, i)
double precision x, y
integer i
f = cmplx(x, y) * i
return
end
我需要從一些 C++ 代碼(通常在帶有g++
的x86_64
上編譯)調用這些函數。
它適用於默認 Fortran KIND=8
:
extern "C" { std::complex<double> f_(double *x, double *y, int *i); }
當我使用-freal-8-real-4
選項強制執行默認的 Fortran KIND=4
時,它可以工作:
extern "C" { std::complex<float> f_(float *x, float *y, int *i); }
當我使用-freal-8-real-16
選項(以及 C++ #include <quadmath.h>
)強制執行默認的 Fortran KIND=16
時,它可以工作:
extern "C" { __complex128 f_(__float128 *x, __float128 *y, int *i); }
令我驚訝的是,在這種情況下,它似乎也可以使用(返回值在*z
中):
extern "C" { void f_(__complex128 *z, __float128 *x, __float128 *y, int *i); }
上面這兩個原型中哪一個是(更多?)正確的?
我的問題是我無法使用-freal-8-real-10
選項使其與我想要的默認 Fortran KIND=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++ 檢索 Fortran KIND=10
復雜值? 注意:我認為我不能使用-ff2c
。
更新(2020.08.04) :我已經能夠欺騙 C++ 編譯器,因此它似乎可以為任何 Fortran KIND=4,8,10
生成正確的代碼。 訣竅是在 C++ 中使用 ISO C99 _Complex
(注意:此技巧僅適用於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
仍然可以。
C++ 中 ISO C99 _Complex
的可用性相當有限。 例如,使用-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
也可能是 go 三連擊。 這是 go 的 C++ 代碼和上面第二個示例中的文件 a.f90。
#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';
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.