Компиляция C++, включая библиотеки Fortran, с операторами COMMON
Я собираю программу на C++, для которой нужны библиотеки. Код для этих библиотек был написан на Фортране и содержит ОБЩИЕ блоки. В основном я делаю что-то вроде:
g++ -o main.cpp main lib1.a lib2.a
Lib1.a и lib2.a кодируются на Фортране:
gfortran -c -o lib1.a Code1.F
gfortran -c -o lib2.a Code2.F
И оба включают заголовочный файл, который содержит что-то вроде:
double precision var1,var2
double precision var3,var4
common /block1/ var1,var2
common /block2/ var3,var4
Кажется, что-то идет не так с общими блоками. Например, изменение порядка переменных после общего или добавление новых переменных приводит к случайным противоречивым результатам.
Я знаю, что ОБЩЕЕ утверждение не следует использовать, когда это возможно, но я не вижу, в чем может быть проблема в этом случае.
3 ответа
В зависимости от Fortran вашего компилятора, это может быть вставка заполнения между переменными. Возможно, с этим не согласны компиляторы Fortran и C++. Просто предположение.
Если вы готовы изменить код на Фортране, это поможет использовать привязку ISO C, поскольку это дает указание компилятору Fortran создавать код, соответствующий соглашениям компилятора C. Пример общего блока приведен по адресу http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/fortran/lin/compiler_f/bldaps_for/common/bldaps_interopc.htm. На основании этого примера:
use, intrinsic :: iso_c_binding
real (c_double) :: var1, var2, var3, var4
common /block1/ var1, var2
common /block2/ var3, var4
bind (C) :: /block1/, /block2/
Если вы хотите создавать другие моды, лучше использовать переменные модуля. Тогда нет никакого беспокойства о расположении в памяти.
module global_vars
use, intrinsic :: iso_c_binding
real (c_double), bind (C) :: var1, var2, var3, var4
end module global_vars
Порядок переменных в COMMON
блоки важны, и после изменения порядка ожидаются плохие вещи. Он должен быть одинаковым везде, где используется общий блок, в том числе в программе на C++. Что неважно, так это имена, данные этим переменным. Например, в одной подпрограмме вы могли бы иметь:
double precision a, b
common /block1/ a, b
И в другой подпрограмме вы могли бы иметь:
double precision c, d
common /block1/ c, d
Еще a
а также c
будет использовать одну и ту же ячейку памяти То же самое относится и к b
а также d
, Это может привести к путанице, и обычная практика - помещать объявления как переменных, так и общих блоков в файл, который include
-d каждой подпрограммой, которая использует определенный общий блок, как это делается в вашем случае. Теперь, если вы измените что-либо в одном из общих блоков, все подпрограммы увидят, что это изменилось, и все будет работать так, как ожидалось.
Проблема в том, что вам также нужно изменить соответствующую структуру C, чтобы привести ее в соответствие с измененным общим блоком. Например
double precision a, b
common /block1/ a, b
соответствует в С:
struct common_block1
{
double a;
double b;
};
extern struct common_block1 block1_;
(примечание: старые компиляторы Фортрана, которые не поддерживают bind(C)
Атрибут ставит подчеркивание в конце каждого экспортируемого идентификатора, поэтому в C/C++ вам придется ссылаться block1
как block1_
)
Если вы измените общий блок на:
integer a
double precision b, c
common /block1/ a, b, c
Вы также должны изменить структуру C на:
struct common_block1
{
int a;
double b;
double c;
};
Все это с учетом компиляторов C и Fortran использует одни и те же правила выравнивания памяти. Выравнивание обычно можно контролировать с помощью параметров компилятора (C/C++/Fortran) и атрибутов типа (C/C++). С помощью ISO_C_BINDING
Модуль Fortran может гарантировать, что типы типов с одинаковым размером памяти используются в Fortran и C. Рекомендуется сначала поместить самые большие объекты (например, массивы, (DOUBLE) COMPLEX
переменные, DOUBLE PRECISION
переменные и т. д.) в начале общего блока, за которым следуют более мелкие объекты и т. д.
Я думаю, что я понял происхождение ошибки. Сначала я скомпилировал библиотеку lib1.a, включая файл заголовка. Затем я фактически изменил этот заголовочный файл (особенно общий блок) и скомпилировал библиотеку lib2.a. Кажется логичным, что это вызывает ошибку между двумя общими блоками в разных библиотеках...
Я проверю это подробно, но я уверен, что это объяснение. Спасибо за ваши советы, это помогло мне проверить все, и вот как я нашел решение!