can anybody enlighten me why the following code is rejected by NAG
Fortran, ifort and gfortran?
It compiles if line 7 is commented out. What am I doing wrong?
module myclass
implicit none
type, abstract :: vector_class
contains
procedure(fun_r_v), deferred :: norm
!== NAG Fortran, ifort and gfortran reject the following line ==
procedure(assign_v_v), deferred :: assign
!==
end type vector_class
abstract interface
real function fun_r_v (this)
import :: vector_class
class(vector_class), intent(in) :: this
end function fun_r_v
subroutine assign_v_v (this,v)
import :: vector_class
class(vector_class), intent(inout) :: this
class(vector_class), intent(in) :: v
end subroutine assign_v_v
end interface
type, extends(vector_class) :: my_vector_type
real :: elements(100)
contains
procedure :: norm => my_norm
procedure :: assign => my_assign
end type my_vector_type
contains
real function my_norm (this)
class(my_vector_type), intent(in) :: this
my_norm = sqrt (sum (this%elements(:)**2))
end function my_norm
subroutine my_assign (this,v)
class(my_vector_type), intent(inout) :: this
class(my_vector_type), intent(in) :: v
this%elements(:) = v%elements(:)
end subroutine my_assign
end module myclass
> can anybody enlighten me why the following code is rejected by NAG
> Fortran, ifort and gfortran?
> It compiles if line 7 is commented out. What am I doing wrong?
[code mostly elided]
Well, the first thing you are doing wrong is not telling us exactly what
the compiler's error message is. Yes, I know we ought to be good enough
to be able to figure out what is wrong without such extra information,
but there is a reason why compilers generally say more than just "nope;
didn't like that; try again." :-)
I threw the code at NAG to find out that it was complaining
"Argument V of overriding type-bound procedure ASSIGN of type
MY_VECTOR_TYPE has the wrong data type"
Ok. That gives me at least something to look at. Ah. I see (well, I
think I do, as my actual experience with this stuff is minimal).
Your abstract type has a deferred type-bound procedure with interface
> subroutine assign_v_v (this,v)
> import :: vector_class
> class(vector_class), intent(inout) :: this
> class(vector_class), intent(in) :: v
> end subroutine assign_v_v
Your extended type is trying to override this binding with a procedure
> subroutine my_assign (this,v)
> class(my_vector_type), intent(inout) :: this
> class(my_vector_type), intent(in) :: v
>
> this%elements(:) = v%elements(:)
> end subroutine my_assign
To quote from page 103 of a certain F2003 book (:-))
When overriding bindings are used in conjunction with
polymorphism, the compiler might not be able to determine
at compile time which specific procedure is invoked by a
particular reference in the code; the same reference might
invoke different procedures for different executions of it.
Therefore the parent binding and the overriding one must
be similar enough that the same invoking code makes sense
for both bindings. The rules and restrictions to ensure that are:
...
2. They must both have the same number of dummy arguments.
Dummy arguments that correspond by position must have
the same names and characteristics, except that the
passed-object dummy will differ in type.
Your argument V is not the passed-object dummy argument and it does not
have the same characteristics as in the parent binding in your example.
THIS is the passed-object dummy, so it is ok.
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
> can anybody enlighten me why the following code is rejected by NAG
> Fortran, ifort and gfortran?
I am very happy to see that you are trying out OOP with gfortran.
However, I must stress that it is, as yet, highly experimental and
lacks some important features.
Janus Weil and I, aided and abetted by Tobias Burnus and the usual
suspects, have been going at it like a storm over the last couple of
months. Unfortunately, we have just missed the 4.5.0 release with
some of the most important improvements. In the next few weeks, you
will see the fortran-dev branch of gcc doing the right thing. We will
backport the improvements as soon as we can and they will certainly
appear in 4.6.0.
Any feedback you can give us would be gratefully received.
Cheers
Paul
I do hope that my original intention is clear enough: all type
extensions
of "vector_class" should provide implementations of "norm" and
"assign".
> To quote from page 103 of a certain F2003 book (:-))
>
> When overriding bindings are used in conjunction with
> polymorphism, the compiler might not be able to determine
> at compile time which specific procedure is invoked by a
> particular reference in the code; the same reference might
> invoke different procedures for different executions of it.
> Therefore the parent binding and the overriding one must
> be similar enough that the same invoking code makes sense
> for both bindings. The rules and restrictions to ensure that are:
>
> ...
> 2. They must both have the same number of dummy arguments.
> Dummy arguments that correspond by position must have
> the same names and characteristics, except that the
> passed-object dummy will differ in type.
>
> Your argument V is not the passed-object dummy argument and it does not
> have the same characteristics as in the parent binding in your example.
> THIS is the passed-object dummy, so it is ok.
Sorry, but I am still completely blank. How should I correctly
specify
the characteristics of V? I had also tried several variations before
the
original posting, but I did not succeed.
Many thanks in advance for a working example.
Cheers,
Harald
> On Nov 7, 1:35 am, nos...@see.signature (Richard Maine) wrote:
> > Harald Anlauf <anlauf.2...@arcor.de> wrote:
> > Your abstract type has a deferred type-bound procedure with interface
> >
> > > subroutine assign_v_v (this,v)
> > > import :: vector_class
> > > class(vector_class), intent(inout) :: this
> > > class(vector_class), intent(in) :: v
> > > end subroutine assign_v_v
> >
> > Your extended type is trying to override this binding with a procedure
> >
> > > subroutine my_assign (this,v)
> > > class(my_vector_type), intent(inout) :: this
> > > class(my_vector_type), intent(in) :: v
> >
> > > this%elements(:) = v%elements(:)
> > > end subroutine my_assign
>
...
> > Your argument V is not the passed-object dummy argument and it does not
> > have the same characteristics as in the parent binding in your example.
> > THIS is the passed-object dummy, so it is ok.
>
> Sorry, but I am still completely blank. How should I correctly specify
> the characteristics of V? I had also tried several variations before
> the original posting, but I did not succeed.
It has to have the same characteristics as in the parent type. Namely,
it has to be class(vector_class) rather than class(my_vector_type).
Of course, then you have to deal with that broader case. You can't just
assume that it is in my_vector_type. If you want to refer to the
elements component of v, you'll need to use select type to make sure
that v has such a component. If you just change the declaration of v
without a select type, the compiler will bitch about that (NAG did when
I tried).
> Many thanks in advance for a working example.
Sorry. I haven't done enough of this to have one handy. Perhaps someone
else does. When I was last seriously looking at this stuff, compilers
didn't have enough of the features done to do much useful with it.
If I change your my_assign to the following, it compiles with NAG
anyway, but I'm not prepared to actually test it and I haven't done
enough of this to be sure it would have the desired result in all cases.
I also just punted on the default case.
subroutine my_assign (this,v)
class(my_vector_type), intent(inout) :: this
class(vector_class), intent(in) :: v
select type(v)
class is (my_vector_type)
this%elements(:) = v%elements(:)
class default
write (*,*) 'Oops'
end select
end subroutine my_assign
OK, I'll admit I "know (absolutely) nuthink" about any of this but I'm
confused why his "this" is ok but "v" isn't since in assign_v_v is
class(vector_class), intent(inout) :: this
while in my_assign its
class(my_vector_type), intent(inout) :: this
Seems to me that this doesn't agree, either???
--
> Richard Maine wrote:
> > Harald Anlauf <anlau...@arcor.de> wrote:
> >
> >> On Nov 7, 1:35 am, nos...@see.signature (Richard Maine) wrote:
> >>> Harald Anlauf <anlauf.2...@arcor.de> wrote:
> >>> Your abstract type has a deferred type-bound procedure with interface
> >>>
> >>>> subroutine assign_v_v (this,v)
> >>>> import :: vector_class
> >>>> class(vector_class), intent(inout) :: this
> >>>> class(vector_class), intent(in) :: v
> >>>> end subroutine assign_v_v
> >>> Your extended type is trying to override this binding with a procedure
> >>>
> >>>> subroutine my_assign (this,v)
> >>>> class(my_vector_type), intent(inout) :: this
> >>>> class(my_vector_type), intent(in) :: v
> >>>> this%elements(:) = v%elements(:)
> >>>> end subroutine my_assign
> > ...
> >>> Your argument V is not the passed-object dummy argument and it does not
> >>> have the same characteristics as in the parent binding in your example.
> >>> THIS is the passed-object dummy, so it is ok.
> OK, I'll admit I "know (absolutely) nuthink" about any of this but I'm
> confused why his "this" is ok but "v" isn't since in assign_v_v is
>
> class(vector_class), intent(inout) :: this
>
> while in my_assign its
>
> class(my_vector_type), intent(inout) :: this
>
> Seems to me that this doesn't agree, either???
Because this is the passed-object dummy. I personally would have
preferred to make that explicit in the declaration. In fact, I had to go
back and check that if you don't say anything about passed-object or
not, then there is one. If the declaration had been explicit, I wouldn't
have had to go look.
Ok. That's the legalistic answer - that the standard makes an exception
for the passed-object dummy and this fits in the exception. I predict
the next question is going to be something along the line of "Why is
that an exception?"
I actually don't recall this stuff very well at all. I'm sure that's
because I didn't use it enough for it to sink in deeply enough to be
second nature. As a result, my comments are based on reconstruction,
rather than memory. I think I have this part more or less right, though;
the reconstruction seems to make sense.
Because THIS is the passed-object dummy, you won't ever end up in
subroutine my_assign unless THIS is of class(my_vector_type). It being
of that class is what makes my_assign be the particular procedure that
gets called, so that procedure never has to deal with the possibility
that THIS might be outside of that class.
The procedure will get invoked by something like (though the name is
evocative of assignment, it isn't an assignment, so the reference will
look like a call statement instead of an assignment statement)
call this%assign(v)
The actual names don't have to match, of course, but I made them do so
for simplicity of exposition. That's how a passed-object dummy
translates in subroutine call syntax; it comes from outside of what
looks like the actual argument list.
(In this case, "because" would probably have been about enough; sorta'
like some of my tongue-in-cheek responses on occasion :) )...
Thanks, as always you provided enough context to make it readable,
Richard...
I'll go back to lurking now... :)
--
OK, now I slowly begin to understand the error messages that I got.
Initially I had the wrong impression that there were some similarity
to the case of generic procedures, where the compiler would know at
compile time which specific procedure to select. So the new features
allow only for checking the appropriateness of the actual parameters
at run time, but not at compile time.
(There are many cases where it would be nice to have a verification
of the compatibility of arguments already at compile time, like for
the assignment in the example. This would leave less room for runtime
trouble...)
> subroutine my_assign (this,v)
> class(my_vector_type), intent(inout) :: this
> class(vector_class), intent(in) :: v
> select type(v)
> class is (my_vector_type)
> this%elements(:) = v%elements(:)
> class default
> write (*,*) 'Oops'
> end select
> end subroutine my_assign
Yes, this does work with NAG Fortran, ifort 11.1 and xlf 12.
I take it from Paul's posting that gfortran is almost there... ;-)
(If there were a possibility to tell the compiler that the
"class default" branch should never be reached, then it would
be able in principle to perform the static analysis mentioned
above; maybe after inlining).
Thanks,
Harald
> (There are many cases where it would be nice to have a verification
> of the compatibility of arguments already at compile time, like for
> the assignment in the example. This would leave less room for runtime
> trouble...)
You do get verification at compile time of those things that can be
verified then. But the whole point of CLASS is that it is saying that
you don't know the exact type at compile time. If you know it at compile
time, you use TYPE instead of class. That doesn't work here because you
are inheriting the binding from an abstract type. An abstract type can
never be the exact type of something; about the only thing you can do
with it is extend it to something that might not be so abstract.
[...]
>
>> subroutine my_assign (this,v)
>> class(my_vector_type), intent(inout) :: this
>> class(vector_class), intent(in) :: v
>> select type(v)
>> class is (my_vector_type)
>> this%elements(:) = v%elements(:)
>> class default
>> write (*,*) 'Oops'
>> end select
>> end subroutine my_assign
>
> Yes, this does work with NAG Fortran, ifort 11.1 and xlf 12.
> I take it from Paul's posting that gfortran is almost there... ;-)
>
> (If there were a possibility to tell the compiler that the
> "class default" branch should never be reached, then it would
> be able in principle to perform the static analysis mentioned
> above; maybe after inlining).
In fact, by saying "class is (<base type>)" you are already
covering the complete inheritance tree. So you can in fact
omit the class default branch because it *will* never be reached
in the above case.
Regards
Reinhold
>
> Thanks,
> Harald
>
> In fact, by saying "class is (<base type>)" you are already
> covering the complete inheritance tree. So you can in fact
> omit the class default branch because it *will* never be reached
> in the above case.
Ack. Sorry for stupidly misreading your code. Please disregard my
previous message.
Actually gfortran *is* right there now :)
Just today I committed a patch, which implements SELECT TYPE with
CLASS IS, to the fortran-dev branch of GCC, and it compiles the above
test case (the repaired version) without a problem.
The branch contains a couple of additional OOP features, which
gfortran 4.5 trunk does not have yet (cf. http://gcc.gnu.org/wiki/OOP).
Anyone brave enough to check out the branch via svn and compile it
from source is welcome to try out gfortran's brand-new OOP
implementation (I'll be happy to help with the technical details). But
beware: These features are still young, and may certainly contain
bugs. The more important it is that people test them ...
Cheers,
Janus
If you are brave enough to test the fortran-dev branch, but not brave
enough to build it yourself, you may want to try the binaries that
Tobias Burnus generously provides at:
http://users.physik.fu-berlin.de/~tburnus/gcc-trunk/
There you can find x86_64 binaries of gfortran 4.5 trunk as well as
the fortran-dev branch.
Cheers,
Janus
> http://users.physik.fu-berlin.de/~tburnus/gcc-trunk/
>
> There you can find x86_64 binaries of gfortran 4.5 trunk as well as
> the fortran-dev branch.
The x86_64 machine at my office runs OpenSuse 11.0 and has minor
problems with
these builds, in particular with debugging (-g). It looks like the
assembler
from binutils is too old for these builds. I mailed Tobias but got no
answer
up to now.
Cheers,
Harald