3

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?

AstroCB
  • 12,337
  • 20
  • 57
  • 73
user3460758
  • 957
  • 7
  • 15
  • 25
  • 4
    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 Sep 15 '14 at 11:27
  • 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 Sep 15 '14 at 12:20
  • 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 luck – user3460758 Sep 16 '14 at 14:58

3 Answers3

2

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).

M.M
  • 138,810
  • 21
  • 208
  • 365
  • 2
    I'm not sure this is correct. I think that the parenthesis like in `Gw(200)` doesn't mean to initialize Gw to 200, but to make Gw an array of 200 elements. In addition variable type declaration are missing in the question, may be a real or something else. If there are no declaration, then the implicit types are used. Fortran assumes any variable beginning with letters i-n or I-N are INTEGER, and those beginning with any other letter are REAL. – siritinga Sep 16 '14 at 06:49
  • Siritinga is correct. 200 is the number of elements in the array. – EvilTeach Feb 17 '15 at 02:05
  • A common block is a chunk of memory that can be shared between multiple modules. The variables and arrays that are declared is how that memory is viewed in that module. The name (blk1) ids the allocation for the other module. The layout of the variables/arrays can be the same or different in the other modules – EvilTeach Feb 17 '15 at 02:17
  • @EvilTeach maybe you could post an answer – M.M Feb 17 '15 at 02:21
  • @M.M do you mind expanding your answer with `struct` for the case of library? I found many package which discuss accessing commons from a single source, but not a single one which would show this for the case of a C++ library with separate .h and .cpp – Denis Apr 02 '18 at 19:49
2

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.

EvilTeach
  • 28,120
  • 21
  • 85
  • 141
  • I'm afraid I don't really see the connection (aside from the contents of `BLK1`) between this and the question. In particular there is nothing there about using storage association tricks. Further, what do you mean by "Common blocks overlap each other in memory."? `BLK1` will always refer to a given storage area and I'd very much hope that `BLK2` was wholly distinct. – francescalus Feb 17 '15 at 18:39
  • Actually, I think I do understand. You mean that all blocks named `BLK1` have the same association (?), so I'm just confused by the question's multiple (non-overlapping) blocks? – francescalus Feb 17 '15 at 19:02
  • Ok. The purpose of a common block is to allow two different modules to share a chunk of common memory. There is a need for that in this program or the variables wouldn't be in a common block. Yes. blk1 will not overlap blk2 they are different static memory allocations. the common block name is how the linker figures out what to link to in each of the modules. – EvilTeach Feb 17 '15 at 19:22
  • WAG. There are three modules in the program that use common blocks. Or maybe there are three functions in a single module, that each do something differently with their own common block. – EvilTeach Feb 17 '15 at 19:27
  • Thanks, the clarifying edit makes things clearer. With the storage association parts, though, you're extending the basic premise of the question? It's fine if you are, it just confused me initially. – francescalus Feb 17 '15 at 19:56
  • Know I'm late to the discussion, but another attribute of COMMON is that it's final size is determined by the linker/binder. Each program declares a common, and all may be different, except for the the name. Program 1 may create a common with a 10 integer array, Program 2 may create a common with a 20 integer array, and the linked/bound program that executes will address a single area of storage large enough to contain 20 integers. (The differing assumptions about the contents of common are one of the big pitfalls.) My point is that any solution can not assume a fixed *length* area. – HiTechHiTouch Feb 07 '18 at 03:44
-1

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.

Luke Dupin
  • 2,275
  • 23
  • 30
  • Thanks for this, it's a little bit above my head at the moment (new at programming!) but would it also work to just declare a struct at the start before the main and declare the variables as part of the struct so they can be called later throughout the program as members of the struct and therefore used the same throughout the program? – user3460758 Sep 15 '14 at 11:38
  • `extern` is used for sharing file-scope variables between units. I'm not sure whether OP wants this, or if all variables will be accessed from one unit (in which case they should be `static`) – M.M Sep 15 '14 at 12:06
  • I apologize, its been so long since I've worked on single file programs, it didn't even occur to me that would be enough in this case. You're correct Matt. – Luke Dupin Sep 16 '14 at 00:18