Reputation: 4129
I explain my question in the form of an example.
I have a type (location2d_t) which includes two members x
, and y
and a type-bound procedure (calcdist2d
). The procedure, in addition of (this
) with class of (location2d_t
), accepts its own type (as second dummy argument) to calculate a distance.
Now, I go further and extend the type to (location3d_t
) which has z
as well.
To re-define the procedure, I cannot override the previous one so I create a new procedure (calcdist3d
) with the second argument of type of (location3d_t
) and make a generic procedure (calcdist
) for them. In other words, second arguments have different types so generic idea is applicable.
In a more general scope, let's say main program here, for the sake of generality I declare my object as class of parent. When I allocate the object with type of child (location3d_t
), a call to (calcdist
) whose second dummy argument is (location3d_t
) refers to parent generic and says
Error: Found no matching specific binding for the call to the GENERIC 'calcdist'
The code is
module point_mod
implicit none
type location2d_t
integer :: x,y
contains
procedure :: calcdist2d => calcdistance2d
procedure :: here => here_location2d
generic :: calcdist => calcdist2d
end type
type, extends(location2d_t) :: location3d_t
integer :: z
contains
procedure :: calcdist3d => calcdistance3d
procedure, public :: here => here_location3d
generic, public :: calcdist => calcdist3d
end type
contains
function calcdistance2d(this,location) result(output)
class(location2d_t) :: this
type(location2d_t) :: location
integer :: output
output = int(sqrt(real((location%x-this%x)**2+(location%y-this%y)**2)))
end function
function calcdistance3d(this,location) result(output)
class(location3d_t) :: this
type(location3d_t) :: location
integer :: output
output = int(sqrt(real((location%x-this%x)**2+ &
(location%y-this%y)**2+(location%z-this%z)**2)))
end function
subroutine here_location2d(this)
class (location2d_t) :: this
print*, "we are in locationd2d_t"
end subroutine
subroutine here_location3d(this)
class (location3d_t) :: this
print*, "we are in locationd3d_t"
end subroutine
end module
The module is compiled without any error. The below program is implemented to use the module:
program main
use point_mod
implicit none
class (location2d_t), allocatable :: loc
type (location3d_t) :: dum
allocate(location2d_t::loc)
call loc%here() ! calls location2d_t procedure
deallocate(loc)
allocate(location3d_t::loc)
call loc%here() !correctly calls procedure of location3d_t
print*,loc%calcdist(dum) ! gives error
select type (loc)
type is (location3d_t)
print*,loc%calcdist(dum) ! runs well
end select
end program
The procedure "Here" finds its dynamic type correctly. Why isn't generic procedure of child (calcdist) explicitly called ? Do I have to use "select type" block always even in this obvious case? N.B.: I checked the code with GNU fortran 4.8 and 4.9, and ifort 14.
Upvotes: 1
Views: 1307
Reputation: 6915
You can accomplish this behavior without generics with only a slight tweak to your calcdistanceXd
functions. The reason you couldn't override the function in your extended type is that the argument type of location
was mismatched. If you instead declare location
in calcdistance2d
to be class(location2d_t)
then you can match this in calcdistance3d
. You will have to add in a select type
construct into calcdistance3d
in order to access the members of location3d_t
from the polymorphic variable location
.
Example:
module point_mod
implicit none
type :: location2d_t
integer :: x, y
contains
procedure, public, pass(this) :: calcdist => calcdistance2d
procedure, public, pass(this) :: here => here_location2d
end type
type, extends(location2d_t) :: location3d_t
integer :: z
contains
procedure, public, pass(this) :: calcdist => calcdistance3d
procedure, public, pass(this) :: here => here_location3d
end type
contains
function calcdistance2d(this, location) result(output)
class(location2d_t) :: this
class(location2d_t) :: location
integer :: output
output = int(sqrt(real((location%x-this%x)**2+(location%y-this%y)**2)))
end function
function calcdistance3d(this,location) result(output)
class(location3d_t) :: this
class(location2d_t) :: location
integer :: output
select type (location)
type is (location3d_t)
output = int(sqrt(real((location%x-this%x)**2+ &
(location%y-this%y)**2+(location%z-this%z)**2)))
class default
output = -1
end select
end function
subroutine here_location2d(this)
class (location2d_t) :: this
print*, "we are in locationd2d_t"
end subroutine
subroutine here_location3d(this)
class (location3d_t) :: this
print*, "we are in locationd3d_t"
end subroutine
end module
With this version of point_mod
, your example program works:
program main
use point_mod
implicit none
class (location2d_t), allocatable :: loc
type (location3d_t) :: dum
allocate(location2d_t::loc)
call loc%here() ! calls location2d_t procedure
deallocate(loc)
allocate(location3d_t::loc)
call loc%here() !correctly calls procedure of location3d_t
print*,loc%calcdist(dum)
end program
It is true this approach still requires a select type
, but it is hidden in the module implementation rather than being required by users of the module.
Upvotes: 0
Reputation: 744
Yes, you have to use "select type". Outside the "type is" block, loc
is polymorphic. Only inside type is (location3d_t)
, loc
has a type and can be passed as dummy argument with defined type.
Generic procedures are always not overridden when the type is extended, so in location3d_t
, calcdist
is the generic binding for calcdist3d
and calcdist2d
and loc
needs a specific type when calling calcdist
to find the appropriate procedure.
When location2d_t
is extended, to location3d_t
, here
binding is overriden and there is only one procedure associated to loc%here()
so can be called outside the "type is" block
Upvotes: 1