Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

overload of an abstract type procedure and call of this procedure

738 views
Skip to first unread message

mg

unread,
Aug 22, 2019, 11:39:16 AM8/22/19
to
Hi,
I have in my code an abstract type which, in addition to many deferred
procedures, has a normal procedure which is used as it is by most of the
types extending this abstract type.

However in some cases, I would like to overload this procedure, but also
to be able to call it in the overloaded procedure ( which is doing
additional operations).
But the compiler ( gfortran 8.3 ) does not accept this as it looks like
it is not permitted to call explicitely a procedure of an abstract type
( although it is done implicitely when there is no overloading).

Thank you for any solution or advice !

Below is a small code reproducing the problem I have. The type shape is
abstract and should not be instanciated. It has a deferred routine and a
normal routine showInfo. The type square extends the type shape, and
overloads showInfo by a routine called showSquareInfo, in which I try to
call the base type showInfo routine - and this does not work. The error
returned by the compiler is at the end.

Marc

module test

type,abstract :: shape
real :: x,y
contains
procedure :: showInfo
procedure(calculateSurfaceProto), deferred :: calculateSurface
end type shape

abstract interface
subroutine calculateSurfaceProto(self,s)
import shape
class(shape) :: self
real :: s
end subroutine calculateSurfaceProto
end interface

type,extends(shape) :: square
real :: size
contains
procedure :: calculateSurface => calculateSquareSurface
procedure :: showInfo => showSquareInfo
end type square

contains

subroutine showInfo(self)
class(shape) :: self
print*, "x = ", x, "y = ", y
end subroutine showInfo

subroutine showSquareInfo(self)
class(square) :: self
real :: surf
call self%shape%showInfo() ! THIS IS WHERE THE PROBLEM IS
call self%calculateSurface(surf)
print *, "Surface = ", surf
end subroutine showSquareInfo

subroutine calculateSquareSurface(self,s)
class(square) :: self
real :: s

s = self%size * self%size
end subroutine calculateSquareSurface


end module test

program expl

use test
implicit none

type(square) ::sq
real :: surf

sq = square(x=1,y=2,size=5)
call sq%showInfo()

end program expl


Below the first error given by gfortran 8.3

marc>> gfortran-8 test.f90
test.f90:39:13:

call self%shape%showInfo()
1
Error: Base object for type-bound procedure call at (1) is of ABSTRACT
type ‘shape’



FortranFan

unread,
Aug 22, 2019, 2:08:45 PM8/22/19
to
On Thursday, August 22, 2019 at 11:39:16 AM UTC-4, mg wrote:

> ..
> Thank you for any solution or advice !
> ..


@mg,

Yes, gfortran is correct - the standard does not allow a reference to an ABSTRACT type as in your 'call self%shape%showInfo()' statement.

Now, if you notice in section 7.5.7.2 on Inheritance in the Fortran standard, it says in paragraph 3, "If a generic binding specified in a type definition has the same generic-spec as an inherited binding, it extends the generic interface and shall satisfy the requirements specified in 15.4.3.4.5" And note section 15.4.3.4.5 deals with restrictions on generic declarations and the need for a processor to DISAMBIGUATE among the possibilities.

So keeping the above extension facility with generic bindings in mind, a solution you may want to consider is to use a generic binding for your showInfo procedure, an option is shown below. You can give it a try and provide your feedback here so other readers with similar interests may benefit from your findings. In the code below, a dummy argument of Fortran intrinsic CHARACTER type is used to disambiguate among the specific procedures in the binding and this fact is also utilized along with a 'private' named constant to try to prevent its use (thus abuse) outside the de facto 'namespace' for your 'class' i.e., your module 'test'.

--- code with generic binding ---
module test

character(len=*), parameter, private :: MAGIC_WORD = "simsim"

type, abstract :: shape
real :: x,y
contains
procedure :: showShapeInfo
procedure(calculateSurfaceProto), deferred :: calculateSurface
generic :: showInfo => showShapeInfo
end type shape

abstract interface
subroutine calculateSurfaceProto(self,s)
import shape
class(shape) :: self
real :: s
end subroutine calculateSurfaceProto
end interface

type,extends(shape) :: square
real :: size
contains
procedure :: calculateSurface => calculateSquareSurface
procedure :: showSquareInfo
generic :: showInfo => showSquareInfo
end type square

contains

subroutine showShapeInfo(self, pw)
class(shape) :: self
character(len=*), intent(in) :: pw
if ( pw == MAGIC_WORD ) then
print*, "x = ", x, "y = ", y
end if
end subroutine showShapeInfo

subroutine showSquareInfo(self)
class(square) :: self
real :: surf
call self%showInfo( pw=MAGIC_WORD ) ! Use generic interfaces to overcome the problem
call self%calculateSurface(surf)
print *, "Surface = ", surf
end subroutine showSquareInfo

subroutine calculateSquareSurface(self,s)
class(square) :: self
real :: s

s = self%size * self%size
end subroutine calculateSquareSurface


end module test

program expl

use test
implicit none

type(square) ::sq
real :: surf

sq = square(x=1,y=2,size=5)
call sq%showInfo()

end program expl
--- end code ---

Upon execution using gfortran,
x = 1.40129846E-45 y = 0.00000000
Surface = 25.0000000

FortranFan

unread,
Aug 22, 2019, 4:42:02 PM8/22/19
to
On Thursday, August 22, 2019 at 2:08:45 PM UTC-4, FortranFan wrote:

> ..
> Upon execution using gfortran,
> x = 1.40129846E-45 y = 0.00000000
> Surface = 25.0000000

@mg,

Oh, forgot to point out: in your procedure showInfo, assume you meant self%x and self%y?

subroutine showInfo(self)
class(shape) :: self
print*, "x = ", self%x, "y = ", self%y !<-- note this line
end subroutine showInfo


Ian Harvey

unread,
Aug 22, 2019, 5:36:53 PM8/22/19
to
On 23/08/2019 1:09 am, mg wrote:
> Hi,
> I have in my code an abstract type which, in addition to many deferred
> procedures, has a normal procedure which is used as it is by most of the
> types extending this abstract type.
>
> However in some cases, I would like to overload this procedure, but also
> to be able to call it in the overloaded procedure ( which is doing
> additional operations).
> But the compiler ( gfortran 8.3 ) does not accept this as it looks like
> it is not permitted to call explicitely a procedure of an abstract type
> ( although it is done implicitely when there is no overloading).

Call the procedure directly, using its name, rather than via a binding
of a type.

See below for the simple change required.

To elaborate on the reasons for the language rules, the syntax
`object%parent`, where parent is a parent type of the type of `object`,
by definition is a designator for an object that has *both* a declared
type and a dynamic type of the parent type.

If the parent type is abstract that doesn't make sense - you cannot
create complete objects of an abstract type. Consider that abstract
types potentially (usually) have bindings that have not been bound to a
specific procedure - what would happen if you referenced such a binding?

The syntax `object%binding` is typically used for dynamic dispatch to a
specific procedure based on the dynamic type of the object. With the
syntax `object%parent%binding` the dynamic type of the thing with the
binding is statically known (as above, it is the parent type) - so there
is no need for dynamic dispatch.
call showInfo(self)

FortranFan

unread,
Aug 22, 2019, 8:21:48 PM8/22/19
to
On Thursday, August 22, 2019 at 5:36:53 PM UTC-4, Ian Harvey wrote:

> ..
> Call the procedure directly, using its name, rather than via a binding
> of a type. ..

Good quick workaround for OP.

There is a reason why I did not suggest such a fix which is a commercial context and/or a complex team situation with multiple priorities/personalities with tendencies to stomp on each others' 'data' either intentionally or otherwise. One can have several of the 'showInfo' type of procedures toward initialization, setter/getter, IO or post-processing, etc. that operate on 'data' encapsulated in an abstract 'class'. One can then end up needing to "expose" them all, meaning one has to effectively move away from the starting premise of an OO approach which is to consider *information hiding* as much as possible. In Fortran parlance that would typically mean paying close attention to what is PUBLIC in a module with the default visibility being made PRIVATE. This will typically accompany the practice where the abstract 'class' is in one module and a concrete extension is in another. Thus a variant of an example in the original post can mean the procedure be public which can be discomforting depending on what it does. See below - try linking it:

module shape_m

private !<--

type, abstract, public :: shape_t
private !<--
real :: x, y
contains
!private !<-- desired generally with only a few bindings made public
procedure :: initShape
procedure :: showInfo
procedure(calculateSurfaceProto), deferred :: calculateSurface
generic :: Init => initShape
end type

abstract interface
subroutine calculateSurfaceProto(self, s)
import shape_t
class(shape_t), intent(in) :: self
real, intent(inout) :: s
end subroutine calculateSurfaceProto
end interface

!public :: showInfo !<-- Under "information hiding" considerations, one is in a bind

contains

subroutine showInfo(self)
class(shape_t), intent(in) :: self
print*, "x = ", self%x, "y = ", self%y
end subroutine

subroutine initShape(self, x, y)
class(shape_t), intent(inout) :: self
real, intent(in) :: x
real, intent(in) :: y
self%x = x
self%y = y
end subroutine

end module

module square_m

use shape_m !<-- preferred is ONLY clause with only the 'class' use-associated

private

type, extends(shape_t), public :: square_t
private !<--
real :: size
contains
!private !<-- private is preferred
procedure :: initSquare
procedure :: showInfo => showSquareInfo
procedure :: calculateSurface => calculateSquareSurface
generic :: Init => initSquare
end type

contains

subroutine showSquareInfo(self)
class(square_t), intent(in) :: self
real :: surf
call showInfo(self)
call self%calculateSurface(surf)
print *, "Surface = ", surf
end subroutine

subroutine calculateSquareSurface(self,s)
class(square_t), intent(in) :: self
real, intent(inout) :: s
s = self%size * self%size
end subroutine

subroutine initSquare(self, x, y, size)
class(square_t), intent(inout) :: self
real, intent(in) :: x
real, intent(in) :: y
real, intent(in) :: size
call self%Init(x, y)
self%size = size
end subroutine

end module

program expl

use square_m, only : square_t

implicit none

type(square_t) ::sq

call sq%Init( x=1.0, y=2.0, size=5.0 )

ga...@u.washington.edu

unread,
Aug 22, 2019, 8:52:06 PM8/22/19
to
On Thursday, August 22, 2019 at 8:39:16 AM UTC-7, mg wrote:
> Hi,
> I have in my code an abstract type which, in addition to many deferred
> procedures, has a normal procedure which is used as it is by most of the
> types extending this abstract type.

> However in some cases, I would like to overload this procedure, but also
> to be able to call it in the overloaded procedure ( which is doing
> additional operations).
> But the compiler ( gfortran 8.3 ) does not accept this as it looks like
> it is not permitted to call explicitely a procedure of an abstract type
> ( although it is done implicitely when there is no overloading).

> Thank you for any solution or advice !

I don't know that I have ever tried it in this way, but as well
as I know, Java allows this.

I believe that a Java abstract class can have methods appropriate
for all subclasses. It is usual, at least in the non-abstract case,
for a subclass after, or maybe before, doing things specific to
the subclass, to then call the same named method of the superclass.

That is done with the super (reserved word) prefix.

I am not an expert in OO programming, though, so I could be wrong.


ga...@u.washington.edu

unread,
Aug 22, 2019, 8:56:42 PM8/22/19
to
On Thursday, August 22, 2019 at 1:42:02 PM UTC-7, FortranFan wrote:

(snip)

> Oh, forgot to point out: in your procedure showInfo,
> assume you meant self%x and self%y?

> subroutine showInfo(self)
> class(shape) :: self
> print*, "x = ", self%x, "y = ", self%y !<-- note this line
> end subroutine showInfo


In Java, unless overridden with local variables, all class
variables are available in class methods without the this. prefix.

It is not so unusual in Java to have local variables with the same name,
usually as method arguments, in which case the this. prefix is used.

pubic void addTo(int x, int y) {
this.x += x;
this.y += y;
}

This would be usual for OO programming.

mg

unread,
Aug 23, 2019, 4:27:01 AM8/23/19
to
@FortranFan
Thank you for your answer. Your technique works well with the simple
code but this simple code, unfortunately, does not show all. In
particular I want to be able to call the showInfo subroutine with the
same number of arguments for any type extending the abstract type. In my
real code I have a large number of types extending the base abstract
type, some of them will overload showInfo ( no additional argument) and
other not ( one additional argument necessary).
The exact type is only known at execution, and as I do not want to test
for the type.

Testing your solution with an additional type extending the shape type (
point type), I also encountered an additional problem.
Declaring an instance of a point and an instance of square like this:

type(point) :: p
type(square) ::sq

compilation is OK.

But with :
class(shape), allocatable :: sq,p
allocate(square::sq)
allocate(point::p)


I have the compilation error:

call sq%showInfo()
1
Error: Found no matching specific binding for the call to the GENERIC
‘showinfo’ at (1)


See the full test code below.

Marc



module test

character(len=*), parameter, private :: MAGIC_WORD = "simsim"

type, abstract :: shape
real :: x,y
contains
procedure :: showShapeInfo
procedure(calculateSurfaceProto), deferred :: calculateSurface
generic :: showInfo => showShapeInfo
end type shape

abstract interface
subroutine calculateSurfaceProto(self,s)
import shape
class(shape) :: self
real :: s
end subroutine calculateSurfaceProto
end interface

type,extends(shape) :: square
real :: size
contains
procedure :: calculateSurface => calculateSquareSurface
procedure :: showSquareInfo
generic :: showInfo => showSquareInfo
end type square

type, extends(shape) :: point
contains
procedure :: calculateSurface => calculatePointSurface
end type point

contains

subroutine showShapeInfo(self, pw)
class(shape) :: self
character(len=*), intent(in) :: pw
if ( pw == MAGIC_WORD ) then
print*, "x = ", self%x, "y = ", self%y
end if
end subroutine showShapeInfo

subroutine showSquareInfo(self)
class(square) :: self
real :: surf
call self%showInfo( pw=MAGIC_WORD ) ! Use generic interfaces to
overcome the problem
call self%calculateSurface(surf)
print *, "Surface = ", surf
end subroutine showSquareInfo

subroutine calculateSquareSurface(self,s)
class(square) :: self
real :: s

s = self%size * self%size
end subroutine calculateSquareSurface

subroutine calculatePointSurface(self,s)
class(point) :: self
real :: s

s = 0.
end subroutine calculatePointSurface

end module test

program expl

use test
implicit none

!type(point) :: p ! OK
!type(square) ::sq ! OK
class(shape), allocatable :: sq,p
allocate(square::sq)
allocate(point::p)


p = point(x=3.,y=7.)
sq = square(x=1.,y=2.,size=5.)
call p%showInfo(pw="simsim")

mg

unread,
Aug 23, 2019, 4:29:38 AM8/23/19
to
Le 22/08/2019 à 22:41, FortranFan a écrit :

> @mg,
>
> Oh, forgot to point out: in your procedure showInfo, assume you meant self%x and self%y?

Yes you are right, I made a mistake. I am surprised that the code could
compile with that error.

Marc




mg

unread,
Aug 23, 2019, 5:03:59 AM8/23/19
to
Thank you Ian for your answer and explanations.

Le 22/08/2019 à 23:36, Ian Harvey a écrit :

> Call the procedure directly, using its name, rather than via a binding
> of a type.
>
> See below for the simple change required.

It is clearly the right solution in my case. I have tested it in my real
program: it works fine. Thanks a lot !

>
> To elaborate on the reasons for the language rules, the syntax
> `object%parent`, where parent is a parent type of the type of `object`,
> by definition is a designator for an object that has *both* a declared
> type and a dynamic type of the parent type.
>
> If the parent type is abstract that doesn't make sense - you cannot
> create complete objects of an abstract type.  Consider that abstract
> types potentially (usually) have bindings that have not been bound to a
> specific procedure - what would happen if you referenced such a binding?

For me the line :
call self%shape%showInfo()
meant only that I was calling the showInfo() procedure of the parent
abstract type. And as this procedure was not declared with the
"deferred" keyword, it meant that is was bound to a specific procedure.
But if I understand well what you say that, before considering the
procedure call it is first the type of self%shape which is considered,
and because it is considered to be of an abstract type, it should not
exist. But it exists only as a parent to self, not independently. This
is a little bit difficult to understand for me !

Marc

mg

unread,
Aug 23, 2019, 5:25:11 AM8/23/19
to
@FortranFan
I understand your point, but I am certainly a bad OO programmer ( maybe
a bad programmer at all ...) as I never use the keyword private, nor
setters, getters ...
I am the only developer - and mostly the only user - of my programs.
For me OO is more a programming style that I use to give a structure to
my programs and try to make them as clear and simple as possible.
The types that are extending my abstract class are indeed in different
modules but as nothing is private the solution proposed by Ian is
perfect for me. But it might not work for more serious OO programmers ...
Marc

Wolfgang Kilian

unread,
Aug 23, 2019, 10:45:32 AM8/23/19
to
This is almost a FAQ in OO design. Fortran does not allow you to call
any procedure for an object of abstract type, even if the procedure only
uses non-deferred features of that type.

My own standard solution is

contains
procedure :: showInfo => showBasicInfo
procedure :: showBasicInfo

[showInfo defaults to showBasicInfo]
or even

contains
procedure(showBasicInfo), deferred :: showInfo
procedure :: showBasicInfo

[showInfo must be implemented with no default, and the implementation
can call showBasicInfo]

and the base-type implementation is

subroutine showBasicInfo(self)
class(shape) :: self
print*, "x = ", x, "y = ", y
end subroutine showBasicInfo

Then showBasicInfo is also inherited by all type extensions, and you
don't refer to the abstract type in any call. You can
gladly override showInfo and still access showBasicInfo, for instance
call the latter in a new procedure that implements the former.

If you like, you can also introduce a generic binding, but often this is
overkill. A naming such as 'showBasicInfo' tells the reader of the
program what actually happens here.

-- Wolfgang




--
E-mail: firstnameini...@domain.de
Domain: yahoo

ga...@u.washington.edu

unread,
Aug 23, 2019, 3:11:36 PM8/23/19
to
On Friday, August 23, 2019 at 1:29:38 AM UTC-7, mg wrote:
> Le 22/08/2019 à 22:41, FortranFan a écrit :

(snip)

> > Oh, forgot to point out: in your procedure showInfo, assume
> > you meant self%x and self%y?

> Yes you are right, I made a mistake. I am surprised that the code could
> compile with that error.

Without IMPLICIT NONE, you get local variables x and y,
that aren't given any value. With most compilers, that will give
a warning, though it might be turned off.

mg

unread,
Aug 24, 2019, 3:09:37 AM8/24/19
to
True ! I forgot it, it was another mistake.
Marc

mg

unread,
Aug 24, 2019, 5:24:00 AM8/24/19
to
Thank you Wolfgang, your first solution is perfect for my case ( tested,
it works). If I understand well your second proposition ( with the
deferred procedure), I should implement the showInfo procedure for each
of the extended types, which is less convenient for me as in my real
code the equivalent of showBasicInfo is enough for most of the extended
types.
Marc



0 new messages