Reputation: 21
I am compiling a C++ program which need some libraries. The code for these libraries were written in Fortran and contain COMMON blocks. Basically I am doing something like :
g++ -o main.cpp main lib1.a lib2.a
Lib1.a and lib2.a are coded in Fortran:
gfortran -c -o lib1.a Code1.F
gfortran -c -o lib2.a Code2.F
And both include a header file which contains something like :
double precision var1,var2
double precision var3,var4
common /block1/ var1,var2
common /block2/ var3,var4
It seems that something goes wrong with the COMMON blocks. For instance changing the order of the variables after the common, or adding new variables, results in randomly inconsistent results.
I know that COMMON statement should not be use when possible, but I don't see what could be the problem in this case.
Upvotes: 2
Views: 790
Reputation: 21
I think I understood the origin of the bug. I compiled first the library lib1.a, including the header file. Then I actually modified this header file (especially the common block) and compiled the library lib2.a. It seems logical that this induce a mistmach between the two common blocks in the different libraries...
I will check this in detail but I am pretty sure that this is the explanation. Thanks for your advices, it helped me to check everything and this is how I found the solution !
Upvotes: 0
Reputation: 74375
The order of variables in COMMON
blocks is important and bad things happening after changing the order is expected. It should be the same everywhere the common block is being used, including in the C++ program. What is insignificant is the names given to those variables. E.g. in one subroutine you could have:
double precision a, b
common /block1/ a, b
And in another subroutine you could have:
double precision c, d
common /block1/ c, d
Still a
and c
would share the same memory location. The same is also valid for b
and d
. This could lead to confusion and the usual practice is to put both variable and common block declarations in a file that is include
-d by every subroutine that uses the specific common block as is done in your case. Now if you change something in any of the common blocks all subroutines will see it changed and everything will work as expected.
The problem is that you also have to change the corresponding C structure in order to bring it in correspondence with the changed common block. E.g.
double precision a, b
common /block1/ a, b
corresponds in C to:
struct common_block1
{
double a;
double b;
};
extern struct common_block1 block1_;
(note: older Fortran compilers that do not support the bind(C)
attribute put underscore at the end of each exported identifier so in C/C++ you'd have to reference block1
as block1_
)
If you change the common block to:
integer a
double precision b, c
common /block1/ a, b, c
you should also change the C structure to:
struct common_block1
{
int a;
double b;
double c;
};
All that given both the C and Fortran compilers use the same memory alignment rules. Alignment can usually be controlled with compiler options (C/C++/Fortran) and type attributes (C/C++). Using ISO_C_BINDING
Fortran module can guarantee that type kinds with same storage size are used in Fortran and C. It is recommended that you first put largest objects (e.g. arrays, (DOUBLE) COMPLEX
variables, DOUBLE PRECISION
variables, etc.) at the beginning of the common block, followed by smaller objects and so forth.
Upvotes: 1
Reputation: 29391
Depending on Fortran your compiler, it might be inserting padding between variables. Maybe the Fortran and C++ compilers disagree on this. Just a guess.
If you are willing to change the Fortran code it would help to use the ISO C Binding since that instructs the Fortran compiler to produce code consistent with the conventions of the C compiler. There is common block example at http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/fortran/lin/compiler_f/bldaps_for/common/bldaps_interopc.htm. Based on that example:
use, intrinsic :: iso_c_binding
real (c_double) :: var1, var2, var3, var4
common /block1/ var1, var2
common /block2/ var3, var4
bind (C) :: /block1/, /block2/
If you are willing to make further mods, a better option is module variables. Then there is no concern about layout in memory.
module global_vars
use, intrinsic :: iso_c_binding
real (c_double), bind (C) :: var1, var2, var3, var4
end module global_vars
Upvotes: 1