- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Consider the following, greatly simplified, sections of code
type :: obj_rcd
type(net_rcd),dimension(:),allocatable :: rcd
integer :: cntr=0
type(rcd_desc),dimension(:), pointer :: def
end type
type,extends(obj_rcd) :: area_rcd
contains
procedure increment_counter
end type
subroutine increment_counter(this)
call increment_counter_object(this)
end subroutine increment_counter
subroutine increment_counter_object(obr)
type(obj_rcd) :: obr
if (obr%cntr >= size(obr%rcd)) call extend_obj(obr%rcd,obr%def)
obr%cntr = obr%cntr+1
end subroutine increment_counter_object
This fails to compile due to type mismatch calling increment_counter_object, and this makes perfect sense. I found that these two modifications both build and both seem to work properly:
[1]
subroutine increment_counter(this)
class(area_rcd),target :: this
class(obj_rcd),pointer :: that
that=>this
call increment_counter_object(that)
end subroutine increment_counter
subroutine increment_counter_object(obr)
type(obj_rcd) :: obr
if (obr%cntr >= size(obr%rcd)) call extend_obj(obr%rcd,obr%def)
obr%cntr = obr%cntr+1
end subroutine increment_counter_object
[2]
subroutine increment_counter(this)
call increment_counter_object(this)
end subroutine increment_counter
subroutine increment_counter_object(obr)
class(obj_rcd) :: obr
if (obr%cntr >= size(obr%rcd)) call extend_obj(obr%rcd,obr%def)
obr%cntr = obr%cntr+1
end subroutine increment_counter_object
I suspect that there is a difference between these two. I can make a reasoned guess as to what it might be, but I'm looking for a more definitive explanation. Anyone have one?
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You very likely want your option two, but there's also an option three.
subroutine increment_counter(this) class(area_rcd) :: this call increment_counter_object(this%obj_rcd) ! reference parent component. end subroutine increment_counter subroutine increment_counter_object(obr) type(obj_rcd) :: obr if (obr%cntr >= size(obr%rcd)) call extend_obj(obr%rcd,obr%def) obr%cntr = obr%cntr+1 end subroutine increment_counter_object
There is a general requirement that a dummy argument must be type compatible with the corresponding actual argument. For non-polymorphic dummy arguments - TYPE(xxx) - this means that the declared type of the dummy and actual arguments must be the same. For polymorphic dummy arguments the declared type of the actual argument must be an extension of the declared type of the dummy argument (where the definition of "extension" is such that a type is considered an extension of itself).
(There are other restrictions for allocatable and pointer dummy arguments, and if coarray like stuff is in use, but they are not pertinent here.)
Your original example, with the non-polymorphic dummy in increment_counter_object, does not satisfy type compatibility - the declared type of the actual argument is `area_rcd`, while the dummy argument is `obj_rcd`, and they are not the same. The three options do satisfy that restriction through various means.
The issue with your option one and option three above is that because the dummy argument is non-polymorphic, inside the increment_counter_object procedure you completely disregard the original dynamic type of the actual argument - that procedure only deals with the parent bit of the actual argument. The syntax of option three makes that explicit - with the reference to the parent component. Option one does that implicitly - because the rules of the language permit a polymorphic actual argument to be associated with a non-polymorphic dummy, so long as the declared types match (the use of pointers is incidental to this - you could also use an intermediate procedure).
The ability to completely ignore the non-parent bits of an object is known in some contexts as "slicing" (not the same thing as Fortran array slicing). It can be problematic if the object as a whole is not robust to being sliced. The ability to implicitly slice an object in this manner in modern Fortran is regarded by some as a defect in the language, but as usual with these sorts of things, there's a trade-off - sometimes the ability to do this sort of stuff might have its place. Permit me to sit on the fence.
When you declare the dummy argument to be polymorphic, the code inside the procedure is still aware of the dynamic type of the associated object. Operations on the dummy object are limited to those of its declared type, but things like binding dispatch still depend on the dynamic type. In situations more complicated than your example code, this may be required to keep data members in the extended type consistent with data members in the object corresponding to the parent type.
An attempt at an example (which is silly for various reasons, but it is just for the sake of example). If an object of the extension type is sliced and then operated on, it can become internally inconsistent.
MODULE m IMPLICIT NONE PRIVATE TYPE, PUBLIC :: Parent INTEGER :: i CONTAINS PROCEDURE :: Scale => parent_Scale END TYPE Parent TYPE, PUBLIC, EXTENDS(Parent) :: Extension ! Lets assume that we want to maintain cosistency between r and i. REAL :: r CONTAINS PROCEDURE :: Scale => extension_Scale END TYPE Extension PUBLIC :: ProbablyNotGood PUBLIC :: ProbablyOk CONTAINS SUBROUTINE parent_Scale(object, factor) CLASS(Parent), INTENT(INOUT) :: object REAL, INTENT(IN) :: factor object%i = object%i * factor END SUBROUTINE parent_Scale SUBROUTINE extension_Scale(object, factor) CLASS(Extension), INTENT(INOUT) :: object REAL, INTENT(IN) :: factor CALL parent_Scale(object, factor) object%r = object%r * factor END SUBROUTINE extension_Scale SUBROUTINE ProbablyNotGood(object) TYPE(Parent), INTENT(INOUT) :: object CALL object%Scale(2.0) END SUBROUTINE ProbablyNotGood SUBROUTINE ProbablyOk(object) CLASS(Parent), INTENT(INOUT) :: object CALL object%Scale(2.0) END SUBROUTINE ProbablyOk END MODULE m PROGRAM p USE m TYPE(Extension) :: a, b, c, d a = Extension(1, 1.0) b = a ! Here at least we are explicitly slicing, so there is some hint ! to the reader that only the Parent bit of the object is being ! operated on. CALL ProbablyNotGood(a%Parent) PRINT "(A,': ',*(G0,:,','))", 'a', a CALL ProbablyOk(b) PRINT "(A,': ',*(G0,:,','))", 'b', b END PROGRAM p
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page