I am learning oop in fortran.
This is my code and the output surprise me :
module my_module
implicit none
type basic_t
real :: basic
end type basic_t
type, extends(basic_t) :: extended_t
real :: extended
end type extended_t
interface print_type
module procedure print_basic_type
module procedure print_extended_type
end interface print_type
contains
subroutine print_basic_type(basic)
type(basic_t), intent(in) :: basic
print *, 'in sub : print_basic_type'
end subroutine print_basic_type
subroutine print_extended_type(extended)
type(extended_t), intent(in) :: extended
print *, 'in sub : print_extended_type'
end subroutine print_extended_type
subroutine print_using_class(basic_or_extended)
class(basic_t), intent(in) :: basic_or_extended
select type (basic_or_extended)
type is (basic_t)
print *, 'the type is basic'
type is (extended_t)
print *, 'the type is extended'
end select
call print_type(basic_or_extended)
end subroutine print_using_class
end module my_module
program main
use my_module
type(basic_t) :: basic
type(extended_t) :: extended
call print_type(basic)
call print_type(extended)
print *,'------'
call print_using_class(basic)
print *,'------'
call print_using_class(extended)
end program main
This is the output :
in sub : print_basic_type
in sub : print_extended_type
------
the type is basic
in sub : print_basic_type
------
the type is extended
in sub : print_basic_type
When I use the interface print_type
directly, it works. But when I call the subroutine print_using_class
, it does not work for the extended type although the type is well recognized (cf select type
before print_type
call in subroutine print_using_class
).
Is it the output expected ? And why ?
Thanks for answer.
Edit 1
As suggested in the comments, I should use "bindings". After many attempts, this is my new code.
module my_module
implicit none
type, abstract :: abstract_t
contains
procedure(print_abstract_t), deferred :: print_sub
end type abstract_t
abstract interface
subroutine print_abstract_t(this)
import abstract_t
class(abstract_t), intent(in) :: this
end subroutine print_abstract_t
end interface
type, extends(abstract_t) :: basic_t
real :: basic
contains
procedure :: print_sub => print_basic_type
end type basic_t
type, extends(basic_t) :: extended_t
real :: extended
contains
procedure :: print_sub => print_extended_type
end type extended_t
contains
subroutine print_basic_type(this)
class(basic_t), intent(in) :: this
print *, 'in sub : print_basic_type'
end subroutine print_basic_type
subroutine print_extended_type(this)
class(extended_t), intent(in) :: this
print *, 'in sub : print_extended_type'
end subroutine print_extended_type
subroutine print_using_class(basic_or_extended)
class(basic_t), intent(in) :: basic_or_extended
select type (basic_or_extended)
type is (basic_t)
print *, 'the type is basic'
type is (extended_t)
print *, 'the type is extended'
end select
call basic_or_extended%print_sub()
end subroutine print_using_class
end module my_module
program main
use my_module
type(basic_t) :: basic
type(extended_t) :: extended
call basic%print_sub()
call extended%print_sub()
print *,'------'
call print_using_class(basic)
print *,'------'
call print_using_class(extended)
end program main
I am surprised but it works. As I am learning OOP in fortran, any criticism would be appreciated.
CodePudding user response:
In the subroutine print_using_class
there is a reference to the generic print_type
with actual argument basic_or_extended
.
The generic print_type
has two specific interfaces, print_basic_type
and print_extended_type
. (Because these specific procedures use non-polymorphic arguments they are not ambiguous (Fortran 2018, 15.4.3.4.5).) In the reference to the generic, we need to resolve the reference to a specific procedure. (F2018, 15.5.5.2)
Resolution to which of these two specific procedures is referenced is not based on dynamic type; resolution is based on declared type.
Why?
The relevant dummy arguments are:
type(basic_t)
inprint_basic_type
type(extended_t)
inprint_extended_type
The referenced specific procedure is the one where the dummy argument is type compatible with class(basic_or_extended)
(F2018, 15.5.2.4). (Recall, only one specific can be consistently referenced.)
type(basic_t)
is type compatible with class(basic_t)
; type(extended_t)
is not (F2018, 7.3.2.3 p.5). The reference to the generic print_type
is always to print_basic_type
. (Note that a class(basic_t)
is type compatible with a type(extended_t)
but we need the dummy to be type compatible with the actual, not the actual to be type compatible with the dummy: type compatability is not symmetric. )
If you want to use a generic and resolve to the dynamic type of the actual argument, well you can't. What you can do is have another object of the desired declared type:
subroutine print_using_class(basic_or_extended)
class(basic_t), intent(in) :: basic_or_extended
select type (basic_or_extended)
type is (basic_t)
print *, 'the type is basic'
call print_type(basic_or_extended)
type is (extended_t)
print *, 'the type is extended'
call print_type(basic_or_extended)
end select
end subroutine print_using_class
But ideally, you wouldn't be doing this. Instead, you'll be using bindings and type-bound procedures where this dynamic resolution just falls out.