如何从 Fortran 2003 正确调用涉及不透明指针的 C 函数?
How to call C functions involving opaque pointers from Fortran 2003 correctly?
我正在学习 Fortran 2003。作为一项训练任务,我正在尝试从 Fortran 2003 调用使用不透明指针的 C 库:
struct foobar_s;
typedef struct foobar_s *foobar;
foobar foo_create(enum foo, unsigned int);
void foo_destroy(foobar);
我在互联网上找到的大多数建议都告诉我将 foobar
类型描述为 type(c_ptr)
,因此以下内容应该有效:
!foobar foo_create(enum foo, unsigned int);
function foo_create(mode,n) bind(c) ret(foo)
type(c_ptr) :: foo
integer(kind(ENUM_FOO_CONSTANT)), value :: mode
integer(kind=c_int), value :: n
end function
这将 foo_create
声明为返回 void*
而不是 foobar
= struct foobar_s *
,但它仍然适用于现代架构。
我一直在尝试创建一个独特的 Fortran 类型,更接近不透明 C 指针的意图。唯一对我有用的是:
type, bind(c) :: foobar
private
type(c_ptr) :: ptr
end type
对应于:
typedef struct {
void * ptr;
} foobar;
在C端。现在,C 标准的 §6.7.2.1 保证 struct
的开头地址是第一个元素的地址(对吗?)但它的末尾可能有一些填充(但在体系结构上我使用没有,因为指针是自对齐的),所以这整个装置在我的机器上工作:
!foobar foo_create(enum foo, unsigned int);
function foo_create(mode,n) bind(c) ret(foo)
type(foobar) :: foo
integer(kind(ENUM_FOO_CONSTANT)), value :: mode
integer(kind=c_int), value :: n
end function
!void foo_destroy(foobar);
sobroutine foo_destroy(foo) bind(c)
type(foobar), value :: foo
end subroutine
我已验证 Valgrind 对于使用此类型定义从 Fortran 调用 C 函数 foo_create()
和 foo_destroy()
的程序没有显示任何错误。尽管如此,它仍然不能成为确定的证据。
struct { void * ptr }
与 struct foobar_s *
具有相同大小和位模式的假设会被打破吗?这是在 Fortran 2003 中包装不透明 C 指针(并创建不同类型)的最佳方式吗?
C 语言要求引用同一对象或函数的所有声明都具有兼容的类型。鉴于 Fortran 代码的有效 C 声明,您的方法违反了该要求。该要求的一个实际结果是,编译器可以使用不同的方法来 return 声明 struct { void * ptr }
的东西,而不是声明 struct foobar_s *
的东西(例如,聚合可能 return 在一个由传递给函数的隐藏参数指定的区域,指针结果可能 returned 在寄存器中)。这种实现上的差异对您的代码来说将是灾难性的。
Fortran 中的 TYPE(C_PTR) 可用于 void *
和 struct foobar_s*
,对于 Fortran 处理器的配套 C 编译器有一个隐含的要求,即相同的表示方法用于所有 C 对象指针类型(参见 f2003 注释 15.9)。
一种典型的方法是围绕 C 函数编写小型 Fortran 包装程序,适当地设置和引用私有 C_PTR 组件。具有 C_PTR 组件的 Fortran 类型不需要可互操作。如果 Fortran 类型不可互操作,您可以使用现代 Fortran 功能,例如类型扩展和终结器 - foo_destroy 看起来对于从终结器调用很有用。
MODULE Fortran_Wrapper
USE, INTRINSIC :: ISO_C_BINDING, ONLY: xxxxx
...
! Enum definition in here somewhere.
...
PUBLIC :: foobar
PUBLIC :: Create
! Wrapper for a pointer to foobar_s.
TYPE :: foobar
PRIVATE
TYPE(C_PTR) :: ptr = C_NULL_PTR
CONTAINS
FINAL :: final
END TYPE foobar
...
CONTAINS
! Wrapper around foo_create, exposed to Fortran client code.
FUNCTION Create(mode, n) RESULT(obj)
INTEGER(KIND(ENUM_FOO_CONSTANT)), INTENT(IN) :: mode
! Perhaps the next argument is taken as default integer, and
! you do kind conversion inside this wrapper.
INTEGER(C_INT), INTENT(IN) :: n
TYPE(foobar) :: obj
INTERFACE
FUNCTION foo_create(mode, n) BIND(C, NAME='foo_create')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT, C_PTR
IMPORT :: ENUM_FOO_CONSTANT
IMPLICIT NONE
INTEGER(KIND(ENUM_FOO_CONSTANT)), VALUE :: mode
INTEGER(KIND=C_INT), VALUE :: n
TYPE(C_PTR) :: foo_create
END FUNCTION foo_create
END INTERFACE
obj%ptr = foo_create(mode, n)
END FUNCTION Create
! Use a finalizer to do automatic cleanup off the C structures.
! (Impure elemental is F2008.)
IMPURE ELEMENTAL SUBROUTINE final(obj)
TYPE(foobar), INTENT(INOUT) :: obj
INTERFACE
SUBROUTINE foo_destroy(obj) BIND(C, NAME='foo_destroy')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
IMPLICIT NONE
TYPE(C_PTR), VALUE :: obj
END SUBROUTINE foo_destroy
END INTERFACE
IF (C_ASSOCIATED(obj%ptr)) CALL foo_destroy(obj%ptr)
END SUBROUTINE final
END MODULE Fortran_Wrapper
(注意问题中的Fortran接口体缺少两个伪参数定义上的VALUE属性,否则对应的C原型为foobar foo_create(enum foo*, unsigned int*)
。)
我正在学习 Fortran 2003。作为一项训练任务,我正在尝试从 Fortran 2003 调用使用不透明指针的 C 库:
struct foobar_s;
typedef struct foobar_s *foobar;
foobar foo_create(enum foo, unsigned int);
void foo_destroy(foobar);
我在互联网上找到的大多数建议都告诉我将 foobar
类型描述为 type(c_ptr)
,因此以下内容应该有效:
!foobar foo_create(enum foo, unsigned int);
function foo_create(mode,n) bind(c) ret(foo)
type(c_ptr) :: foo
integer(kind(ENUM_FOO_CONSTANT)), value :: mode
integer(kind=c_int), value :: n
end function
这将 foo_create
声明为返回 void*
而不是 foobar
= struct foobar_s *
,但它仍然适用于现代架构。
我一直在尝试创建一个独特的 Fortran 类型,更接近不透明 C 指针的意图。唯一对我有用的是:
type, bind(c) :: foobar
private
type(c_ptr) :: ptr
end type
对应于:
typedef struct {
void * ptr;
} foobar;
在C端。现在,C 标准的 §6.7.2.1 保证 struct
的开头地址是第一个元素的地址(对吗?)但它的末尾可能有一些填充(但在体系结构上我使用没有,因为指针是自对齐的),所以这整个装置在我的机器上工作:
!foobar foo_create(enum foo, unsigned int);
function foo_create(mode,n) bind(c) ret(foo)
type(foobar) :: foo
integer(kind(ENUM_FOO_CONSTANT)), value :: mode
integer(kind=c_int), value :: n
end function
!void foo_destroy(foobar);
sobroutine foo_destroy(foo) bind(c)
type(foobar), value :: foo
end subroutine
我已验证 Valgrind 对于使用此类型定义从 Fortran 调用 C 函数 foo_create()
和 foo_destroy()
的程序没有显示任何错误。尽管如此,它仍然不能成为确定的证据。
struct { void * ptr }
与 struct foobar_s *
具有相同大小和位模式的假设会被打破吗?这是在 Fortran 2003 中包装不透明 C 指针(并创建不同类型)的最佳方式吗?
C 语言要求引用同一对象或函数的所有声明都具有兼容的类型。鉴于 Fortran 代码的有效 C 声明,您的方法违反了该要求。该要求的一个实际结果是,编译器可以使用不同的方法来 return 声明 struct { void * ptr }
的东西,而不是声明 struct foobar_s *
的东西(例如,聚合可能 return 在一个由传递给函数的隐藏参数指定的区域,指针结果可能 returned 在寄存器中)。这种实现上的差异对您的代码来说将是灾难性的。
TYPE(C_PTR) 可用于 void *
和 struct foobar_s*
,对于 Fortran 处理器的配套 C 编译器有一个隐含的要求,即相同的表示方法用于所有 C 对象指针类型(参见 f2003 注释 15.9)。
一种典型的方法是围绕 C 函数编写小型 Fortran 包装程序,适当地设置和引用私有 C_PTR 组件。具有 C_PTR 组件的 Fortran 类型不需要可互操作。如果 Fortran 类型不可互操作,您可以使用现代 Fortran 功能,例如类型扩展和终结器 - foo_destroy 看起来对于从终结器调用很有用。
MODULE Fortran_Wrapper
USE, INTRINSIC :: ISO_C_BINDING, ONLY: xxxxx
...
! Enum definition in here somewhere.
...
PUBLIC :: foobar
PUBLIC :: Create
! Wrapper for a pointer to foobar_s.
TYPE :: foobar
PRIVATE
TYPE(C_PTR) :: ptr = C_NULL_PTR
CONTAINS
FINAL :: final
END TYPE foobar
...
CONTAINS
! Wrapper around foo_create, exposed to Fortran client code.
FUNCTION Create(mode, n) RESULT(obj)
INTEGER(KIND(ENUM_FOO_CONSTANT)), INTENT(IN) :: mode
! Perhaps the next argument is taken as default integer, and
! you do kind conversion inside this wrapper.
INTEGER(C_INT), INTENT(IN) :: n
TYPE(foobar) :: obj
INTERFACE
FUNCTION foo_create(mode, n) BIND(C, NAME='foo_create')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT, C_PTR
IMPORT :: ENUM_FOO_CONSTANT
IMPLICIT NONE
INTEGER(KIND(ENUM_FOO_CONSTANT)), VALUE :: mode
INTEGER(KIND=C_INT), VALUE :: n
TYPE(C_PTR) :: foo_create
END FUNCTION foo_create
END INTERFACE
obj%ptr = foo_create(mode, n)
END FUNCTION Create
! Use a finalizer to do automatic cleanup off the C structures.
! (Impure elemental is F2008.)
IMPURE ELEMENTAL SUBROUTINE final(obj)
TYPE(foobar), INTENT(INOUT) :: obj
INTERFACE
SUBROUTINE foo_destroy(obj) BIND(C, NAME='foo_destroy')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
IMPLICIT NONE
TYPE(C_PTR), VALUE :: obj
END SUBROUTINE foo_destroy
END INTERFACE
IF (C_ASSOCIATED(obj%ptr)) CALL foo_destroy(obj%ptr)
END SUBROUTINE final
END MODULE Fortran_Wrapper
(注意问题中的Fortran接口体缺少两个伪参数定义上的VALUE属性,否则对应的C原型为foobar foo_create(enum foo*, unsigned int*)
。)