I have an old FORTRAN code with a dozen COMMON blocks. I am trying to reform it to modern Fortran and some parts to C++. I want to do this incrementally. The way I was thinking of achieving this by using user defined structures in the new code (probably in a special MODULE) and use EQUIVALENCE to map the same data to the old COMMON BLOCKs in the older part of the code.
Firstly, is it legal in Fortran (I have not tried it yet). Secondly, is there a better way to accomplish what I am trying to do?
Thanks for any help.
I would approach this without using equivalence. First, without changing any legacy code, put the common block and associated type declarations into a module, i.e. given a legacy procedure:
SUBROUTINE legacy IMPLICIT NONE COMMON /mycommon/a,b,c INTEGER :: a REAL :: b(2) LOGICAL :: c PRINT *, a, b, c END SUBROUTINE legacy
create a module such as:
MODULE common_wrapper IMPLICIT NONE COMMON /mycommon/a,b,c INTEGER :: a REAL :: b(2) LOGICAL :: c END MODULE common_wrapper
New code then uses this module, without needing to know that the variables provided by the module are in a common block. This new code continues to interoperate with legacy code, due to the way that storage association works.
If you want to have the variables in an object of derived type, you can use a sequence type. Again, this is without changing any legacy code.
MODULE common_wrapper2 IMPLICIT NONE TYPE :: t SEQUENCE INTEGER :: a REAL :: b(2) LOGICAL :: c END TYPE t COMMON /mycommon/x TYPE(t) :: x END MODULE common_wrapper2
New code using this module would work with the components of the `x` object, again agnostic to the common block storage of those components, but continuing to interoperate with legacy code that directly uses the common block.
Perhaps that's where your conversion stops, or perhaps you then start to progressively rework the legacy code to use the module instead of directly referencing the common block. Note that because common blocks work on storage association, names for the same bit of storage can be different for different declarations of the same common block (and worse, the same name in different scopes can refer to different bits of storage of the same common block) - be very careful of such cases when changing legacy code.
Once all references to the common block have been migrated to be via the module, then the common block can be deleted from the module, any variable initialisations from block data can be changed to be module variable initialisations, and away you go, no more storage association.
What you propose is (mostly) legal in Fortran. If the old code refers to the variables a certain way, you can leave it and not add EQUIVALENCE as long as you make sure that the derived types you use occupy the same storage. I can't offhand think of a better alternative.
Ian and Steve:
Thank you very much for great ideas and quick response. I think I am going to start with Ian's first solution which is simple enough for me to follow. I may later try the variation he is suggesting because it would be then easy to pass the derived type to C++.
I learn so much at this place (Intel Fortran forum). Thanks very much again. I am also extremely happy that Intel is supporting and updating the Fortran compiler. What would I do if they did not!
From Steve's comment and looking at the reference documents, it seems like as long as one does not mess around splitting and slicing the COMMON BLOCK, it is OK to use EQUIVALENCE. Anyway, I am not going to follow that path.
You may find online resources online such as Fortran Wiki as well as those in Steve's Dr Fortran blog on "modern Fortran" (almost all of them have sections on modernizing legacy code) useful in case you have not reviewed them already:
It will be useful for readers if you can explain how EQUIVALENCE is used in the legacy code of interest to you. Note the section at Fortran Wiki which states with EQUIVALENCE, "in old code it often turns out to be used in ways which are non-standard, for example to associate numerical variables with ones of character type," i.e., effectively a UNION type of a data structure. If that's the case, please see this thread: https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/816710 and know the code may need to either continue using the non-standard compiler-based extensions or require considerable refactoring to work with the type (and kind and rank) requirements of standard Fortran
I have implemented Ian's suggestion with a small FORTRAN function which shares a COMMON BLOCK with legacy FORTRAN and a C++ class. All seems to work (I can pass contents including CHARACTER varibales back and forth), but I encountered a strange (perhaps small) problem.
This is my MODULE containing my COMMON BLOCK
MODULE mycommons COMMON/KEYS/PROB(4),RUNTYP(4),TITLE(80),KEY(15),KOPT(125,10) BIND(C,NAME='KEYS') :: /KEYS/ CHARACTER :: PROB,RUNTYP,TITLE END MODULE mycommons
When I change the CHARACTER declaration to CHARACTER(C_CHAR) or CHARACTER(KIND=C_CHAR)
MODULE mycommons COMMON/KEYS/PROB(4),RUNTYP(4),TITLE(80),KEY(15),KOPT(125,10) BIND(C,NAME='KEYS') :: /KEYS/ CHARACTER(C_CHAR) :: PROB,RUNTYP,TITLE END MODULE mycommons
error #6756: A COMMON block data object must not be an automatic object. [TITLE]
This declaration works if I put the code within the FORTRAN function code instead of MODULE.
Any ideas why? Is it safe to use CHARACTER without C_KIND qualifier within a mixed C++/FORTRAN program?
You forgot to USE ISO_C_BINDING, which makes C_CHAR an implicitly declared variable. That turns the declaration of TITLE into an object with a run-time length (automatic) which is not allowed here.