* Re: Classwide Parameter?
2004-10-11 17:08 ` matthias_k
@ 2004-10-11 19:16 ` Simon Wright
2004-10-11 22:53 ` Brian May
` (3 subsequent siblings)
4 siblings, 0 replies; 13+ messages in thread
From: Simon Wright @ 2004-10-11 19:16 UTC (permalink / raw)
matthias_k <nospam@digitalraid.com> writes:
> Thanks for that answer. However, I'm still having problems (I have
> rewritten the code to be in one single package now):
>
> <snip>
> package Graphics is
>
> type Shape is abstract tagged null record;
> procedure Draw (Obj: Shape'Class);
Replace this with
procedure Draw (Obj : Shape) is abstract;
as in
package Graphics is
type Shape is abstract tagged null record;
procedure Draw (Obj: Shape) is abstract;
type Circle is new Shape with record
Radius: Float;
Center: Float;
end record;
procedure Draw (Obj: Circle);
type Square is new Shape with record
Size: Float;
end record;
procedure Draw (Obj: Square);
end;
with Ada.Text_IO; use Ada.Text_IO;
package body Graphics is
procedure Draw (Obj: Circle) is
begin
Put_Line ("circle");
end Draw;
procedure Draw (Obj: Square) is
begin
Put_Line ("square");
end Draw;
end Graphics;
with Graphics;
use Graphics;
procedure Demo is
type Reference is access all Shape'Class;
Object: Reference;
begin
Object := new Circle;
Draw( Object.all );
Object := new Square;
Draw( Object.all );
end;
(use gnatchop)
which said
smaug.pushface.org[2]$ gnatmake demo
gcc -c demo.adb
gcc -c graphics.adb
gnatbind -x demo.ali
gnatlink demo.ali
smaug.pushface.org[3]$ ./demo
circle
square
smaug.pushface.org[4]$
If you say
type Reference is access all Shape'Class;
then your procedure
procedure Draw (Obj: Shape'Class);
is bound to get called, since A_Reference.all is a Shape'Class.
--
Simon Wright 100% Ada, no bugs.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Classwide Parameter?
2004-10-11 17:08 ` matthias_k
2004-10-11 19:16 ` Simon Wright
@ 2004-10-11 22:53 ` Brian May
2004-10-12 2:29 ` Matthew Heaney
` (2 subsequent siblings)
4 siblings, 0 replies; 13+ messages in thread
From: Brian May @ 2004-10-11 22:53 UTC (permalink / raw)
>>>>> "matthias" == matthias k <nospam@digitalraid.com> writes:
matthias> The Shape's Draw method is always called here but it
matthias> shouldn't. No late binding happens. I have tried to make
matthias> it abstract, but it didn't even compile then. What's
matthias> wrong?
Hmmm.. I find it curious that code will even compile. I guess when Ada
has to match a Shape'Class type against these procedures:
procedure Draw (Obj: Shape'Class);
procedure Draw (Obj: Circle);
procedure Draw (Obj: Square);
It isn't ambiguous, even if the shape happens to be a circle or a
square.
I find this case interesting, with your code, too:
procedure Demo is
C : Circle;
begin
Draw(C);
end;
Here it replies that the call to draw is ambiguous, because
it doesn't know if it should call:
procedure Draw (Obj: Shape'Class);
or
procedure Draw (Obj: Circle);
because both could apply equally.
I found this confusing at first, I would have thought using
"Shape'Class" is the correct way of doing it, as you want explicitly
allow any type of Shape to get passed through. However, this isn't how
Ada supports inheritance.
In order to try and understand how this works better, I once wrote
test code that tested every possible situation. I suggest other
interested people do the same thing. Unfortunately, my hard disk died
since then :-(, but the lessons remained.
Consider the following (dodgy) code:
package Graphics is
type Shape is abstract tagged null record;
procedure Draw (Obj: Shape);
procedure Draw_Class (Obj: Shape'Class);
type Circle is new Shape with record
Radius: Float;
Center: Float;
end record;
procedure Draw (Obj: Circle);
procedure Draw_Class (Obj: Circle);
type Square is new Shape with record
Size: Float;
end record;
end;
My way of "visualizing" the above code (not sure if this is
technically correct), is the compiler sees the "Shape" type, and
associates two methods, "Draw" and "Draw_Class".
The compiler continues, and process the "Circle" class, and sees that
the same two functions have been redefined. Nothing more to do.
It then sees the Square type, and realizes that functions have not
been redefined. As such, it will turn/copy/inherent/whatever this
function:
procedure Draw (Obj: Shape);
into
procedure Draw (Obj: Square);
So now you effectively have:
package Graphics is
type Shape is abstract tagged null record;
procedure Draw (Obj: Shape);
procedure Draw_Class (Obj: Shape'Class);
type Circle is new Shape with record
Radius: Float;
Center: Float;
end record;
procedure Draw (Obj: Circle);
procedure Draw_Class (Obj: Circle);
type Square is new Shape with record
Size: Float;
end record;
procedure Draw (Obj: Square); -- is the same as Draw (Obj: Shape)
end;
It doesn't do the same thing for Draw_Class, perhaps it is clever
enough to realize it isn't needed. So in the above code,
Draw_Class(MyCircle) would be ambiguous, but Draw_Class(MySquare)
wouldn't be.
In summary, there are times you might want to use 'class for procedure
parameters, but not for procedures that can be overloaded in child
types, as the compiler already does everything for you.
Some of my analogies confuse people (instead of making it more clear),
lets hope this helps...
--
Brian May <bam@snoopy.apana.org.au>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Classwide Parameter?
2004-10-11 17:08 ` matthias_k
2004-10-11 19:16 ` Simon Wright
2004-10-11 22:53 ` Brian May
@ 2004-10-12 2:29 ` Matthew Heaney
2004-10-12 8:01 ` matthias_k
2004-10-12 7:59 ` Dmitry A. Kazakov
2004-10-12 8:10 ` Martin Krischik
4 siblings, 1 reply; 13+ messages in thread
From: Matthew Heaney @ 2004-10-12 2:29 UTC (permalink / raw)
matthias_k <nospam@digitalraid.com> writes:
> Thanks for that answer. However, I'm still having problems (I have
> rewritten the code to be in one single package now):
>
> <snip>
> package Graphics is
>
> type Shape is abstract tagged null record;
> procedure Draw (Obj: Shape'Class);
Get rid of this operation. It's a "class-wide" operation (like a static
method in C++), and hence the operation isn't primitive for the type
(and therefore doesn't dispatch).
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Classwide Parameter?
2004-10-12 2:29 ` Matthew Heaney
@ 2004-10-12 8:01 ` matthias_k
2004-10-12 8:53 ` Martin Krischik
2004-10-12 13:10 ` Matthew Heaney
0 siblings, 2 replies; 13+ messages in thread
From: matthias_k @ 2004-10-12 8:01 UTC (permalink / raw)
Matthew Heaney wrote:
> matthias_k <nospam@digitalraid.com> writes:
>
>
>>Thanks for that answer. However, I'm still having problems (I have
>>rewritten the code to be in one single package now):
>>
>><snip>
>>package Graphics is
>>
>> type Shape is abstract tagged null record;
>> procedure Draw (Obj: Shape'Class);
>
>
> Get rid of this operation. It's a "class-wide" operation (like a static
> method in C++), and hence the operation isn't primitive for the type
> (and therefore doesn't dispatch).
>
>
Oha? I thought it would be analogous to 'virtual' in C++, not static.
If I leave out the method completely, then this is not what I know as
'Inheritance' anymore.
Shape is supposed to be an abstract base class, which -declares- (not
necessarily -defines-) a common interface for its subtypes. The code I
posted may not reflect this too well, it's just samplecode.
I doubt -any- dynamic binding will happen when completely leaving out
Shape's Draw method.
And btw.: What does dispatch mean? :) Is that the Ada term for dynamic
binding?
- Matthias
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Classwide Parameter?
2004-10-12 8:01 ` matthias_k
@ 2004-10-12 8:53 ` Martin Krischik
2004-10-12 13:10 ` Matthew Heaney
1 sibling, 0 replies; 13+ messages in thread
From: Martin Krischik @ 2004-10-12 8:53 UTC (permalink / raw)
matthias_k wrote:
> Matthew Heaney wrote:
>> matthias_k <nospam@digitalraid.com> writes:
>>
>>
>>>Thanks for that answer. However, I'm still having problems (I have
>>>rewritten the code to be in one single package now):
>>>
>>><snip>
>>>package Graphics is
>>>
>>> type Shape is abstract tagged null record;
>>> procedure Draw (Obj: Shape'Class);
>>
>>
>> Get rid of this operation. It's a "class-wide" operation (like a static
>> method in C++), and hence the operation isn't primitive for the type
>> (and therefore doesn't dispatch).
"static" would be a function in the same package but without any X or
X'Class parameter. It's more like non virtual function.
> Oha? I thought it would be analogous to 'virtual' in C++, not static.
I did the same mistake in the beginning. In Ada all operations defined in
the same package as the class are virtual.
> If I leave out the method completely, then this is not what I know as
> 'Inheritance' anymore.
> Shape is supposed to be an abstract base class, which -declares- (not
> necessarily -defines-) a common interface for its subtypes. The code I
> posted may not reflect this too well, it's just samplecode.
> I doubt -any- dynamic binding will happen when completely leaving out
> Shape's Draw method.
you need:
procedure Draw (Obj: Shape) is abstract;
which
> And btw.: What does dispatch mean? :) Is that the Ada term for dynamic
> binding?
That's right.
Martin
--
mailto://krischik@users.sourceforge.net
http://www.ada.krischik.com
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Classwide Parameter?
2004-10-12 8:01 ` matthias_k
2004-10-12 8:53 ` Martin Krischik
@ 2004-10-12 13:10 ` Matthew Heaney
1 sibling, 0 replies; 13+ messages in thread
From: Matthew Heaney @ 2004-10-12 13:10 UTC (permalink / raw)
matthias_k <nospam@digitalraid.com> writes:
> Matthew Heaney wrote:
> > matthias_k <nospam@digitalraid.com> writes:
> >
> >>Thanks for that answer. However, I'm still having problems (I have
> >>rewritten the code to be in one single package now):
> >>
> >><snip>
> >>package Graphics is
> >>
> >> type Shape is abstract tagged null record;
> >> procedure Draw (Obj: Shape'Class);
> > Get rid of this operation. It's a "class-wide" operation (like a
> > static
> > method in C++), and hence the operation isn't primitive for the type
> > (and therefore doesn't dispatch).
> >
>
> Oha? I thought it would be analogous to 'virtual' in C++, not static.
I should have said: "change the type from Shape'Class to Shape, and
optionally declare the operation as abstract."
In Ada, you don't need to say "virtual". An operation for a type is
"primitive" whenever it has the type as a parameter or return type, and
the operation is declared in the same package as the type.
To make a "static" method in Ada, declare the parameter as T'Class.
> And btw.: What does dispatch mean? :) Is that the Ada term for dynamic
> binding?
Yes.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Classwide Parameter?
2004-10-11 17:08 ` matthias_k
` (2 preceding siblings ...)
2004-10-12 2:29 ` Matthew Heaney
@ 2004-10-12 7:59 ` Dmitry A. Kazakov
[not found] ` <ckg3h6$qau$03$1@news.t-online.com>
2004-10-12 8:10 ` Martin Krischik
4 siblings, 1 reply; 13+ messages in thread
From: Dmitry A. Kazakov @ 2004-10-12 7:59 UTC (permalink / raw)
On Mon, 11 Oct 2004 19:08:24 +0200, matthias_k wrote:
> Thanks for that answer. However, I'm still having problems (I have
> rewritten the code to be in one single package now):
>
> <snip>
> package Graphics is
>
> type Shape is abstract tagged null record;
> procedure Draw (Obj: Shape'Class);
>
> type Circle is new Shape with record
> Radius: Float;
> Center: Float;
> end record;
> procedure Draw (Obj: Circle);
>
> type Square is new Shape with record
> Size: Float;
> end record;
> procedure Draw (Obj: Square);
>
> end;
> </snip>
>
> Now, what I want is to have different implementations of the Draw method
> for each Subtype.
This means that the base should declare Draw as a method. [In Ada terms it
should be a primitive operation. ] Class-wide is NOT a primitive operation.
When you declare Shape abstract, then what is abstract there? Presumably
Draw, which should be overridden (implemented) by each concrete derived
type. This all means that Shape.Draw should be at least primitive:
procedure Draw (Obj: Shape);
Very likely it should also be abstract:
procedure Draw (Obj: Shape) is abstract;
---
Class-wide subprograms come into consideration only if you want and are
able to write something valid for *all* derived types. This something is
said to be defined on the class rooted in the given type. For example,
Shape'Class is rooted in Shape. Now imagine that your Shape also has a
primitive operation Flip, then you can write a class-wide procedure
Flip_n_Draw which will be able to Flip and then Draw anything derived from
Shape:
type Shape is abstract tagged ...
-- The methods:
procedure Draw (Obj : Shape) is abstract;
procedure Flip (Obj : in out Shape) is abstract;
-- The class-wides:
procedure Flip_n_Draw (Obj : in out Shape'Class);
procedure Flip_n_Draw (Obj : in out Shape'Class) is
begin
Flip (Obj); -- Dispatches to an appropriate Flip
Draw (Obj); -- Dispatches to an appropriate Draw
end Flip_n_Draw;
Because Flip_n_Draw is class-wide it is valid for *any* type derived from
Shape. Once you have an object of Circle, Rectangle, whatsoever, you can
use Flip_n_Draw with it, without any further coding. The only thing you
have to do is to implement the "methods" of Shape.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: Classwide Parameter?
2004-10-11 17:08 ` matthias_k
` (3 preceding siblings ...)
2004-10-12 7:59 ` Dmitry A. Kazakov
@ 2004-10-12 8:10 ` Martin Krischik
4 siblings, 0 replies; 13+ messages in thread
From: Martin Krischik @ 2004-10-12 8:10 UTC (permalink / raw)
matthias_k wrote:
> Thanks for that answer. However, I'm still having problems (I have
> rewritten the code to be in one single package now):
>
> <snip>
> package Graphics is
>
> type Shape is abstract tagged null record;
> procedure Draw (Obj: Shape'Class);
You want an abstract method not a class wide procedure:
procedure Draw (Obj: Shape) is abstract;
>
> type Circle is new Shape with record
> Radius: Float;
> Center: Float;
> end record;
> procedure Draw (Obj: Circle);
>
> type Square is new Shape with record
> Size: Float;
> end record;
> procedure Draw (Obj: Square);
>
> end;
> </snip>
>
> Now, what I want is to have different implementations of the Draw method
> for each Subtype. However, if I run this program:
>
> <snip>
> with Graphics;
> use Graphics;
>
> procedure Demo is
> type Reference is access all Shape'Class;
> Object: Reference;
> begin
> Object := new Circle;
> Draw( Object.all );
>
> Object := new Square;
> Draw( Object.all );
Only use pointer in Ada if you realy need to:
Test_1:
declare
Object: Shape'Class := Circle'(Radius => 10.0, Center => 10.0);
begin
Draw( Object);
end
Test_2:
declare
Object: Shape'Class := Square'(Size => 10.0);
begin
Draw( Object);
end
> end;
> </snip>
>
> The Shape's Draw method is always called here but it shouldn't. No late
> binding happens. I have tried to make it abstract, but it didn't even
> compile then. What's wrong?
>
> - Matthias
--
mailto://krischik@users.sourceforge.net
http://www.ada.krischik.com
^ permalink raw reply [flat|nested] 13+ messages in thread