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.
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.)