【发布时间】:2014-07-05 13:59:52
【问题描述】:
我有一个模块,它定义了三种类型以及对它们的一些操作。
在一个单独的模块中,我想定义一个算法,该算法使用模块中定义的操作对这些类型之一进行操作。无论类型如何,算法都是相同的。我可以重载它,但我想知道是否可以通过定义某种模板算法来节省大量输入。我想到的是类似于 C++ 中的类模板。
谢谢
【问题讨论】:
我有一个模块,它定义了三种类型以及对它们的一些操作。
在一个单独的模块中,我想定义一个算法,该算法使用模块中定义的操作对这些类型之一进行操作。无论类型如何,算法都是相同的。我可以重载它,但我想知道是否可以通过定义某种模板算法来节省大量输入。我想到的是类似于 C++ 中的类模板。
谢谢
【问题讨论】:
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.
您可能会感兴趣。我没试过。
【讨论】:
我的答案类似于@Fortranner 的答案,但我通常将整个实现放在包含文件中。 它的优点是可以由一个单独的包含文件处理多个过程,并且更易于维护,例如 你可以看到整个过程的定义等。
需要一些预处理器(参见PASTE、CONCATHELP 等),但它可以工作。
@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
【讨论】:
新兴的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 循环对于为现有类型实例化模板非常方便。
【讨论】: