从 Fortran 调用 C 函数,其中 C 函数名称最初是从 C 传入的

Posted

技术标签:

【中文标题】从 Fortran 调用 C 函数,其中 C 函数名称最初是从 C 传入的【英文标题】:Calling a C function from Fortran where the C function name was originally passed in from C 【发布时间】:2013-01-23 17:08:18 【问题描述】:

出于不相关的原因,我需要将 C/C++ 函数名称传递给 Fortran 子例程,该子例程又调用该 C 函数。我发现我可以成功地将函数名传递到 Fortran 子例程中。在那个子程序中,我可以调用正确的 C 函数。但是,C 函数的参数在此调用中被破坏(当直接从 C 调用时,它工作正常)。我已经使用ISO C Binding 尝试让它工作,但无济于事。

这是一个 MWE:

fortranRoutine.h:

extern "C" 
    void fortranRoutine_(void(int status));
;

从Fortran.h调用:

void calledfromFortran(int status);

main.cpp:

#include "fortranRoutine.h"
#include "calledfromFortran.h" 

using namespace std;

int main(int argc, char** argv) 
    calledfromFortran(12);
    fortranRoutine_(calledfromFortran);
    return 0;


fortranRoutine.f90:

subroutine fortranRoutine(calledfromFortran)

    use iso_c_binding

    implicit none

    interface
        subroutine calledfromFortran(status) bind (c)
            use iso_c_binding
            integer(kind = c_int), intent(in) :: status
        end subroutine calledfromFortran
    end interface

    integer(kind = c_int) :: one

    one = 2
    call calledfromFortran(one)
    return

end subroutine fortranRoutine

从Fortran.cpp调用:

#include <iostream>
#include "stdlib.h"

using namespace std;

void calledfromFortran(int status) 
    cout << "In calledfromFortran:" << endl;
    cout << " status: " << status << endl;


当前结果

目前运行这个会给出:

In calledfromFortran:
 status: 12
In calledfromFortran:
 status: -1641758848

第一次从main 调用calledfromFortran 可以正常工作,但是从fortranRoutine 调用它时,值会损坏。请注意,每次运行时,后一个值都会发生变化。我做错了什么?

【问题讨论】:

已经有一段时间了,但我相信 fortran 通过引用传递所有参数。不应该调用fromFortran(int * status);吗? @frankc 嗯,是的。你完全正确。想把它写成答案吗? 不,没关系。我现在看到另一个人建议强制它按值传递,这与考虑默认通过引用行为一样好。请注意,如果您传递字符串,您还需要做其他事情,例如处理隐藏的长度参数。 【参考方案1】:

在 Fortran 和 C 之间传递标量值时,基本上有两种选择:

您通过引用传递它们:在 C 端,您必须确保使用指向这些标量的指针。

您通过值传递它们:在 Fortran 方面,您必须确保使用 VALUE 属性,正如其他帖子所建议的那样。

至于intent(in)属性,它可以留在那里,因为它不影响变量是通过值传递还是通过引用传递。在 Fortran 中,除非您指定 VALUE 属性,否则参数始终通过引用传递。属性intent(in) 仅告诉编译器防止在例程中使用该虚拟参数,这会改变其值。

关于命名的附加说明:您应该使用 bind(c) 指定您的 Fortran 例程 fortranRoutine。通过这种方式,您可以指定它的名称,如从 C 中看到的那样,即使它位于模块内:

module my_interface
  use iso_c_binding

contains

  subroutine fortranRoutine(calledFromFortran) bind(c, name='fortranroutine')
  ...
  end subroutine fortranRoutine

end module my_interface

这样你可以确定,从 C 调用的函数的名称是fortranroutine,独立于编译器附加下划线、前置模块名称或将名称转换为小写的约定。因此,您将有一个 C 中的头文件,它应该可以独立运行编译器

extern "C" 
  void fortranroutine(void(int status));
;

【讨论】:

感谢您指出这两种方法。如果从 Fortran 而不是 C 调用 bind(c, name=...) 会破坏它吗? 如果你从 Fortran 调用它,你只需通过它的 Fortran 名称来调用它(fortranRoutinefortranroutine 因为 Fortran 名称与 C 名称不区分大小写相反)。当然,您必须在该范围内导入模块 my_interface。【参考方案2】:

我会将接口定义改为阅读

interface
    subroutine calledfromFortran(status) bind (c)
        use iso_c_binding
        integer(kind = c_int), VALUE :: status
    end subroutine calledfromFortran
end interface

我不确定intent(in) 是什么,但那部分代码看起来不正确。除此之外,其余部分(乍一看)看起来合理且正确。

注意。 ISO C 绑定仅在 FORTRAN 语言的 2003 版本中添加,因此如果您使用的是旧版本,可能值得查看编译器文档以获取详细信息。一些编译器允许 ISO C 绑定,但调用方式可能与我上面显示的略有不同。


编辑。查看intent 关键字后,您可以尝试将intent(in) 与以下类型声明结合使用,该类型声明遵循interface 定义

integer (c_int), parameter :: one = 2

我希望这会有所帮助。

【讨论】:

请参阅this 了解其意图。实际上,这需要保留(遗留代码问题)。我相信 ISO C 绑定语法是正确的。 value 属性似乎有效!现在我只需要确保它不会破坏其他功能... 我很感兴趣。我已经这样做了几次,并使用了始终采用VALUE 关键字的示例代码。你用的是什么编译器?什么版本的语言?我不确定为什么 intent(in) 在这种情况下不起作用;查看我的编辑。 这是遗留代码,所以它的一部分是用从 F77 到 F2003 的所有代码编写的。它还会生成其他人使用的库,因此我试图尽量减少链接到 C 代码所需的更改数量。 另外,如果我理解正确的话,VALUE 属性本质上是通过值而不是地址传递的。有没有办法修改代码的 C 部分以期望地址而不是值?

以上是关于从 Fortran 调用 C 函数,其中 C 函数名称最初是从 C 传入的的主要内容,如果未能解决你的问题,请参考以下文章

Fortran 调用 C:如何获得有效的矢量化函数

在 Fortran 派生类型中保存指向 C 函数的指针

C语言调用Fortran

使用 Fortran 中的内存数据调用 C 代码

无法从 Fortran 90 中返回的 C 浮点指针获取数据

用于从 FORTRAN 代码调用的 C++ 函数调用 C++ 代码的 Cmake 配置文件