【问题标题】:Template in Fortran?Fortran 中的模板?
【发布时间】:2014-07-05 13:59:52
【问题描述】:

我有一个模块,它定义了三种类型以及对它们的一些操作。

在一个单独的模块中,我想定义一个算法,该算法使用模块中定义的操作对这些类型之一进行操作。无论类型如何,算法都是相同的。我可以重载它,但我想知道是否可以通过定义某种模板算法来节省大量输入。我想到的是类似于 C++ 中的类模板。

谢谢

【问题讨论】:

    标签: class templates fortran


    【解决方案1】:

    Fortran 没有模板,但是您可以将处理不同类型的函数的通用代码放在一个包含文件中,作为模拟模板的组合,如下代码所示:

    ! file "cumul.inc"
    ! function cumul(xx) result(yy)
    ! return in yy(:) the cumulative sum of xx(:)
    ! type, intent(in) :: xx(:)
    ! type             :: yy(size(xx))
    integer :: i,n
    yy = 0
    n  = size(xx)
    if (n < 1) return
    yy(1) = xx(1)
    do i=2,n
       yy(i) = yy(i-1) + xx(i)
    end do
    return
    ! end function cumul
    ! end file "cumul.inc"
    
    module foo
    implicit none
    integer, parameter :: sp = kind(1.0), dp = kind(1.0d0)
    interface cumul
       module procedure cumul_double,cumul_real,cumul_int
    end interface cumul
    contains
    !
    function cumul_double(xx) result(yy)
    real(kind=dp), intent(in) :: xx(:)
    real(kind=dp)             :: yy(size(xx))
    include "cumul.inc"
    end function cumul_double
    !
    function cumul_real(xx) result(yy)
    real(kind=sp), intent(in) :: xx(:)
    real(kind=sp)             :: yy(size(xx))
    include "cumul.inc"
    end function cumul_real
    !
    function cumul_int(xx) result(yy)
    integer, intent(in) :: xx(:)
    integer             :: yy(size(xx))
    include "cumul.inc"
    end function cumul_int
    end module foo
    
    program xcumul
    use foo, only: cumul
    print*,cumul([10,20,30])
    print*,cumul(sin([10.0,20.0,30.0]))
    print*,cumul(sin([10.0d0,20.0d0,30.0d0]))
    end program xcumul
    ! output:
    ! 10 30 60
    ! -0.5440211 0.36892414 -0.6191075
    ! -0.5440211108893698 0.3689241398382579 -0.6191074842546039
    

    论文中提到的工具

    Car、David 和 Michael List(2010 年)。 PyF95++:Fortran 95/2003 语言的模板功能。 ACM Fortran 论坛 29(1), 2-20.

    您可能会感兴趣。我没试过。

    【讨论】:

    【解决方案2】:

    我的答案类似于@Fortranner 的答案,但我通常将整个实现放在包含文件中。 它的优点是可以由一个单独的包含文件处理多个过程,并且更易于维护,例如 你可以看到整个过程的定义等。

    需要一些预处理器(参见PASTECONCATHELP 等),但它可以工作。 @Fortranner 给出的相同示例可以写成如下。

    主文件main.f90

    program main
      use cumul_m, only: cumul
      implicit none
    
      print *, cumul([1,     2,     3    ])     ! output: 1   3   6
      print *, cumul([1.1,   2.2,   3.3  ])     ! output: 1.1 3.3 6.6
      print *, cumul([1.1d0, 2.2d0, 3.3d0])     ! output: 1.1 3.3 6.6
    end program
    

    模块文件cumul.f90:

    module cumul_m
      use iso_fortran_env, only: real32, real64
      implicit none
    
      private
      public cumul
    
      interface cumul
        module procedure :: cumul_int, cumul_sp, cumul_dp
      end interface
    
    contains
    
    #define T int
    #define TT integer
    #include "cumul_imp.f90.inc"
    
    #define T sp
    #define TT real(real32)
    #include "cumul_imp.f90.inc"
    
    #define T dp
    #define TT real(real64)
    #include "cumul_imp.f90.inc"
    
    end module
    

    实现的包含文件cumul_imp.f90.inc:

    #define PASTE(X)         X
    #define PASTE2(X)        PASTE(X)_
    #define CONCATHELP(X, Y) PASTE2(X)Y
    #define CONCAT(X, Y)     CONCATHELP(X,Y)
    
    #define CUMUL            CONCAT(cumul,T)
    
    function CUMUL(x) result(y)
      !! the cumulative sum of x
    
      TT, intent(in) :: x(:)
        !! array of values
      TT             :: y(size(x))
        !! cumulative sum of x
    
      integer :: i, n
    
      n = size(x)
      if (n < 1) return
    
      y(1) = x(1)
      do i = 2, n
        y(i) = y(i-1) + x(i)
      end do
    end function
    
    #undef T
    #undef TT
    
    #undef PASTE
    #undef PASTE2
    #undef CONCATHELP
    #undef CONCAT
    
    #undef CUMUL
    

    请注意,编译时需要启用预处理器(例如,-cpp 用于 gfortran):

    $ gfortran -cpp -c cumul.f90
    $ gfortran cumul.o main.f90 
    $ ./a.out
               1           3           6
       1.10000002       3.30000019       6.60000038    
       1.1000000000000001        3.3000000000000003        6.5999999999999996
    

    【讨论】:

      【解决方案3】:

      新兴的Fortran Standard Library 项目使用@BálintAradi 的fypp 预处理器进行模板化。

      例如,您可以定义应为其生成子程序的类型和种类,并在for 循环中为所有选定的类型和种类循环:

      #:set REAL_KINDS = ["sp", "dp", "qp"]
      #:set REAL_TYPES = ["real({})".format(k) for k in REAL_KINDS]
      
      ...
      
      #:set RCI_KINDS_TYPES = REAL_KINDS_TYPES + CMPLX_KINDS_TYPES + INT_KINDS_TYPES
      
      #:for k1, t1 in RCI_KINDS_TYPES
            module procedure trace_${t1[0]}$${k1}$
      #:endfor
      

      使用一些 fypp 宏来包含文件

      #:include "macrodefs.fypp"
      

      然后您可以使用与其他预处理器非常相似的技术,例如在 jacks answer 以及此站点上的其他问题和答案中使用的 cpp(例如我的 https://stackoverflow.com/a/24070364/721644)。

      上面的 for 循环对于为现有类型实例化模板非常方便。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-17
        • 2014-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多