FPM: Preprocessing into multiple files (real32 and real64)

Hi,

I’m wondering if there’s any support for using a preprocessor to generate different versions of a file which are all expected to be compiled into a single library. In meson I would use compiler.preprocess.

E.g. I have a file foo.F90 that should be compiled into foo_r32.f90 and foo_r64.f90 and I want to create a single library that allows for using either.

foo.F90

module foo_MYPRECISION
   ...
end module foo_MYPRECISION
gfortran -cpp -D"foo_MYPRECISION=foo_r32" -E -P foo.F08 -o foo_r32.f90
gfortran -cpp -D"foo_MYPRECISION=foo_r64" -E -P foo.F08 -o foo_r64.f90

I know the code could be refactored to use some looping from fypp but that’s not an option at the moment.

FPM supports preprocessing with cpp.
In your toml, you can add the following section:

[preprocess]
cpp.suffixes = ["F90", "f90"]
cpp.macros = ["_WIN32", "_FPM", "_NOEXPORT"]

For your specific case you may want to write your loop manually with a combination of define/include/undef

There is a kind of “poor man’s template” pattern I’ve seen people use. Something like

module foo_real32
  use iso_fortran_env, only: wp => real32
contains
  include "impl.inc"
end module
module foo_real64
  use iso_fortran_env, only: wp => real64
contains
  include "impl.inc"
end module
! impl.inc
  subroutine stuff(x)
    real(wp) :: x
    ...
  end subroutine
  subroutine other(y)
    real(wp) :: y
    ...
  end subroutine
...

Not sure if it’s feasible in your situation, but it does avoid the need for a preprocessor.

@vsnyder used that expression in (one of) his article(s) on include:

If I’m not mistaken this is also how the Fortran Template Library works (see the Introduction section in the readme).

I use that method exactly. Sometimes I mix INCLUDE and other preprocessors so I might use something just like what you show but use the fpp/cpp #include with a #define right before that sets something like #define mytype real32 and then have some conditional #if/#else/#endif in the file based on the defined variable(s).

I have not called INCLUDE a “Poor man’s template” but I have called it the most minimalist preprocessor of any computer language.

Funny, I do something almost exactly opposite. I have a module that defines kinds:

module mo_kind
  use, intrinsic :: iso_c_binding, only: c_float, c_double, c_long, c_int, c_bool
  implicit none
  public
  integer, parameter :: dp = c_double, sp = c_float, i8 = c_long, i4 = c_int
  !
  ! Floating point working precision
  !
#ifdef USE_SP
  integer, parameter :: wp = sp
#else
  integer, parameter :: wp = dp
#endif

  !
  ! Logical - for use with kernels
  !
  ! Note that c_boolians don't work with PGI compiler currently
  !
#ifdef USE_CBOOL
  integer, parameter :: wl = c_bool
#else
  integer, parameter :: wl = kind(.true.)
#endif

end module mo_kind

which other modules all rely on:

! -------------------------------------------------------------------------------------------------
module mo_foo
  use mo_kind,       only: wp, wl 
contains
  function a(x, y) 
    real(wp), intent(in) :: x, y
    logical(wl)              :: a
  ...
  end function 
end module

This mean I have a default value and need to compile with -DUSE_SP if I want everything to be in single precision

1 Like

Thanks for the suggestions. In this case, we have something the team is happy with and I was hoping to add fpm support on top of it rather than changing the code.

I think I’ll just look into making the fpm build work with either real64 or real32 rather than both at the same time. Which is a shame because it means it’s not going to be quite as good as the meson build.

Since you are already using the C-like preprocessor, it sounds like you just need to add a macro for use as a suffix for each procedure name. Then define the macro to a value like “r32” or “r64”.

Once done, you can then create a module that USEs the two modules, and associates each of the two specific procedures with a generic name. E.g.,

module foo
  use foo_r32
  use foo_r64
  implicit none

  interface foo_sub1
    module procedure foo_sub1_r32
    module procedure foo_sub1_r64
  end interface

end module

This doesn’t fix the problem of having to generate 2 files with cpp which is the obstacle for supporting fpm in the project.

Strip all the subroutines and functions into a separate file. Then just #include the file twice in the foo module. Do the appropriate #defines before each #include. (Will have to do #undef between them.)

module foo
  implicit none

  interface foo_sub1
    module procedure foo_sub1_r32
    module procedure foo_sub1_r64
  end interface

contains

#define foo_MYPRECISION foo_r32
#include "foo_procs.F90"
#undef foo_MYPRECISION

#define foo_MYPRECISION foo_r64
#include "foo_procs.F90"

end module
1 Like