How to access FORTRAN COMMON blocks from C/C++

The short answer to this is - “don’t”. Common blocks are an archaic and flawed, while very practical, concept from FORTRAN 77 which eventually has been fixed in the FORTRAN 90 standard. The applicable solutions are:

Your own FORTRAN code:
Use MODULEs instead which provide the necessary and important access & initialization order restrictions. You can find plenty of detailed explanations of this concept and how it fixes the flaws of COMMON blocks in every good FORTRAN book covering FORTRAN 90 or later.

Accessing FLUKA variables:
Consolidation of code design takes quite some time and large portions of FLUKA are still written in FORTRAN 77. Thus, you might still find data that you want to access in common blocks.
There are three possibilities to make these data accessible and you can find them below in the order of recommendation.

1.) Pass as arguments:
Capture the data you need in a FORTRAN routine from the corresponding COMMON block and pass it to your C/C++ code. This is the safest and easiest way to do it!

2.) Create a view interface following the “property service idiom”:
The basic idea is that you create a FORTRAN routine which captures the current status of the variables you need and passes it to a “view” class in C++. This view class is basically a wrapper for an associated heterogeneous container (e.g. a map using boost’s any datatype that spares you from reinventing the wheel) which acts like a dictionary and which you can query for the value of a specific variable. At the entrance of your C++ code you call the FORTRAN capture routine once to update and register all variables that are needed and then in C++ you will get their values from the “property service”. A very rough (!) sketch of how such a service could be implemented is given in the snippet below. Please note that this should just serve as an illustration and for production code quality would need several improvements which are elided here for reasons of clarity.

#include "boost/any.hpp"

class CVariableView {
public:
  
  // store a variable with a specific name in our storage
  template<typename T>
  void Register( const std::string& Name, T Val ) { m_VariableStorage[MakeUppercase(Name)] = Val; };

  // Note: this will throw an exception if the cast to int fails!
  int GetInt( std::string Name ) { Name = MakeUppercase(Name); 
                                   assert( m_VariableStorage.find(Name) != m_VariableStorage.end() ); 
                                   return boost::any_cast<int>( m_VariableStorage[Name]); 
                                 };

protected:
  std::string MakeUppercase( std::string Text ) {
    std::transform(Text.begin(), Text.end(), Text.begin(), [](unsigned char c){ return std::toupper(c); } );
    return Text;
  }

protected:
  std::map<std::string, boost::any> m_VariableStorage;  
}

3.) Direct access of the COMMON block data:
Common blocks are basically raw blocks of data in memory that can be addressed via pointers. Yet, there are a number of rules that need to be respected:

a.) Do not use unnamed COMMON blocks!!
b.) Follow the same declaration order of the variables in C/C++ as in FORTRAN
c.) Memory alignment must be adhered to!

The following common block in FORTRAN:

DOUBLE PRECISION X
INTEGER A, B, C
COMMON /MYBLOCK/ X, A, B, C

can be accessed in C++ by declaring:

 extern "C" {
   extern struct{
     double x;
     int a, b, c;
   } myblock_;  // ATTENTION: you must add the trailing underscore and use lower case names!
}

and used in the program as:

void myfunc() {
  std::cout << "X = " << myblock_.x << ", C = " << myblock_.c <<  std::endl;
}

PITFALLS:

  • Memory alignment must be strictly adhered to! Thus, it is MANDATORY to strictly respect the declaration of the order in the C/C++ structure that is used in the FORTRAN block. In addition equivalent datatypes must be used to avoid mismatches!
    To ensure this the following compiler flags are recommended which enforces alignment via doubles that have 8 bytes:
GCC/G++: 
-Wpadded -Wpacked -malign-double -mpreferred-stack-boundary=8 
GFORTRAN:
-falign-commons
  • In order to check the alignment you can do the following. Create a breakpoint in the C/C++ code where the structure is visible and output the memory address of a variable in GDB. For example:
    print &myblock_.c

Create an equivalent breakpoint in the FORTRAN section where the common block is visible and to the same:
print &c

If the resulting memory address is not equivalent then some misalignment has occured when passing the memory from FORTRAN to C/C++.