Polymorphic container implementation in FORTRAN?

Polymorphic container implementation in FORTRAN?

What is the proper FORTRAN way to handle polymorphic containers?

The following code results in "catastrophic error: **Internal compiler error: segmentation violation signal raised** Please report this error along with the circumstances in which it occurred in a Software Problem Report.  Note: File and line given may not be explicit cause of this error.    " with Intel fortran xe_2011_sp1.7.256 and gives apparently incorrect behviour with sp1.9.293. In this example a simple superclass and subclass are defined  with overloaded print and assignment operator.  The container, defined using superclass, than should be usable for all subclasses and the container data type is set through allocate (,mold=) during initialisation. The methods called on the items of the container data should then (or I thought so) be bound to the correct type. Unfortunately both print and overloaded assignment in this example use superclass methods irrespective of the actual type of container data.  Is there a way to make the addValue method use correct version of assignment for the data type the container has been initialised with???

Any ideas, would be greatly appreciated.

(Replacing assignment with an overloaded copy subroutine does not work either as it is not possible to overload with a different argument type...)

Thanks

Evgeny

Example:

subclass.f90

module subclass_mod
 use superclass_mod
    type, extends(superclass) :: subclass

     integer :: i2
    contains
     generic, public :: assignment(=) => copySubclass
     generic, public :: print=>printSubclass
     procedure, private :: copySubclass
     procedure, private :: printSubclass
    end type
 contains

   subroutine copySubclass(this, from)
   class(subclass), intent (out) :: this
   class(subclass), intent (in)  :: from

    this%i1=from%i1
    this%i2=from%i2
   end subroutine
!
   subroutine printSubclass(this)
   class(subclass), intent (in) :: this
    print *, "Subclass: ",this%i1,this%i2
   end subroutine
end module subclass_mod

superclass.f90

module superclass_mod
    type superclass

     integer :: i1
    contains
     generic, public :: assignment(=) => copySuperclass
     generic, public :: print=>printSuperclass
     procedure, private :: copySuperclass
     procedure, private :: printSuperclass
    end type
 contains
   subroutine copySuperclass(this, from)
   class(superclass), intent (out) :: this
   class(superclass), intent (in)  :: from

    this%i1=from%i1
   end subroutine

   subroutine printSuperclass(this)
   class(superclass), intent (in) :: this
    print *, "Superclass: ",this%i1
   end subroutine
end module superclass_mod

container.f90

module polycontainer_mod
use superclass_mod
use subclass_mod

 type, public  :: container
   integer :: number
   class(superclass), pointer :: data (:)
  contains
   procedure, public ::  init
   procedure, public ::  addValue
   procedure, public ::  printContainer
 end type
 contains
!
 subroutine init(this,value, temp)
 class(container), intent (inout)  :: this
 class(superclass), intent (inout) :: value
 integer, intent(in) :: temp
 !
 this%number =temp
 allocate(this%data(this%number),mold=value)
 end subroutine
 !
 subroutine addValue(this,value,position)
 class(container)  :: this
 class(superclass) :: value
 integer  :: position

  this%data(position)=value
 end subroutine

 subroutine printContainer(this)
 class(container), intent (inout)  :: this
 integer :: i

  print *, "Number of elements: ",this%number
  do i=1,this%number
   call this%data(i)%print()
  end do
 end subroutine
end module polycontainer_mod

tes.f90

program test
 use superclass_mod
 use subclass_mod
 use polycontainer_mod

 type(superclass) :: sup1,sup2
 type(subclass) :: sub1,sub2
 type(container) :: containerSup, containerSub

  sub1%i1=2.; sub1%i2=3.
 sub2=sub1

 print *, "Container.."
 call containerSub%init(sub2,2)
 print *, "Before assignment.."
 call containerSub%printContainer()
 call containerSub%addValue(sub2,1)
 print *, "After assignment.."
 call containerSub%printContainer()

 end program

9 posts / 0 nouveau(x)
Dernière contribution
Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.

Evgeny,

I'm only 80% sure I understand your question. It would help to know what output you would expect to see from the program. It compiles without error with 13.0.0, and it appears to always use the print method from superclass as you seemed to have found with an earlier version. Here's why: The specific bindings printsubclass and printsuperclass for the generic print are ambiguous -- and this is an error the compiler should have caught but didn't (should be reported as a bug). When you extended superclass, you added printsubclass as a specific binding for the generic print method -- you weren't overriding the superclass print, which is what I think you really wanted to do. To fix this, ditch the generic print type-bound procedure (that's not appropriate) and do the following:

In superclass:
procedure :: print => printSuperClass

In subclass:
procedure :: print => printSubClass

This gives the output I think you expect. There is however another error that the compiler isn't detecting and it has to do again with ambiguous specific bindings for the defined assignment (I'm finding this using the NAG compiler). I'm not sure what the right thing to do here is; I'll have to give it some more thought.

-Neil

Evgeny,

As I said in my previous comment, the way you've defined type-bound ASSIGNMENT(=) for the parent type and its extension is not legal, even though the Intel compiler doesn't complain (a bug!). The two specific bindings copySuperClass and copySubclass don't satisfy the conditions for disambiguation (see 12.4.3.4.5 in the standard). Since assignments and operators have to be generic, my earlier suggestion for fixing the print tbp doesn't apply here. But this will work: change the second dummy argument of copySuperClass and copySubClass from polymorphic to a fixed type. That is,


   subroutine copySuperclass(this, from)

     class(superclass), intent (out) :: this

     type(superclass), intent (in)  :: from


and

   subroutine copySubclass(this, from)

     class(subclass), intent (out) :: this

     type(subclass), intent (in)  :: from


The two specific bindings are now distinguished on the basis of the second dummy argument.
Also, when defining the copy method for the extended type it is often useful to use the parent copy method to take care of the inherited components (you didn't in this simple case). With the way the second arguments are declared it would not seem possible to do this
because you'd be passing a TYPE(subclass) actual to a TYPE(superclass) dummy. However this is possible by referring to the parent component:

   subroutine copySubclass (this, from)

     class(subclass), intent (out) :: this

     type(subclass), intent (in)  :: from

     call copySuperclass (this, from%superclass)

     this%i2 = from%i2

   end subroutine

Hope this helps,
Neil

Hi Neil

Thanks for the reply! Unfortunately this does not seem to work. The assignment operator called inside addValue method of the container is always that of the superclass. E.g. if in the test program I test assignment as follows:

print *, "Superclass: "
print *, "Before copy.."
call sup2%print()
sup1%i1=1.
sup2=sup1
print *, "After copy.."
call sup2%print()

print *, "Subclass: "
print *, "Before copy.."
call sub2%print()
sub1%i1=2.; sub1%i2=3.
sub2=sub1
print *, "After copy.."
call sub2%print()

print *, "Sub container.."
call containerSub%init(sub2,2)
print *, "Before assignment.."
call containerSub%printContainer()
call containerSub%addValue(sub2,1)
print *, "After assignment.."
call containerSub%printContainer()
!
I also added a printout in the respective copy subroutines. The expected outcome is that the first element of containerSub will be initialised with the value of sub2 (i1=2,i2=3). However the actual output is:

Superclass:
Before copy..
Superclass: 0
_____________Superclass assignment
After copy..
Superclass: 1
Subclass:
Before copy..
Subclass: 0 0
_____________Subclass assignment
After copy..
Subclass: 2 3
Sub container..
Before assignment..
Number of elements: 2
Subclass: 0 0
Subclass: 0 0
_____________Superclass assignment
After assignment..
Number of elements: 2
Subclass: 2 0
Subclass: 0 0

I.e. the superclass (=) operator is used and the i2 element is not set.

Evgeniy

P.S. This seems to be consistent with 12.4.5, which says
"If the binding-name in a procedure-designator is that of a generic type bound procedure, the generic
35 binding with that name in the declared type of the data-ref is used to select a specific binding:". (And an overloaded operator has to be generic.). What I am trying to do is effectively to find a way around it in order to implement a limited template container which would work for a declared class and its sub-classes.

P.P.S Just to clarify - the desired behaviour can be achieved with the following addValue subroutine for the container: But this is defeating the purpose a bit since the container has to have knowledge of the type it stores.

subroutine addValue(this,value,position)
class(container) :: this
class(superclass) :: value
integer :: position
select type(x=>this%data(position))
type is (superclass)
call this%data(position)%copySuperclass(value)
type is (subclass)
select type (v=>value)
type is (subclass)
call x%copySubclass(v)
end select
end select
end subroutine

Evgeny,

Yes, my suggestion for modifying the defined assignments was only intended to make it legal Fortran; I hadn't really looked at what you were trying to do with the container type.

I think I understand now what you are trying to do with the container. I think your difficulties stem from a common misunderstanding (one I still stumble on at times) about what the polymorphic array pointer DATA(:) is. Individual elements of the array do not have independent dynamic types. An array is always a homogeneous container (if you will); it has---as a whole---a dynamic type and every element of the array has that same dynamic type regardless of how its value was assigned. What you're really looking for, I think, is an array of polymorphic pointers (as opposed to a pointer to a polymorphic array). Fortran doesn't allow this directly, but there's a standard technique for achieving the effect. The trick is to 'box' the polymorphic pointer in a type and define an array of this type:


type :: box

class(superclass), pointer :: sc => null()

end type

type :: container

type(box), allocatable :: data(:);

! and the rest ...


Then in addValue you do a sourced-allocation of the superclass pointer in the specified position

allocate(this%data(position%sc, source=value)


This pointer now has the dynamic type of value, independent of all the others. There are additional changes that need to be made to accommodate the change to the data component. I think this is closer to achieving what you want. I've attached my mods to your original source, and here is its output:

Superclass:
Before copy..
Superclass: 0
_____________Superclass assignment
After copy..
Superclass: 1
Subclass:
Before copy..
Subclass: 0 0
_____________Superclass assignment
_____________Subclass assignment
After copy..
Subclass: 2 3
Sub container..
Before assignment..
Number of elements: 2
After assignment..
Number of elements: 2
Subclass: 2 3

Fichiers joints: 

Fichier attachéTaille
Télécharger evgeny.txt3.79 Ko

Neil thanks again, this is very useful. However I was not looking for an array where individual elements can have a different dynamic type. The whole array should have one dynamic type as you have suggested. This type is set in container%init routine when data is allocated with a mold based on the supplied value. From this point and until the data attribute has been deallocated its dynamic type stays the same and is equal either to subclass or to superclass based on the value supplied to the init subroutine.
Then I want then to store a value of the same dynamic type in the data array by using an addValue method, and I am looking for a way to do this without using select type inside addValue, which becomes costly on a large number of operations. (For the same reason encapsulating one more level in another container will not work for me (an elegant solution although it is)).
So in summary:
1. Overloaded assignment operator will not work inside addValue as it must be generic and will be bound to declared rather than dynamic type, so will only do the superclass assignment part
2. I cannot overload a copy subroutine in the same way as the print subroutine because the second argument (rhs) has to be of the same type in both superclass and subclass for an overloaded subroutine, unless it is generic, in which case it will again be bound to the superclass because of the declaration of data attribute.
3. Direct call to a specific copy subroutine inside addValue will still require a select type (on both data and value supplied!)

So far I cannot see an alternative method, unless I take storage one level up and let it be done by the caller. I.e. - replace addValue with a getValue returning a pointer to an element of data which is cast by the caller and then the caller can methods of superclass or subclass to set attributes.

P.S. to be more precise - with Intel Fortran 13, adding values to a polymorphic container is ~2 times slower than adding values to a container where data attribute is a type, not a class ...

Okay, maybe I finally understand what you want, and I see your dilemma. I don't yet understand why the superclass assignment binding is being used (I'm not convinced that 12.5.6 is the explanation); the NAG compiler, which I find very trustworthy, behaves the same. It's an interesting problem. At this point my only suggestion is to not make addValue a type-bound procedure. The container module would declare it as a generic with a specific binding for TYPE(SUPERCLASS) argument (not polymorphic!), and the subclass module would add to this generic by adding a specific binding for TYPE(SUBCLASS) argument (and so on for other extensions of superclass). This would require making the subclass module
(and other extensions) depend on the container module, which is a little ugly, but I think it's probably cleaner than having to go modify the container module every time a new extension to superclass is defined.
This is an interesting issue that I'd like to get to the bottom of. I'll reply here if I come up with anything new.
-Neil

Connectez-vous pour laisser un commentaire.