3
votes

I'm attempting to create global-ish-ly available allocatable array of a set of derived types that share inheritance with a single object. Fortran does not seem to make this very easy. The below is what I have so far.

First the derived types and module with the allocatable array.

Module Environment
    use Entity_M
    type(Entity_C), dimenion(:), allocatable :: objects
End Module Environment

Module Entity_M
    type Entity_T
        integer :: id
        real*8 :: time
        real*8, dimension(3) :: currPos

        type(TrajectoryDatum), dimension(:), allocatable :: trajTable

    end type Entity_T

    type Entity_C
        class(Entity_T), pointer :: e
    end type Entity_C

    type, extends(Entity_T) :: Aircraft_T
        real*8 :: altitude
    end type Aircraft_T

    type, extends(Entity_T) :: Missile_T
        integer :: targetID
    end type Missile_T

End Module Entity

Now the main program

Program Main

    use Initialization
    use Environment
    use Entity_M

    call simInit(3)
    write(*,*) objects%trajTable !<---- this does not persist

    call runSim()

End Program Main

The code with the issue

Module Initialization

    use Entity_M

    contains

    subroutine simInit(numOfObjects)

        integer, intent(in) :: numOfObjects

        call objectsInit(numOfObjects)
        call launchersInit()

    end subroutine simInit


    subroutine objectsInit(numOfObjects)

        use Environment

        integer, intent(in) :: numOfObjects

        !local
        type(Aircraft_T) :: aircraft
        integer :: i

        allocate(objects(numOfObjects)

        do i = 1, numOfObjects

            aircraft%trajTable = getTrajectoryData()

            call allocatePointer(objects(i)%e, aircraft)

        end do

    end subroutine objectsInit

    subroutine allocatePointer(c, t)

        class(Entity), pointer, intent(out) :: c
        type(Aircraft), target, intent(in) :: t

        c => t

    end subroutine allocatePointer

End Module Initialization

This above just example code written on a computer that doesn't have a compiler. I did my best and hopefully if there are typos they are few. I did my best to mirror the structure of the original code.

The problem is that the field "objects%trajTable" goes back to a undefined pointer after it leaves the "objectsInit" subroutine. The other values like time, id, and currPos are still correct. How can I correct this?

I am using Visual Studio 2012 and Intel Visual Fortran 2015.

1
It is always better to submit working example. I believe that you need use Entity in module Environment. Apart from that, the problem that I see is that aircraft in objectsInit is a local variable that disapear when you get out of that subroutine. So associating it as a target to your pointer variable will bot help you. The stack memory is used for some other things when the subroutine returns. And so you have a problem there. - innoSPG
I agree working example is better. But the code is on a classified machine (and large swaths of it are classified) and I don't really have a way to compile on my unclassified one. I'll fix a couple of the typos mentioned now. - QuantumDebris

1 Answers

2
votes

Because the program has many overlapping names (like Aircraft and aircraft, which are regarded as the same in Fortran), I have attached "_t" to all the types (e.g., Aircraft to Aircraft_t etc) and "_m" to all the module names (e.g., Entity to Entity_m) to make the program work (at least formally).

More importantly, as @innoSPC commented above, type(Aircraft) :: aircraft is a local variable, so I think a pointer associated to it becomes undefined after exiting objectsInit(). The code works if

call allocatePointer( objects( i )% e, aircraft )

is replaced by

allocate( objects( i )% e, source=aircraft )

so that each objects( i )% e is given an independent memory having the type of Aircraft_t, with the contents of aircraft copied to it.


Edit Here is a minimum example that I used for test.

Module Entity_m
    implicit none

    type Entity_t   !! base type                                                    
        integer :: trajTable( 2 )
    endtype

    type, extends(Entity_t) :: Aircraft_t
        real*8 :: altitude
    endtype

    type, extends(Entity_t) :: Missile_t  !! dangerous...
        integer :: targetID
    endtype

    type Entity_c   !! container type                                               
        class(Entity_t), pointer :: e
    endtype

    type(Entity_c), allocatable :: objects(:)

contains

    subroutine objectsInit( numObj )

        integer :: numObj

        !local                                                                      
        type(Aircraft_t) :: aircraft
        type(Missile_t)  :: missile
        integer :: i

        allocate( objects( numObj ) )

        do i = 1, numObj

            if ( mod( i, 2 ) == 1 ) then
                aircraft% trajTable(:) = i
                aircraft% altitude = 10.0d0 * i

                allocate( objects( i )% e, source= aircraft )
            else
                missile% trajTable(:) = 10000 * i
                missile% targetID = -100 * i

                allocate( objects( i )% e, source= missile )  !! missile loaded !!
            endif
        enddo

    endsubroutine

EndModule

Program Main
    use Entity_m

    call objectsInit( 3 )

    do i = 1, 3
        print *, objects( i )% e% trajTable(:)  !! access members of base type      

        select type ( t => objects( i )% e )    !! access members of derived type   
            type is ( Aircraft_t ) ; print *, t% altitude
            type is ( Missile_t )  ; print *, t% targetID
        endselect
    enddo

EndProgram