0
votes

When we create multiple instances from a data type (class) that has a pass procedure pointer, is the actual procedure (subroutines/functions) copied in each instance? Or is just the pointer copied?

For example consider the following code that compiles and runs correctly.

module mod2
  implicit none

  private

  type class_type
     integer :: a, b, c
   contains
     procedure :: add => add_it
  end type class_type

  public :: class_type

contains

  subroutine add_it(this)
    implicit none
    class(class_type), intent(inout) :: this

    this%c = this%a + this%b

  end subroutine add_it

end module mod2

program tester
  use mod2
  implicit none

  type(class_type), dimension(10) :: objs

  objs(:) = class_type(1, 2, 0)

end program tester

Is subroutine add_it duplicated in each of the 10 objects created from data type class_type? Or is the instruction-set of subroutine add_it stored somewhere and the pointers to it, i.e. "procedure :: add => add_it" copied in each object?

1

1 Answers

2
votes

Typically neither. Note this is very much implementation specific - what I describe below is typical but different processors may do things differently.

Note there are no procedure pointers in your example. The type class_type has a binding. If the class_type had a procedure pointer, things are different.

Typical implementation for bindings is that the compiler creates a table of machine level pointers, with one entry for each specific binding, with the pointer pointing at the code for the procedure. A table (sometimes known as a "vtable", from the similar technique used for virtual member functions in C++ and similar languages) is created for each type in the program.

For polymorphic objects (things declared with CLASS), the compiler then creates a descriptor that has a machine level pointer to the relevant table for the dynamic (runtime) type of the object. This pointer effectively indicates the dynamic type of the object and may be used in constructs such as SELECT TYPE and invocations of things like SAME_TYPE_AS. If you have a polymorphic array the compiler will initially typically only create one descriptor for the entire array, as individual elements in the array must all have the same dynamic type.

When you call a binding on a polymorphic object, the compiler follows the pointer to the vtable, then looks up the relevant pointer to the procedure binding.

No such descriptor or pointer dereferencing is required for non-polymorphic objects (things declared with TYPE) as the dynamic and declared type are always the same, the compiler knows what the declared type is and the compiler knows, at compile time, which procedure will be called.

If you have a procedure call where a non-polymorphic actual argument is associated with a polymorphic dummy argument, then the compiler will typically create the necessary descriptor as part of making the procedure call. Similarly for passing a polymorphic array element to a procedure taking a polymorphic scalar.

The main program of your code contains no polymorphic entities, and you call no procedures, so there may not be any machine pointers back to the vtable.

Procedure pointer components (components declared PROCEDURE(xxx), POINTER :: yyy before the CONTAINS of the type declaration) can be different for every object (including being different for every element in an array). In that case typical implementation is to store a machine level pointer to the code for the relevant procedure (or a null pointer if the procedure pointer component has not been associated).