3

我有一堆旧的 F77 源代码(通常在x86_64with上编译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_64with上编译g++)调用这些函数。

  1. 它适用于默认的 Fortran KIND=8

    extern "C" { std::complex<double> f_(double *x, double *y, int *i); }
    
  2. 当我KIND=4使用以下-freal-8-real-4选项强制执行默认 Fortran 时,它可以工作:

    extern "C" { std::complex<float> f_(float *x, float *y, int *i); }
    
  3. 当我KIND=16使用该-freal-8-real-16选项(以及在 C++ 中#include <quadmath.h>)强制执行默认的 Fortran 时,它可以工作:

    extern "C" { __complex128 f_(__float128 *x, __float128 *y, int *i); }
    

    令我惊讶的是,在这种情况下,它似乎也可以使用(返回值在 中*z):

    extern "C" { void f_(__complex128 *z, __float128 *x, __float128 *y, int *i); }
    

    上面这两个原型中哪一个是(更多?)正确的?

  4. 我的问题是我无法KIND=10使用该-freal-8-real-10选项使其与我想要的默认 Fortran 一起工作。在 Fortran 内部kindprecisionrangesizeof返回值直接对应于 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复杂值?注意:我认为我不能使用-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++ 中,您不能使用 eg 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);
4

1 回答 1

1

如果您想正确完成这项工作,请学习如何实际使用 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';
 }
于 2020-08-03T16:36:48.700 回答