3
votes

I am trying to convert an old Fortran 77 code to C++ and most of the variables are declared in Common blocks such as:

COMMON/BLK1/Gw(200),Eta(4096),t(4096),Phi(200),w(200)
COMMON/BLK2/g,dw,Vel,M,dt,N,Ioutp1,Ioutp2
COMMON/BLK3/Hs,Std,E,Hs1,Tdt

As I understand it, common blocks are used simply to make variables accessible throughout the program in different subroutines. Therefore, in a C++ program, would I be able to create structs with the variables (outside of the main) and call the variables this way as members of the struct?

3
My advice is to avoid copying common blocks behaviour to any other language. They are a pain to work with, maybe valid in the '70 but not today. Hard to debug, hard to isolate and your code won't work concurrently. Try to embed the variables in classes and propagate your objects in your function calls.siritinga
Yeah, don't do this. You'll not be able to use multiple threads with common blocks. Not an issue with Fortran 77, but times have changed. That said, if you are desperate to convert this code, and I mean the entire Fortran code, then just use f2c -a and the job is done.David Heffernan
ok with using f2c -a I'm assuming I need to write that command while in the folder where the fortran code is? I was trying to use fable but no luckuser3460758

3 Answers

2
votes

Based on my understanding of COMMON from this page, the C++ equivalent would be to make a file called common.h (with include guards) that contains:

 namespace BLK1
 {
      int const Gw = 200;
      int const Eta = 4096;
      int const t = 4096;
      int const Phi = 200;
      int const w = 200;
 }

 namespace BLK2
 {
      extern int g, dw, Vel, M, dt, N, Ioutp1, Ioutp2;
 }

 namespace BLK3
 {
      extern int Hs, Std, E, Hs1, Tdt;
 }

Also, in exactly one .cpp file in your project you need to provide a definition for any non-consts, e.g. in foo.cpp:

 #include "common.h"

 namespace BLK2
 {
      int g, dw, Vel, M, dt, N, Ioutp1, Ioutp2;
 }

 namespace BLK3
 {
      int Hs, Std, E, Hs1, Tdt;    // initialized to 0 by default
 }

You may want to use a different type than int, e.g. unsigned long. I'm assuming the initialized values are meant to be const; if not then change int const to extern int and remove the initializer. The initializer would have to go in the definition in the .cpp file.

Avoid the mistake of declaring a non-const, non-extern variable in the header; this causes undefined behaviour if the header is included in two different units.

You access these variables by writing BLK1::Eta for example.

As you surmise it might be considered tidier to use a struct instead of a namespace, although you'd still have to create an instance of the struct which is declared extern in the header, and defined in exactly one .cpp file; and if you are pre-C++11 it's more annoying to provide initializers.

(Of course, even better would be to refactor the code to not use globals. But it might be useful as a first pass to do a direct translation).

2
votes

Common blocks with the same name overlap each other in memory. You can allocate a chunk of memory, and typecast pointers to it. Another option is to declare them in a union. That is why a union was invented. Certainly once your union is set up, you use extern in the other modules.

Put the following into a common header and an instance of it into module 1. Add an extern so it can be seen in module 2.

union blk1
{
    struct module_1_view
    {
        double gw(200);
        double eta(4096);
        double t(4096);
        double phi(200);
        double w(200);
    } 

    struct module_2_view
    {
        double parameters(8592);   // 200 + 4096 + 4096 + 200
        double w_status(200);
    } 
}

Imagine module 1 being responsible for loading some set of doubles from a file into the the variables in the module_1_view. Once those parameters are loaded and validated, module 2 is called, which accesses the parameters from the module 2 view. Nearly all of them are accessed via the parameters variable, except for the w_status, which is actually 200 indicators that indicate something about the success or validation of the parameters.

The key point is that module 1 and 2 access the same chunk of memory (hence the union) and they use their own set of variable names.

-1
votes

I can't speak directly to Fortran, but if you want to make a variable accessible throughout the entire program in c/c++, extern is the keyword you are looking for.

Extern is a C style way of sharing data. C++ would push you into OO design, of which a variable that is tightly coupled to many objects isn't good OO.

If you are trying to do C++ things on top of legacy code, sometimes static methods, which are passed pointers to your C++ class can act as wrappers. Heres a simple example of that:

extern int _magicVariable;

static void call( void* klass )
{
  ((MyClass*)klass)->functionCall( _magicVariable );
}

Inside MyClass, you'll need to name void call( void* ) as a friend. Now the legacy code can call(void*) with a pointer to your class, passing in the _magicVariable into your OO design. From there c++ will do its thing.

The bottom line is you have a lot of ways to accomplish the task, try to do what makes sense based on your desired code structure.