Search code examples
oopconstructorfortranabstract-data-type

Automatically execute type-bound procedure of abstract parent type at the end of extended type's constructor


Consider the following types.

TYPE, ABSTRACT:: base
...
  CONTAINS

  SUBROUTINE sanity_check
  END SUBROUTINE

END TYPE

TYPE, EXTENDS(base):: concrete1
...
END TYPE

TYPE, EXTENDS(base):: concrete2
...
END TYPE

where ... indicate some data which is not relevant for the question. The types concrete1 and concrete2 have their constructors defined in the code, and the subroutine sanity_check is also implemented.

Now, I would like to automatically execute sanity_check at the end of the constructors of concrete1 and concrete2. In other words, sanity_check should be executed at the end of the constructors of any type that extends base, without the need to call it explicitly in the constructors. This would be useful if other programmers were to write a type that extends base, to check that all the data has been initialized properly by their extended type, without the need for the programmer to call sanity_check explicitly.

Is this somehow possible? I have seen that it is possible to define an interface for an abstract type, but I don't know if that can be used to achieve what I describe above (or if it is of any use at all, since an abstract type cannot be instantiated by definition).


Solution

  • As francescalus says, it's not possible to do exactly what you want.

    However, if all of your constructors take the same arguments then you can get reasonably close to what you want by replacing your constructors with initialisation subroutines.

    You can give the base class an initialise subroutine which initialises an object by first calling a deferred helper subroutine, and then calling sanity_check.

    The base class would look something like:

    module base_module
      implicit none
      
      type, abstract :: base
      contains
        procedure :: sanity_check
        procedure :: initialise
        procedure(helper_base), deferred :: helper
      end type
    contains
      subroutine sanity_check(this)
        class(base), intent(in) :: this
      end subroutine
      
      subroutine initialise(this, args)
        class(base), intent(out) :: this
        integer, intent(in) :: args
        
        call this%helper(args)
        call this%sanity_check
      end subroutine
      
      abstract interface
        subroutine helper_base(this, args)
          import base
          class(base), intent(out) :: this
          integer, intent(in) :: args
        end subroutine
      end interface
    end module
    

    Each child class would then overload helper, and the initialise subroutine would automatically call sanity_check as desired.

    A child class would look something like:

    module concrete_module
      implicit none
      
      type, extends(base) :: concrete
      contains
        procedure :: helper => helper_concrete
      end type
    contains
      subroutine helper_concrete(this, args)
        class(concrete), intent(out) :: this
        integer, intent(in) :: args
      end subroutine
    end module
    

    You would then construct a concrete object as e.g.

    type(concrete) :: foo
    integer :: args = 1
    call foo%initialise(args)
    

    If you wanted, you could re-introduce the foo = concrete(args) syntax by writing a thin wrapper which calls initialise, although this might be more work than just writing constructors manually.