C Preprocessor

From WikiROMS
Jump to: navigation, search
C Preprocessor

ROMS extensively uses the C preprocessor (cpp) during compilation to replace code statements, insert files into the code, and select relevant parts of the code depending on its directives. There are numerous cpp options that can be activated in header files cppdefs.h and globaldefs.h.

The plain cpp program is in the ANSI C standard, but that version is not quite right for preprocessing of Fortran. We therefore use the -traditional flag with the GNU cpp and similar flags with others.

Preprocessor Directives

  • The #define directive comes in three flavors:
  1. Turning on a logical switch. For example, the following definition will activate tidal forcing in the open boundary conditions to the barotropic momentum equations:
    #define UV_TIDES
  2. String substitution. The preprocessor will replace the symbolic name with the specified text everywhere in the code. For example, the following assigment is used to set the dimensions of private, automatic 2D arrays:
    #define PRIVATE_2D_SCRATCH_ARRAY Istr-3:Iend+3,Jstr-3:Jend+3
  3. Macro substitution. This functionality is better done through statement functions or inline functions which allow the Fortran compiler to do type checking.
  • The #undef directive undefines or deactiaves symbolic names. For example, the following option is used in parallel debugging to turn on/off the writing of current date and cpp options in ROMS history NetCDF files:
    #undef DEBUGGING
  • The #include directive allows one to insert the contents of another file into the source code. For example, the following statement is used to insert the tile-bounds header file that computes the horizontal sub-domain indices of RHO-, U- and V-type variables.
    #include "set_bounds.h"
  • The #ifdef, #endif directive or its equivalent #if defined, #endif allows to control whether the preprocessor omits and includes part of the source code. For example, the following code section is used to activate time profiling during execution:
    #ifdef PROFILE
    CALL wclock_on (ng, iNLM, 13)
    #endif
  • The #ifndef, #endif directive or its equivalent #if !defined, #endif is the opposite. It is executed only when the symbol is not defined or activated. For example, the following statement is used to reference to the equation of state module only when the option TS_FIXED is not activated.
    #ifndef TS_FIXED
    USE rho_eos_mod, ONLY: rho_eos
    #endif
  • The #if <test1>, #elif <test2> #endif is a more general case of the above. In this case, the tests are often of the form:
    #if defined BBL_MODEL && (defined MB_BBL || defined SSW_BBL)
    Here, && is a logical and while || is a logical or. Notice that conditional expressions as used in ROMS require the defined syntax.
  • The conditional tests are a matter of style and the GNU coding standards use a different style. Theirs is to always:
    #define FLOATS 1
    instead of simply:
    #define FLOATS
    This allows a test to be on the tag rather than defined tag.

C and C++ Comments

  • C language comments are text between /* and */. These comments are removed by the C preprocessor.
  • C++ comments are text between // and the end of the line. Since this is also a valid Fortran operator, it is best to tell the C preprocessor not to remove these if possible. Using GNU cpp with the -traditional flag does this. We also have the source code to an older cpp that behaves the way we want if the GNU cpp is not available.

cpp_clean

The preprocessor reads the source file (*.F) and builds a target file (*.f90) according to activated cpp options. This target *.f90 file is then processed by the perl script cpp_clean to clean all commented (#) lines inserted by cpp to make it more readable. This is actually the file that is compiled by the Fortran compiler.