6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

FortranのGenerics (template)の現状

Last updated at Posted at 2023-05-08

概要

Fortranのtemplateは,先の記事から結構変化していて,私の要望に近づいています.

FortranのGenericsの現状

先の記事で,FortranのGenerics(主にtemplate)について紹介し,自身の要望を述べました.

その後,Genericsを策定するチームのメンバーがtemplateの現状について説明している動画を紹介していただいたので,現状を改めて紹介まします.

axpy

動画で説明されていたaxpyの例を紹介します.

axpyは,$$y = ax + y$$を計算する手続の総称です.二つのベクトルx, yを受け取り,xをスカラ倍(factorがa)した結果をyに加算します.Fortranでは,簡単に

subroutine axpy(a, x, y)
  real, intent(in) :: a
  real, intent(in) :: x(:)
  real, intent(inout) :: y(:)

  y = y + a*x
end subroutine

と書けます.これは単精度浮動小数点数に対するaxpyなので,single-precisionのsを付けてsaxpyと呼ばれます.単精度以外にも,倍精度浮動小数点数に対するaxpy(daxpy)もあります.
現在検討中のtemplateでは,saxpyとdaxpyはまとめて次のように書ける(ようになる)そうです.

template axpy_tmpl(k)
  private
  public :: axpy
  integer, constant :: k
  interface axpy
    procedure axpy_
  end interface
constants
  subroutine axpy_(a, x, y)
    real(k), intent(in) :: a
    real(k), intent(in) :: x(:)
    real(k), intent(inout) :: y(:)

    y = y + a*x
  end subroutine
end template

テンプレートをインスタンス化し,呼び出すには下記のようにします.

integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(sp)
instantiate axpy_tmpl(dp)
real(sp) :: a, x(10), y(10)
real(dp) :: da, dx(10), dy(10)

call axpy(a, x, y)
call axpy(da, dx, dy)

型の種別を表す定数integer, constant :: kは,parameterized derived typeであったinteger, kindの拡張と見なせるでしょう.これは実数型の種別を切り替えるだけですが,requirementの要求が不要であること,インスタンス化した際に同じ名前が利用でき,引数の型(種別)で呼び出される手続が切り替えられていることは,非常に素晴らしいことだと思います.(interface宣言なしでそれが可能ならなお素晴らしいのですが…)

次に,任意の型を引数に取れるように発展させます.任意の型を引数に取り,axpyを計算するには,加算と乗算を行う演算子が定義されていなければなりません.任意の型に対応するには,先の記事で紹介した通りにrequirementが必要なようです.また,演算を行う手続のインタフェースも必要です.

型パラメータTは,これまでのtype T; end type(空のユーザ定義派生型定義)から,type, deferred :: Tという宣言に変わっています.deferredは,現状の仕様では遅延宣言とよばれるオブジェクト指向プログラミングの機能で,C++のvirtual(仮想関数の定義)に対応しています.それを変数に拡張しようとしているのでしょう.これはよい拡張だと思います.

requirement bin_op(T, op)
  type, deferred :: T
  elemental function op(a, b)
    type(T), intent(in) :: a, b
    type(T) :: op
  end function
end requirement

関数テンプレートでは,任意の型に対する加算と乗算の抽象インタフェース(のようなものを)requiresで記述します.

template axpy_tmpl(T, plus, times)
  private
  public :: axpy
  requires bin_op(T, plus)
  requires bin_op(T, times)
  interface axpy
    procedure axpy_
  end interface
constants
  subroutine axpy_(a, x, y)
    type(T), intent(in) :: a
    type(T), intent(in) :: x(:)
    type(T), intent(inout) :: y(:)

    y = plus(y, times(a, x))
  end subroutine
end template

これで,加算や演算が定義されていない型Tが与えられたときの問題は排除できます.先の記事では,既に演算子が定義されている組込型に対しても,いちいち加算や乗算を書かなければならないなら総称名でよいと書きました.現状では,インスタンス化の際にオペレータは必要になるものの,既定のオペレータを渡すことでその問題を回避しているようです.

integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(real(sp), operator(+), operator(*))
instantiate axpy_tmpl(real(dp), operator(+), operator(*))
instantiate axpy_tmpl(integer, operator(+), operator(*))

real(sp) :: a, x(10), y(10)
real(dp) :: da, dx(10), dy(10)
integer :: ia, ix(10), iy(10)

call axpy(a, x, y)
call axpy(da, dx, dy)
call axpy(ia, ix, iy)

異なる型同士の演算については,上記の例を素直に拡張して,

requirement bin_op(T, U, V, op)
  type, deferred :: T, U, V
  elemental function op(a, b)
    type(T), intent(in) :: a
    type(U), intent(in) :: b
    type(V) :: op
  end function
end requirement

template axpy_tmpl(T, U, V, plus, times)
  private
  public :: axpy
  requires bin_op(T, U, V, plus)
  requires bin_op(T, U, V, times)
  interface axpy
    procedure axpy_
  end interface
constants
  subroutine axpy_(a, x, y)
    type(T), intent(in) :: a
    type(U), intent(in) :: x(:)
    type(V), intent(inout) :: y(:)

    y = plus(y, times(a, x))
  end subroutine
end template

とし,インスタンス化の際に適切な型を当てはめます.

integer, parameter :: sp = kind(1.0), dp = kind(1.d0)
instantiate axpy_tmpl(real(sp), integer, real(dp), operator(+), operator(*))

real(sp) :: a
real(dp) :: dy(10)
integer :: ix(10)

call axpy(a, ix, dy)

異なる型間の加算や乗算は定義していませんが,組込型であれば,Fortranに備わっている機能(暗黙の型変換)で実行できるということでしょう.

例に対する私の感想

先の記事と比較すると,私が希望していた内容に近くなってきていると思います.急にどうした…?

しかし,相変わらず記述量が多いのは気になります.requirementrequires,関数テンプレート内のinterface等々.組込型の種別を変えるだけの例ではrequirementが必要ではなかったのですから,既定の組込型と演算に限ってrequirementが不要とかはできないものでしょうか.type, intrinsic, deferred :: Tとかで識別自体はできる気がします.characterlogicalが入ってきたときのことを考えると,完璧ではないでしょうが…
interfaceは,インスタンス化した関数テンプレートに同じ名前を使うために必要なのだと推察します.interfaceを書くか別名を与えるかになるのでしょうか?

まとめ

かなり良くなっていました.Fortranは記述量が多いため,なるべく記述を簡略化できる方向で改善していってもらえるとうれしいと思います.あとは,スカラ変数と配列,あるいは配列次元をどうテンプレート化できるようになるのか気になります.

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?