6

c++ から fortran (f90) に char 配列を渡すのに問題があります。

これが私の 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 で 'string' および 'expected' 文字配列をサイズ 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 の char 配列に 3 文字を割り当てました。これは、終端の '\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側でISOCバインディングを使用することをお勧めします。すでに「externC」を使用しています。Fortran2003のISOCバインディング(現在、ほとんどのFortran 95/部分的なFortan2003コンパイラーに実装されています)により、これはコンパイラーおよびプラットフォームに依存しないアプローチになります。Charles Baileyは、2つの言語の文字列の違いについて説明しました。このStackoverflowの質問には、コード例があります。CからFORTRANサブルーチンを呼び出す

既存のFortranコードを変更したくない場合は、C++コードと既存のFortranコードの間に「接着」ルーチンを書くことができます。ISO Cバインディングを使用してFortranで接着ルーチンを作成すると、言語標準の機能に基づくため、信頼性と安定性が向上します。

于 2012-04-15T18:35:44.947 に答える