Search code examples
inheritancefortranpolymorphism

Fortran TYPE inheritance on runtime (declared by user)


I want to know if it's possible (and if so how) to have a Fortran TYPE inherit another TYPE on runtime. For example the user provides an input file that states which TYPE needs to be inherited. Below is an example,

MODULE A_MOD
  type A
    real(8) :: m, n 
  end type
END MODULE

And similarly for B_MOD

MODULE B_MOD
  type B
    real(8) :: param 
  end type
END MODULE

Both A and B also have procedures and other elements defined in the modules. I now have another TYPE C and I want it to inherit either A or B depending on a user input. As a general idea,

input = A ! provided by user
...
type, extends(input) :: C
  end type

I can do something similar to this in Python. I'm not very familiar yet with Fortran polymorphism so any help would be appreciated !


Solution

  • No, this is not possible. The compiler needs to know the memory requirements of a type at compile time, so runtime type definitions are not possible.

    A possible workaround would be to define two C types, CA and CB, and then use a factory method to pick which type to use at runtime.

    Code for this might look something like:

    module m
      type, abstract :: Base
      contains
        procedure(hi_Base), deferred :: hi
      end type
    
      abstract interface
        subroutine hi_Base(self)
          import :: Base
          class(Base), intent(in) :: self
        end subroutine
      end interface
    
      type, extends(Base) :: A
      contains
        procedure :: hi => hi_A
      end type
    
      type, extends(Base) :: B
      contains
        procedure :: hi => hi_B
      end type
    
      type, extends(A) :: CA
      end type
    
      type, extends(B) :: CB
      end type
    contains
      subroutine  hi_A(self)
        class(A), intent(in) :: self
        write(*,*) "Hi, I'm an A!"
      end subroutine
    
      subroutine  hi_B(self)
        class(B), intent(in) :: self
        write(*,*) "Hi, I'm a B!"
      end subroutine
    
      function factory(switch) result(output)
        logical, intent(in) :: switch
        class(Base), allocatable :: output
    
        if (switch) then
          output = CA()
        else
          output = CB()
        endif
      end function
    end module
    
    program p
      use m
      implicit none
    
      class(Base), allocatable :: foo
    
      foo = factory(.true.)
      call foo%hi()
    
      foo = factory(.false.)
      call foo%hi()
    end program
    

    Having said that, you might have better luck if C has an A or a B instead of inheriting from an A or a B. This would look something like

    module m
      type, abstract :: Base
      contains
        procedure(hi_Base), deferred :: hi
      end type
    
      abstract interface
        subroutine hi_Base(self)
          import :: Base
          class(Base), intent(in) :: self
        end subroutine
      end interface
    
      type, extends(Base) :: A
      contains
        procedure :: hi => hi_A
      end type
    
      type, extends(Base) :: B
      contains
        procedure :: hi => hi_B
      end type
    
      type :: C
        class(Base), allocatable :: foo
      end type
    
      interface C
        module procedure new_C
      end interface
    contains
      subroutine  hi_A(self)
        class(A), intent(in) :: self
        write(*,*) "Hi, I'm an A!"
      end subroutine
    
      subroutine  hi_B(self)
        class(B), intent(in) :: self
        write(*,*) "Hi, I'm a B!"
      end subroutine
    
      function new_C(switch) result(self)
        logical, intent(in) :: switch
        type(C) :: self
    
        if (switch) then
          self%foo = A()
        else
          self%foo = B()
        endif
      end function
    end module
    
    program p
      use m
      implicit none
    
      type(C) :: bar
    
      bar = C(.true.)
      call bar%foo%hi()
    
      bar = C(.false.)
      call bar%foo%hi()
    end program