在 Fortran 派生类型中保存指向 C 函数的指针
Posted
技术标签:
【中文标题】在 Fortran 派生类型中保存指向 C 函数的指针【英文标题】:Holding a pointer to a C function inside a Fortran derived type 【发布时间】:2014-02-06 02:45:15 【问题描述】:我有一个从 C 程序调用的 Fortran DLL,我的一个程序需要定期调用 C 程序提供的回调函数。我目前让它以其“简单”形式运行良好,但我希望能够将我的回调指针存储在派生类型中,以便可以更轻松地在我的 Fortran 代码中传递它。到目前为止,我尝试过的任何方法似乎都不起作用。
首先,这是我目前拥有的,这确实工作:
从C(OK,实际上是C++)程序开始,回调的头部原型是:
typedef void (*fcb)(void *)
Fortran 调用的原型是:
extern "C" __declspec(dllexport) int fortran_function(int n,
uchar *image_buffer,
fcb callback,
void *object);
实际的回调函数是:
void callback(void* pObject)
// Cast the void pointer back to the appropriate class type:
MyClass *pMyObject = static_cast<MyClass *>(pObject);
pMyObject -> updateImageInGUI();
从 C++ 调用 Fortran 代码是:
int error = fortran_function(m_image.size(), m_image.data, callback, this);
其中m_image
是一个图像数据数组,它是当前对象的成员属性。发生的情况是 C++ 将原始图像数据传递给 Fortran DLL 并要求 Fortran 对其进行处理,并且由于这需要很长时间,因此 Fortran 会定期更新图像缓冲区并调用回调以刷新 GUI。无论如何,转到 Fortran 方面,我们为 C 回调定义一个接口:
abstract interface
subroutine c_callback(c_object) bind(c)
use, intrinsic :: iso_c_binding
type(c_ptr), intent(in) :: c_object
end subroutine c_callback
end interface
并这样定义我们的主 Fortran 例程:
integer(c_int) fortran_function(n, image, callback, c_object) &
bind(c, name='fortran_function')
integer(c_int), value :: n
integer(4), intent(inout), dimension(n) :: image
procedure(c_callback) :: callback
type(c_ptr), intent(in) :: c_object
在主程序中的某个地方,我们称之为子程序foo
:
call foo(data, callback, c_object)
...foo
定义为:
subroutine foo(data, callback, c_object)
type(my_type), intent(inout) :: data
procedure(c_callback) :: callback
type(c_ptr), intent(in) :: c_object
...
call callback(c_object)
...
end function foo
正如我所说,所有这些都运作良好,并且已经这样做了很长时间。
现在对于我尝试过但不起作用的事情:
天真的方法,只是将参数复制到结构的字段中
我希望这能奏效,因为我所做的只是将原始元素复制到一个结构中而无需修改。 C 端没有任何变化,Fortran 主函数的定义和c_callback
的抽象接口也没有变化。我所做的只是创建一个新的 Fortran 派生类型:
type :: callback_data
procedure(c_callback), pointer, nopass :: callback => null()
type(c_ptr) :: c_object
end type callback_data
然后在我的主函数中,我用从 C 应用程序接收到的值填充它:
data%callback_data%callback => callback
data%callback_data%c_object = c_object
call foo(data)
子例程 foo 已稍作修改,现在它在结构中查找回调和 C 对象:
subroutine foo(data)
type(my_augmented_type), intent(inout) :: data
...
call data%callback_data%callback(data%callback_data%c_object)
...
end function foo
这在调用时失败,并显示“访问冲突读取位置 0xffffffffffffffff”。
使用更多 iso_c_binding 功能的复杂方法
C 端也没有任何变化,但我修改了主函数的 Fortran 端以接收回调作为 c_funptr
:
integer(c_int) fortran_function(n, image, callback, c_object) &
bind(c, name='fortran_function')
integer(c_int), value :: n
integer(4), intent(inout), dimension(n) :: image
type(c_funptr), intent(in) :: callback
type(c_ptr), intent(in) :: c_object
我像以前一样定义了subroutine c_callback
的抽象接口,尽管我已经尝试了保留bind(c)
的一部分并省略它。调用子程序foo
的主函数中的代码现在是:
call c_f_procpointer(callback, data%callback_data%callback)
data%callback_data%c_object = c_object
call foo(data)
...子例程 foo 本身仍然像前面的例子一样定义。
不幸的是,这与前面的示例完全相同。
我假设有一个正确的语法来实现我在这里想要实现的目标,我将非常感谢任何建议。
【问题讨论】:
您正在通过引用传递一个意图(输入)指针。这是故意的吗?由于您所说的处理程序的方式,您的第一个“工作”方法和天真的方法都不符合。请显示 a) C 调用的 Fortran 过程的 C 原型 b) 对 fortran 的实际 C 调用(传递回调),c) C 调用的 Fortran 过程的接口,以及 d) 的原型C 回调函数。 谢谢 IanH;我不确定我是否理解您的观点,而且我引用的代码显然有点过于简单化了。我将编辑问题并提供更多详细信息。 【参考方案1】:Fortran 过程中具有BIND(C)
属性但没有 VALUE 参数的虚拟参数在 C 端等效于指针参数(这与通过引用传递事物的通常 Fortran 约定大体一致)。因此,如果在 Fortran 端有 INTEGER(C_INT) :: a
(无值属性),则在 C 端相当于 int *a
。
也许这很明显,但它有一个令人惊讶的结果 - 如果你有 TYPE(C_PTR) :: p
,那相当于 void **p
- C_PTR 是一个指针,所以一个没有值传递的 C_PTR 是一个指向指针的指针。鉴于此,您的回调接口已失效(您需要添加VALUE
)。
Fortran 中指向函数的 C 指针(在 C 中是无括号的函数名)在类型意义上的可互操作模拟是 TYPE(C_FUNPTR)
。关于缺少 VALUE 属性和 C_PTR
的相同考虑适用 - 声明为 TYPE(C_FUNPTR) :: f
的参数是指向函数指针的指针。鉴于此以及您对 Fortran 的 C 端调用,对应于函数指针的参数应具有 VALUE
属性。
Fortran 过程指针恰好工作的事实只是 C 函数指针和 Fortran 过程指针的底层实现以及 Fortran 过程指针的传递方式的巧合(并不令人非常惊讶)。
总之,您的 Fortran 程序可能需要一个如下所示的界面:
integer(c_int) fortran_function(n, image, callback, c_object) &
bind(c, name='fortran_function')
integer(c_int), value :: n
integer(c_signed_char), intent(inout), dimension(n) :: image
type(c_funptr), intent(in), value :: callback
type(c_ptr), intent(in), value :: c_object
(您在原始代码中对图像数组的声明似乎误入歧途 - 也许上面是合适的,也许不是)
你的C回调接口声明需要有一个接口:
abstract interface
subroutine c_callback(c_object) bind(c)
use, intrinsic :: iso_c_binding
implicit none
type(c_ptr), intent(in), value :: c_object
end subroutine c_callback
end interface
(正如过去几个月在英特尔论坛上所讨论的那样(你去哪里了?),当前的 ifort 可能在处理 C_PTR
和 VALUE
方面存在问题。)
【讨论】:
IanH,你是绅士和学者!这解决了问题。我所做的是重新实现我在原始问题中描述的“复杂”方法,但在您建议的地方添加“价值”限定词。现在可以了。 顺便说一句,英特尔论坛出于某种原因不允许我在那里发布任何内容:我只是被拒绝为垃圾邮件发送者! Intel Premier Support 正在为我调查这个问题。我错过了最近关于 C_PTR 和 VALUE 的任何讨论,尽管我在论坛中搜索了有关 C_FUNPTR 的任何提及,但没有发现任何相关内容。值得一提的是,我刚刚使用 ifort 编译器的 14.0.1.139 版本进行了成功的测试。以上是关于在 Fortran 派生类型中保存指向 C 函数的指针的主要内容,如果未能解决你的问题,请参考以下文章