【Fortran】サブルーチンの引数にサブルーチンを渡す

`subroutine`や`function`といった手続きの引数に他の手続きを渡すには，`interface`文を書くだけで良い．
`external`文という機能もあったそうだが，現在では非推奨．
この記事では，特に「手続きが複数あり，それを引数として渡すことで実行するものを選択したい」という場合に焦点．
（例：ルンゲ・クッタ法をいろいろな関数に適用したい）

# 例

```Fortran:args1.f90
module mymod
implicit none
contains

subroutine sub1(a, b)
    integer, intent(in) :: a, b
    write(*, *) 'sub1', a + b
end subroutine sub1

subroutine sub2(a, b)
    integer, intent(in) :: a, b
    write(*, *) 'sub2', a - b
end subroutine sub2

subroutine wrapper(sub, a, b)
    interface ! ここで引数がサブルーチンであることを明示
        subroutine sub(a, b)
            integer, intent(in) :: a, b
        end subroutine sub
    end interface ! ここまで

    integer, intent(in) :: a, b
    call sub(a, b)
end subroutine wrapper

end module mymod


program main
    use mymod
    implicit none
    call sub1(5, 3) ! sub1 8
    call sub2(5, 3) ! sub2 2
    call wrapper(sub1, 5, 3) ! sub1 8
    call wrapper(sub2, 5, 3) ! sub2 2
end program main
```

# 引数にする手続きが，異なる個数の引数をとるとき1

1つの解決策は，全ての手続きの引数の数を揃えること．
単体でも動くように`optional`属性をつけても良いが（例えば下の例の`sub1`の引数`c`），
`sub2`，`wrapper`にも`optional`属性をつける必要がある．

```Fortran:args2.f90
module mymod
implicit none
contains

subroutine sub1(a, b, c)
    integer, intent(in) :: a, b, c
    write(*, *) 'sub1', a + b
end subroutine sub1

subroutine sub2(a, b, c)
    integer, intent(in) :: a, b, c
    write(*, *) 'sub2', a * b * c
end subroutine sub2

subroutine wrapper(sub, a, b, c)
    interface
        subroutine sub(a, b, c)
            integer, intent(in) :: a, b, c
        end subroutine sub
    end interface

    integer, intent(in) :: a, b, c
    call sub(a, b, c)
end subroutine wrapper

end module mymod


program main
    use mymod
    implicit none
    call sub1(5, 3, 2)
    call sub2(5, 3, 2)
    call wrapper(sub1, 5, 3, 2)
    call wrapper(sub2, 5, 3, 2)
end program main
```

# 引数にする手続きが，異なる個数の引数をとるとき2

もう1つの解決策は，引数を配列で渡すこと．
但しFortranには配列を展開して手続きに渡すという機能は存在しないため，配列を受けて展開するというためだけの手続きを用意する必要がある（下の例の`exe_sub*`）．

- 配列のサイズに関する`select`文や，配列を使わずに`if (present())`で展開出来なくもないが，実際に呼び出す手続きの間で`intent()`属性が異なる場合，より複雑になってしまう
  - `intent()`属性を削除するという手もあるが，流石にまずいと思う
  - `exe_sub3(arr_inout, arr_out, arr_in)`のように，`intent()`属性に応じて配列を分けることを想定
- いっそのこと手続きを引数にせず，文字列で`'sub1'`等と指定して`select`文で分岐させる方が楽？

``` Fortran:args3.f90
module mymod
implicit none
contains

subroutine sub1(a, b)
    integer, intent(in) :: a, b
    write(*, *) 'sub1', a + b
end subroutine sub1

subroutine sub2(a, b, c)
    integer, intent(in) :: a, b, c
    write(*, *) 'sub2', a * b * c
end subroutine sub2

subroutine exe_sub1(arrin)
    integer, intent(in) :: arrin(:)
    call sub1(arrin(1), arrin(2))
end subroutine exe_sub1

subroutine exe_sub2(arrin)
    integer, intent(in) :: arrin(:)
    call sub2(arrin(1), arrin(2), arrin(3))
end subroutine exe_sub2

subroutine wrapper(sub, arrin)
    interface
        subroutine sub(arrin)
            integer, intent(in) :: arrin(:)
        end subroutine sub
    end interface

    integer, intent(in) :: arrin(:)
    call sub(arrin(:))
end subroutine wrapper

end module mymod


program main
    use mymod
    implicit none
    call sub1(5, 3)
    call sub2(5, 3, 2)
    call wrapper(exe_sub1, (/5, 3/))
    call wrapper(exe_sub1, (/5, 3, 2/))
    call wrapper(exe_sub2, (/5, 3, 2/))
end program main
```


参考：
[あらきけいすけの雑記帳: fortran でサブルーチンの引数にサブルーチンを渡す][1]
[stackoverflow: How to pass subroutine names as arguments in Fortran?][2]

[1]: https://arakik10.hatenadiary.org/entry/20080531/p0
[2]: https://stackoverflow.com/questions/32809769/how-to-pass-subroutine-names-as-arguments-in-fortran