【发布时间】: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;我不确定我是否理解您的观点,而且我引用的代码显然有点过于简单化了。我将编辑问题并提供更多详细信息。
标签: c interop fortran fortran-iso-c-binding derived-types