Fortran polymorphism with CMake

Fortran polymorphism and generic programming (sets, lists, generators) are consistently the highest ranked survey feature requests for the next Fortran standard. Fortran programmers introduce polymorphic procedures and variables into modern Fortran by the following methods.

C preprocessor #ifdef etc. is simplest, and is widely supported and used. C preprocessor is invoked by convention when the source file suffix is capitalized like .F90. Build systems like CMake and Meson introspect Fortran source code, and so it’s important to use uppercase filename suffix if a Fortran source file needs preprocessing.

Fortran 2003 static procedure polymorphism is simple to use and is a standard Fortran language feature.

Fortran derived types with duplicated procedures for every desired type/kind. This is more verbose to use, but it is the most powerful and flexible true Fortran polymorphism.

The preprocessor method might be thought of as compile-time polymorphism. It’s not a perfect solution, and not true polymorphism since each procedure still requires exactly one type/kind per argument. However, this technique combined with static polymorphism is simple to develop and handles many real-life use cases quickly and easily.

Example: compile-time Fortran polymorphic REAL For each REAL variable and function, make kind=wp. For example polyreal.F90 (notice the capital F90):

program demo
use, intrinsic:: iso_fortran_env
implicit none (type, external)

#if REALBITS==32
integer,parameter :: wp=real32
#elif REALBITS==64
integer,parameter :: wp=real64
#elif REALBITS==128
integer,parameter :: wp=real128
#endif

real(wp) :: pi,b
integer :: i

pi = 4._wp * atan(1._wp)

b = timestwo(pi)

print *,'pi',pi,'2pi',b

contains

elemental real(wp) function timestwo(a) result(b)

real(wp), intent(in) :: a

b = 2*a

end function timestwo

end program

Make a command-line options -Drealbits=64 or -Drealbits=32 etc. in CMakeLists.txt:

project(realpoly Fortran)

if(NOT realbits)
  set(realbits 64)
endif()

# your modules and programs
add_executable(poly polyreal.f90)
target_compile_definitions(poly PRIVATE REALBITS=${realbits})

We typically have a comm.f90 that contains various constants including wp used throughout a program.

Generate then build as usual:

cmake -Drealbits=64 -B build

pi 3.1415926535897931 2pi 6.2831853071795862

That uses double-precision real64 variables and functions. The concept is trivially extensible to large programs consisting of many files and modules.

To then select a different kind and rerun, perhaps to evaluate accuracy vs. runtime tradeoffs (real32 is generally faster than real64, but less accurate):

cmake -Drealbits=32 -B build

pi 3.14159274 2pi 6.28318548

or for quad-precision Fortran real128:

cmake -Drealbits=128 -B build

pi 3.14159265358979323846264338327950280 2pi 6.28318530717958647692528676655900559