Eos*_*ern 6 c interop fortran derived-types fortran-iso-c-binding
我有一个从C程序调用的Fortran DLL,我的一个程序需要定期调用由C程序提供的回调函数.我目前在它的'简单'形式下运行良好,但我希望能够将我的回调指针存储在派生类型中,以便它可以更容易地在我的Fortran代码中传递.到目前为止,我尝试过的任何东西似乎都没有用.
首先,这是我目前所拥有的,这确实有效:
从C(OK,实际上是C++)程序开始,回调的头部原型是:
typedef void (*fcb)(void *)
Run Code Online (Sandbox Code Playgroud)
Fortran调用的原型是:
extern "C" __declspec(dllexport) int fortran_function(int n,
uchar *image_buffer,
fcb callback,
void *object);
Run Code Online (Sandbox Code Playgroud)
实际的回调函数是:
void callback(void* pObject)
{
// Cast the void pointer back to the appropriate class type:
MyClass *pMyObject = static_cast<MyClass *>(pObject);
pMyObject -> updateImageInGUI();
}
Run Code Online (Sandbox Code Playgroud)
并且从C++调用Fortran代码是:
int error = fortran_function(m_image.size(), m_image.data, callback, this);
Run Code Online (Sandbox Code Playgroud)
其中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
Run Code Online (Sandbox Code Playgroud)
并定义我们的主要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
Run Code Online (Sandbox Code Playgroud)
在主程序中的某个地方,我们称之为子程序foo
:
call foo(data, callback, c_object)
Run Code Online (Sandbox Code Playgroud)
......其中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
Run Code Online (Sandbox Code Playgroud)
正如我所说,所有这些都运作良好并且已经这么做了很长时间.
现在我已经尝试但不起作用的东西:
天真的方法,只是将参数复制到结构的字段中
我希望这可以工作,因为我所做的就是将原始元素复制到一个没有修改的结构中.C方面没有任何变化,主要的Fortran函数和抽象接口也没有变化c_callback
.我所做的就是创建一个新的Fortran派生类型:
type :: callback_data
procedure(c_callback), pointer, nopass :: callback => null()
type(c_ptr) :: c_object
end type callback_data
Run Code Online (Sandbox Code Playgroud)
然后在我的main函数中,我用从C应用程序收到的值填充它:
data%callback_data%callback => callback
data%callback_data%c_object = c_object
call foo(data)
Run Code Online (Sandbox Code Playgroud)
子程序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
Run Code Online (Sandbox Code Playgroud)
这在通话时失败,出现"访问冲突读取位置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
Run Code Online (Sandbox Code Playgroud)
我subroutine c_callback
像以前一样定义了抽象接口,尽管我已经尝试将其中的bind(c)
一部分留在其中并省略它.调用子例程的main函数中的代码foo
现在是:
call c_f_procpointer(callback, data%callback_data%callback)
data%callback_data%c_object = c_object
call foo(data)
Run Code Online (Sandbox Code Playgroud)
...子程序foo本身仍然如上例所定义.
不幸的是,这与前一个例子完全相同.
我假设有一个正确的语法来实现我在这里想要实现的目标,并且我非常感谢任何建议.
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
Run Code Online (Sandbox Code Playgroud)
(您在原始代码中对图像数组的声明似乎误入歧途 - 也许上面的内容是合适的,也许不是)
并且您对 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
Run Code Online (Sandbox Code Playgroud)
(正如过去几个月在英特尔论坛上讨论的那样(你去哪儿了?),当前的 ifort 可能在处理 和 方面存在问题C_PTR
。VALUE
)