c++ - 调用从 C++ 返回 KIND=10 复数值的 Fortran 函数

标签 c++ fortran g++ gfortran mixed-programming

我有一堆旧的 F77 源代码(通常在 x86_64gfortran -std=legacy 上编译)。 它在形式上包含了相当多的功能:

      double complex function f(x, y, i)
      double precision x, y
      integer i
      f = cmplx(x, y) * i
      return
      end

我需要从一些 C++ 代码调用这些函数(通常在 x86_64g++ 上编译)。

  1. 它使用默认的 Fortran KIND=8 :

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

    extern "C" { std::complex<float> f_(float *x, float *y, int *i); }
    
  3. 当我强制执行默认的 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)中哪个是(更多?)合适的?

  4. 我的问题是我无法让它与我想要的默认 Fortran 一起工作 KIND=10使用 -freal-8-real-10选项。在 Fortran 内部,kind , precision , rangesizeof直接对应于 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复杂的值(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/

相关文章:

c++ - 使用 QGraphicsView 或 QGraphicsScene 在较大图像中叠加图像

C++ DLL 不会链接?

debugging - Gfortran:出于调试目的将纯函数视为普通函数?

fortran - 编译 FORTRAN IV 还是转换为 FORTRAN 77?

r - 设置使用 Fortran 和 PpenMP 的 R 包中的线程数

c++ - 如何让 g++ 列出所有 #included 文件的路径

c++ - 在 2 个程序之间传输信息 (Windows)

java - 验证由 JAVA DSA 签名的 openssl c++ 中的签名?

windows - cmake:指定编译器的问题

c++ - g++ 不能在 Mac OS X Mavericks 上编译