调用 FORTRAN 函数的 C 包装器

3

我是新手,但需要从C语言中调用一些旧的Fortran77函数。如可能,我不想改变Fortran代码,我希望编写一个包装器来调用Fortran函数。我希望能得到一个简单的工作示例(在Linux上)。我的做法:

在somefunction.f文件中:

REAL*8 FUNCTION MYFUNC(ZZ)
      IMPLICIT NONE
      REAL*8 ZZ, T1
      T1 = ZZ + 1.0D0
      MYFUNC = T1
      RETURN
      END

使用gfortran -c somefunction.f -o somefunction.o编译。

在debug.c文件中:

#include <stdio.h>

double cfunc(double x) {
    double result = myfunc_( &x );
    return result;
}
int main() {
    double test = cfunc(3.0);
    printf(" %.15f ",test);
}

通过gcc -c debug.c -o debug.o进行编译。

然后我使用gcc debug.o somefunction.o./a.out

但是,我得到的是无意义的数字,而不是3+1=4。我该如何纠正这个问题?


P.S: 如果这个问题解决了,我实际使用的函数会更加复杂:

  1. 如果MYFUNC不再是类型为COMPLEX*16 FUNCTION MYFUNC(ZZ)且ZZ也是一个复数,我应该改变什么?

  2. 如果MYFUNC调用某些内置的Fortran函数,比如CDLOG(ZZ),该怎么办?

  3. 如果它访问一个公共块,能否也适应这种情况?


1
你在 C 语言中声明了 myfunc 的返回类型吗? - Ian Bush
@IanBush 如果我没记错的话,real*8 是 Fortran 指定 double 的方式。它[大致上]是“占用 8 字节的浮点数”。而且,在 Fortran 中,real*8 已经存在了几十年... - Craig Estey
2
@CraigEstey 不是这样的。Real*8从未成为标准Fortran的一部分,因此其行为是实现定义的,并且有人提出了对不支持它的编译器的问题。相反,应该使用标准定义的kind机制。请参见https://dev59.com/oHRA5IYBdhLWcg3wwwzD 此外,回答这个问题的最佳方法是使用Fortran与C进行接口的标准定义方式。 - Ian Bush
1
@IanBush 我并没有说这是一种标准,只是它已经存在了几十年。Fortran是一种落后的语言。编写新代码的唯一原因是与传统科学库进行接口交互[这些库可能有数百万行使用real*8的代码]。使用real*8kind早至少二十年。因此,每个需要保持相关性的可信Fortran编译器都将支持real*8。而且,real*8 xsREAL(KIND=8) :: XS更紧凑,因此由于需要/在任何地方使用的简洁性而获胜。此外,F2008允许使用REAL64,这仍然更好。 - Craig Estey
3
您已经标记了Fortran77,但如果在Fortran端编写包装器是一个选项,我强烈建议使用由FORTRAN2003提供的iso_c_binding模块:a)它定义了保证与其C对应类型匹配的数据类型(=不需要猜测real*8是否真正是double等),b)一个bind(c,name="foo")函数在C中具有清晰定义的签名和符号名称(foo(...)保证按预期工作)。简而言之,它使您脱离了模糊的实现定义行为领域,进入到了规范符合的C/fortran明确定义的领域。 - cmaster - reinstate monica
显示剩余5条评论
2个回答

3
除了返回类型外,您还需要注意Fortran过程名称如何转换为在C中访问的符号。没有标准,完全取决于Fortran编译器和平台,因此如果要实现可移植性,您将需要使用一些配置时检测和宏。
例如,从C中调用Fortran的MYFUNC()需要按以下方式进行调用:
- 在Windows上使用Intel Fortran编译器时调用MYFUNC() - 在IBM XL Fortran (xlf)中调用myfunc() - 在大多数其他情况下(包括Windows上的GNU Fortran),调用myfunc_() (当Fortran例程在模块中时,情况会更加复杂,因为这时候修改后的名称由模块和_MOD__mp_等前缀组成。)
现代Fortran解决方案是在Fortran端声明一个bind(C)例程,这将关闭名称修饰(或您可以指定显式绑定名称)。

2

好的,仅供参考,正如@IanBush所说,C程序应该声明myfunc的返回类型。

#include <stdio.h>

double myfunc_(double*);

double cfunc(double x) {
    double result = myfunc_( &x );
    return result;
}
int main() {
     double test = cfunc(3.0);
     printf(" %.15f ",test);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接