我有一个从 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 本身仍然像前面的例子一样定义。
不幸的是,这与前面的示例完全相同。
我假设有一个正确的语法来实现我在这里想要实现的目标,我将非常感谢任何建议。