Home > Software design >  Automatically execute type-bound procedure of abstract parent type at the end of extended type'
Automatically execute type-bound procedure of abstract parent type at the end of extended type'

Time:11-01

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).

CodePudding user response:

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.

  • Related