複数の引数を持つ関数を一つの引数の関数として関数等から呼びたいin Fortran

Fortranで、一変数関数f(x)を引数とする関数g(f)があった時、このgの中にf2(x,y)のyを固定したものf2(x,10)等を入れる方法についてです。多分Fortran2008からできるらしいです。
https://stackoverflow.com/questions/31208594/how-to-pass-a-function-with-multiple-arguments-to-a-subroutine-that-expects-a-fu/31208717#31208717

これを応用すると、オブジェクト指向で作ったオブジェクトに付随する関数も引数として呼べるようになります。

#バージョン
gfortran: gcc version 8.2.0 (Homebrew GCC 8.2.0) 

#コード

Fortranで以下のmoduleを考えます。

```fortran
module test
    contains
    subroutine tasu(a)
        implicit none
        real(8),intent(in)::a
        write(*,*) "a+10=",a+10 
    end subroutine tasu

    subroutine kakeru(a)
        implicit none
        real(8),intent(in)::a
        write(*,*) "a*10=",a*10
    end subroutine kakeru

    subroutine keisan(tasuorkakeru)
        interface 
            subroutine tasuorkakeru(a)
                real(8),intent(in)::a
            end subroutine
        end interface
        real(8)::a,b

        a = 100
        call tasuorkakeru(a)

        a = 1000
        call tasuorkakeru(a)

    end subroutine keisan
```
このmoduleのkeisanというsubroutineは、引数に関数を入れることができます。
例えば、

```fortran
program main
    use test
    implicit none

    call keisan(tasu)
    call keisan(kakeru)    

end program
```

```
 a+10=   110.00000000000000     
 a+10=   1010.0000000000000     
 a*10=   1000.0000000000000     
 a*10=   10000.000000000000  
```
というような形で、関数を切り替えて使えるわけです。
ここで入れられる関数は一変数関数だけです。なので、

```fortran
    subroutine tasu_a(a,b)
        implicit none
        real(8),intent(in)::a,b
        write(*,*) "a+b=",a+b
    end subroutine tasu_a
```
という関数は

```fortran
call keisan(tasu_a)
```
と入れることはできません。
しかし、bの値を固定してxの関数として引数に使いたいこともあるかと思います。

そのような場合には、
containsを使って中にラッパー関数を定義すると、bの値を固定した関数を作ることができます。
つまり、

```fortran
module test
    contains
    subroutine tasu(a)
        implicit none
        real(8),intent(in)::a
        write(*,*) "a+10=",a+10 
    end subroutine tasu

    subroutine kakeru(a)
        implicit none
        real(8),intent(in)::a
        write(*,*) "a*10=",a*10
    end subroutine kakeru

    subroutine tasu_a(a,b)
        implicit none
        real(8),intent(in)::a,b
        write(*,*) "a+b=",a+b
    end subroutine tasu_a

    subroutine keisan(tasuorkakeru)
        interface 
            subroutine tasuorkakeru(a)
                real(8),intent(in)::a
            end subroutine
        end interface
        real(8)::a,b

        a = 100
        call tasuorkakeru(a)

        a = 1000
        call tasuorkakeru(a)

    end subroutine keisan
end module test

program main
    use test
    implicit none
    real(8)::b

    call keisan(tasu)
    call keisan(kakeru)

    b = 100d0
    call keisan(wrapper)
    b = 20d0
    call keisan(wrapper)

    contains
    subroutine wrapper(a)
        real(8),intent(in)::a
        call tasu_a(a,b)
    end subroutine

end program
```
とすれば、出力は

```
 a+10=   110.00000000000000     
 a+10=   1010.0000000000000     
 a*10=   1000.0000000000000     
 a*10=   10000.000000000000     
 a+b=   200.00000000000000     
 a+b=   1100.0000000000000     
 a+b=   120.00000000000000     
 a+b=   1020.0000000000000   
```
となり、ちゃんとbを固定した関数ができていることがわかります。

#オブジェクトに付随する関数を引数としたい

以下のようなFortran2003以降でできるようになったオブジェクト指向なFortranコードを用意しました。

```fortran
module test2
    implicit none
    type testtype
        real(8)::a
        contains
        procedure::plus => plus1
    end type testtype

    contains

    subroutine plus1(self,a,b)
        class(testtype)::self
        real(8),intent(in)::a
        real(8),intent(out)::b
        write(*,*) "self%a",self%a
        b = self%a*2+a
        write(*,*) "plus1"
        return 
    end subroutine

    subroutine plus0(a,b)
        real(8),intent(in)::a
        real(8),intent(out)::b
        b = a*2
        write(*,*) "plus0"
    end subroutine

    subroutine matome(func)
        interface 
            subroutine func(a,b)
                implicit none
                real(8),intent(in)::a
                real(8),intent(out)::b
            end 
        end interface
        real(8)::a,b
        a = 10d0
        b = 0d0
        call func(a,b)
        write(*,*) b
    end subroutine
end module
```

ここで定義したplus1(self,a,b)はtype testtypeに付随するようにしたので、

```fortran
program main
    use test2
    implicit none
    type(testtype)::kk
    real(8)::a,b

    kk%a = 5d0
    a = 15d0
    call kk%plus(a,b)
    write(*,*) b

    call plus0(a,b)
    write(*,*) b
end program main
```

```
 self%a   5.0000000000000000     
 plus1
   25.000000000000000     
 plus0
   30.000000000000000     
 plus0
   20.000000000000000 
```

のように、type testtypeの変数kkのsubroutineとしてkk%plus(a,b)で呼べるようになっています。
この引数は見た目二つなので、plus0(a,b)を引数にしたkeisan(plus0)のように、keisan(kk%plus)とできそうに見えます。

```fortran
call matome(kk%plus)
```

しかし、これは

```
     call matome(kk%plus)
                       1
Error: Expected argument list at (1)
```
というエラーが出てしまってコンパイルできません。

これではオブジェクトに付随した関数は引数にできません。
これを解決するには、

```fortran

program main
    use test2
    implicit none
    type(testtype)::kk
    real(8)::a,b

    kk%a = 5d0
    a = 15d0
    call kk%plus(a,b)
    write(*,*) b


    call plus0(a,b)
    write(*,*) b

    call matome(plus0)
    call matome(wrapper)

    kk%a = 25d0
    call matome(wrapper)

    contains

    subroutine wrapper(a,b)
        real(8),intent(in)::a
        real(8),intent(out)::b
        call kk%plus(a,b)
        return
    end subroutine

end program main
```

```
 self%a   5.0000000000000000     
 plus1
   25.000000000000000     
 plus0
   30.000000000000000     
 plus0
   20.000000000000000     
 self%a   5.0000000000000000     
 plus1
   20.000000000000000     
 self%a   25.000000000000000     
 plus1
   60.000000000000000  
```
のようにラッパー関数をつけてやればよいです。これによって、testtypeの中身が変わればちゃんと値が変わるようになりました。