Reputation: 55
Coming from C++, I know that non-virtual member functions are tied to their classes through name-mangling. I'm trying to learn modern Fortran and my question is how are Fortran type-bound procedures linked to their types?
For example, consider the derived type
module shape_mod
type shape
integer :: color
logical :: filled
contains
procedure :: isFilled
end type shape
contains
logical function isFilled(this)
class(shape) :: this
isFilled = this%filled
end function isFilled
end module
My question is about the internals of procedure dispatch. Does the memory layout of an instance shp
of type shape
hold a pointer to a function or vtable that needs to be de-referenced when calling shp%isFilled()
, as happens with virtual member functions in C++? Or is the name of the type-bound procedure shp%isFilled()
mangled into something like _Z5shape_isFilled(shp)
, as happens with non-virtual member functions in C++?
I know that Fortran uses name mangling for procedures in modules see wiki for example.
Upvotes: 2
Views: 620
Reputation: 59998
The procedures you write are completely ordinary normal module procedures. They just have a polymorphic dummy argument. You do not have to call them as type-bound, you can use them in a normal way.
What makes them type-bound is the creation of the binding in the type declaration.
procedure :: isFilled
that means there will be a virtual table and a pointer to the procedure in the virtual table and so on and it will point the right type-bound procedure for the right actual type.
But the procedures are completely ordinary ones. At least conceptually, I did not study the compiler, I am describing the standard concept.
In your case you can call
type(shape) :: instance
print *, isFilled(instance)
and it will work perfectly fine, there is nothing special about the procedure itself.
Note that when you are extending the type
type, extends(shape) :: outlined_shape
integer :: outline_color
contains
procedure :: isFilled => isFilled_outlined shape
end type shape
you have to use a different name for the procedure. Hence no name mangling is necessary, you already have a procedure with a different name
logical function isFilled_outlined_shape(this)
class(outlined_shape) :: this
Again, you can call it as a type bound procedure:
type(outlined_shape) :: instance
print *, instance%isFilled()
but you can also call it directly (non-virtually)
print *, isFilled_outlined_shape(instance)
and you can even call the parent one directly
print *, isFilled(instance)
The reason why C++ needs name mangling is because it uses the same name fir the virtual function in each type. It does not have these normal procedures that have different names as in Fortran. Because if that, C++ needs name-mangling.
Consider this example:
module m
type t1
contains
procedure :: s => s_t1
end type
type t2
contains
procedure :: s => s_t2
end type
contains
subroutine s_t1(self)
class(t1) :: self
end subroutine
subroutine s_t2(self)
class(t2) :: self
end subroutine
end module
In gfortran, the result contains these symbols:
000000000000000f T __m_MOD___copy_m_T1
0000000000000000 T __m_MOD___copy_m_T2
0000000000000000 B __m_MOD___def_init_m_T1
0000000000000001 B __m_MOD___def_init_m_T2
0000000000000029 T __m_MOD_s_t1
000000000000001e T __m_MOD_s_t2
0000000000000000 R __m_MOD___vtab_m_T1
0000000000000040 R __m_MOD___vtab_m_T2
their meaning is the following:
0000000000000029 T __m_MOD_s_t1
000000000000001e T __m_MOD_s_t2
the subroutine, you can call them directly this way (non-virtually).
000000000000000f T __m_MOD___copy_m_T1
0000000000000000 T __m_MOD___copy_m_T2
the intrinsic assignments of the two types.
0000000000000000 B __m_MOD___def_init_m_T1
0000000000000001 B __m_MOD___def_init_m_T2
the default constructors of the two types
0000000000000000 R __m_MOD___vtab_m_T1
0000000000000040 R __m_MOD___vtab_m_T2
the virtual table functions, one per type (not per procedure, it is not name-mangling of the procedures).
When you add more procedures, https://godbolt.org/z/3q4KWf you can see how the virtual tables contains pointers to them:
__m_MOD___vtab_m_T1:
.long 69979407
.zero 4
.quad 0
.quad 0
.quad __m_MOD___def_init_m_T1
.quad __m_MOD___copy_m_T1
.quad 0
.quad 0
.quad __m_MOD_r_t1
.quad __m_MOD_s_t1
__m_MOD___vtab_m_T2:
.long 69979408
.zero 4
.quad 0
.quad 0
.quad __m_MOD___def_init_m_T2
.quad __m_MOD___copy_m_T2
.quad 0
.quad 0
.quad __m_MOD_s_t2
.quad __m_MOD_r_t2
Upvotes: 1