classfortranpolymorphismfortran2008

Heterogeneous array of Fortran classes


I have an abstract type and several types which inherit from him. Now I need to make an array of instances of those inherited types, but I'm not sure, if it's even possible in Fortran.

I've tried to make some wrapper type, like in Creating heterogeneous arrays in Fortran.

module m
implicit none

type, abstract :: a
    integer, public :: num 
end type a

type, extends(a) :: b
end type b

type, extends(a) :: c
end type c

type :: container
    class(*), allocatable :: ptr 
end type
end module m

program mwe 
use m

type(b) :: b_obj
class(*), allocatable :: a_arr(:)

b_obj = b(1)

allocate(container :: a_arr(3))
a_arr(1) = container(b_obj)

end program mwe 

But I'm getting this error:

test3.f90:28:25:

 a_arr(1) = container(b_obj)
                     1
Error: Can't convert TYPE(b) to CLASS(*) at (1)

What am I doing wrong? Or is there any other, correct way to do it?


Attempt 2

I edited the code accordingly to francescalus's answer:

program mwe 
use m

type(b) :: b_obj
type(c) :: c_obj
type(container), allocatable :: a_arr(:)
integer :: i

b_obj = b(1)
c_obj = c(2)

allocate(container :: a_arr(3))
a_arr(1)%ptr = b(1)
a_arr(2)%ptr = c(3)
a_arr(3)%ptr = c(1000)

do i=1,3
    write(*,*) a_arr(i)%ptr%num
end do

end program mwe 

And I'm getting another error:

test3.f90:36:35:

     write(*,*) a_arr(i)%ptr%num
                               1
Error: ‘num’ at (1) is not a member of the ‘__class__STAR_a’ structure

Solution

  • As IanH commented when outlining the approach you take, the then current version of gfortran

    does not appear to support definition of an unlimited polymorphic component via a structure constructor

    container(b_obj) is such a thing. So, leaving aside whether you are still coming up against this problem, one may be interested in still allowing older versions/other compilers to use the code.

    An alternative approach is not to use a constructor for the element of your container. Instead the single component can feature directly in an assignment:

    use m
    type(container) a_arr(3)  ! Not polymorphic...
    a_arr%ptr = b(1)          ! ... so it has component ptr in its declared type
    end mwe
    

    Naturally, we still have the component of the container type polymorphic so any attempts to reference/define/etc., that component will be subject to those various restrictions. In your question you have the component unlimited polymorphic, but I see that you first talk about restricting the container's consideration to elements which extend the first type. Rather than declaring the container component as unlimited polymorphic it could be much more helpfully of declared type a:

    type :: container
        class(a), allocatable :: ptr 
    end type
    

    This would be sufficient to solve the problem with

    do i=1,3
        write(*,*) a_arr(i)%ptr%num
    end do
    

    because num is a component of the declared type of a_arr(i)%ptr (that is., a). In general, it isn't the complete solution because

    do i=1,3
        write(*,*) a_arr(i)%ptr%num_of_type_b
    end do
    

    wouldn't work (with num_of_type_b a component in the extending type). Here you have to use the usual tricks (defined input/output, dynamic resolution, select type and so on). Those are beyond the scope of this answer and many other questions may be found covering them.