将char数组从c ++传递给fortran

时间:2012-04-15 15:43:04

标签: c++ fortran arrays

我无法将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标志。

执行'test'的输出是:

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的'string'和'expected'字符数组,即:

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个字符,这意味着缺少终止'\ 0',并导致'abcxyz'输出 - 这是我的预期申请不可接受。

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

3 个答案:

答案 0 :(得分:10)

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;
}

答案 1 :(得分:5)

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

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

答案 2 :(得分:1)

给出的例子太重要了,只要你不想传递多个字符串就可以使用&#34; hidden&#34;长度参数...

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构造函数可以为你完成所有这些操作。