LoginSignup
3
3

More than 3 years have passed since last update.

オブジェクト指向Fortranで抽象型を使う

Posted at

Fortran2003からオブジェクト指向がサポートされたFortranですが、何ができて何ができないのかがよくわからない印象です。この記事では、抽象型を使う方法についてメモを書いておきます。
オブジェクト指向Fortranについてはこちらを参考にしてください。

抽象型とは

Fortran2003の抽象型は

    type,abstract::animal
    end type

こんな感じでabstractをつけて宣言します。abstractをつけると、この型はインスタンスを作ることができなくなります。つまり、実際の計算では常に何らかの具象型が生成されています。この場合では、

    type,extends(animal)::dog
        integer::dogage
    end type

    type,extends(animal)::cat
        integer::catage
    end type

dogとcatという具象型を定義します。ここでextendsをつけると派生型の意味になるのですが、animalが抽象型なのでdogとcatはanimalsに対する具象型となります。
型、とここで言っていますが、実際はclassです。ですので、

    interface dog
        module procedure::init_dog
    end interface

    interface cat
        module procedure::init_cat
    end interface

contains 

    type(dog) function init_dog(age) result(d)
        integer::age
        d%dogage = age
    end function

    type(cat) function init_cat(age) result(c)
        integer::age
        c%catage = age
    end function

とコンストラクタを作っておきましょう。

抽象型を含むクラス

このままだとご利益があまりわかりませんので、以下のような型を用意します。

    type life
        class(animal),allocatable::ianimal
        class(food),allocatable::ifood
    end type

このlifeという型(というかクラス)は成分としてanimal型とfood型という二つの抽象型をもっています。実際の計算では、このlifeという型にはdogあるいはcatが入ったりして、dogかcatかで挙動が変わったりするわけです。
ここで注意点(私が引っかかった点)は、

    type life
        class(animal)::ianimal
        class(food)::ifood
    end type

はコンパイルエラーがおきます。これがよくわからなくて、抽象型をうまく使う方法がわからなかったのでした。しかし、考えてみると、どの型になるかわからないから抽象型ですから、具体的なデータを格納するためにはどのような型であるかを知る必要があり、「割付」つまりallocateが必須なわけです。
このlifeのコンストラクタは

    interface life
        module procedure::init_life
    end interface

contains
    type(life) function init_life(str) result(l)
        character(len=*)::str
        if (str == "dog") then
            allocate(dog::l%ianimal)
            allocate(dogfood::l%ifood)
            l%ianimal = dog(4)
            l%ifood = dogfood(10)

        else if(str == "cat") then
            allocate(cat::l%ianimal)
            allocate(catfood::l%ifood)
            l%ianimal = cat(10)
            l%ifood = catfood(2)
        else
            write(*,*) "we need cat or dog!"
        end if
    end

こんな感じにしました。もしstrという文字列がdogなら型はdogだよとallocateし、catなら型はcatとallocateします。このallocate(dog::l%ianimal)がポイントで、l%ianimaldog型だよ、と宣言しています。

全体のコード

全体のコードはこんな感じです。

module test
    implicit none

    type life
        class(animal),allocatable::ianimal
        class(food),allocatable::ifood

        contains
        procedure::showdata
    end type

    type,abstract::animal
    end type

    type,extends(animal)::dog
        integer::dogage
    end type

    interface dog
        module procedure::init_dog
    end interface

    type,extends(animal)::cat
        integer::catage
    end type

    interface cat
        module procedure::init_cat
    end interface

    type,abstract::food
        integer::num
    end type

    type,extends(food)::dogfood
    end type

    type,extends(food)::catfood
    end type

    interface life
        module procedure::init_life
    end interface

    interface dogfood
        module procedure::init_dogfood
    end interface

    interface catfood
        module procedure::init_catfood
    end interface

    contains 

    subroutine showdata(self)
        class(life)::self
        select type(p => self%ifood)
            type is (dogfood)
                write(*,*) "num of dogfoods is ",p%num
            type is (catfood)
                write(*,*) "num of catfoods is ",p%num
        end select

        select type(p => self%ianimal)
            type is (dog)
                write(*,*) "dog's age is ",p%dogage
            type is (cat)
                write(*,*) "cat's age is ",p%catage
        end select

    end subroutine


    type(dog) function init_dog(age) result(d)
        integer::age
        d%dogage = age
    end function

    type(dogfood) function init_dogfood(num) result(d)
        integer::num
        d%num = num
    end function

    type(cat) function init_cat(age) result(c)
        integer::age
        c%catage = age
    end function

    type(catfood) function init_catfood(num) result(c)
        integer::num
        c%num = num
    end function

    type(life) function init_life(str) result(l)
        character(len=*)::str
        if (str == "dog") then
            allocate(dog::l%ianimal)
            allocate(dogfood::l%ifood)
            l%ianimal = dog(4)
            l%ifood = dogfood(10)

        else if(str == "cat") then
            allocate(cat::l%ianimal)
            allocate(catfood::l%ifood)
            l%ianimal = cat(10)
            l%ifood = catfood(2)
        else
            write(*,*) "we need cat or dog!"
        end if
    end

end module

program main
    use test
    implicit none
    type(life)::life1,life2

    life1 = life("dog")
    call life1%showdata()

    life2 = life("cat")
    call life2%showdata()

end program

実行すると、

 num of dogfoods is           10
 dog's age is            4
 num of catfoods is            2
 cat's age is           10

と出力されます。ちゃんとlifeという変数にdogやcatが入っていることがわかります。

3
3
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
3
3