簡體   English   中英

調用 Fortran 函數從 C++ 返回 KIND=10 復數值

[英]Calling Fortran functions which return KIND=10 complex values from C++

我有一堆舊的 F77 源代碼(通常使用gfortran -std=legacyx86_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上編譯)調用這些函數。

  1. 它適用於默認 Fortran KIND=8

     extern "C" { std::complex<double> f_(double *x, double *y, int *i); }
  2. 當我使用-freal-8-real-4選項強制執行默認的 Fortran KIND=4時,它可以工作:

     extern "C" { std::complex<float> f_(float *x, float *y, int *i); }
  3. 當我使用-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); }

    上面這兩個原型中哪一個是(更多?)正確的?

  4. 我的問題是我無法使用-freal-8-real-10選項使其與我想要的默認 Fortran KIND=10一起工作。 在 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++ 檢索 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM