Heterogeneous array of Fortran classes

668 Views Asked by At

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
2

There are 2 best solutions below

1
On BEST ANSWER

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.

0
On

I add the correction to resolve the following 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

The unlimited polymorphic variable cannot directly access any component of the dynamic data type. In this case, a simple solution is to avoid class(*). The definition of container is changed to

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

So the working code in summary is as follows,

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(a), allocatable :: ptr 
  end type

end module m

program mwe

  use m

  type(container), allocatable :: a_arr(:)
  integer :: i

  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