Search code examples
constructorfortranderived-types

Constructor of derived types


I am trying to write a constructor for a derived type of an abstract one to solve this other question, but it seems that it's not working, or better, it isn't called at all.

The aim is to have a runtime polymorphism setting the correct number of legs of an animal.

These are the two modules:

animal

module animal_module
    implicit none

    type, abstract :: animal
        private
        integer, public :: nlegs = -1
    contains
        procedure :: legs
    end type animal

contains

    function legs(this) result(n)
        class(animal), intent(in) :: this
        integer :: n

        n = this%nlegs
    end function legs

cat

module cat_module
    use animal_module, only : animal
    implicit none

    type, extends(animal) :: cat
        private
    contains
        procedure :: setlegs => setlegs
    end type cat

    interface cat
        module procedure init_cat
    end interface cat

contains

    type(cat) function init_cat(this)
        class(cat), intent(inout) :: this
        print *, "Cat!"
        this%nlegs = -4
    end function init_cat

main program

program oo
    use animal_module
    use cat_module
    implicit none

    type(cat) :: c
    type(bee) :: b

    character(len = 3) :: what = "cat"

    class(animal), allocatable :: q

    select case(what)
    case("cat")
        print *, "you will see a cat"
        allocate(cat :: q)
        q = cat() ! <----- this line does not change anything

    case default
        print *, "ohnoes, nothing is prepared!"
        stop 1
    end select

    print *, "this animal has ", q%legs(), " legs."
    print *, "cat  animal has ", c%legs(), " legs."
end program

The constructor isn't called at all, and the number of legs still remains to -1.


Solution

  • The available non-default constructor for the cat type is given by the module procedure init_cat. This function you have defined like

    type(cat) function init_cat(this)
        class(cat), intent(inout) :: this
    end function init_cat
    

    It is a function with one argument, of class(cat). In your later reference

    q = cat()
    

    There is no specific function under the generic cat which matches that reference: the function init_cat does not accept a no-argument reference. The default structure constructor is instead used.

    You must reference the generic cat in a way matching your init_cat interface to have that specific function called.

    You want to change your init_cat function to look like

    type(cat) function init_cat()
        ! print*, "Making a cat"
        init_cat%nlegs = -4
    end function init_cat
    

    Then you can reference q=cat() as desired.

    Note that in the original, you are attempting to "construct" a cat instance, but you aren't returning this constructed entity as the function result. Instead, you are modifying an argument (already constructed). Structure constructors are intended to be used returning such useful things.

    Note also that you don't need to

    allocate (cat :: q)
    q = cat()
    

    The intrinsic assignment to q already handles q's allocation.