【问题标题】:Return string from Fortran to C++将字符串从 Fortran 返回到 C++
【发布时间】:2017-06-07 12:32:26
【问题描述】:

我在 C++ 中有以下函数调用:

int strLength = 20;
char* name;

getName(name, strLength);

printf("name: %s\n", name);

在 Fortran 中:

subroutine getName(name) bind (c, name='GETNAME')
use,intrinsic :: iso_c_binding
implicit none
character, intent(out) :: name

name = 'Martin'
end subroutine getName

当我执行 C++ 例程时,输出为:name: M。现在,我想这是因为 character, intent(out) :: name 声明了大小为 1 的 name 变量,但是如果我将声明更改为:character(len=6), intent(out) :: name,我会收到此错误消息:Error: Character argument 'name' at (1) must be length 1 because procedure 'getname' is BIND(C)。我也试过这个:character(len=6,kind=c_char), intent(out) :: name,同样的错误信息。最后,我尝试了这个声明:character(c_char),dimension(6), intent(out) :: name,它编译但产生了这个结果:name: MMMMMM

我的问题归结为:如何将字符串从 Fortran 返回到 C++?

【问题讨论】:

  • 你为什么不实际尝试 return 它而不是传递一个指针作为参数?或者,如果您想将其作为参数传递,请确保它是通过引用完成的,否则您需要为字符串分配内存。
  • 在使用了一些 google-fu 之后,我的印象是,如果你想将一个字符串从 Fortran 返回到 C++,这或多或少应该是这样做的,但显然我错过了一些东西。这不会崩溃,但是当使用我列出的name 的最后一个声明时,似乎name 输出变量只填充了我分配给它的'Martin' 字符串的第一个字母。
  • This question 密切相关。
  • 确实如此,但据我所知,他所做的与我的示例中的几乎相同,只是他没有分配并且字符串是输入而不是输出。
  • 好吧,我最初将其标记为 C/C++,但有人将其编辑为 C :P

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


【解决方案1】:

这种基于 Fortran 函数的方法简洁明了,非常适合在 C 例程中尚未设置字符串(或为其分配内存)的情况。请注意,您不需要传入字符串长度参数,或使用假定大小的数组来构建/返回字符串值。1

使用了 Fortran 可分配字符串常量,因此该函数可重用于任何字符串。

一个整数参数也被传递到 Fortran 函数中以演示,例如,您可以如何指示所需的响应应该是什么。

请注意,在本例中,“intent(out)”用于指示整数参数在传入之前不需要定义,但可以在返回之前对其进行更新。因此,您可以修改其值并将其返回给调用程序,以便将其用作“返回码”。

Fortran 函数

! f_string.f90
! Called from C routine as:  `myString = get_string(rc)`

function get_string(c_rc) bind(c, name='get_string')
    use, intrinsic :: iso_c_binding
    implicit none
    integer(c_int), intent(out) :: c_rc           ! <- Pass by reference; acts as return code in this example.
    type(c_ptr) :: get_string                     ! <- C_PTR to pass back to C
    character(len=:), allocatable, target, save :: fortstring   ! <- Allocatable/any length is fine

    fortstring = "Append C_NULL_CHAR to any Fortran string constant."//C_NULL_CHAR
    get_string = c_loc(fortstring)                ! <- C_LOC intrinsic gets loc of our string.
    c_rc = 1                                      ! <- Set the return code value.
end function get_string

C 程序

// c_string.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *get_string(int *rc);              // <- The Fortran function signature.

int main(){
    int rc;                             // <- No value set.
    char *mynameptr;                    // <- Just a plain ol' char *, no memory allocation needed.

    mynameptr = get_string(&rc);

    printf("mynameptr=%s\n", mynameptr);
    printf("len =%d\n", strlen(mynameptr));
    printf("rc  =%d\n", rc);
    return rc;
}

编译、调用、输出:

ifort /c f_string.f90
icl c_string.c /link f_string.obj
c_string.exe
# example output: 
# mynameptr=Append C_NULL_CHAR to any Fortran string constant.
# len =48
# rc  =1

1这也可以干净地编译而没有警告(the OP's solution 确实会发生这种情况,而我在其 cmets 中建议的修改没有)。

【讨论】:

  • 这似乎是一个不错的解决方案。不幸的是,我在一个遗留系统中工作,我认为这需要进行很多更改才能实现,但如果从头开始我肯定会考虑这个!
  • 这不符合代码:c_loc(fortstring) 表示fortstring 必须是指针或目标。此外,fortstring 是一个未保存的可分配变量,因此在过程完成时被释放,这意味着它不是 C 指针的有用目标。
  • @francescalus 添加了目标和保存属性以实现完全合规性/可移植性。谢谢。我想This Intel documentation 也应该用target attr 更新(滚动到底部并找到GetFortranWords)。
  • 我几乎但不完全相信在这种情况下需要save,但它可能不会受到伤害!
  • 这是我在被 Fortran-to-C 恶魔传递字符串折磨了一整天后找到的最好的解决方案!
【解决方案2】:

问题在于将等级为n 的字符数组分配给长度为n 的字符标量,这在this 问题中得到解决。如下所示更改 Fortran 例程解决了该问题。

解决方案

由于我在旧系统中工作,因此我不得不采用以下所示的解决方案。显然它并不完全相同,但它仍然显示了大致的结构。如果其他人也应该采用此解决方案,您可能应该遵循 Matt P 评论中的建议。

虽然这是我寻求的解决方案,但我觉得 Matt P 的答案是对问题标题中所述一般问题的更好解决方案,这就是我接受它作为答案的原因。

C++

int strLength = 20;
char* name = new char[strLength];   
getName(name, strLength);    
printf("name: %s\n", name);

Fortran

subroutine getName(name) bind (c, name='GETNAME')
use,intrinsic :: iso_c_binding
implicit none
character(c_char),dimension(20), intent(out) :: name
character(20) :: fName

fName = 'Martin'//c_null_char

do j=1,len(fName )
  name(j) = fName (j:j)  
enddo

end subroutine getName

【讨论】:

  • 这看起来很狡猾。 character(6) :: fNamedimension(6)?那 NUL 如何终止 C 的字符串?从 C 传递的 strLength 参数在哪里?
  • 使用此代码,我建议进行以下更改:1)在 C 代码中分配 ptr(例如:name = calloc(strLen, sizeof(char)))。 2) 将name 作为假定大小的数组传递给dimension(*),以避免出现幻数。 3) 将fname 的大小增加1 并像fname='Martin'//c_null_char 一样以NULL 终止它(如@AndrewHenle 所述)。
猜你喜欢
  • 2021-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-12
  • 2013-12-10
  • 2012-01-14
相关资源
最近更新 更多