chw21
chw21

Reputation: 8140

gfortran no-traditional preprocessor

I want to write a module overloading a swap routine, which takes an array and two indices and swaps the two elements around.

This routine should work for a wide range of arrays, so I wanted to overload it:

module mod_swap

    use iso_fortran_env
    implicit none

    interface swap
        module procedure swap_int64, swap_int32, swap_int16, &
            swap_real64, swap_real32
    end interface swap

    public :: swap
    private :: swap_int64, swap_int32, swap_int16, &
        swap_real64, swap_real32

contains

#define VAR_TYPE real
#define TYPE_KIND real64
#include  "swap.inc"
#undef TYPE_KIND

#define TYPE_KIND real32
#include  "swap.inc"
#undef TYPE_KIND
#undef VAR_TYPE

#define VAR_TYPE integer
#define TYPE_KIND int64
#include  "swap.inc"
#undef TYPE_KIND

#define TYPE_KIND int32
#include  "swap.inc"
#undef TYPE_KIND

#define TYPE_KIND int16
#include  "swap.inc"
#undef TYPE_KIND
#undef VAR_TYPE

end module mod_swap

with swap.inc being:

#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define SWAP EVALUATOR(swap, TYPE_KIND)

    subroutine SWAP(a, i, j)
        implicit none
        VAR_TYPE(kind=TYPE_KIND), intent(inout) :: a(:)
        integer, intent(in) :: i, j
        VAR_TYPE(kind=TYPE_KIND) t
        t = a(i)
        a(i) = a(j)
        a(j) = t
    end subroutine SWAP

When I run gfortran -o test.o -c -cpp test.F90 it fails, and when I run gfortran -E -cpp test.F90 I find out why: the SWAP macro has been expanded to swap ## _ ## int16, not swap_int16 as expected.

However, cpp directly works:

$ cpp test.F90 > test.f90
$ gfortran -c -o test.o test.f90

After browsing this forum and Google in general, I have deduced that the issue is this:

The preprocessor is run in traditional mode.

And indeed, cpp --traditional exhibits the same behaviour as gfortran -E -cpp

So here are my questions:

  1. Is there a better way to implement this routine so that I don't have to repeat the same instructions just because the array type has changed. (Note that the variable t needs to have the same type as a).

  2. Is there a way to make gfortran use the non-traditional preprocessor?

  3. If not, how would I do what I want to do with the traditional preprocessor?

Upvotes: 2

Views: 1017

Answers (1)

jme52
jme52

Reputation: 1123

  1. You can implement what you are looking for by using compiler-dependent preprocessor macros. Note that a similar case was already discussed and answered in Concatenating an expanded macro and a word using the Fortran preprocessor. I believe it could be adapted to your case as follows:

    #if defined(__GFORTRAN__) || defined(NAGFOR)
    #define PASTE(a) a
    #define ADD_TRAIL_USCORE(a) PASTE(a)_
    #define CAT(a,b) ADD_TRAIL_USCORE(a)b
    #else
    #define PASTE(a,b) a ## _ ## b
    #define CAT(a,b) PASTE(a,b)
    #endif
    
    #define SWAP CAT(swap,TYPE_KIND)
    

    Note that

    • The above-mentioned answer states that "basically most Fortran compilers (i.e. Intel and PGI) use a relatively normal C-preprocessor with a token pasting operator". My understanding is that the NAG compiler also can't deal with ##, and hence needs to be added to the exception.
    • In the last #define above, there is no space between swap, and TYPE_KIND.
  2. Not that I know of.
  3. As @Vladimir pointed out in the comments, you probably don't want to do that, since it would break the Fortran concatenation operator //.

Upvotes: 1

Related Questions