【问题标题】:C and Fortran interoperability for strings字符串的 C 和 Fortran 互操作性
【发布时间】:2016-12-20 16:43:03
【问题描述】:

我正在尝试从 C 调用一些 Fortran 代码,但我没有找到传递 C 字符数组的正确方法。

   SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
     USE ISO_C_BINDING
     IMPLICIT NONE
     CHARACTER*(C_CHAR)      c_message
     CHARACTER*(256)         f_message
     CALL C_F_POINTER( C_LOC(c_message), f_message)
     WRITE(*,*) f_message,LEN(f_message)
   END

BIND(C) 强制参数 c_message 的大小为 1。如何访问 c_message 字符串的其他元素?

编译器:GCC 4.8.2

【问题讨论】:

  • 我很困惑。 Fortran 77(和 Fortran 90)没有 F2K C 互操作功能。你的代码 Fortran 77 是怎样的?
  • 我也很困惑。 Gfortran 将代码编译为 Fortran 2008,bind(C) 接口为 Fortran 2003。
  • 你必须显示你正在连接的子程序和你得到的错误信息。这很重要。
  • 例如,CHARACTER*(C_CHAR) 在语法上不正确。您应该只使用CHARACTER(kind=C_CHAR) 而不要关心您的代码是 F77 还是 F 其他。大多数 Fortran 77 代码都是有效的 Fortran 2008 代码。
  • 没错,gfortran 使用的是 f2008 标准,并且没有 ISO_C_BINDING 和 f95 标准

标签: c arrays fortran fortran-iso-c-binding


【解决方案1】:

我试图从 C 中调用一些 Fortran77 代码,但我没有找到传递 C 字符数组的正确方法。

我曾经使用过的所有 Fortran 77 实现都提供了与 C 互操作的特定于实现的机制。通常这些涉及 C 端了解 Fortran 编译器采用的名称修饰和参数传递约定,并在界面。 GCC 系统的约定并不难使用。

不过,您似乎对 Fortran 2003 中引入的 Fortran/C 互操作工具感兴趣。假设您的 Fortran 77 代码也符合 Fortran 2003(或可以这样做),那么应该可以在 Fortran 2003 中编写可互操作的包装器。但是请注意,C 互操作工具不提供(直接)用于 Fortran 变量或 character 类型的子程序参数的互操作性,长度大于 1 或与 c_char 不同的类型。另一方面,请记住,Fortran 字符对象的 length 与字符数组的 dimension(s) 不同。

您有几个可互操作的替代方案来提供面向 C 的接口,通过该接口接受 C char 数组。也许最清楚的方法是接受一个 Fortran 假定大小的 character 数组:

SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
    USE ISO_C_BINDING
    IMPLICIT NONE
    CHARACTER(kind=C_CHAR), dimension(*), intent(in) :: c_message
    ! ...
END

最可能的选择是直接接受 C 的数组指针:

SUBROUTINE My_F_Code (c_message)  BIND(C, NAME='my_f_code')
    USE ISO_C_BINDING
    IMPLICIT NONE
    type(C_PTR), value :: c_message
    ! ...
END

如果 C 端数组指针可能为空,则需要后者。如果不能依赖数组以空值终止,则两者都需要传递显式长度。

无论如何,如果您最终想要一个长度大于 1 的 Fortran character 变量(而不是维度大于 1 的数组),那么 interoperable 接口无法提供那直接——这些类型不属于 C 互操作规定适用的类型。除非您可以依赖默认字符类型为 c_char,否则您需要将它们与 copy-in(/ copy-out) 结合起来,以便在类型之间转换字符。使用前一个接口,如何将数组复制到长度大于 1 的 Fortran 标量 character 应该很明显。对于指针变体,使用类似这样的转换函数可能很有用:

subroutine C_string_ptr_to_F_string(C_string, F_string)
    use ISO_C_BINDING
    type(C_PTR), intent(in) :: C_string
    character(len=*), intent(out) :: F_string
    character(len=1, kind=C_CHAR), dimension(:), pointer :: p_chars
    integer :: i
    if (.not. C_associated(C_string)) then
      F_string = ' '
    else
      call C_F_pointer(C_string, p_chars, [huge(0)])
      do i = 1, len(F_string)
        if (p_chars(i) == C_NULL_CHAR) exit
        F_string(i:i) = p_chars(i)
      end do
      if (i <= len(F_string)) F_string(i:) = ' '
    end if
end subroutine

(源自 Fortran Wiki 的 C_interface_moduleC_F_string_ptr 子例程)

另一方面,如果您可以(或无论如何)依赖默认字符类型为c_char,那么您还有其他选择。您可以使字符数组(例如第一个示例中的参数)与默认类型和长度大于 1 的标量字符对象相关联。特别是,如果包装函数的虚拟参数是具有假定长度的标量字符,或者具有不超过数组元素数量的固定长度,那么您可以依靠参数关联将其与包装器中的字符数组相关联。换句话说,在这种情况下,您可以将数组作为实际参数传递。

【讨论】:

  • 关于最后一段,只有在需要种类转换时才需要复制。如果需要,可以将 C_CHAR 类型的延迟长度指针与 C 字符串占用的相同存储序列相关联。通过简单的检查,wiki 页面没有使用延迟长度字符,可能是因为在编写它时缺乏 gfortran 支持。
  • @IanH,感谢您的观察。我已经修改并扩展了我的 cmets 以讨论关联而不是复制,这确实是可能的首选。
【解决方案2】:
  SUBROUTINE FCODE(STR, N)
  CHARACTER*(*) STR
  INTEGER N
  WRITE(*,*), STR, N
  END


  void foo(char *str)
  {
      int N = 10;
      printf("Calling fortran\n");
      fcode_(str,  &N, strlen(str)); 
  }

您需要将字符串长度作为隐藏参数传递。 在这里查看我的文章 http://www.malcolmmclean.site11.com/www/MpiTutorial/CandFortran77.html

【讨论】:

  • 给定代码假定 Fortran 编译器以特定方式修饰其符号(小写,尾随下划线)。这种约定远非无处不在。隐藏字符长度的约定也可能有所不同。
  • 它们确实有所不同。在 gfortran 中将其从 int 更改为 size_t 的补丁已经在桌面上。我假设大量无知的提问者使用了这样的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多