Передача массивов символов из с ++ в фортран
У меня проблемы с передачей массивов символов из 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;
}
Вот мой файл на Фортране, '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 '
Объявление массивов символов в '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 ответа
Строки Си заканчиваются нулями, тогда как строки Фортрана, как правило, дополняются пробелами, но имеют фиксированный размер. Вы не должны ожидать, что сможете передавать строки C в Фортран без некоторого преобразования.
Например:
#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;
}
Я рекомендую использовать ISO C Binding на стороне Fortran, как это было предложено "High Performance Mark". Вы уже используете "extern C". Привязка ISO C к Fortran 2003 (в настоящее время реализованная в большинстве компиляторов Fortran 95 / частично Fortan 2003) делает этот подход независимым от компилятора и платформы. Чарльз Бейли описал различия между строками в двух языках. Этот вопрос Stackru имеет пример кода: вызов подпрограммы FORTRAN из C
Если вы не хотите изменять существующий код на Фортране, вы можете написать "склеивающую" подпрограмму между вашим кодом C++ и существующим кодом на Фортране. Написание процедуры склеивания на Фортране с использованием привязки ISO C было бы более надежным и стабильным, поскольку оно основывалось бы на особенностях языкового стандарта.
Приведенные примеры слишком тяжелые, если вы не хотите передавать более одной строки, вы можете использовать "скрытый" параметр длины...
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 может сделать все это за вас.