How to make some generic programming in fortran 90/95 working with intrinsic types
Asked Answered
K

3

7

I would like to program some procedure that will work with different types. I am planning to use the "include" method used in flibs described here and here. I give here a simple exemple.

  ! -------------------------------------------------------------- ! 
  module data_type

  type ivalue
  integer :: v
  end type

  type rvalue
  real(8) :: v
  end type

  end module data_type
  ! -------------------------------------------------------------- ! 
  module imod

  use data_type, only: T => ivalue 

  include "template.f90"

  end module imod
  ! -------------------------------------------------------------- ! 
  module rmod

  use data_type, only: T => rvalue 

  include "template.f90"

  end module rmod
  ! -------------------------------------------------------------- ! 
  module mod

  use imod, only:
 &     ivalue => T,
 &     iprintme => printme

  use rmod, only:
 &     rvalue => T,
 &     rprintme => printme

  private
  public :: ivalue, rvalue
  public :: printme

  interface printme
  module procedure iprintme
  module procedure rprintme
  end interface printme

  end module mod
  ! -------------------------------------------------------------- !
  program hello

  use mod

  implicit none

  type(ivalue) :: iv
  type(rvalue) :: rv

  iv%v=42
  rv%v=3.14

  call printme(iv)
  call printme(rv)      

  end program hello

with the included file:

  contains

  subroutine printme(a)

  implicit none

  type(T) :: a

  print *,a

  end subroutine printme

What bothers me is that it seems only to work with derived type, and not with intrinsic types. If the user of the module mod want to use the printme routine on an simple integer, it is really annoying for him to encapsulate it in a ivalue type and cannot doing:

integer :: a=42
call printme(a)

Is there any way to extend this method to intrinsic types, or another method that would do it in strict f90/f95 (I don't want to use the "transfer" method because of the data copy)

Tanks!

Kincardine answered 5/6, 2014 at 15:46 Comment(1)
Use the CPP preprocessor's #include, instead of the Fortran include.Microclimatology
S
8

You can use the C preprocessor (CPP) in all major Fortran compilers. Usually there is a flag for invoking it (-cpp in gfortran) or it is invoked automatically if the file suffix contains capital F (.F90, .F). The preprocessor allows more powerful inclusion of sources with the usage of macros.

module imod

  use data_type, only: ivalue 

#define T type(ivalue)
#include "template.f90"
#undef T

end module imod


module intmod

#define T integer
#include "template.f90"
#undef T

end module intmod

and template.f90

contains

subroutine printme(a)

  implicit none

  T :: a

  print *,a

end subroutine printme

This is not strict f90 / f95, but it uses a preprocessor, included in the compilers, which produces another (strict f95) source file and it automatically compiles it instead of the original source that contains the macros.

The compilation is then straightforward

gfortran -cpp main.f90

--Edit--

For non-believers, if you want to see some real code using this, check https://github.com/LadaF/fortran-list (disclaimer: my own code). You can use the parametric linked list there as:

list of len(20) character strings:

module str_list

#define TYPEPARAM character(20)

#include "list-inc-def.f90"
contains
#include "list-inc-proc.f90"
#undef TYPEPARAM
end module

list of integers

module int_list

#define TYPEPARAM integer

#include "list-inc-def.f90"
contains
#include "list-inc-proc.f90"
#undef TYPEPARAM
end module

list of some derived type

module new_type_list
  use, new_type_module, only: new_type

#define TYPEPARAM type(newtype)

#include "list-inc-def.f90"
contains
#include "list-inc-proc.f90"
#undef TYPEPARAM
end module
Sarcenet answered 5/6, 2014 at 21:20 Comment(3)
If ivalue is an intrinsic type, as per the question, you need F2008 for the resulting type declaration syntax.Caridadcarie
No, really not, my compilers do not support this f2008 feature, which I am aware of quite well. See the difference, I use 'T ::' not, 'type(T) :: ', also, there is no renaming in use.Microclimatology
Ah true - I missed that in the definition.Caridadcarie
C
1

You can use implicit typing. Make sure you wash your hands though - as this opens the possibility for the usual errors associated with implicit typing.

Consider a replacement for your module imod.

module imod
  use data_type    ! oops - I forgot this.
  implicit type(itype) (q)
contains
  include 'template.f90'
end module imod

(I've moved the contains statement into the including module - as it then allows you to have more than one templated include file.)

and then a procedure in included file that looks like:

 ! Assume anything starting with q is the type to be templated.
 subroutine printme(q_arg)
   print *, q_arg
 end subroutine printme

If you wanted to template printme for an intrinsic type, then you just change the implicit statement in the parent module appropriately.


It's debatable, but there's also the view that you can use the module renaming facility to introduce new names for intrinsic types. If so, and if you have a F2008 compiler (so we aren't talking strict F95) then your current approach should still be able to work - using an intermediate module to allow renaming of the intrinsic integer type to have a name T.

But this confuses most (all?) compilers I've used in some way. Given that, plus the debatable legality, plus it requiring F2008, it's not a real solution.

Caridadcarie answered 6/6, 2014 at 2:17 Comment(0)
S
0

Another way to achieve this is using Python to preprocess the Fortran source code. In the code that I'm working on, we use this Python script to preprocess the Fortran template. For instance, this is used to write interfaces for MPI functions, like this Fortran code snippet below:

module mod_mpi_grid
  implicit none

@python ftypes=["integer(2)","integer(4)","integer(8)","real(4)","real(8)","complex(4)","complex(8)","logical"]
@python fsuffixes=["_i2","_i4","_i8","_f","_d","_c","_z","_l"]
@python fsizeof=["2","4","8","4","8","8","16","2"]
@python fmpitypes=["MPI_INTEGER2","MPI_INTEGER","MPI_INTEGER8","MPI_REAL","MPI_DOUBLE_PRECISION","MPI_COMPLEX","MPI_DOUBLE_COMPLEX","MPI_LOGICAL"]
@python ntypes=8

  interface mpi_grid_send
@template begin
@template variable fsuffix
@python for i in range(ntypes): fsuffix=fsuffixes[i];
    module procedure mpi_grid_send#fsuffix
@template end
  end interface

contains

:

@template begin
@template variable fsuffix
@template variable ftype
@template variable fmpitype
@python for i in range(ntypes): fsuffix=fsuffixes[i]; ftype=ftypes[i]; fmpitype=fmpitypes[i];
  subroutine mpi_grid_send#fsuffix(val,n,dims,dest,tag)
    use mpi
    implicit none

    ! arguments
    #ftype, intent(in) :: val
    integer, intent(in) :: n
    integer, dimension(:), intent(in) :: dims
    integer, dimension(:), intent(in) :: dest
    integer, intent(in) :: tag

    ! local variables
    integer :: comm, dest_rank, req, ierr
    integer :: idims(0:ndmax)

    idims = convert_dims_to_internal(dims)
    comm = mpi_grid_get_comm_internal(idims)
    dest_rank = mpi_grid_rank_internal(comm,idims(0),dest)
    call mpi_isend(val,n,#fmpitype,dest_rank,tag,comm,req,ierr)

    return
  end subroutine mpi_grid_send#fsuffix
@template end

:

end module mod_mpi_grid

Then, at build time, the Makefile calls the following line to preprocess the template into a Fortran source code file for compilation:

python ftemplate.py < src/addons/mod_mpi_grid.tpl > src/addons/mod_mpi_grid.f90

All instances of #ftype in the subroutine are replaced by the intrinsic types integer(2), integer(4), ... logical. So, this approach works perfectly with intrinsic types!

The other parts of the code that need to send data over MPI would simply need to use mod_mpi_grid and the generic subroutine call mpi_grid_send( ... ).

Slung answered 17/5, 2021 at 18:54 Comment(3)
And presumably one writes another preprocessor step to populate the list of intrinsic kind parameters?Gigolo
BTW, there is also FYPP github.com/aradi/fypp We ought to have an answer showing it, but I would prefer if it is written by someone who actually uses it.Microclimatology
@Gigolo That's preprocessor inception!Slung

© 2022 - 2024 — McMap. All rights reserved.