2

I am writing object destructors in Fortran, using the final keyword in the type definition. But the destructor is not called when an array of those instances leave the scope.

module problem_module

  type :: destructable_object
    integer :: nr
  contains
    final :: destruct
  end type destructable_object

  type :: collection
    type(destructable_object) :: single_member
    type(destructable_object), dimension(3) :: multiple_members
  end type collection

contains

  subroutine destruct(instance)
    type(destructable_object), intent(in) :: instance
    write(*,*) "Destruct ",instance%nr
  end subroutine destruct

end module problem_module

In this example module, any scalar of the destructable_object type will be deconstructed with the destruct routine induced by the final keyword. Arrays of the destructable_object type will, however, not be deconstructed. For destructable objects in other classes will also only be properly deconstructed if they are scalar (In this example, the single_member gets deconstructed properly, the multiple_members not). This is independent of whether or not the containing object is in an array or not. So, for example

program main

  ! Destructors are only called at end of subroutines, not at the end     of the program.
  ! Therefore, I move the entire program to a subroutine.
  call main_execute

contains

  subroutine main_execute

    use problem_module

    implicit none

    type(destructable_object) :: single_instance
    type(destructable_object), dimension(3) :: multiple_instances
    type(collection) :: single_collection
    type(collection), dimension(3) :: multiple_collections

    single_instance%nr = 1
    multiple_instances(1)%nr = 2
    multiple_instances(2)%nr = 3
    multiple_instances(3)%nr = 4
    single_collection%single_member%nr = 5
    single_collection%multiple_members(1)%nr = 6
    single_collection%multiple_members(2)%nr = 7
    single_collection%multiple_members(3)%nr = 8
    multiple_collections(1)%single_member%nr = 9
    multiple_collections(1)%multiple_members(1)%nr = 10
    multiple_collections(1)%multiple_members(2)%nr = 11
    multiple_collections(1)%multiple_members(3)%nr = 12
    multiple_collections(2)%single_member%nr = 13
    multiple_collections(2)%multiple_members(1)%nr = 14
    multiple_collections(2)%multiple_members(2)%nr = 15
    multiple_collections(2)%multiple_members(3)%nr = 16
    multiple_collections(3)%single_member%nr = 17
    multiple_collections(3)%multiple_members(1)%nr = 18
    multiple_collections(3)%multiple_members(2)%nr = 19
    multiple_collections(3)%multiple_members(3)%nr = 20

  end subroutine main_execute

end program main

returns

Destruct            1
Destruct            5
Destruct            9
Destruct           13
Destruct           17

Exactly all the scalar instances of the destructable object and not the arrays of the destructable objects, independent of their situation. If I want an array of objects with a destructor, I can fix that by adding a layer of indirectness with a container object. That seems clumsy, requiring nested %-constructions or a bunch of pointers. Is there a more elegant way to force the destruction of arrays?

1 Answers1

4

Different final subroutines must be specified for scalar and for each rank of the array of the type to finalize. E.g.:

module problem_module

  type :: destructable_object
    integer :: nr
  contains
    final :: destruct, destruct_array
  end type destructable_object

  type :: collection
    type(destructable_object) :: single_member
    type(destructable_object), dimension(3) :: multiple_members
  end type collection

contains

  subroutine destruct(instance)
    type(destructable_object), intent(in) :: instance
    write(*,*) "Destruct ",instance%nr
  end subroutine destruct

  subroutine destruct_array(instance)
    type(destructable_object), intent(in) :: instance(:)
    write(*,*) "Destruct array ",instance%nr
  end subroutine destruct_array

end module problem_module

To avoid multiple definition you can add the elemental attribute to the subroutine:

module problem_module

  type :: destructable_object
    integer :: nr
  contains
    final :: destruct
  end type destructable_object

  type :: collection
    type(destructable_object) :: single_member
    type(destructable_object), dimension(3) :: multiple_members
  end type collection

contains

  impure elemental subroutine destruct(instance)
    type(destructable_object), intent(in) :: instance
    write(*,*) "Destruct ",instance%nr
  end subroutine destruct

end module problem_module

(I also added impure to have the possibility to use a write in an elemental procedure).

I strongly suggest to use more recent compilers. Full implementation of final stuff may be not available in the old compilers.

Franz
  • 534
  • 3
  • 8