【问题标题】:How to bind C's char** argument in Fortran?如何在 Fortran 中绑定 C 的 char** 参数?
【发布时间】:2014-03-24 21:08:42
【问题描述】:

我需要在 Fortran 中绑定一个 C 函数。 C 函数的原型类似于

/* If argv[i] is NULL, then i is the length of argv[] */
int foo(char* argv[]);

我想在 Fortran 子程序 bar 中调用 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

【问题讨论】:

  • 因此,从您的问题的上下文来看,您似乎在询问如何将 C 样式函数翻译 为 FORTRAN 样式函数。这对吗?
  • 你有两次声明argv的维度。
  • 不是将 C 翻译成 Fortran,而是在 Fortran 中调用 C。
  • @JunchaoZhang - 是的,我明白了。我的错误,我已经在下面编辑了我的答案,并包含了一些我希望对您有所帮助的链接。

标签: c binding fortran


【解决方案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。

【讨论】:

  • 我明白了。 Fortran 中的 argv_c 必须是 c_ptr 的数组。对于每个 arv_c(i),我需要进行动态内存分配以将 argv(i) 复制到 argv_c(i) 并放入 NULL 字符。这太可怕了。
  • argv_c 不是 c_ptr's 的数组,它是指向数组的 c_ptr 指针。但是同样,从您的代码中不清楚,c 过程实际上是做什么的?我想它分配了字符串。另请注意,我刚刚更正了代码中的一个错误。
  • 我的意思是,如果它只接受来自 Fortran 的字符串,你可以直接使用 foo(c_loc(argv_c))
  • C 函数只是读取输入 argv,然后做其他事情。换句话说,我希望 Fortran 在调用 C 之前分配临时内存并准备好 argv_c。
  • 在这种情况下使用我的原型,使用 c_loc(argv_c) 调用并使用问题中的代码进行分配,这很简单。但是用于阅读的 char** 很奇怪。
【解决方案2】:

C 声明 char *argv[] 是指向 char 的指针数组。等效的 Fortran 参数声明是 TYPE(C_PTR) :: argv(*),其中数组中的每个元素都是 C 类型字符的 C 地址。

通常的 C 约定是字符实际上是字符数组中的第一个字符(即,您有 C 类型字符数组的 C 地址),并且该 char 数组的长度以 null 结尾(也就是说,你有一个指向 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

【讨论】:

  • 感谢@IanH。你的解释是惊人的。我认为 Fortran 教科书应该添加这个 char** 案例,而不是仅仅谈论 char*。由于 char** 很常见,它与传递 char* 有很大不同。顺便说一句,在我的例子中,argv 是假定大小的,因为界面设计者认为被调用者可以通过查找空行来解码 argv 的长度。
  • 请注意,适合char ** 的内容取决于所指向事物的性质。您问题中代码中的 cmets(以及参数的名称)建议了此答案中的方法 - 它可能并不总是合适的。
【解决方案3】:

标准没有提供编写接受 C 字符串的 Fortran 可互操作例程的简单方法。您必须将虚拟参数声明为单个字符的数组。您可以使用在内部模块 ISO_C_BINDING 中定义的 C_LOC 和 C_F_POINTER 过程的组合,将字符数组参数“强制转换”为指向字符串的 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) 

来自这里(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 到 FORTRAN,反之亦然)HERE如果你可以使用的话

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-13
    • 1970-01-01
    • 2018-06-20
    • 1970-01-01
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    • 2017-03-27
    相关资源
    最近更新 更多