如何在fortran 90/95中使用内部类型进行一些通用编程

时间:2014-06-05 15:46:47

标签: fortran fortran90 generic-programming fortran95

我想编写一些适用于不同类型的程序。我打算使用在herehere描述的flib中使用的“include”方法。我在这里给出一个简单的例子。

  ! -------------------------------------------------------------- ! 
  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

包含文件:

  contains

  subroutine printme(a)

  implicit none

  type(T) :: a

  print *,a

  end subroutine printme

困扰我的是它似乎只适用于派生类型,而不适用于内在类型。如果模块mod的用户想要在一个简单的整数上使用printme例程,那么将它封装在一个ivalue类型中并且无法做到这一点真的很烦人:

integer :: a=42
call printme(a)

有没有办法将此方法扩展为内部类型,或者在严格的f90 / f95中执行此操作的另一种方法(由于数据复制,我不想使用“transfer”方法)

坦克!

3 个答案:

答案 0 :(得分:6)

您可以在所有主要的Fortran编译器中使用C预处理器(CPP)。通常有一个用于调用它的标志(gfortran中为-cpp),或者如果文件后缀包含大写字母F(.F90.F),则会自动调用它。预处理器允许使用宏来更强大地包含源。

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

和template.f90

contains

subroutine printme(a)

  implicit none

  T :: a

  print *,a

end subroutine printme

这不是 strict f90 / f95,但它使用编译器中包含的预处理器,它会生成另一个(严格的f95)源文件,它会自动编译它而不是包含原始源的文件宏。

编译很简单

gfortran -cpp main.f90

<强> - 编辑 -

对于非信徒,如果你想看到一些使用它的真实代码,请检查https://github.com/LadaF/fortran-list(免责声明:我自己的代码)。您可以在其中使用参数链表:

len(20)个字符串列表:

module str_list

#define TYPEPARAM character(20)

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

整数列表

module int_list

#define TYPEPARAM integer

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

某些派生类型的列表

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

答案 1 :(得分:1)

您可以使用隐式输入。确保你洗手 - 因为这打开了与隐式打字相关的常见错误的可能性。

考虑替换你的模块imod。

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

(我已将contains语句移动到包含模块中 - 因为它允许您拥有多个模板化包含文件。)

然后是包含文件中的过程,如下所示:

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

如果您想为内部类型模板printme,那么您只需适当更改父模块中的隐式语句。


这是值得商榷的,但也有一种观点认为您可以使用模块重命名工具为内在类型引入新名称。如果是这样,并且如果你有一个F2008编译器(所以我们不是严格说F95)那么你当前的方法应该仍然能够工作 - 使用一个中间模块允许重命名内部整数类型以具有名称T

但是这会混淆我以某种方式使用的大多数(所有?)编译器。考虑到这一点,再加上有争议的合法性,加上它要求F2008,它不是一个真正的解决方案。

答案 2 :(得分:0)

实现此目的的另一种方法是使用 Python 预处理 Fortran 源代码。在我正在处理的代码中,我们使用 this Python script 来预处理 Fortran 模板。例如,它用于为 MPI 函数编写接口,如下面的 Fortran 代码片段:

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

然后,在构建时,Makefile 调用以下行将模板预处理为 Fortran 源代码文件以进行编译:

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

子例程中 #ftype 的所有实例都被内部类型 integer(2)integer(4)、... logical 替换。因此,这种方法适用于内在类型!

需要通过 MPI 发送数据的其他部分代码只需要 use mod_mpi_grid 和通用子例程 call mpi_grid_send( ... )