Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

Array of Objects

Panagiotis_K_
Beginner
1,809 Views

Hello,

I am trying to create an array of Objects in FORTRAN but I cannot seem to find a way of doing this. I just want to know if this is possible as google did not really encouraged me. I have seen some older posts in this forum but they were old so I thought I should bring this into your attention again. 

Below there is an example of my code: 

module substance_init
    implicit none
      type substance
            real     :: property1  ! diffusion coefficient
            logical  :: filled     ! variable to tell if cell is filled with substance or not
            real :: length
            real :: width
       contains
            procedure :: initialize => initSubstance
            procedure, public :: print ! allow access to print type-bound procedure
      end type substance
  
      type, EXTENDS (substance) :: cyto
            
       contains
            procedure :: initialize => initcyto
            
       end type cyto
      
      type, EXTENDS (cyto) :: leaks
      end type leaks 
      
      
      
     
    CONTAINS
      
    Subroutine initSubstance (this,property1,filled,length,width)
    !initialise shape objects
    
    class(substance) :: this  !polymorphic dummy argument
    real     :: property1          
    logical  :: filled
    real, optional :: length  ! ignored for substance
    real, optional :: width   ! ignored for substance
      
    end subroutine initSubstance 
    
    Subroutine initcyto (this,property1,filled,length,width)
    !initialise cyto objects
    class(cyto) :: this
    real     :: property1          
    logical  :: filled
    real, optional :: length  
    real, optional :: width   
      
    end subroutine initcyto
    
    subroutine print(this)
    class (substance) :: this
    print *, '' ,this%property1, this%filled,this%length,this%width
    
    end subroutine 
    
    end module substance_init
    
   
    
     
 
   Program SC_2D
  
 
    use substance_init
    use array
    integer, parameter :: gridsize = 10                      ! cyto layers
    integer :: i, j, generation=0

        
  
    type(substance)  :: sub
    type(cyto) :: cor1
    type(cyto) :: cor2
    type(cyto) :: cor3
    type(cyto) :: cor4
    type(leaks) :: lip
    sub = substance  (0.0012345567, .false.,0,0) 
    cor1 = cyto ( 0.00000002, .true. ,0.001,0.001)
    cor2 = cyto ( 0.00000005, .true. ,0.001,0.001)
    cor3 = cyto ( 0.00000009, .true. ,0.001,0.001)
    cor4 = cyto ( 0.0000001,  .true. ,0.001,0.001)
    lip = leaks( 0.0000001,  .true. ,0.00001,0.000001)
 
    

    call sub%print()
    call cor1%print()
    call cor2%print()
    call cor3%print()
    call cor4%print()
    call lip%print()
   

   
    write(*,'(" Enter to exit")')
    read (*,*)

    END PROGRAM SC_2D

  

So as you can see I created some objects like sub, cor1, cor2, cor3, cor4 and lip , initialized them and now I wish to arrange them in a 2D array and be able to acces them efficiently.  Is there a way of doing this in fortran ?  ( A small example would be perfect)

 

  

 

 

0 Kudos
1 Solution
IanH
Honored Contributor III
1,809 Views

As FortranFan suggested, you need a wrapper type.

This shows the basic approach. 

module substances
  implicit none
  type :: substance
    ! ...
  contains
    procedure :: initialize => initsubstance
  end type substance
  
  type, extends(substance) :: cyto
    procedure :: initialize => initcyto
  end type cyto
  
  type, extends(cyto) :: leaks
  end type leaks
  
  ! The wrapper type.
  type :: substance_element
    ! The wrapped item.
    class(substance), allocatable :: item
  end type substance_element
contains
  !...
end module substances

program SC_2D
  implicit none
  ! The container.
  type(substance_element), allocatable :: SCarray(:,:)
  
  type(substance) :: sub
  type(cyto) :: cor1
  type(leaks), allocatable :: lip
  !...
  
  sub = substance(...)
  cor1 = corneocyte(...)
  lip = lipid(...)
  !...
  
  allocate(SCarray(3,xx))
  
  ! Allocate and set dynamic type and value using SOURCE.
  allocate(SCarray(1,1)%item, source=sub)
  ! As above, but we have a different dynamic type.
  allocate(SCarray(2,1)%item, source=cor1)
  
  ! Alternative approach, move an allocatable object into place.
  call move_alloc(lip, SCarray(3,1)%item)
  
  ! Eventually ifort might support F2008's polymorphic assignment, then 
  ! we could just do:
!  SCarray(2,2)%item = cor2
  ! but you cannot do that today.
  
  ! Another approach is to implement defined assignment for the 
  ! substance_element type, that takes a right hand side object of 
  ! class(substance), and then use the allocate(...source=rhs) 
  ! approach inside that defined assignment.
  
  ...
  
  ! Call bindings of the declared type of the wrapped item.
  call SCarray(x,y)%item%initialize(...)
  
  call SCarray(i,j)%print()
  
end program SC_2D

 

View solution in original post

0 Kudos
9 Replies
FortranFan
Honored Contributor III
1,809 Views

Panagiotis K. wrote:

Hello,

I am trying to create an array of Objects in FORTRAN but I cannot seem to find a way of doing this. I just want to know if this is possible as google did not really encouraged me. I have seen some older posts in this forum but they were old so I thought I should bring this into your attention again. 

..

So as you can see I created some objects like sub, cor1, cor2, cor3, cor4 and lip , initialized them and now I wish to arrange them in a 2D array and be able to acces them efficiently.  Is there a way of doing this in fortran ?  ( A small example would be perfect)

Can you define what you mean by "acces them efficiently"?  How do you measure efficiency, it is relative to what?

You can consider a "container class" with an unlimited polymorphic component defined as

TYPE ObjArray
    ...
    CLASS(*), ALLOCATABLE :: MyArrary(:)
    ...
END TYPE ObjArray

and you can "store" your objects in said container.  But then, you'd need to "cast" the array elements from the unlimited polymorphic component to the type of interest in the form of SELECT TYPE kind of a construct whenever you need to access them.  You need to decide whether that is "efficient" for your needs.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,809 Views

type(cyto), allocatable :: corArray(:)
...
allocate(corArray(4)) ! add stat=...
...
corArray(1) = cor1
corArray(2) = cor2

Jim Dempsey

0 Kudos
IanH
Honored Contributor III
1,809 Views

FortranFan wrote:

You can consider a "container class" with an unlimited polymorphic component defined as

TYPE ObjArray
    ...
    CLASS(*), ALLOCATABLE :: MyArrary(:)
    ...
END TYPE ObjArray

and you can "store" your objects in said container.  But then, you'd need to "cast" the array elements from the unlimited polymorphic component to the type of interest in the form of SELECT TYPE kind of a construct whenever you need to access them.  You need to decide whether that is "efficient" for your needs.

For the OP's case the component can be `CLASS(substance)` as it is the common parent type.  Behaviour of the objects that is extension specific can then be accessed through the bindings available in that parent type (in this case `initialize` and `print`) without requiring SELECT TYPE. 

(In some cases it then makes sense to provide wrapper bindings (or normal standalone procedures) for the `ObjArray` type that have the same name as the bindings in `substance` and that simply forward the call on to the binding of the component.  In some further cases, particularly when the contained type is completely abstract (it carries interface information only, which is not the case here - the contained type has data components), to ensure consistency of interface between the container type and the contained type you can make the container an extension of the contained type, which still messes with my head whenever I decide to do this.)

Be cautious around compiler bugs to do with assignment of objects that are of a type that has polymorphic components.

Jim's example is only applicable if the dynamic types of all the objects to be stored are the same, which is not the case here (an array has a single declared and dynamic type across all its elements, but components of that array may differ in dynamic type from element to element).

0 Kudos
Panagiotis_K_
Beginner
1,809 Views

Thank you all for your responses. I

I haven't managed to solve my problem yet. So now I created a 2D array as you can see below: 

 

  Program SC_2D
  
    use substance_init
         
    
    type (cyto), allocatable :: SCarray(:,:) 
  
    type(substance)  :: sub
    type(cyto) :: cor1
    type(cyto) :: cor2
    type(cyto) :: cor3
    type(cyto) :: cor4
    type(leaks) :: lip
    
    allocate(SCarray(3,3))
    
    sub = substance  (0.0012345567, .false.,0,0) 
    cor1 = corneocyte ( 0.00000002, .true. ,0.001,0.001)
    cor2 = corneocyte ( 0.00000005, .true. ,0.001,0.001)
    cor3 = corneocyte ( 0.00000009, .true. ,0.001,0.001)
    cor4 = corneocyte ( 0.0000001,  .true. ,0.001,0.001)
    lip = lipid( 0.0000001,  .true. ,0.00001,0.000001)
 
    SCarray(1,1) = cor1 ; SCarray(1,2) = cor1 ; SCarray(1,3) = cor1
    SCarray(2,1) = cor2 ; SCarray(2,2) = cor2 ; SCarray(2,3) = cor3
    SCarray(3,1) = cor3 ; SCarray(3,2) = cor3 ; SCarray(3,3) = cor3
    
    !call sub%print()
    !call cor1%print()
    !call cor2%print()
    !call cor3%print()
    !call cor4%print()
    !call lip%print()
   
    print*,' ',SCarray
  
   
    write(*,'(" Enter to exit")')
    read (*,*)
   
    END PROGRAM SC_2D

I think this is what you were saying, and thank you; it worked. But, what I am trying to do ,  is create a 2D array of all types. like the example below.   

e.g :

 
    SCarray(1,1) = lip ; SCarray(1,2) = lip ; SCarray(1,3) = sub
    SCarray(2,1) = cor2 ; SCarray(2,2) = sub ; SCarray(2,3) = cor3
    SCarray(3,1) = sub ; SCarray(3,2) = cor3 ; SCarray(3,3) = cor3        

error#6197: An assignment of different structure types is invalid

In the examples provided I have used type (cyto) as an example , the same thing happens when i used the parent type , substance.  

Is there a way of doing this? 

 

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,809 Views

I think you would create an array of pointers to polymorphic types:

    CLASS(*), POINTER, ALLOCATABLE :: ObjArray(:,:)

Sorry for not writing a proper example. Allocate the array the size you want (nullifying the pointers). Next allocate the elements as you populate the array. These allocations would have the SOURCE=... to assure allocation of the write polymorphic type.

InaH may have a better idea. He is better at this kind of programming than I am.

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
1,809 Views

I think Jim did not intend to have both POINTER and ALLOCATABLE in that example.

0 Kudos
IanH
Honored Contributor III
1,810 Views

As FortranFan suggested, you need a wrapper type.

This shows the basic approach. 

module substances
  implicit none
  type :: substance
    ! ...
  contains
    procedure :: initialize => initsubstance
  end type substance
  
  type, extends(substance) :: cyto
    procedure :: initialize => initcyto
  end type cyto
  
  type, extends(cyto) :: leaks
  end type leaks
  
  ! The wrapper type.
  type :: substance_element
    ! The wrapped item.
    class(substance), allocatable :: item
  end type substance_element
contains
  !...
end module substances

program SC_2D
  implicit none
  ! The container.
  type(substance_element), allocatable :: SCarray(:,:)
  
  type(substance) :: sub
  type(cyto) :: cor1
  type(leaks), allocatable :: lip
  !...
  
  sub = substance(...)
  cor1 = corneocyte(...)
  lip = lipid(...)
  !...
  
  allocate(SCarray(3,xx))
  
  ! Allocate and set dynamic type and value using SOURCE.
  allocate(SCarray(1,1)%item, source=sub)
  ! As above, but we have a different dynamic type.
  allocate(SCarray(2,1)%item, source=cor1)
  
  ! Alternative approach, move an allocatable object into place.
  call move_alloc(lip, SCarray(3,1)%item)
  
  ! Eventually ifort might support F2008's polymorphic assignment, then 
  ! we could just do:
!  SCarray(2,2)%item = cor2
  ! but you cannot do that today.
  
  ! Another approach is to implement defined assignment for the 
  ! substance_element type, that takes a right hand side object of 
  ! class(substance), and then use the allocate(...source=rhs) 
  ! approach inside that defined assignment.
  
  ...
  
  ! Call bindings of the declared type of the wrapped item.
  call SCarray(x,y)%item%initialize(...)
  
  call SCarray(i,j)%print()
  
end program SC_2D

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,809 Views

Steve,

Thanks for correcting me, pointers have allocatable attribute by default. This was an oversight in my copy/paste from IanH's post.

IanH's suggestion above is good when the objects have an inheritance. In most cases they will. In other situations the objects in the blob (container array) may differ so greatly such as using a hierarchy makes little sense. In these cases, the use of the polymorphic object pointer might be a better choice. It would be good to keep both tricks in your tool kit.

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
1,809 Views

To correct a possible misimpression, one can allocate both pointers and allocatables, but it is not correct to say that "pointers have allocatable attribute" - in the language these are two different things. 

0 Kudos
Reply