Fortranの抽象型の配列のそれぞれに異なる具象型を入れる

Fortranでオブジェクト指向プログラミングをしたい時には、やはり抽象型を使いたくなるかと思います。Fortranの抽象型に関しては、以前[オブジェクト指向Fortranで抽象型を使う](https://qiita.com/cometscome_phys/items/c750eb3e963e752ad0b6)で解説しました。今回は、

- 抽象型の配列に異なる具象型を入れる

ことを行いました。これ、gfortran (gcc version 14.2.0)で苦戦しましたので、記録を残しておきます。

# 抽象型と具象型
今回は、以下のような抽象型と具象型を考えます。

```fortran
module module_abstractlayer
    implicit none

    type, abstract,public :: Layer
    contains
        procedure(display_layer), deferred :: display
    end type Layer

    abstract interface
       subroutine display_layer(this) 
            import :: Layer
            class(Layer), intent(in) :: this
       end subroutine display_layer
    end interface

end module module_abstractlayer

module module_denselayer
    use iso_fortran_env
    use module_abstractlayer,only:Layer
    implicit none

    type, public,extends(Layer) :: Denselayer
        integer::indim,outdim
        real(real64),allocatable::W(:,:)
        real(real64),allocatable::b(:)
    contains
        procedure :: display => Denselayer_display
    end type Denselayer

    interface Denselayer
        procedure::init_Denselayer
    end interface 

    contains

    type(Denselayer) function init_Denselayer(indim,outdim) result(layer)
        implicit none
        integer,intent(in)::indim,outdim
        allocate(layer%W(outdim,indim))
        allocate(layer%b(outdim))

        call random_number(layer%W)
        call random_number(layer%b)
        layer%indim = indim
        layer%outdim = outdim
    end function

    subroutine Denselayer_display(this) 
        class(Denselayer), intent(in) :: this
        write(*,*) "Dense layer."
        write(*,*) "Input and output dimensions: ",this%indim,this%outdim
    end subroutine Denselayer_display


end module module_denselayer

module module_unitlayer
    use iso_fortran_env
    use module_abstractlayer,only:Layer
    implicit none

    type, public,extends(Layer) :: Unitlayer
    contains
        procedure :: display => Unitlayer_display
    end type Unitlayer

    contains

    subroutine Unitlayer_display(this) 
        class(Unitlayer), intent(in) :: this
        write(*,*) "Unitlayer layer."
    end subroutine Unitlayer_display
end module
```
を考えます。ニューラルネットワークのレイヤーみたいなものを念頭に置いたものです。レイヤーごとに異なる演算がしたいので、配列としてレイヤーごとに異なる処理ほ保持しておきたい、というところです。


# 素朴なやり方
まず、一番素朴なやり方は

```fortran
class(Layer),allocatable::layers(:)
allocate(layers(2))
```
です。これで、配列の長さが確保できる、気がします。しかし、gfortranではこれは
```
   64 |     allocate(larers(2))
      |             1
Error: Allocate-object at (1) is neither a data pointer nor an allocatable variable
```
というエラーが出ます。webで調べるとこのやり方でできるような雰囲気があったのですが、できませんでした。allocatableをpointerにしても結果は変わりません。

# 解決方法
解決するには、ラッパー型を用意します。[こちら](https://fortran.cat/2022/02/14/how-to-declare-an-array-of-an-abstract-type/)を参考にしました。

つまり、ラッパーとして、
```fortran
module module_abstractlayer
    implicit none

    type, abstract,public :: Layer
    contains
        procedure(display_layer), deferred :: display
    end type Layer


    type :: LayerWrapper
        class(Layer), allocatable :: layer
        contains
        procedure :: display 
    end type

    abstract interface
       subroutine display_layer(this) 
            import :: Layer
            class(Layer), intent(in) :: this
       end subroutine display_layer
    end interface

    contains

    subroutine display(self)
        class(LayerWrapper),intent(in)::self
        call self%layer%display()
    end


end module module_abstractlayer

```
のように、LayerWrapperという型を用意して、


```fortran
program main
    use module_denselayer,only:Denselayer
    use module_abstractlayer,only:Layer,LayerWrapper
    use module_unitlayer,only:Unitlayer
    implicit none
    type(LayerWrapper),allocatable::layers(:)
    integer::numlayer,ilayer
    integer,allocatable::numnodes(:)


    numlayer = 2
    allocate(numnodes(numlayer+1))
    numnodes(1) = 44
    numnodes(2) = 10
    numnodes(3) = 1
    allocate(layers(numlayer))

    ilayer= 1
    allocate(layers(ilayer) % layer,source=Denselayer(numnodes(ilayer),numnodes(ilayer+1)))
 
    ilayer= 2
    allocate(Unitlayer:: layers(ilayer) % layer)
    layers(ilayer) % layer = Unitlayer()

    do ilayer = 1,numlayer
        call layers(ilayer)%display()
    end do

end program
```
としますと、出力結果は、
```
 Dense layer.
 Input and output dimensions:           44          10
 Unitlayer layer.
```
となり、配列のそれぞれに異なる具象型を入れることができます。

なぜ、素朴なやり方ができなかったのか、謎です。