senseiwa
senseiwa

Reputation: 2489

Initialize a parameter in a derived type

I am still wrapping my head around Fortran with an "object oriented" flavor.

Is it possible to initialize a variable in a derived type, and what if I'd like that variable to be a parameter?

For example, a classic animal, cat, bee set of types that should set the number of legs of each animal:

animal module

module animal_module
    implicit none

    type, abstract :: animal
        private
        integer, public :: nlegs = -1
    contains
...

cat module

module cat_module
    use animal_module, only : animal
    implicit none

    type, extends(animal) :: cat
        private
        ! error here about redefining nlegs...
        integer, public :: nlegs = 4
...

I've found online the keyword initial, but my compiler (Intel) complains about the keyword with a syntax error.

Addendum

I've tried a custom constructor but apparently I am unable to write one for derived types. This is my attempt, only on the cat type:

module cat_module
    use animal_module, only : animal
    implicit none

    type, extends(animal) :: cat
        private
        real :: hidden = 23.
    contains
        procedure :: setlegs => setlegs
        procedure :: speak
    end type cat

    interface cat
        module procedure init_cat
    end interface cat

contains

    type(cat) function init_cat(this)
        class(cat), intent(inout) :: this
        this%nlegs = -4
    end function init_cat
...

program oo
    use animal_module
    use cat_module
    use bee_module
    implicit none

    character(len = 3) :: what = "cat"
    class(animal), allocatable :: q

    select case(what)
    case("cat")
        print *, "you will see a cat"
        allocate(cat :: q)
...
    print *, "this animal has ", q%legs(), " legs."

As the animal type has integer, public :: nlegs = -1, I expected the cat to have -4 legs, but alas, it's still -1.

Upvotes: 1

Views: 425

Answers (1)

francescalus
francescalus

Reputation: 32366

It is not possible for a component to be a named constant. Also, it isn't possible to declare a component of an abstract type and then define default initialization for its value in an extending type. Attempting to redeclare the component with default initialization leads to the error of the question.

High Performance Mark's comment offers one route: provide a custom constructor for each extending type setting the value appropriately. You can further set the component to private for encapsulation or protection.

Alternatively, you can provide a "getter" type-bound procedure which references a named constant:

module animal_module
  implicit none

  type, abstract :: animal
   contains
     procedure(getter), deferred :: nlegs
  end type animal

  abstract interface
     integer function getter(creature)
       import animal
       class(animal) creature
     end function getter
  end interface

end module animal_module

module cat_module
  use animal_module, only : animal
  implicit none

  type, extends(animal) :: cat
   contains
     procedure :: nlegs => nlegs_cat
  end type cat

contains

  integer function  nlegs_cat(creature)
    class(cat) creature
    integer, parameter :: my_cat_has_legs=3
    nlegs_cat = my_cat_has_legs
  end function nlegs_cat

end module cat_module

  use cat_module
  implicit none

  class(animal), allocatable :: fido

  fido = cat()
  print*, "Fido has", fido%nlegs(), "legs"
end

Finally, initial is not standard Fortran (and would have similar problems).

Upvotes: 2

Related Questions