6

我在将 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

此外,当我运行 OSX 10.6 时,需要 -m64 标志。

执行“测试”的输出是:

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 '

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

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 个字符分配给大小为 3 的 char 数组,这意味着缺少终止的 '\0',并导致输出 'abcxyz' - 这是不可接受的我的预期应用。

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

4

3 回答 3

11

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;
}
于 2012-04-15T16:25:14.310 回答
5

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

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

于 2012-04-15T18:35:44.947 回答
1

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

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;
  /// ...

}

你可以从 fortran 中调用它

  call function( "some string or other" )

您无需为单独的复制操作而烦恼,因为 std::string 构造函数可以为您完成所有这些操作。

于 2015-09-21T08:55:56.333 回答