如何在Fortran中绑定C的char **参数?

时间:2014-03-24 21:08:42

标签: c binding fortran

我需要在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

3 个答案:

答案 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(http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/fortran-mac/GUID-ECF6FF36-1397-43D8-88BB-CCD2F6108DA5.htm

HERE 是BIND(C)的附加链接

还有一些翻译概念(C to FORTRAN,反之亦然) HERE 如果你可以使用它们