comp.lang.ada
 help / color / mirror / Atom feed
* Dynamich Dispatching...
@ 2004-10-02 15:39 Rick Santa-Cruz
  2004-10-03 17:11 ` Martin Krischik
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Rick Santa-Cruz @ 2004-10-02 15:39 UTC (permalink / raw)


Hi,

I know what to understand about Dynamic Dispatching, but with the following 
example in Ada I have some problems to understand the whole thing:

-- package Objects
package Objects is
 type Object is abstract tagged private;
 type Object_Ptr is access all Object'Class;

 procedure Draw(O: Object) is abstract;

 procedure Set_Text(O: in out Object; Text: String);
 function Get_Text(O: Object) return String;

private
 type Object is abstract tagged record
  Text: String(1..50);
 end record;
end Objects;

package body Objects is
 procedure Set_Text(O: in out Object; Text: string) is
 begin
  O.Text(1..Text'Length) := Text;
 end Set_Text;

 function Get_Text(O: Object) return String is
 begin
  return O.Text;
 end Get_Text;
end Objects;

-- package Circles:
with Objects;
use Objects;

package Circles is
 type Circle is new Object with private;

 procedure Draw(C: Circle);

 package Constructor is
  function Create(Text: String; Radius: Integer) return Circle;
 end Constructor ;

 private
  type Circle is new Object with record
   Radius: Integer;
  end record;
end Circles;

with Ada.Text_IO;
use Ada.Text_IO;

package body Circles is
 procedure Draw(C: Circle) is
 begin
  Put_Line("Circle: " & Get_Text(C) & Integer'Image(C.Radius));
 end Draw;

 package body Constructor is
  function Create(Text: String; Radius: Integer) return Circle is
   C: Circle;
  begin
   Set_Text(C, Text);
   C.Radius := Radius;
   return C;
  end Create;
 end Constructor ;
end Circles;

-- main-procedure:
with Ada.Text_IO;
with Objects;
with Circles;

procedure Main is
  procedure Call_Dynamic(Object_Ptr: Objects.Object_Ptr) is
 begin
    Objects.Draw(Object_Ptr.all);
 end Call_Dynamic;
 C2: Circles.Circle;
begin
  C2 := Circles.Constructor.Create("Circle1",50);
 Call_Dynamic(new Circles.Circle'(C2));
end Main;

 My question is now, why with Call_Dynamic I call the Draw-procedure in the 
package Circles, although in the body of Call_Dynamic I directly call the 
Method in the Objects Package? Surely it is syntactly correct, cause there 
is an abstract mehtod Draw in the Objects-Package, but I don't understand 
why such work with classes in different packages. I tested the same, when 
all classes are in the same package and surely it worked too, and for me and 
the understanding of dynamic-dispatching this is clear. How does this 
internally work with the different-package approach as described above in 
the source-code?

Thanks in advance,
Rick 





^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-02 15:39 Dynamich Dispatching Rick Santa-Cruz
@ 2004-10-03 17:11 ` Martin Krischik
  2004-10-03 22:59   ` Brian May
  2004-10-03 18:56 ` Ludovic Brenta
  2004-10-04  8:02 ` Dmitry A. Kazakov
  2 siblings, 1 reply; 13+ messages in thread
From: Martin Krischik @ 2004-10-03 17:11 UTC (permalink / raw)


Rick Santa-Cruz wrote:

> Hi,
> 
> I know what to understand about Dynamic Dispatching, but with the
> following example in Ada I have some problems to understand the whole
> thing:

Unlike C++ dipaching in Ada is not connected to using pointers. It is
connected to using Object'Class.

> -- package Objects
> package Objects is
>  type Object is abstract tagged private;
>  type Object_Ptr is access all Object'Class;

Avoid "access all" unless you need it. The compiler need to do more checking
when using "access all".

>  
>  procedure Draw(O: Object) is abstract;
> 
>  procedure Set_Text(O: in out Object; Text: String);
>  function Get_Text(O: Object) return String;
> 
> private
>  type Object is abstract tagged record
>   Text: String(1..50);

Consider using a Bounded_String or Unbounded_String here.

>  end record;
> end Objects;
> 
> package body Objects is
>  procedure Set_Text(O: in out Object; Text: string) is
>  begin
>   O.Text(1..Text'Length) := Text;

This will leave you with garbage characters inside the string. Use
Ada.Strings.Fixed_Strings which pad the string with spaces.

>  end Set_Text;
> 
>  function Get_Text(O: Object) return String is
>  begin
>   return O.Text;
>  end Get_Text;
> end Objects;
> 
> -- package Circles:
> with Objects;
> use Objects;
> 
> package Circles is
>  type Circle is new Object with private;
> 
>  procedure Draw(C: Circle);
> 
>  package Constructor is
>   function Create(Text: String; Radius: Integer) return Circle;
>  end Constructor ;
> 
>  private
>   type Circle is new Object with record
>    Radius: Integer;
>   end record;
> end Circles;
> 
> with Ada.Text_IO;
> use Ada.Text_IO;
> 
> package body Circles is
>  procedure Draw(C: Circle) is
>  begin
>   Put_Line("Circle: " & Get_Text(C) & Integer'Image(C.Radius));
>  end Draw;
> 
>  package body Constructor is
>   function Create(Text: String; Radius: Integer) return Circle is
>    C: Circle;
>   begin
>    Set_Text(C, Text);
>    C.Radius := Radius;
>    return C;
>   end Create;
>  end Constructor ;
> end Circles;
> 
> -- main-procedure:
> with Ada.Text_IO;
> with Objects;
> with Circles;
> 
> procedure Main is
>   procedure Call_Dynamic(Object_Ptr: Objects.Object_Ptr) is

procedure Call_Dynamic(Object: Objects.Object'Class) is

>  begin
>     Objects.Draw(Object_Ptr.all);

Objects.Draw(Object);

>  end Call_Dynamic;
>  C2 : Circles.Circle;

C3 : Object_Ptr := new Circles.Circle'Class'(C2);

> begin
>   C2 := Circles.Constructor.Create("Circle1",50);
>  Call_Dynamic (new Circles.Circle'(C2));

Call_Dynamic (C3);

> end Main;
> 
>  My question is now, why with Call_Dynamic I call the Draw-procedure in
>  the
> package Circles, although in the body of Call_Dynamic I directly call the
> Method in the Objects Package? Surely it is syntactly correct, cause there
> is an abstract mehtod Draw in the Objects-Package, but I don't understand
> why such work with classes in different packages. I tested the same, when
> all classes are in the same package and surely it worked too, and for me
> and the understanding of dynamic-dispatching this is clear. How does this
> internally work with the different-package approach as described above in
> the source-code?

"new Circles.Circle" created an object of Circles.Circle and not of
Circles.Circle'Class.

With Regards

Martin

-- 
mailto://krischik@users.sourceforge.net
http://www.ada.krischik.com




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-02 15:39 Dynamich Dispatching Rick Santa-Cruz
  2004-10-03 17:11 ` Martin Krischik
@ 2004-10-03 18:56 ` Ludovic Brenta
  2004-10-04  4:45   ` Jeffrey Carter
  2004-10-04  8:02 ` Dmitry A. Kazakov
  2 siblings, 1 reply; 13+ messages in thread
From: Ludovic Brenta @ 2004-10-03 18:56 UTC (permalink / raw)


"Rick Santa-Cruz" writes:
> Hi,
>
> I know what to understand about Dynamic Dispatching, but with the
> following example in Ada I have some problems to understand the
> whole thing:

RM 3.9.2(20, 21) state that the only factor that determines which
subprogram (method) is called is the tag of the operand (i.e. the
actual run-time type).  Whether or not the subprogram is visible at
the point of call is irrelevant.  Thus:

package Objects is
   type Object is tagged private;
   procedure Draw (O : in Object); -- a primitive suprogram
   procedure Draw_Any (O : in Object'Class); -- not a primitive
private ...
end Objects;

package body Objects is
   ...
   procedure Draw_Any (O : in Object'Class) is
   begin
      Draw (O); -- dynamic dispatching call
   end Draw_Any;
end Objects;

with Objects;
package Circles is
   type Circle is new Objects.Object with private;
   procedure Draw (C : in Circle); -- overrides Objects.Draw
private ...
end Circles; 

with Circles;
with Objects;
procedure Main is
   C : Circles.Circle;
begin
   Objects.Draw_Any (C);
end Main;

Even though the procedure Draw_Any does not see the package Circles,
dynamic dispatching still causes Circles.Draw to be called.  This is
good; it means that Draw_Any does not have to be aware of the
existence of all types derived from Objects.Object.  This in turns
means that new derived types can be added to the program without
changing Draw_Any.

-- 
Ludovic Brenta.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-03 17:11 ` Martin Krischik
@ 2004-10-03 22:59   ` Brian May
  2004-10-04  8:22     ` Martin Krischik
  0 siblings, 1 reply; 13+ messages in thread
From: Brian May @ 2004-10-03 22:59 UTC (permalink / raw)


>>>>> "Martin" == Martin Krischik <krischik@users.sourceforge.net> writes:

    Martin> Avoid "access all" unless you need it. The compiler need
    Martin> to do more checking when using "access all".

Some references recommend using "all" - not sure why. What extra
checking does it require? My references say when to use it, but never
say when not to use it or why it shouldn't be used if not required.

    >> procedure Main is
    >> procedure Call_Dynamic(Object_Ptr: Objects.Object_Ptr) is

    Martin> procedure Call_Dynamic(Object: Objects.Object'Class) is

    >> begin
    >> Objects.Draw(Object_Ptr.all);

    Martin> Objects.Draw(Object);

    >> end Call_Dynamic;
    >> C2 : Circles.Circle;

    Martin> C3 : Object_Ptr := new Circles.Circle'Class'(C2);

    >> begin
    >> C2 := Circles.Constructor.Create("Circle1",50);
    >> Call_Dynamic (new Circles.Circle'(C2));

    Martin> Call_Dynamic (C3);

I think you meant "Call_Dynamic(C2)" here. You can't pass an access
type here...

    >> end Main;
-- 
Brian May <bam@snoopy.apana.org.au>



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-03 18:56 ` Ludovic Brenta
@ 2004-10-04  4:45   ` Jeffrey Carter
  2004-10-04 21:01     ` Ludovic Brenta
  0 siblings, 1 reply; 13+ messages in thread
From: Jeffrey Carter @ 2004-10-04  4:45 UTC (permalink / raw)


Ludovic Brenta wrote:

> package Objects is
>    type Object is tagged private;
>    procedure Draw (O : in Object); -- a primitive suprogram
>    procedure Draw_Any (O : in Object'Class); -- not a primitive

Draw_Any is unnecessary. Draw may be called with any type in Object'Class.

-- 
Jeff Carter
"It's symbolic of his struggle against reality."
Monty Python's Life of Brian
78




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-02 15:39 Dynamich Dispatching Rick Santa-Cruz
  2004-10-03 17:11 ` Martin Krischik
  2004-10-03 18:56 ` Ludovic Brenta
@ 2004-10-04  8:02 ` Dmitry A. Kazakov
  2004-10-04 11:02   ` Brian May
  2 siblings, 1 reply; 13+ messages in thread
From: Dmitry A. Kazakov @ 2004-10-04  8:02 UTC (permalink / raw)


On Sat, 2 Oct 2004 17:39:15 +0200, Rick Santa-Cruz wrote:

> I know what to understand about Dynamic Dispatching, but with the following 
> example in Ada I have some problems to understand the whole thing:
> 
> -- package Objects
> package Objects is
>  type Object is abstract tagged private;

If you plan Objects to be always constructed using a call to some
constructor function, make sure that they will:

type Object (<>) is abstract tagged private;

This will prevent declarations like:

X : Derived_Object; -- No initial value given

>  type Object_Ptr is access all Object'Class;

Avoid pointers, where possible.

>  procedure Draw(O: Object) is abstract;
> 
>  procedure Set_Text(O: in out Object; Text: String);
>  function Get_Text(O: Object) return String;
> 
> private
>  type Object is abstract tagged record
>   Text: String(1..50);

Consider bounded and unbounded strings here. Note also that if in the
future you will need initialization/finalization, then Object should better
be a descendant of Ada.Finalization.Controlled:

type Object is abstract
   new Ada.Finalization.Controlled with record

Further, if public view of Object remains just tagged, while in the full
view it is derived from Ada.Finalization.Controlled, then in effect it will
be practically impossible to derive from Object in non-children packages.
So, to expose or not Ada.Finalization.Controlled as the base is an
important decision.

>  end record;
> end Objects;

[snip]
 
> -- package Circles:
> with Objects;
> use Objects;
> 
> package Circles is

Probably it should be a child package of Objects, especially if it needs to
look into Object's privates.

>  type Circle is new Object with private;
> 
>  procedure Draw(C: Circle);
> 
>  package Constructor is
>   function Create(Text: String; Radius: Integer) return Circle;
>  end Constructor ;

You do not need a nested package here.

>  private
>   type Circle is new Object with record
>    Radius: Integer;
>   end record;
> end Circles;

[snip] 

> -- main-procedure:
> with Ada.Text_IO;
> with Objects;
> with Circles;
> 
> procedure Main is
>   procedure Call_Dynamic(Object_Ptr: Objects.Object_Ptr) is
>  begin
>     Objects.Draw(Object_Ptr.all);
>  end Call_Dynamic;
>  C2: Circles.Circle;
> begin
>   C2 := Circles.Constructor.Create("Circle1",50);
>  Call_Dynamic(new Circles.Circle'(C2));

This is a memory leak. You allocate an object, but never destroy it. Avoid
pointers! (:-))

> end Main;
> 
>  My question is now, why with Call_Dynamic I call the Draw-procedure in the 
> package Circles, although in the body of Call_Dynamic I directly call the 
> Method in the Objects Package?

You call a dispatching procedure. When its argument is class-wide, which is
the case, because Object_Ptr is a pointer to Object'Class, then the target
is selected according to the actual type tag, which is in your case Circle. 

> Surely it is syntactly correct, cause there 
> is an abstract mehtod Draw in the Objects-Package, but I don't understand 
> why such work with classes in different packages. I tested the same, when 
> all classes are in the same package and surely it worked too, and for me and 
> the understanding of dynamic-dispatching this is clear. How does this 
> internally work with the different-package approach as described above in 
> the source-code?

You have used a qualified expression Circles.Circle'(C2) in new, which
allocates a new object of Circle and initializes it with the value of C2.
Then a pointer to this new object is taken and "converted" to a class-wide
pointer Object_Ptr. The result is passed to Call_Dynamic. Where it is
dereferenced to Object'Class, on which dispatching Draw is called. The
dispatching call gets the tag (->Circle), looks into the dispatching table
of Draw and finds there Draw for Circle (an override of Object.Draw). That
is called.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-03 22:59   ` Brian May
@ 2004-10-04  8:22     ` Martin Krischik
  0 siblings, 0 replies; 13+ messages in thread
From: Martin Krischik @ 2004-10-04  8:22 UTC (permalink / raw)


Brian May wrote:

>>>>>> "Martin" == Martin Krischik <krischik@users.sourceforge.net> writes:
> 
>     Martin> Avoid "access all" unless you need it. The compiler need
>     Martin> to do more checking when using "access all".
> 
> Some references recommend using "all" - not sure why.

"access all" can hold pointers to all sorts of elements. They may be on the
stack or inside any user defined heap. Therfore they are far more flexible.
However - all flexibility brings the change for error. For example I can
think of at least 3 times as many things which can go wrong on an
Unchecked_Deallocation for an "access all" then for an simple "access".

Want to know?

For access:

Deallocating memory still in use.

For access all:

Deallocating memory still in use.
Deallocating memory from the wrong storrage pool.
Deallocating stack memory.

See that's three times.

> What extra 
> checking does it require? My references say when to use it, but never
> say when not to use it

My 2 cent: "access all" should not be used when user defined storrage pools
are used as well. 

> or why it shouldn't be used if not required. 

access is allways allocated on one specific heap - So especially for library
level access no live time check is needed. Ada prevents dangling pointer by
making livetime checks. The classical C mistake

int*
f ()
  {
   auto int Retval;

   return &Retval;
   }

will cause an error in Ada - since Retval goes out of scope at function end.
Most of the livetime checks are done at compile time - however there are a
few to be done at run time. 

Important here is that for a named "access" - that is "type X is access Y"
all livetime checks can be done at compile time. 

>     >> procedure Main is
>     >> procedure Call_Dynamic(Object_Ptr: Objects.Object_Ptr) is
> 
>     Martin> procedure Call_Dynamic(Object: Objects.Object'Class) is
> 
>     >> begin
>     >> Objects.Draw(Object_Ptr.all);
> 
>     Martin> Objects.Draw(Object);
> 
>     >> end Call_Dynamic;
>     >> C2 : Circles.Circle;
> 
>     Martin> C3 : Object_Ptr := new Circles.Circle'Class'(C2);
> 
>     >> begin
>     >> C2 := Circles.Constructor.Create("Circle1",50);
>     >> Call_Dynamic (new Circles.Circle'(C2));
> 
>     Martin> Call_Dynamic (C3);
> 
> I think you meant "Call_Dynamic(C2)" here. You can't pass an access
> type here...

No I meant "Call_Dynamic(C3.all)". Of course you might not need a pointer at
all. An Ada one can often use 'Class where in C++ a pointer or reference is
needed.

When I was 2 to 3 month into Ada I reorganised my Ada programs removing 
more then 80% of all pointers used until then.

With Regards

Martin
-- 
mailto://krischik@users.sourceforge.net
http://www.ada.krischik.com




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-04  8:02 ` Dmitry A. Kazakov
@ 2004-10-04 11:02   ` Brian May
  2004-10-04 12:50     ` Dmitry A. Kazakov
  0 siblings, 1 reply; 13+ messages in thread
From: Brian May @ 2004-10-04 11:02 UTC (permalink / raw)


>>>>> "Dmitry" == Dmitry A Kazakov <mailbox@dmitry-kazakov.de> writes:

    >> package Constructor is
    >> function Create(Text: String; Radius: Integer) return Circle;
    >> end Constructor ;

    Dmitry> You do not need a nested package here.

<URL:http://www.adahome.com/articles/1998-02/ar_lessons95.html>,
section 1.4, recommends it. Actually, I don't understand why.

There are other parts of this reference which seem dodgy though (at
least to me), so I am not sure how much you can rely on it.

(Sections 1.2 and 7 really have me confused too).
-- 
Brian May <bam@snoopy.apana.org.au>



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-04 11:02   ` Brian May
@ 2004-10-04 12:50     ` Dmitry A. Kazakov
  2004-10-15 18:27       ` Matthew Heaney
  0 siblings, 1 reply; 13+ messages in thread
From: Dmitry A. Kazakov @ 2004-10-04 12:50 UTC (permalink / raw)


On Mon, 04 Oct 2004 21:02:44 +1000, Brian May wrote:

>>>>>> "Dmitry" == Dmitry A Kazakov <mailbox@dmitry-kazakov.de> writes:
> 
>     >> package Constructor is
>     >> function Create(Text: String; Radius: Integer) return Circle;
>     >> end Constructor ;
> 
>     Dmitry> You do not need a nested package here.
> 
> <URL:http://www.adahome.com/articles/1998-02/ar_lessons95.html>,
> section 1.4, recommends it. Actually, I don't understand why.

Yes, it indeed reads strange:

"If a class declares a function 'Create' returning an instance of a class,
all the child classes inherit that operation. This can be hazardous,
especially if the child class has more attributes than its parent (note: to
make programmers pay attention, the inherited function will be abstract
unless overridden). Indeed, the inherited 'Create' might not initialize
these attributes."

One *cannot* inherit Create, because, many tanks to Ada's designers,
functions are covariant in their results. Quick checks shows it:

package Foo is
   type X is tagged null record;
   function Create return X;
   
   type Y is new X with null record;
      -- Compile error, Y is not abstract,
      -- yet it does not override Create
end Foo;

One could treat Y's Create is as abstract, like in the quote, but where is
any danger? Let Y be abstract, then it may have no instances of its own!

Then if one really needs a contravariant result, then Create should be
class-wide. It is as simple as:

   function Create return X'Class;

One could use this variant for an object factory which decides which of
X'Class to create.

> There are other parts of this reference which seem dodgy though (at
> least to me), so I am not sure how much you can rely on it.
> 
> (Sections 1.2 and 7 really have me confused too).

Yes. I have an impression that the document reflects some preliminary
status of Ada.Finalization development. For example it talks about "void"
objects etc. Just a guess.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-04  4:45   ` Jeffrey Carter
@ 2004-10-04 21:01     ` Ludovic Brenta
  2004-10-05  0:32       ` Jeffrey Carter
  0 siblings, 1 reply; 13+ messages in thread
From: Ludovic Brenta @ 2004-10-04 21:01 UTC (permalink / raw)


Jeffrey Carter writes:
> Ludovic Brenta wrote:
>
>> package Objects is
>>    type Object is tagged private;
>>    procedure Draw (O : in Object); -- a primitive suprogram
>>    procedure Draw_Any (O : in Object'Class); -- not a primitive
>
> Draw_Any is unnecessary. Draw may be called with any type in
> Object'Class.

Yes, I know, but this thread is about dynamic dispatching.  If I
write:

with Circles;
procedure Main
   C : Circles.Circle;
begin
   Draw (C);
end Main;

this is static dispatching since the type of C is known at compile
time, and no 'Class was ever used.  Incidentally, for those not aware
of it, Ada has a pragma Restriction (No_Dispatch) which makes 'Class
illegal or just this reason (RM H.4(19)).

-- 
Ludovic Brenta.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-04 21:01     ` Ludovic Brenta
@ 2004-10-05  0:32       ` Jeffrey Carter
  0 siblings, 0 replies; 13+ messages in thread
From: Jeffrey Carter @ 2004-10-05  0:32 UTC (permalink / raw)


Ludovic Brenta wrote:

> Yes, I know, but this thread is about dynamic dispatching.  If I
> write:
> 
> with Circles;
> procedure Main
>    C : Circles.Circle;
> begin
>    Draw (C);
> end Main;

[no dynamic dispatching]

Yes, but you can also say

with Objects;
procedure George is
    function Get return Objects.Object'Class is separate;
begin -- George
    Objects.Draw (Get);
end George;

and get dynamic dispatching.

-- 
Jeff Carter
"You tiny-brained wipers of other people's bottoms!"
Monty Python & the Holy Grail
18




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-04 12:50     ` Dmitry A. Kazakov
@ 2004-10-15 18:27       ` Matthew Heaney
  2004-10-16 19:25         ` Dmitry A. Kazakov
  0 siblings, 1 reply; 13+ messages in thread
From: Matthew Heaney @ 2004-10-15 18:27 UTC (permalink / raw)



"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:uv6ftd235dgf$.1px68p9wf1tjb.dlg@40tude.net...
> On Mon, 04 Oct 2004 21:02:44 +1000, Brian May wrote:
>
> Then if one really needs a contravariant result, then Create should be
> class-wide. It is as simple as:
>
>    function Create return X'Class;

The issue is not whether one needs a contravariant result.  Rather, the
issue is whether the constructor is primitive for the type or not.

In general, constructors should *not* be primitive.  One way to do that is
to declare the constructor in a nested package:

package P is
   type X is tagged ...;
   package Constructors is
      function Create return X;
   end;
end P;

A constructor should always return a specific type, not a class-wide type.
Your example above is wrong, since it would require the declaration of an
object whose type is class-wide, and hence force the use of dispatching.
Clearly that's wrong, when you know the specific type of object you want to
create.

The only time is makes sense for a constructor to return a class-wide type
is when this is a factory method pattern.  In that case, the operation is
primitive for parameter type:

package Q is
   type X is abstract tagged ...;
   type Y is abstract tagged ...;

   function Create (O : X) return Y'Class is abstract;
end Q;






^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Dynamich Dispatching...
  2004-10-15 18:27       ` Matthew Heaney
@ 2004-10-16 19:25         ` Dmitry A. Kazakov
  0 siblings, 0 replies; 13+ messages in thread
From: Dmitry A. Kazakov @ 2004-10-16 19:25 UTC (permalink / raw)


On Fri, 15 Oct 2004 14:27:12 -0400, Matthew Heaney wrote:

> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:uv6ftd235dgf$.1px68p9wf1tjb.dlg@40tude.net...
>> On Mon, 04 Oct 2004 21:02:44 +1000, Brian May wrote:
>>
>> Then if one really needs a contravariant result, then Create should be
>> class-wide. It is as simple as:
>>
>>    function Create return X'Class;
> 
> The issue is not whether one needs a contravariant result.  Rather, the
> issue is whether the constructor is primitive for the type or not.

What do you mean here under "constructor"? A constructor in the sense this
word is used in C++ should be definitely primitive. Initialize in Ada is
also primitive, though it is not a full constructor in C++ sense.

> In general, constructors should *not* be primitive.

Why so? I buy a little inconvenience of overriding it in rare cases where
the base could do the job.

BTW, when and if Ada will have true user-defined constructors, then I think
we should seriously consider a requirement to override them in each derived
type, in all non-trivial cases, like when an uninitialized component is
added, or representation is changed etc. Even better would be to require
this always and provide a syntactical sugar for *explicit* reuse of the
base constructor.

> One way to do that is
> to declare the constructor in a nested package:
>
> package P is
>    type X is tagged ...;
>    package Constructors is
>       function Create return X;
>    end;
> end P;
>
> A constructor should always return a specific type, not a class-wide type.
> Your example above is wrong, since it would require the declaration of an
> object whose type is class-wide, and hence force the use of dispatching.

Not necessarily:

Object : X := X (Create); -- This is OK

> Clearly that's wrong, when you know the specific type of object you want to
> create.

I can always rename to a specific view when I know it:

Object : X'Class := Create;
I_know_you : X renames X (Object);

> The only time is makes sense for a constructor to return a class-wide type
> is when this is a factory method pattern.

Yes

>  In that case, the operation is
> primitive for parameter type:
> 
> package Q is
>    type X is abstract tagged ...;
>    type Y is abstract tagged ...;
> 
>    function Create (O : X) return Y'Class is abstract;

That isn't a factory, that should be multiple dispatch, I bet:

   function Create (O : X) return Y is abstract; -- Illegal in Ada

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2004-10-16 19:25 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-10-02 15:39 Dynamich Dispatching Rick Santa-Cruz
2004-10-03 17:11 ` Martin Krischik
2004-10-03 22:59   ` Brian May
2004-10-04  8:22     ` Martin Krischik
2004-10-03 18:56 ` Ludovic Brenta
2004-10-04  4:45   ` Jeffrey Carter
2004-10-04 21:01     ` Ludovic Brenta
2004-10-05  0:32       ` Jeffrey Carter
2004-10-04  8:02 ` Dmitry A. Kazakov
2004-10-04 11:02   ` Brian May
2004-10-04 12:50     ` Dmitry A. Kazakov
2004-10-15 18:27       ` Matthew Heaney
2004-10-16 19:25         ` Dmitry A. Kazakov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox