Совместимость с Fortran <-> C ++, если я скомпилирую фортран с опцией -finit-local-zero - PullRequest
2 голосов
/ 06 октября 2019

Я обнаружил странный результат при связывании программы с C ++ и Fortran. Тест (см. Далее) очень прост: определите переменную в модуле fortran и получите доступ к ней в программе C ++. Если я скомпилирую фортран (gfortran из GCC 7.4.0) с опцией «-finit-local-zero», то ссылка потерпит неудачу с жалобой на множественное определение переменных. Конечно, если я отключу эту опцию, она будет работать хорошо, но для меня это pb, потому что мне нужно установить «0» для моих неинициализированных данных.

Я ставлю здесь после кода фортрана, кода C ++ иКоманды для создания exe.

        module A_module

        USE ISO_C_BINDING
        implicit none
        real(C_FLOAT) , bind(C) :: isareal
        integer(C_INT), bind(C, NAME="MMS") :: MMS 

        CONTAINS

        subroutine CHANGE() bind(c)
        use iso_c_binding
        implicit none
        isareal = 5.0 
        MMS=40
        write(*,*) 'fortran changes isareal =>',isareal
        write(*,*) 'fortran changes MMS =>',MMS
        end subroutine CHANGE
        end module
#include <iostream>

using namespace std;

//Fortran definitions "translated" to C++
extern "C" {
void change();
float isareal;
int MMS; // 
}
int main() {
    // Can access to a variable in a fortran module ? 
    cout << "In C before: isareal = " << isareal;
    cout << " and MMS = " << MMS << "\n";

    change(); //call fortran to change the values 

    // Control
    cout << "In C after: isareal = " << isareal;
    cout << " and MMS = " << MMS << "\n";

    return 0;
}

И команды:

 mpif90  -finit-local-zero -c mod_A_module.f
 mpicxx -Wextra -std=c++11 -c cpp_main_1.cpp
 mpif90 *.o -std=c++11 -lstdc++ -o bad.exe
./bad.exe

здесь ошибка (при использовании -finit-local-zero)

mod_A_module.o:(.bss+0x0): multiple definition of `isareal'
cpp_main_1.o:(.bss+0x0): first defined here
mod_A_module.o:(.bss+0x4): multiple definition of `MMS'
cpp_main_1.o:(.bss+0x4): first defined here

Спасибозаранее за вашу помощь ...

Ответы [ 2 ]

1 голос
/ 06 октября 2019

Проблема на самом деле (частично, по крайней мере) в коде C ++. Вы не сказали, что isareal и MMS являются внешними, поэтому для них компилятор сгенерирует «экземплярный» символ (кавычки, потому что я не помню правильный термин). Вы можете видеть это, например, с помощью "readelf -s main.o"

0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 MMS

Здесь важно отметить, что столбец Ndx (следующий за последним столбцом, рядом с именем символа) говоритчто MMS находится в разделе # 4.

Если вы исправите код C ++, добавив extern к объявлениям isareal и MMS, вы увидите, что номера разделов изменятся на UNDЭто означает, что это ссылка на символ в каком-то другом объектном файле, которая должна быть разрешена компоновщиком.

Вторая часть головоломки состоит в том, что если вы компилируете код Fortran с помощью -finit-local-zero, компилятор Fortranтакже будет генерировать "экземпляры" символов. И затем, когда вы пытаетесь связать их вместе, вы получаете ошибку «множественное определение». Теперь, если вы опустите опцию -finit-local-zero, GFortran поместит эти два символа в секцию COM (сокращение от COMDAT), которая является секцией особого вида, где допускается несколько определений. Я не уверен, почему это сделано, хотя я бы предположил, что COMDAT не позволяет символам, которые инициализируются до некоторого значения.

Если вы будете искать в GCC bugzilla, вы найдете кучуПроблемы Фортрана, связанные с проблемой множественных определений, все связаны в лабиринте извилистых отрывков.

0 голосов
/ 06 октября 2019

Проблема в том, что

extern "C" {
  void change();
  float isareal;
  int MMS; // 
}

определяет хранилище для этих переменных. Это не просто объявить их. Вот простой пример:

$ cat x.cpp 
extern "C" { int foo; }
$ make x.o
g++    -c -o x.o x.cpp
$ nm x.o
0000000000000000 B foo

extern "C" не делает переменных extern;он определяет связь для соглашения C, то есть неискаженные имена функций. Поскольку имена переменных области файла не искажены (они не могут быть перегружены), нет необходимости хранить их там:

extern "C" {
  void change();
}
extern float isareal;
extern int MMS;  

должно помочь.

Возможно, что с помощью -finit-local-zero вы предотвратили объединение компоновщика двух одинаковых глобальных определений.

...