从 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 名称来调用它(fortranRoutine
或 fortranroutine
因为 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 传入的的主要内容,如果未能解决你的问题,请参考以下文章