我需要在Fortran中绑定一个C函数。 C函数的原型类似于
/* If argv[i] is NULL, then i is the length of argv[] */
int foo(char* argv[]);
我想在Fortran子程序栏中调用foo
! If argv(i) is a blank string, then i is the length of argv.
! Constant MAX_STRING_LEN is the maximal length of any possible element in argv.
subroutine bar(argv)
character(len=*), intent(in) :: argv(*)
character(kind=c_char), dimension(*), allocatable :: argv_c(*) !???
! convert argv to argv_c element by element, something like
! allocate(argv_c(len_trim(argv(i)) + 1) ! Plus 1 to put C_NULL_CHAR
! assign argv(i) to argv_c(i)
ierror = foo(argv_c)
! deallocate each argv_c(i)
end subroutine bar
我想知道如何为foo声明原型。我设计了这个,但我不确定它是否正确。这个argv是否可与char **互操作?
function foo(argv) bind(c, name="foo")
character(kind=c_char), dimension(*), intent(in) :: argv(*)
end function
答案 0 :(得分:2)
C声明char *argv[]
是指向char的指针数组。等效的Fortran参数声明是TYPE(C_PTR) :: argv(*)
,其中数组中的每个元素都是C类字符的C地址。
通常的C约定是字符实际上是字符数组中的第一个字符(即,您具有C类字符数组的C地址),并且该字符数组的长度为空终止(即所有你有一个指向C字符串的指针数组)。 C代码中的注释还表示指针数组的大小由NULL的最终数组元素指示。这与C中argv
个参数的约定一致。
为了能够在Fortran中找到对象的C地址,该对象必须具有TARGET属性。评估表达式的结果没有目标属性。
如上所述,bar
子例程不知道argv
虚拟数组的大小(它是假设的大小参数)。但是,如果argv
伪参数可能是假定形状,则可以执行以下操作:
SUBROUTINE bar(argv)
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_LOC, C_CHAR, C_INT, &
C_NULL_CHAR, C_NULL_PTR
! The input to bar (?) that needs to be converted for use in the C function.
CHARACTER(*), INTENT(IN) :: argv(:)
! The array of C pointers.
TYPE(C_PTR) :: argv_c(SIZE(argv) + 1)
! A local type to simplify memory management.
TYPE string
CHARACTER(LEN=:,KIND=C_CHAR), ALLOCATABLE :: item
END TYPE string
! Temporary arrays of character. Need to have TARGET for C_LOC.
TYPE(string), TARGET :: tmp(SIZE(argv))
! Utility index.
INTEGER :: i
! The interface for the C function
INTERFACE
FUNCTION foo(argv) BIND(C, NAME='foo')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR, C_INT
IMPLICIT NONE
! INTENT(IN) here assuming foo doesn't modify the array of pointers.
TYPE(C_PTR), INTENT(IN) :: argv(*)
INTEGER(C_INT) :: foo
END FUNCTION foo
END INTERFACE
INTEGER(C_INT) :: ierror
DO i = 1, SIZE(argv)
! This may involve kind conversion.
tmp(i)%item = TRIM(argv(i)) // C_NULL_CHAR
argv_c(i) = C_LOC(tmp(i)%item)
END DO
! Size of the argv array on the C side indicated by a NULL.
argv_c(SIZE(argv_c)) = C_NULL_PTR
ierror = foo(argv_c)
...
! memory allocated for the components of the tmp object (and hence
! the things pointed at by the argv_c array) will go away when this
! subroutine terminates.
END SUBROUTINE bar
答案 1 :(得分:1)
类似的问题和答案:Creating a FORTRAN interface to a C function that returns a char*有点难以定位(至少对我而言)。
由于char**
function foo(argv) bind(c, name="foo")
type(c_ptr) :: argv !note no `value` here
! what is the `intent` to put here?
end function
普通数组参数只传递你不想要的char*
。
首先尝试:
ierror = foo(c_loc(argv_c))
如果C部分没有分配它,上面会有效,它可能会这样做。问题是获得字符串的长度。
type(c_ptr) :: argv_c_ptr
character(kind=c_char),pointer :: argv_c(:)
ierror = foo(argv_c_ptr)
现在你必须找出数组的长度,我会调用C程序strlen(参见http://fortranwiki.org/fortran/show/c_interface_module中的C_strlen)。之后你可以打电话:
call c_f_pointer(argv_c_ptr, argv_c, [len])
不要忘记正确释放C分配的字符串,不要使用Fortran`deallocate。
答案 2 :(得分:0)
该标准没有提供一种简单的方法来编写从C接受字符串的Fortran可互操作例程。您必须将伪参数声明为单个字符数组。您可以使用C_LOC和C_F_POINTER过程的组合(均在内部模块ISO_C_BINDING中定义)将字符数组参数“强制转换”为指向字符串的Fortran指针。一个例子如下:
subroutine Interop_sub(arg) BIND(C)
USE, INTRINSIC :: ISO_C_BINDING
CHARACTER, DIMENSION(*) :: ARG
CHARACTER(1000), POINTER :: LCL_ARG ! Local pointer to argument
CALL C_F_POINTER(C_LOC(ARG), LCL_ARG)
HERE 是BIND(C)的附加链接
还有一些翻译概念(C to FORTRAN,反之亦然) HERE 如果你可以使用它们