Search code examples
classooptypesfortran

How to overload a procedure declared in an abstract type in fortran?


In the following module, an abstract type (Base_Arrays_class) is declared and two types are derived (One_Array_t and Two_Arrays_t). In the abstract type, there is a procedure abstract_init deferred to init.

In the derived types, the subroutines one_array_init and two_arrays_init are used to init the array(s).

module my_mod
    implicit none

    type, abstract :: Base_Arrays_class
        integer :: array_size
        real,dimension(:), allocatable :: array
    contains
        procedure(abstract_init), deferred :: init
    end type  Base_Arrays_class

    abstract interface
        subroutine abstract_init(this,array_size)
            import Base_Arrays_class
            class(Base_Arrays_class), intent(inout) :: this
            integer, intent(in) :: array_size
        end subroutine abstract_init
    end interface

    type, extends(Base_Arrays_class) :: One_Array_t
    contains
        procedure :: init => one_array_init
    end type One_Array_t

    type, extends(Base_Arrays_class) :: Two_Arrays_t
        real,dimension(:), allocatable :: second_array
    contains
        procedure :: init => two_arrays_init
    end type Two_Arrays_t

contains

    subroutine one_array_init(this,array_size)
        class(One_Array_t), intent(inout) :: this
        integer, intent(in) :: array_size
        
        this%array_size=array_size
        allocate(this%array(array_size))
        this%array=1.0
    end subroutine one_array_init
    
    subroutine two_arrays_init(this,array_size)
        class(Two_Arrays_t), intent(inout) :: this
        integer, intent(in) :: array_size
        
        this%array_size=array_size
        allocate(this%array(array_size), this%second_array(array_size))
        this%array=2.0
        this%second_array=3.0
    end subroutine two_arrays_init

end module my_mod

Below, an example of program that uses this module.

program pgm
    use my_mod
    implicit none
    
    type(One_Array_t) :: one_array
    type(Two_Arrays_t) :: two_arrays
    integer :: i, size
    
    size = 4    
    call one_array%init(size)
    call two_arrays%init(size)
    
    print *,"one_array"
    do i=1,size
        print *, one_array%array(:)
    end do
    
    print *,"two_arrays"
    do i=1,size
        print *, two_arrays%array(:)
    end do
    print *
    do i=1,size
        print *, two_arrays%second_array(:)
    end do

end program pgm

It works perfectly.

But now, I would like to overload init to choose the init value(s). For example : call one_array%init(size,4) and call two_arrays%init(size, 5,8).

So, overload of init is needed. Something like that (for One_Array_t) :

  interface init
     module procedure one_array_init, one_array_init2
  end interface init

But, it does not conform to abstract_init and I could not be able to do this overload.

Is it possible or not ? Thanks for answers.


Solution

  • As mentioned in the comments, I also think we can use generic for this purpose, but it may be necessary to use it in the parent type, eg...

    module test_m
        implicit none
    
        type, abstract :: Parent_t
        contains
            generic   :: sub => sub1, sub2
            procedure :: sub1 => Parent_sub1
            procedure :: sub2 => Parent_sub2
        endtype
    
        type, extends(Parent_t) :: Child1_t
        contains
            procedure :: sub1 => Child1_sub1
        endtype
    
        type, extends(Parent_t) :: Child2_t
        contains
            procedure :: sub2 => Child2_sub2
        endtype
    
    contains
    
        subroutine Parent_sub1(this, n)
            class(Parent_t) :: this
            integer :: n
            stop "sub1 not implemented"
        end
        subroutine Parent_sub2(this, n1, n2)
            class(Parent_t) :: this
            integer :: n1, n2
            stop "sub2 not implemented"
        end
    
        subroutine Child1_sub1(this, n)
            class(Child1_t) :: this
            integer :: n
            print *, "Child1: n = ", n
        end
        subroutine Child2_sub2(this, n1, n2)
            class(Child2_t) :: this
            integer :: n1, n2
            print *, "Child2: n1, n2 = ", n1, n2
        end
    end module
    
    program main
        use test_m
        implicit none
        type(Child1_t) :: c1
        type(Child2_t) :: c2
    
        call c1 % sub( 100 )
        call c2 % sub( 1, 2 )
    
        !! call c1 % sub( 1, 2 )  !! stop
        !! call c2 % sub( 100 )   !! stop
    end
    

    (can be tested in this page)

    Here, I did not use abstract interface for the parent type, such that only one procedure of interest (e.g. sub1) can be implemented in a child type. (If necessary, both sub1 and sub2 can be defined in a child type.) A new routine like sub3() can also be added later in the parent type.

    Another approach may be not to use overloading, but (as also mentioned in the comments), just to define a single routine that receives a more "general" argument (like arrays, derived types, etc).

    (By the way, if init() is used as a "constructor" (or "initializer"), I usually do not make it "virtual" in the parent type but directly define it in each child type, if the concrete type is known for initialization and the signature of init() can be different among different child types. For comparison, this Q/A for C++ might also be related.)