2012-04-15 12 views
6

Mam problemy z przekazywaniem tablic znaków od C++ do fortranu (f90).przechodzi przez tablice znaków od C++ do fortranu

Oto mój C++ plik 'cmain.cxx':

Oto moja Fortran plik '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 

Proces kompilacji jest:

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

Edycja: zauważ, że plik wykonywalny można również zbudować za pomocą:

g++ -lgfortran -o test ftest.o cmain.o 

Wymagana jest także flaga -m64, ponieważ korzystam z OSX 10.6.

Wyjście z wykonaniem 'test' jest:

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

Deklarowanie 'string' i 'Oczekiwano' tablice znaków w ftest.f90 o rozmiarze 4, a mianowicie:

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

i rekompilacji daje następujący wynik:

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 ' 

deklarowania tablic znaków w 'cmain.cxx' o rozmiarze 3, tj:

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

int main() { 
    char string[2][3]; 

i powraca do pierwotnego rozmiaru w pliku Fortran (3), tj:

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

i rekompilacji daje następujący wynik:

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

Więc ostatni przypadek jest tylko jeden, który działa, ale tutaj przypisałem 3 znaki do tablicy znaków o rozmiarze 3, co oznacza, że ​​brakuje "\ 0" zakończenia i prowadzi do wyjścia "abcxyz" - jest to niedopuszczalne dla mojej zamierzonej aplikacji.

Każda pomoc będzie bardzo doceniona, to doprowadza mnie do szału!

+0

Nie widzę żadnego znaku, że korzystasz z możliwości Interoperability with C firmy Fortran, które mają na celu złagodzenie takich problemów, z jakimi masz do czynienia. Sugeruję, żebyś z nich skorzystał. –

+0

Cześć Mark, mam zewnętrznie dostarczony program fortranowy, z którym chcę się połączyć za pomocą C++. Nie mogę zmodyfikować kodu Fortran. Czy widzisz sposób, aby to zadziałało bez modyfikowania oryginalnego pliku Fortran? – user1334640

+0

To zależy od faktycznego rodzaju interakcji, ale jeśli posiadasz kod Fortran, nawet jeśli nie możesz go zmienić, możesz dodać do niego moduł interfejsu C. Moduł po prostu uczyniłby niektóre funkcje konwersji widocznymi dla C. Jako bonus, możesz pozbyć się kończących _ w nazwach funkcji. –

Odpowiedz

10

Struny C są zakończone zerem, podczas gdy łańcuchy fortran, według konwencji, są wypełnione spacjami, ale o stałym rozmiarze. Nie powinieneś oczekiwać, że będziesz mógł przekazywać struny C do fortranu bez jakiejś konwersji.

Na przykład:

#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, ' '); 
} 

które można następnie użyć albo z 3 lub 4 wersji długości 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; 
} 
5

polecam korzystania z ISO C wiążące dla Fortran boku jak sugeruje przez "High Performance Mark". Używasz już "extern C". ISO C Binding z Fortran 2003 (obecnie implementowane w większości kompilatorów Fortran 95/partial Fortan 2003) sprawia, że ​​jest to podejście niezależne od kompilatora i platformy. Charles Bailey opisał różnice między łańcuchami w dwóch językach.To pytanie Stackoverflow ma przykład kodu: Calling a FORTRAN subroutine from C

Jeśli nie chcesz modyfikować istniejącego kodu Fortran, możesz napisać procedurę "klejenia" pomiędzy twoim C++ a istniejącym kodem Fortrana. Napisanie rutyny kleju w Fortranie przy użyciu wiązania ISO C byłoby bardziej niezawodne i stabilne, ponieważ byłoby oparte na cechach standardu językowego.

1

Podane przykłady są zbyt dużej gramaturze, o ile nie chcesz przekazać więcej niż jeden łańcuch można skorzystać z „ukryty” parametr długość ...

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

} 

które można zadzwonić z Fortran jak

call function("some string or other") 

nie musisz się o faff z poszczególnych operacji kopiowania, ponieważ konstruktor std :: string może zrobić wszystko, co dla Ciebie.

+1

Być może są one w wadze ciężkiej (ale nie jest to moim zdaniem, dodanie "bind (C)" jest proste), ale są one przenośne. W standardzie Fortran nie ma nic, co mówi, że symbolem funkcji będzie nazwa z podkreśleniem i nie ma niczego, co zagwarantowałoby typ ukrytego argumentu i jego pozycję na liście argumentów. Nie ma nawet żadnej gwarancji, że istnieje jakiś ukryty argument. –

+0

Dzięki, chociaż przykładowe opakowanie Fortrana, które trzeba napisać np. Na http://stackoverflow.com/questions/8207997/calling-a-fortran-subroutine-from-c/8208960, jest znacznie bardziej skomplikowane niż mój 3-liniowy przykład, więc będę trzymać się mojego podejścia. –

Powiązane problemy