将 char 数组从 C++ 传递到 Fortran

passing char arrays from c++ to fortran

本文关键字:Fortran C++ char 数组      更新时间:2023-10-16

我在将 char 数组从 c++ 传递到 fortran (f90) 时遇到问题。

这是我的c ++文件'cmain.cxx':

#include <iostream>
using namespace std;
extern "C" int ftest_( char (*string)[4] );
int main() {
    char string[2][4];
    strcpy(string[0],"abc");
    strcpy(string[1],"xyz");
    cout << "c++: string[0] = '" << string[0] << "'" << endl;
    cout << "c++: string[1] = '" << string[1] << "'" << endl;
    ftest_(string);
    return 0;
}

这是我的 fortran 文件,'ftest.f90':

SUBROUTINE FTEST(string)
CHARACTER*3 string(2)
CHARACTER*3 expected(2)
data expected(1)/'abc'/
data expected(2)/'xyz'/
DO i=1,2
    WRITE(6,10) i,string(i)
10  FORMAT("fortran: string(",i1,") = '", a, "'" )
    IF(string(i).eq.expected(i)) THEN
        WRITE(6,20) string(i),expected(i)
20      FORMAT("'",a,"' equals '",a,"'")
    ELSE
        WRITE(6,30) string(i),expected(i)
30      FORMAT("'",a,"' does not equal '",a,"'")
    END IF
ENDDO
RETURN
END

构建过程为:

gfortran -c -m64   ftest.f90 
g++ -c  cmain.cxx
gfortran -m64 -lstdc++ -gnofor_main -o test ftest.o cmain.o

编辑:请注意,可执行文件也可以通过以下方式构建:

g++ -lgfortran -o test ftest.o cmain.o
此外,-

m64 标志是必需的,因为我运行的是 OSX 10.6。

执行"测试"的输出是:

c++: string[0] = 'abc'
c++: string[1] = 'xyz'
fortran: string(1) = 'abc'
'abc' equals 'abc'
fortran: string(2) = 'xy'
'xy' does not equal 'xyz'

在 ftest.f90 中声明大小为 4 的"字符串"和"预期"字符数组,即:

CHARACTER*4 string(2)
CHARACTER*4 expected(2)

重新编译会给出以下输出:

c++: string[0] = 'abc'
c++: string[1] = 'xyz'
fortran: string(1) = 'abc'
'abc' does not equal 'abc '
fortran: string(2) = 'xyz'
'xyz' does not equal 'xyz '

在大小为 3 的"cmain.cxx"中声明字符数组,即:

extern "C" int ftest_( char (*string)[3] );
int main() {
    char string[2][3];

并恢复到 fortran 文件 (3) 中的原始大小,即:

CHARACTER*3 string(2)
CHARACTER*3 expected(2)

重新编译会给出以下输出:

c++: string[0] = 'abcxyz'
c++: string[1] = 'xyz'
fortran: string(1) = 'abc'
'abc' equals 'abc'
fortran: string(2) = 'xyz'
'xyz' equals 'xyz'

所以最后一种情况是唯一有效的情况,但在这里我为大小为 3 的 char 数组分配了 3 个字符,这意味着缺少终止的"\0",并导致"abcxyz"输出 - 这对于我的预期应用程序来说是不可接受的。

任何帮助将不胜感激,这让我发疯了!

C 字符串

以零结尾,而 fortran 字符串按照惯例是空格填充但大小固定的。您不应该期望能够在没有一些转换的情况下将 C 字符串传递给 fortran。

例如:

#include <algorithm>
void ConvertToFortran(char* fstring, std::size_t fstring_len,
                      const char* cstring)
{
    std::size_t inlen = std::strlen(cstring);
    std::size_t cpylen = std::min(inlen, fstring_len);
    if (inlen > fstring_len)
    {
        // TODO: truncation error or warning
    }
    std::copy(cstring, cstring + cpylen, fstring);
    std::fill(fstring + cpylen, fstring + fstring_len, ' ');
}

然后,您可以将其与 3 或 4 长度版本的 ftest 一起使用:

#include <iostream>
#include <ostream>
extern "C" int ftest_( char string[][4] );
void ConvertToFortran(char* fstring, std::size_t fstring_len,
                      const char* cstring);
int main()
{
    char cstring[2][4] = { "abc", "xyz" };
    char string[2][4];
    ConvertToFortran(string[0], sizeof string[0], cstring[0]);
    ConvertToFortran(string[1], sizeof string[1], cstring[1]);
    std::cout << "c++: string[0] = '" << cstring[0] << "'" << std::endl;
    std::cout << "c++: string[1] = '" << cstring[1] << "'" << std::endl;
    ftest_(string);
    return 0;
}

我建议按照"高性能标记"的建议在Fortran端使用ISO C绑定。 您已经在使用"外部 C"。 Fortran 2003 的 ISO C 绑定(目前在大多数 Fortran 95/部分 Fortan 2003 编译器中实现)使其成为一种独立于编译器和平台的方法。Charles Bailey描述了两种语言中字符串之间的差异。这个 Stackoverflow 问题有一个代码示例:从 C 调用 FORTRAN 子例程

如果您不想修改现有的 Fortran 代码,您可以在C++代码和现有 Fortran 代码之间编写一个"胶水"例程。 使用 ISO C 绑定在 Fortran 中编写胶水例程会更加可靠和稳定,因为这将基于语言标准的功能。

给出的例子太重量级了,只要你不想传递多个字符串,你就可以利用"隐藏"长度参数......

extern "C" void function_( const char* s, size_t len )  {  
  std::string some_string( s, 0, len );
  /// do your stuff here ...
  std::cout << "using string " << some_string << std::endl;
  /// ...
}

你可以从福特兰打电话,比如

  call function( "some string or other" )

你不需要对单个复制操作感到困惑,因为 std::string 构造函数可以为你完成所有这些工作。