Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stringify macro with GNU gfortran

How can I stringify a preprocessor macro with GNU gfortran? I would like to pass a macro definition to GNU gfortran which will then be used as a string in the code.

Effectively I would like to do this:

program test
implicit none
character (len=:), allocatable :: astring
astring = MYMACRO
write (*, *) astring
end program test

and then build with:

gfortran -DMYMACRO=hello test.F90

I tried creating various macro, for example:

#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
...
astring = STRINGIFY(MYMACRO)

but this doesn't work with the gfortran preprocessor.

I also tried using a different style of macro:

#define STRINGIFY(x) "x"
...
astring = STRINGIFY(MYMACRO)

but this just creates a string containing the text 'MYMACRO'.

I then tried changing the macro definition to:

-DMYMACRO=\"hello\"

but this caused unrelated problem in the build process.

Thank you for your help

like image 805
Jim Eliot Avatar asked Jul 27 '15 09:07

Jim Eliot


2 Answers

Your attempt to use the well-known stringification recipe of the C preprocessor, viz:

#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)

fails for two reasons, each sufficient by itself.

First and simplest, the source file in which you attempt to employ it apparently has the extension .f90. What this extension signifies to gfortran (and to the GCC compiler driver, by any other name) is: Free form Fortran source code that should not be preprocessed. Similarly .f95, .f03 and .f08. If you want gfortran to infer that a source file contains free form Fortran code that must be preprocessed, give it one of the extensions .F90, .F95, .F03 or .F08. See the GCC documentation of these points

Even if you do that simple thing, however, the second reason bites.

The use of the C preprocessor to preprocess Fortran source is as old as C (which though old, is much younger than Fortran). gfortran is obliged not to break ancient working code; so, when it invokes the C preprocessor, it invokes it in traditional mode. The traditional mode of the C preprocessor is the way in which the preprocessor behaved prior to the first standardization of the C language (1989), insofar as that unstandardized behaviour can be pinned down. In traditional mode, the preprocessor does not recognize the stringizing operator '#', which was introduced by the first C Standard. You can verify this by invoking the preprocessor directly like:

cpp -traditional test.c

where test.c contains some attempt to employ the stringification recipe. The attempt fails.

You cannot coax gfortran on its own to work the the stringification recipe.

But there is a workaround. You can invoke cpp directly, unencumbered by traditional mode, to preprocess the Fortran source in which you want the stringification done and relay its output to gfortran. If you already know this and were looking for a gfortran-alone solution you need read no further.

Doing the stringification in your test source this way would look like:

cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' '-DMYMACRO=STRINGIFY(hello)' test.f90

The output of that is:

# 1 "test.f90"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.f90"
program test
implicit none
character (len=:), allocatable :: astring
astring = "hello"
write (*, *) astring
end program test

And that output is what you want to to compile. You could accomplish that as well by:

cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' test.f90 > /tmp/test.f90 \
&& gfortran -o test /tmp/test.f90

Then you will find that ./test exists and that executing it outputs hello.

You can eliminate the intermediate temporary file with further refinement. Your F90 source code will compile as F95, as the latter is conservative of the former. So you can take advantage of the fact that GCC will compile source piped to its standard input if you tell it what language you are piping, using its -x language option. The Fortran dialects that you may specify in this way are f77, f77-cpp-input, f95 and f95-cpp-input, where the -cpp-input prefix denotes that the source is to be preprocessed and its absence denotes that it is not. Thus

cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' test.f90 |  gfortran -x f95 -o test -

works as well as the previous solution, minus the temporary file, and emits the innocuous warning:

Warning: Reading file '<stdin>' as free form

(Note and retain the final - on the commandline. That is what tells gfortran to compile the standard input.). The meaning of -x f95 brings the additional economy that the source, which is preprocessed by cpp, is not preprocessed again by the compiler.

The use of the option -std=c89 when invoking cpp calls for a cautionary explanation. It has the effect of making cpp conform to the earliest C Standard. That is as close to -traditional as we can get while still availing of the #-operator, on which the stringification recipe depends, But with it you embrace the possibility of breaking some Fortran code if you preprocess it this way; otherwise gfortran itself would not enforce -traditional. In the case of your test program, you could safely omit -std=c89, allowing cpp to conform to the default C standard when it was built. But if you permit it or direct it to conform to -std=c99 or later, then the standard will mandate recognition of // as the beginning of a one-line comment (as per C++), by which any line of Fortran that contains the concatenation operator will be truncated at the first occurrence.

Naturally, if you use are using make or another build system to build the code in which you want the stringified macros, you will have a way of telling the build system what actions constitute compiling a given class of compilable files. For any Fortran source file fsrc you wanted to compile with a stringification preamble, the actions to specify would be in the vein:

cpp -std=c89 '-DSTRINGIFY_(x)=#x' '-DSTRINGIFY(x)=STRINGIFY_(x)' \
'-DMYMACRO=STRINGIFY(hello)' fsrc.f90 | gfortran -x f95 -c -o fsrc.o -
like image 175
Mike Kinghan Avatar answered Oct 17 '22 16:10

Mike Kinghan


Although this is an old and answered question, I wanted to achieve macro stringification in gfortran without changing the default preprocessor or build process. I found that the preprocessor would do what I want, as long as there was no initial quotation mark on the line, therefore the desired stringification can be achieved by breaking lines with ampersands:

astring = "&
&MYMACRO"

A caveat is that this really only works with the traditional preprocessor, and for examples breaks with intel ifort compiler, which is too smart to fall for this trick. My current solution is to define separate stringification macros for gfortran as:

#ifdef __GFORTRAN__
# define STRINGIFY_START(X) "&
# define STRINGIFY_END(X) &X"
#else /* default stringification */
# define STRINGIFY_(X) #X
# define STRINGIFY_START(X) &
# define STRINGIFY_END(X) STRINGIFY_(X)
#endif

program test
implicit none
character (len=:), allocatable :: astring
astring = STRINGIFY_START(MYMACRO)
STRINGIFY_END(MYMACRO)
write (*, *) astring
end program test

It looks really ugly, but it does get the job done.

like image 37
none_00 Avatar answered Oct 17 '22 15:10

none_00