Trouble with polymorphic entities

Trouble with polymorphic entities

Bild des Benutzers Christopher W.

Hello,

I am experiencing several error messages about polymorphic entities with ifort 14.0.1 (Linux) which I find strange.However, I am relatively new to OO Fortran features, so I am not sure if this is a compiler or a personal problem ;-)

Could anyone of you be so kind and have a look at the following sample code?

     MODULE shapes

      ! A general shape
      TYPE, ABSTRACT :: shape
      CHARACTER :: color *8 = 'black'
      CONTAINS
      PROCEDURE :: list   => shape_list
      END TYPE shape

      ! A specialized shape : a sphere
      TYPE, EXTENDS(shape) :: sphere
      REAL :: radius = 1.0
      CONTAINS
      PROCEDURE :: list   => sphere_list
      END TYPE sphere

      CONTAINS

      SUBROUTINE shape_list(this)
      CLASS(shape), INTENT(IN) :: this
      print '(A6, " = ",A8)', "COLOR", this % color
      END SUBROUTINE

      SUBROUTINE sphere_list(this)
      CLASS(sphere), INTENT(IN) :: this
      CALL this % shape % list                !  <---------------------   error
      print '(A6, " = ",F6.3)', "RADIUS", this % radius
      END SUBROUTINE

      END MODULE

ifort gives me the following error on this:

sample01.f(28): error #8307: If the rightmost part-name is of abstract type, data-ref shall be polymorphic   [SHAPE]
      CALL this % shape % list      !  <---------------------   error
------------------^
sample01.f(28): error #8318: If the component immediately preceding the type-bound procedure is abstract, the entire data reference before the procedure name must be polymorphic.   [SHAPE]
      CALL this % shape % list      !  <---------------------   error

Why is "this" not a polymorphic entity?

Why am I not allowed to call the list method of the (abstract) base class? Since "this" is a real object, everything should be well-defined.

My workaround is to rename the list method of shape to list_base, or to call shape_list directly.

What I actually want to do is to call the list method of the parent class (something like super::list() in C++).
Is there any way to do this in Fortran without mentioning shape explicitely, so I could add a class "round_shape"
between shape and sphere at some later time without having to rename the in list() ?

 ---------------------

Second, I am confused when I am allowed to pass derived types as dummy arguments and when not.

If I add a field currentShape and a subroutine SAVE_ANY_SHAPE to my shape module (*not* to a specific type):

 
      CLASS(shape), POINTER :: currentShape

      ... CONTAINS ...

      SUBROUTINE SAVE_ANY_SHAPE(a_shape)
      CLASS(shape), POINTER, INTENT(IN) :: a_shape
      currentShape => this
      END SUBROUTINE

and have the following main program:

      PROGRAM test
      
      USE shapes
      CLASS(shape),  POINTER :: pshape
      CLASS(sphere), POINTER :: psphere

      ALLOCATE(psphere)
      CALL SAVE_ANY_SHAPE(psphere)  <------------------   error

      END PROGRAM test

I get a compile-time "error #6633: The type of the actual argument differs", but

      ALLOCATE(psphere)
      pshape => psphere
      CALL SAVE_ANY_SHAPE(pshape)

works. Also, if I remove the POINTER attribute and add a TARGET attribute to a_shape, I can call SAVE_ANY_SHAPE with either of pshape and psphere as argument without error. Is this really how it should be? Why are targets polymorphic and pointers not?

Thank you for your hints!

   Christopher

4 Beiträge / 0 neu
Letzter Beitrag
Nähere Informationen zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.
Bild des Benutzers reinhold-bader

Hello,

with respect to your first question: "this" is a polymorphic entity, but "this%shape", being a reference to the abstract parent type, is not. Disallowing an object to be of abstract dynamic type is a fundamental property of the concept, by the way. From the point of view of re-using your template method in overrides, you could replace

CALL this % shape % list   

by

CALL shape_list(this)

As far as your second question is concerned: If a polymorphic dummy argument has the POINTER or ALLOCATABLE attribute, the actual argument is required to have the same declared type as the dummy. The reason for this is that the type compatibility rules must be applied in two ways: First, the incoming object must be either of the same type or an extension of that of the dummy. Second, an allocation or pointer assignment inside the procedure may cause the dynamic type of the object to become, at worst, the declared type of the dummy. Hence, on return from the procedure you'd get an inconsistency if the actual argument were of an extension type of the declared type of the dummy.

(Note that the pointer assignment inside your SAVE_ANY_SHAPE is to an undeclared entity "this"; presumably it should be to "a_shape"?

Regards

Reinhold

 

Bild des Benutzers Izaak Beekman

I will also note that it's a bit odd to declare a non-deferred type bound procedure and data components in an abstract type in this context. Typically, the point of an abstract type is to define the methods which the concrete type should implement, and their interfaces. It is an interface specification/contract tool. Sure, it can have default data elements, and perhaps some non-override-able default methods, but it must have a concrete implementation to be used. (You can only use these methods and data components from a concrete type which extends it.)

You could simply remove 'abstract' from the type declaration and I think it would work. Also, if you don't overload list, i.e. keep distinct names you could CALL this%shape_list which sphere inherits from shape.

-Zaak
Bild des Benutzers Christopher W.

Hi,

first of all thank you very much for the enlightening answers. Of course, "this" is wrong and should be "a_shape" in the above SAVE_SHAPE - sorry for the Typo.

I do not think it odd at all to collect the properties (in the example: color and list) which all of my objects have in common in a (incomplete) base class, and prevent such objects from being instantinated by accident (because not all methods can be defined on this level of abstraction) by declaring it abstract. Of course I could implement all the methods I need and write a STOP into them, but then the compiler would not be able to force me to override them. I do not want to take this risk, because our actual code is huge, complex and still growing.

By the way, we found a way to call superclass type-bound procedures (above: shape_list) without naming the superclass there. If I would decide to define a (possibly abstract) class "round_shape" which extends "shape" and which should become the new base class for sphere, I only have to change "shape" to "round shape" in one single place. No way to forget to change "shape_list" in the code further below. The price of it is some prepocessor magic and slightly less readable code, but it will pay off as the code gets large:

#include super_magic.fh

#define SUPER shape
      TYPE, EXTENDS(SUPER) :: sphere
      REAL :: radius = 1.0
      CONTAINS
      PROCEDURE :: list   => sphere_list
      END TYPE sphere

[... be sure not to redefine SUPER here! ... ]

      SUBROUTINE sphere_list(this)
      CLASS(sphere), INTENT(IN) :: this
      CALL SUPERMETH(list)  !  ( plus args in a further couple of parentheses, if there were any)
      print '(A6, " = ",F6.3)', "RADIUS", this % radius
      END SUBROUTINE

supermagic.fh contains the following (admittedly weird) lines:

  #define fppconcat2( a , b ) a ## _ ## b
  #define fppconcat1( a , b ) fppconcat2( a , b )
  #define SUPERMETH( a )  fppconcat1( SUPER, a)

Best Regards

Christopher

Melden Sie sich an, um einen Kommentar zu hinterlassen.