字符串的 C 和 Fortran 互操作性
C and Fortran interoperability for strings
我正在尝试从 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
I am trying to call some Fortran77 code from C but I didn't find the proper way of passing a C char array.
我曾经使用过的所有 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
,否则你需要将它们与复制输入(/复制输出)结合起来,以便在类型之间转换字符。使用前一个接口,很明显如何将数组复制到长度大于 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_module 的 C_F_string_ptr
子例程)
另一方面,如果您可以(或者无论如何做)依赖默认字符类型c_char
,那么您还有一个额外的选择。您可以有用地使字符数组(例如第一个示例中的参数)与默认种类和长度大于 1 的标量字符对象相关联。特别是,如果包装函数的伪参数是一个具有假定长度的标量字符,或者固定长度不超过数组元素的数量,那么您可以依靠参数关联将其与包装器中的字符数组相关联。换句话说,在那种情况下,您可以将数组作为实际参数传递。
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
我正在尝试从 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
I am trying to call some Fortran77 code from C but I didn't find the proper way of passing a C char array.
我曾经使用过的所有 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
,否则你需要将它们与复制输入(/复制输出)结合起来,以便在类型之间转换字符。使用前一个接口,很明显如何将数组复制到长度大于 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_module 的 C_F_string_ptr
子例程)
另一方面,如果您可以(或者无论如何做)依赖默认字符类型c_char
,那么您还有一个额外的选择。您可以有用地使字符数组(例如第一个示例中的参数)与默认种类和长度大于 1 的标量字符对象相关联。特别是,如果包装函数的伪参数是一个具有假定长度的标量字符,或者固定长度不超过数组元素的数量,那么您可以依靠参数关联将其与包装器中的字符数组相关联。换句话说,在那种情况下,您可以将数组作为实际参数传递。
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