comp.lang.ada
 help / color / mirror / Atom feed
* tagged record child: override constructor?
@ 2005-09-13  5:58 sean.gilbertson
  2005-09-13  6:39 ` David Trudgett
                   ` (3 more replies)
  0 siblings, 4 replies; 38+ messages in thread
From: sean.gilbertson @ 2005-09-13  5:58 UTC (permalink / raw)


I have a tagged record that is declared private, along with a
constructor function which returns an instance.  I do this to enforce
the assignment of several required fields in the record, so if you know
how to do this another way, please let me know!

But Ada is telling me I have to override this function in the child.
This doesn't make a lot of sense.  How can I deal with this?  I'm not
currently using Ada 2005.




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

* Re: tagged record child: override constructor?
  2005-09-13  5:58 tagged record child: override constructor? sean.gilbertson
@ 2005-09-13  6:39 ` David Trudgett
  2005-09-13  7:32 ` Dmitry A. Kazakov
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 38+ messages in thread
From: David Trudgett @ 2005-09-13  6:39 UTC (permalink / raw)


sean.gilbertson@gmail.com writes:

> I have a tagged record that is declared private, along with a
> constructor function which returns an instance.  I do this to enforce
> the assignment of several required fields in the record, so if you know
> how to do this another way, please let me know!

Have you thought about using Ada.Finalization? (Check the Ada
Reference Manual.) Others, more knowledgeable, might have other
suggestions for you.

David



-- 

David Trudgett
http://www.zeta.org.au/~wpower/

In his essay 'The Banality of Evil', the great American dissident
Edward Herman described the division of labour among those who design
and produce weapons like cluster bombs and daisy cutters and those who
take the political decisions to use them and those who create the
illusions that justify their use. 'It is the function of the experts,
and the mainstream media,' he wrote, 'to normalise the unthinkable for
the general public.' It is time journalists reflected upon this, and
took the risk of telling the truth about an unconscionable threat to
much of humanity that comes not from faraway places, but close to
home.

    -- John Pilger, 'The truths they never tell us' , 
       New Statesman, November 26, 2001




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

* Re: tagged record child: override constructor?
  2005-09-13  5:58 tagged record child: override constructor? sean.gilbertson
  2005-09-13  6:39 ` David Trudgett
@ 2005-09-13  7:32 ` Dmitry A. Kazakov
  2005-09-13  7:56   ` tmoran
  2005-09-13 15:23   ` sean.gilbertson
  2005-09-13  9:33 ` Georg Bauhaus
  2005-09-13 16:37 ` Jeffrey Carter
  3 siblings, 2 replies; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-13  7:32 UTC (permalink / raw)


On 12 Sep 2005 22:58:54 -0700, sean.gilbertson@gmail.com wrote:

> I have a tagged record that is declared private, along with a
> constructor function which returns an instance.  I do this to enforce
> the assignment of several required fields in the record, so if you know
> how to do this another way, please let me know!

Ada.Finalization?

> But Ada is telling me I have to override this function in the child.

I presume you have something like:

   type X is private;
   function Create (...) return X;
private
   type X is tagged ...;

> This doesn't make a lot of sense.

On the contrary, Create above is covariant, that means that a derived type
cannot inherit it, because otherwise the result would be of the parent type
X. Thus it has to be overridden in each derived type. This is a very
important feature that saves a lot of debugging.

>  How can I deal with this?

Just make Create contravariant, provided that you know how to create
derived instances from there. In Ada contravariant subroutines are
class-wide:

   type X is tagged private;
      -- Publicly tagged to have an ability to declare class-wides
   function Create (...) return X'Class;
      -- This is same for all derived types
private
   type X is tagged ...;

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



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

* Re: tagged record child: override constructor?
  2005-09-13  7:32 ` Dmitry A. Kazakov
@ 2005-09-13  7:56   ` tmoran
  2005-09-13 15:23   ` sean.gilbertson
  1 sibling, 0 replies; 38+ messages in thread
From: tmoran @ 2005-09-13  7:56 UTC (permalink / raw)


>  type X is tagged private;
>     -- Publicly tagged to have an ability to declare class-wides
>  function Create (...) return X'Class;
  If you then have
    type Y is new X ...
then function Create will of course still return an X, not a Y unless
you make a new Create that knows how to return a Y.

> constructor function which returns an instance.  I do this to enforce
> the assignment of several required fields in the record, so if you know
> how to do this another way, please let me know!
   Make X a child of Ada.Finalization.Controlled (as suggested)
and writing an Initialize doesn't work?  Or can you simply make those
required fields discriminants?
   type X(Required_A, Required_B : Integer) is ...



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

* Re: tagged record child: override constructor?
  2005-09-13  5:58 tagged record child: override constructor? sean.gilbertson
  2005-09-13  6:39 ` David Trudgett
  2005-09-13  7:32 ` Dmitry A. Kazakov
@ 2005-09-13  9:33 ` Georg Bauhaus
  2005-09-13 16:37 ` Jeffrey Carter
  3 siblings, 0 replies; 38+ messages in thread
From: Georg Bauhaus @ 2005-09-13  9:33 UTC (permalink / raw)


sean.gilbertson@gmail.com wrote:
> I have a tagged record that is declared private, along with a
> constructor function which returns an instance.  I do this to enforce
> the assignment of several required fields in the record, so if you know
> how to do this another way, please let me know!

Another option in addition to those already
mentioned is to make the constructor function part of
a nested construction package. This way the function is no
longer a primitive operation of the type, and can be used
for initialising the T-components of type derived from T.

package P ...
  type T ....

  package Construction ...
     function make(...) return T ...
  end Construction;
end P;



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

* Re: tagged record child: override constructor?
  2005-09-13  7:32 ` Dmitry A. Kazakov
  2005-09-13  7:56   ` tmoran
@ 2005-09-13 15:23   ` sean.gilbertson
  2005-09-13 17:37     ` Martin Krischik
  1 sibling, 1 reply; 38+ messages in thread
From: sean.gilbertson @ 2005-09-13 15:23 UTC (permalink / raw)


I don't feel totally comfortable with the 'Class function, but after
looking at the Ada Wikibook, it seems like I can use it something like
a "static class method" in Java.  I'm going to research all of these
suggestions.

Thanks, Dmitry!  And thanks to everyone else!  I was surprised at the
quick and learned replies.




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

* Re: tagged record child: override constructor?
  2005-09-13  5:58 tagged record child: override constructor? sean.gilbertson
                   ` (2 preceding siblings ...)
  2005-09-13  9:33 ` Georg Bauhaus
@ 2005-09-13 16:37 ` Jeffrey Carter
  2005-09-13 18:55   ` Robert A Duff
  3 siblings, 1 reply; 38+ messages in thread
From: Jeffrey Carter @ 2005-09-13 16:37 UTC (permalink / raw)


sean.gilbertson@gmail.com wrote:
> I have a tagged record that is declared private, along with a
> constructor function which returns an instance.  I do this to enforce
> the assignment of several required fields in the record, so if you know
> how to do this another way, please let me know!

It depends on what you mean by "enforce the assignment of several 
required fields". It is impossible to force the client to call your 
function, though you can arrange things so that the type is useless to 
the client if the client doesn't first call your function.

If you have

    type T is private;
    function Make return T;

then you don't need Make at all. You can ensure the required fields are 
initialized through default values:

private
    type T is record
       X : Integer   := -37;
       Y : Character := '*';
       Z : Duration;
    end record;

(Note that this is a property of record types in general; whether your 
record type is tagged doesn't affect this.) If you have fields you can't 
easily initialize through defaults, you can use the initialization 
features of controlled types:

private
    type T is new Ada.Finalization.Controlled with record
       ...
    end record;

    procedure Initialize (Object : in out T);

Initialize is called for every object of type T when it is created.

On the other hand, if you mean the assignment of client-supplied values 
to the required components, your choices are more limited. If the values 
are such that they may be discriminants, then that's a way to force the 
client to supply them:

    type T (X : Integer) is private;

If your required fields cannot be discriminants, then things get more 
complicated. You can include a Boolean field, default initialized to 
False, which is set to True by your function. All other operations of 
the type then check this field, and raise an exception if it is False:

    type T is private;

    procedure Initialize (X : out T; ...);

    Not_Initialized : exception;

    procedure Op (X : in out T);

private
    type T is record
       Initialized : Boolean := False;
       ...
    end record;

...

procedure Initialize (X : out T; ...) is
    ...
begin -- Initialize
    X.Initialized := True;
    ...
end Initialize;

procedure Op (X : in out T) is
    ...
begin -- Op
    if not X.Initialized then
       raise Not_Initialized;
    end if;

    ...
end Op;

This makes the type useless unless an object is first initialized.

None of this has anything to do with the fact that if T is tagged and a 
primitive operation of T is a function that returns T, then, yes, you do 
have to override such functions for each child of T.

-- 
Jeffrey Carter
"Now go away or I shall taunt you a second time."
Monty Python and the Holy Grail
E-mail: jeffrey_r_carter-nr [commercial-at]
         raytheon [period | full stop] com



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

* Re: tagged record child: override constructor?
  2005-09-13 15:23   ` sean.gilbertson
@ 2005-09-13 17:37     ` Martin Krischik
  2005-09-13 19:29       ` Ludovic Brenta
  0 siblings, 1 reply; 38+ messages in thread
From: Martin Krischik @ 2005-09-13 17:37 UTC (permalink / raw)


sean.gilbertson@gmail.com wrote:

> I don't feel totally comfortable with the 'Class function, but after
> looking at the Ada Wikibook, it seems like I can use it something like
> a "static class method" in Java.  I'm going to research all of these
> suggestions.

In this case it's more like a "non virtual" functions. That's because all
class members are virtual in Ada. So 'Class methods replace both static and
not virtual - depending on context. Actually it's static as in:

class C
  {
  static F (C& Param);
  };

I guess we have to improve the text here.

Martin

-- 
mailto://krischik@users.sourceforge.net
Ada programming at: http://ada.krischik.com



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

* Re: tagged record child: override constructor?
  2005-09-13 16:37 ` Jeffrey Carter
@ 2005-09-13 18:55   ` Robert A Duff
  2005-09-13 22:18     ` Jeffrey Carter
  0 siblings, 1 reply; 38+ messages in thread
From: Robert A Duff @ 2005-09-13 18:55 UTC (permalink / raw)


Jeffrey Carter <spam@spam.com> writes:

> sean.gilbertson@gmail.com wrote:
> > I have a tagged record that is declared private, along with a
> > constructor function which returns an instance.  I do this to enforce
> > the assignment of several required fields in the record, so if you know
> > how to do this another way, please let me know!
> 
> It depends on what you mean by "enforce the assignment of several
> required fields". It is impossible to force the client to call your
> function, though you can arrange things so that the type is useless to
> the client if the client doesn't first call your function.
[... lots of good possibilities snipped...]

There's another way to "enforce...":

    package P is
        type T(<>) is private;
        function Make_T(...) return T;
    private
        type T is record...
    end P;

    use P;

Now clients can't create uninitialized objects of type T:

    X: T; -- illegal
    type T_Ptr is access T;
    Y: T_Ptr := new T; -- illegal

    type R is
        record
            Component: T; -- illegal
        end record;

    type A is array(...) of T; -- illegal

The client is forced to call Make_T:

    X: T := Make_T(...); -- OK
    Y: T_Ptr := new T'(Make_T(...)); -- OK

etc.

- Bob



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

* Re: tagged record child: override constructor?
  2005-09-13 17:37     ` Martin Krischik
@ 2005-09-13 19:29       ` Ludovic Brenta
  2005-09-14  7:49         ` Dmitry A. Kazakov
                           ` (2 more replies)
  0 siblings, 3 replies; 38+ messages in thread
From: Ludovic Brenta @ 2005-09-13 19:29 UTC (permalink / raw)


Martin Krischik <krischik@users.sourceforge.net> writes:
> That's because all class members are virtual in Ada.

I think this needs to be elaborated on.

All _primitive operations of a tagged type_ are _potentially_
"virtual" in Ada.  Each caller of such a primitive operation decides,
at the call site, whether the dispatching is static or dynamic.

In contrast, in (my recollection of) C++, all virtual methods dispatch
dynamically from all call sites: the decision is not made at the point
of call but at the point of definition of the method.

-- 
Ludovic Brenta.



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

* Re: tagged record child: override constructor?
  2005-09-13 18:55   ` Robert A Duff
@ 2005-09-13 22:18     ` Jeffrey Carter
  0 siblings, 0 replies; 38+ messages in thread
From: Jeffrey Carter @ 2005-09-13 22:18 UTC (permalink / raw)


Robert A Duff wrote:
> 
> There's another way to "enforce...":
> 
>     package P is
>         type T(<>) is private;
>         function Make_T(...) return T;
>     private
>         type T is record...
>     end P;

Right. I forgot indefinite types.

-- 
Jeffrey Carter
"Now go away or I shall taunt you a second time."
Monty Python and the Holy Grail
E-mail: jeffrey_r_carter-nr [commercial-at]
         raytheon [period | full stop] com



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

* Re: tagged record child: override constructor?
  2005-09-13 19:29       ` Ludovic Brenta
@ 2005-09-14  7:49         ` Dmitry A. Kazakov
  2005-09-14  9:05           ` Maciej Sobczak
  2005-09-14 16:14           ` Martin Krischik
  2005-09-14  9:28         ` Alex R. Mosteo
  2005-09-14 16:10         ` Martin Krischik
  2 siblings, 2 replies; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-14  7:49 UTC (permalink / raw)


On Tue, 13 Sep 2005 21:29:06 +0200, Ludovic Brenta wrote:

> Martin Krischik <krischik@users.sourceforge.net> writes:
>> That's because all class members are virtual in Ada.
> 
> I think this needs to be elaborated on.
> 
> All _primitive operations of a tagged type_ are _potentially_
> "virtual" in Ada.  Each caller of such a primitive operation decides,
> at the call site, whether the dispatching is static or dynamic.

Ada's model is quite simple and logical. If T appears as a formal parameter
or the result of a primitive operation of T, then this parameter is
controlled (= the operation can dispatch on it.)

Dispatching is determined not by any site but exclusively by the actual
parameter type. It must be of T'Class.

For this reason re-dispatching never happens. To re-dispatch, one should
explicitly convert T to T'Class.

> In contrast, in (my recollection of) C++, all virtual methods dispatch
> dynamically from all call sites: the decision is not made at the point
> of call but at the point of definition of the method.

C++ model is more tricky and in some aspects more limited. Only the prefix
(distinguished) parameter can be controlled (the "this-parameter".) The
result cannot be controlled as well. There is no difference between T and
T'Class (the source of countless problems.) So all operations re-dispatch
except for constructors and destructors, which don't. I.e. it is rather C++
where the call site determines whether a virtual function dispatches.

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



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

* Re: tagged record child: override constructor?
  2005-09-14  7:49         ` Dmitry A. Kazakov
@ 2005-09-14  9:05           ` Maciej Sobczak
  2005-09-14 13:20             ` Dmitry A. Kazakov
  2005-09-14 16:14           ` Martin Krischik
  1 sibling, 1 reply; 38+ messages in thread
From: Maciej Sobczak @ 2005-09-14  9:05 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

> C++ model is more tricky and in some aspects more limited. Only the prefix
> (distinguished) parameter can be controlled (the "this-parameter".)

There is a distinction between static and dynamic type and also a 
distinction between class member non-static functions and free functions 
as well as between non-static member functions being virtual or not.

The "first" parameter causes the virtual dispatch on the dynamic type of 
the object when it is used as a prefix for a virtual member function.
In all other cases (including further parameters) it is the static type 
of the object that drives the dispatch.

> There is no difference between T and
> T'Class (the source of countless problems.)

Could you elaborate?

> So all operations re-dispatch

What does that mean?

> except for constructors and destructors, which don't.

They do, but there the dynamic and static type of the object is the 
same, which actually saves a lot of problems.

> I.e. it is rather C++
> where the call site determines whether a virtual function dispatches.

It always does, independent on the call site. It is rather the dynamic 
type of the object that changes during construction and destruction, 
thus leading to the "virtual functions don't work" impression.

-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: tagged record child: override constructor?
  2005-09-13 19:29       ` Ludovic Brenta
  2005-09-14  7:49         ` Dmitry A. Kazakov
@ 2005-09-14  9:28         ` Alex R. Mosteo
  2005-09-14 16:10         ` Martin Krischik
  2 siblings, 0 replies; 38+ messages in thread
From: Alex R. Mosteo @ 2005-09-14  9:28 UTC (permalink / raw)


Ludovic Brenta wrote:
> Martin Krischik <krischik@users.sourceforge.net> writes:
> 
>>That's because all class members are virtual in Ada.
> 
> 
> I think this needs to be elaborated on.
> 
> All _primitive operations of a tagged type_ are _potentially_
> "virtual" in Ada.  Each caller of such a primitive operation decides,
> at the call site, whether the dispatching is static or dynamic.
> 
> In contrast, in (my recollection of) C++, all virtual methods dispatch
> dynamically from all call sites: the decision is not made at the point
> of call but at the point of definition of the method.

This is worth stressing. I was bitten by this distinction in cases like 
this:

type Blah is tagged ...;

procedure Do_Something (B : Blah); -- Default processing,
--  To be overriden/extended by descendant types.

procedure Run (B : Blah) is
begin
   ...
   Do_Something (B);
end Run;

If you make a descendant type (say Duh), the call inside Duh.Run will 
not be dispatching, but will call the Blah.Do_Something.

This is plain vanilla consequence of what you say, but for newcomers to 
Ada could be an easy mistake to made.

-- 
Take the Snape polls: http://snape.mosteo.com [Updated 16/05]



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

* Re: tagged record child: override constructor?
  2005-09-14  9:05           ` Maciej Sobczak
@ 2005-09-14 13:20             ` Dmitry A. Kazakov
  2005-09-14 13:52               ` Hyman Rosen
  0 siblings, 1 reply; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-14 13:20 UTC (permalink / raw)


On Wed, 14 Sep 2005 11:05:10 +0200, Maciej Sobczak wrote:

> Dmitry A. Kazakov wrote:
> 
>> There is no difference between T and
>> T'Class (the source of countless problems.)
> 
> Could you elaborate?

1. It is space inefficient. To have T and T'Class same requires keeping the
type tag in all Ts, while it is only necessary in T'Class.

2. There is no chance to get a consistent type system where each specific
type would have classes and primitive operations. If Boolean and
Boolean'Class have to be same, then the size of Boolean cannot be 1 bit.
(Presently Ada does not use this opportunity, but I hope it some day will)

3. It is inefficient, because it has to dispatch everywhere (except for
constructors and destructors, which still have the overhead of unused
dispatching.) Maybe this is the reason why so many people believe that OO
isn't suitable for real-time. I believe that this is rather an ungrounded
projection from C++ to Ada.

4. It is error-prone because of enforced re-dispatch (see below.) One
should explicitly specify T::Foo() referring member functions within a
member function. In practice nobody cares to do this, which often leads to
nasty surprises.

5. When T and T'Class are same, objects have identity. This identity might
be arbitrary. Nevertheless people will exploit it. This leads to bad
fragile design. Another form of identity is the object's address. Which is
quite similar case. Here again Ada prevents you from occasional using this
sort of identity (you have to make the object aliased), while C++
encourages it (you can apply & to almost everything.)

>> So all operations re-dispatch
> 
> What does that mean?

type Base is tagged ...;
procedure Foo (X : Base); -- Primitive operation
procedure Bar (X : Base);  -- Another primitive operation

procedure Foo (X : Base) is
begin
   Bar (X); -- This does not dispatch! It calls Base.Bar
end Foo;

The implementation of Foo stay consistent no matter what a derived type
would do with Bar. Note that this is nothing but a trivial contract model
which C++ lacks in this case. Within Foo, X is declared of the type Base.
So it would be infeasible to dispatch again. The type is known. If dispatch
is really needed, then that should be specified in the contract. I.e. the
type of X should be Base'Class. [Ada allows explicit conversions to
Base'Class, a bad idea though. Better pattern is to make the objects
explicitly having identity, using the Rosen's trick.]

Re-dispatch usually indicates a design problem.

>> except for constructors and destructors, which don't.
> 
> They do, but there the dynamic and static type of the object is the 
> same, which actually saves a lot of problems.

Yes, they do as if they wouldn't. To me it is that they don't.

Of course this saves the problems just because there is no re-dispatch,
which is in general a bad thing. Actually what C++ does in constructors and
destructors is using Ada's (consistent) model.

>> I.e. it is rather C++
>> where the call site determines whether a virtual function dispatches.
> 
> It always does, independent on the call site. It is rather the dynamic 
> type of the object that changes during construction and destruction, 
> thus leading to the "virtual functions don't work" impression.

Yes. It changes from T'Class to T and backwards depending on the location,
utterly inefficient and very error-prone.

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



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

* Re: tagged record child: override constructor?
  2005-09-14 13:20             ` Dmitry A. Kazakov
@ 2005-09-14 13:52               ` Hyman Rosen
  2005-09-14 16:47                 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 38+ messages in thread
From: Hyman Rosen @ 2005-09-14 13:52 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> 1. It is space inefficient. To have T and T'Class same requires keeping the
> type tag in all Ts, while it is only necessary in T'Class.

No Ada implementation takes this approach, though.
They all keep a type tag in every tagged object.

> 3. It is inefficient, because it has to dispatch everywhere

In C++, however, you have already declared some functions to be
dispatching (virtual) and some not. For those functions which are
virtual, dispatching is what's wanted. How many Ada newbies get
completely confused trying to figure out what dispatches and what
doesn't? Do any of them write T'Class the first time?

> 4. It is error-prone because of enforced re-dispatch

It is error-prone not to dispatch, because the first thing everyone
learns about OO is that methods dispatch! It's only after significant
hair-pulling that an Ada newbie will realize why his overriden methods
are not being called.

> 5. When T and T'Class are same, objects have identity.

Huh? All type tags for a given type are identical. Why does embedding
a type tag in an object give it any more identity than not doing so?

> The implementation of Foo stay consistent no matter what a derived type
> would do with Bar.

Except that when the programmer overrides Bar for derived types,
he's going to be monumentally confused as to why Foo isn't calling
it. Foo is going to be consistent, but as far as the programmer is
concerned, it's going to be consistently incorrect.

> Re-dispatch usually indicates a design problem.

What nonsense!

> Yes, they do as if they wouldn't. To me it is that they don't.

As has been repeatedly demonstrated to you, this is just wrong.
Once again, here is the code:

    struct A {
        virtual void f() { print("A"); }
        void g() { f(); }
        A() { g(); }
        ~A() { g(); }
    };
    struct B : A {
        void f() { print("B"); }
        B() { g(); }
        ~B() { g(); }
    };
    int main() { B b; }

This will print "ABBA", demonstrating that dispatching is occuring
within g() even when it is called from a constructor or destructor.




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

* Re: tagged record child: override constructor?
  2005-09-13 19:29       ` Ludovic Brenta
  2005-09-14  7:49         ` Dmitry A. Kazakov
  2005-09-14  9:28         ` Alex R. Mosteo
@ 2005-09-14 16:10         ` Martin Krischik
  2 siblings, 0 replies; 38+ messages in thread
From: Martin Krischik @ 2005-09-14 16:10 UTC (permalink / raw)


Ludovic Brenta wrote:

> Martin Krischik <krischik@users.sourceforge.net> writes:
>> That's because all class members are virtual in Ada.
> 
> I think this needs to be elaborated on.
> 
> All _primitive operations of a tagged type_ are _potentially_
> "virtual" in Ada.  Each caller of such a primitive operation decides,
> at the call site, whether the dispatching is static or dynamic.
 
> In contrast, in (my recollection of) C++, all virtual methods dispatch
> dynamically from all call sites: the decision is not made at the point
> of call but at the point of definition of the method.

In theory: No, C++ will staticly call function when possible.

In praxis: In 99% of the time there is only a pointer/reference available
and C++ need to dispatch then.

You see the point of a virtual function that there are child classes (unlike
i.E. string<> where there are no child classes). Since C++ has no 'Class
all child classes need to be dealt by pointer/reference use. so you need to
dispatch most of the time

Martin

-- 
mailto://krischik@users.sourceforge.net
Ada programming at: http://ada.krischik.com



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

* Re: tagged record child: override constructor?
  2005-09-14  7:49         ` Dmitry A. Kazakov
  2005-09-14  9:05           ` Maciej Sobczak
@ 2005-09-14 16:14           ` Martin Krischik
  2005-09-14 16:57             ` Dmitry A. Kazakov
  1 sibling, 1 reply; 38+ messages in thread
From: Martin Krischik @ 2005-09-14 16:14 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

> here is no difference between T and
> T'Class (the source of countless problems.)

Actually there is - its called T and T&. Problem is that T will allways pass
by copy and therefor is useless except for very primitive classes like
maybe string<>.

Martin
-- 
mailto://krischik@users.sourceforge.net
Ada programming at: http://ada.krischik.com



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

* Re: tagged record child: override constructor?
  2005-09-14 13:52               ` Hyman Rosen
@ 2005-09-14 16:47                 ` Dmitry A. Kazakov
  2005-09-14 17:16                   ` Hyman Rosen
  0 siblings, 1 reply; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-14 16:47 UTC (permalink / raw)


On 14 Sep 2005 06:52:55 -0700, Hyman Rosen wrote:

> Dmitry A. Kazakov wrote:
>> 1. It is space inefficient. To have T and T'Class same requires keeping the
>> type tag in all Ts, while it is only necessary in T'Class.
> 
> No Ada implementation takes this approach, though.
> They all keep a type tag in every tagged object.

May be, so what? I already said that Ada 95 didn't use all advantages of
its OO model. The question was about the model, not its particular
implementation in Ada 95.

>> 3. It is inefficient, because it has to dispatch everywhere
> 
> In C++, however, you have already declared some functions to be
> dispatching (virtual) and some not.

Which is bad. All operations should be primitive if not specified
otherwise. Moreover, it would be reasonable to prohibit operations whose
arguments are neither controlling nor class-wide. The only reason not to do
this is absence of multiple dispatch.

> For those functions which are
> virtual, dispatching is what's wanted. How many Ada newbies get
> completely confused trying to figure out what dispatches and what
> doesn't? Do any of them write T'Class the first time?

Why should they care? Most of the operations are primitive, yet meaningful
dispatch is rare. How often the specific type is really unknown?

A goal of Ada design was safe software construction. Putting former C++
programmers on the right track wasn't. (:-))

>> 4. It is error-prone because of enforced re-dispatch
> 
> It is error-prone not to dispatch, because the first thing everyone
> learns about OO is that methods dispatch!

In the past everyone learnt that the Earth is flat...

The effect of C++ as the first language is sometimes comparable to
undergoing the lobotomy...

>> 5. When T and T'Class are same, objects have identity.
> 
> Huh? All type tags for a given type are identical. Why does embedding
> a type tag in an object give it any more identity than not doing so?

Because having the tag you can determine the object's type, which might
differ from what the contract states. Ada is based on contract model.

>> The implementation of Foo stay consistent no matter what a derived type
>> would do with Bar.
> 
> Except that when the programmer overrides Bar for derived types,

Especially in this case!

> he's going to be monumentally confused as to why Foo isn't calling
> it.

Because it is the contract to determine what's going on.

>> Yes, they do as if they wouldn't. To me it is that they don't.
> 
> As has been repeatedly demonstrated to you, this is just wrong.
> Once again, here is the code:
> 
>     struct A {
>         virtual void f() { print("A"); }
>         void g() { f(); }
>         A() { g(); }
>         ~A() { g(); }
>     };
>     struct B : A {
>         void f() { print("B"); }
>         B() { g(); }
>         ~B() { g(); }
>     };
>     int main() { B b; }
> 
> This will print "ABBA", demonstrating that dispatching is occuring
> within g() even when it is called from a constructor or destructor.

It demonstrates that in the calls made from T::T and T::~T the type of
"this" is T.

f() in A::g() dispatches only if g() wasn't called [directly or indirectly]
from a constructor or destructor. If that was A::A then it calls A::f(). If
that was B::B then it calls B::f() (which might be inherited from A.) And
so on.

No mystery, just a mess.

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



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

* Re: tagged record child: override constructor?
  2005-09-14 16:14           ` Martin Krischik
@ 2005-09-14 16:57             ` Dmitry A. Kazakov
  2005-09-14 18:35               ` Martin Krischik
  0 siblings, 1 reply; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-14 16:57 UTC (permalink / raw)


On Wed, 14 Sep 2005 18:14:20 +0200, Martin Krischik wrote:

> Dmitry A. Kazakov wrote:
> 
>> here is no difference between T and
>> T'Class (the source of countless problems.)
> 
> Actually there is - its called T and T&.

Not quite. Compare:

T& X = Factory (...);

with

X : T'Class := Factory (...);

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



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

* Re: tagged record child: override constructor?
  2005-09-14 16:47                 ` Dmitry A. Kazakov
@ 2005-09-14 17:16                   ` Hyman Rosen
  2005-09-14 20:20                     ` Dmitry A. Kazakov
  0 siblings, 1 reply; 38+ messages in thread
From: Hyman Rosen @ 2005-09-14 17:16 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> meaningful dispatch is rare. How often the specific type is really unknown?

This is just baffling. The whole point of object-oriented
programming is that you have classes which conform to an
interface but have differing implementations, and those
implementations are dispatched to by making a call using a
classwide type. That's the whole reason tagged types were
implemented in Ada 95. Without it, you just have ADTs.

> A goal of Ada design was safe software construction. Putting former C++
> programmers on the right track wasn't. (:-))

How nice for Ada. But what does dispatching have to do
with safety?

> Because having the tag you can determine the object's type, which might
> differ from what the contract states. Ada is based on contract model.

That's how tagged types work. That's the whole point of
tagged types. You're supposed to be able to get at aspects
of the object's real type, via dispatching.

> Because it is the contract to determine what's going on.

You've got a tagged type, so it's methods should dispatch.
That's why you use a tagged type in the first place. When
the method call doesn't dispatch that's surprising and
unexpected. OO came to Ada late. Making its behavior weirdly
different from other OO languages isn't helpful.

> It demonstrates that in the calls made from T::T and T::~T the type of
> "this" is T.

Yes, that's one thing it demonstrates.

> f() in A::g() dispatches only if g() wasn't called [directly or indirectly]
> from a constructor or destructor.

That's false. B::B() and B::~B() both call A::g() (there
is no separate B::g() defined), and that dispatches to
B::f(). There is no difference in the dispatch mechanism
when f() is called, whether or not that call originates
in a constructor. The only difference is what type tag
the object contains when the dispatching happens.




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

* Re: tagged record child: override constructor?
  2005-09-14 16:57             ` Dmitry A. Kazakov
@ 2005-09-14 18:35               ` Martin Krischik
  0 siblings, 0 replies; 38+ messages in thread
From: Martin Krischik @ 2005-09-14 18:35 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

> On Wed, 14 Sep 2005 18:14:20 +0200, Martin Krischik wrote:
> 
>> Dmitry A. Kazakov wrote:
>> 
>>> here is no difference between T and
>>> T'Class (the source of countless problems.)
>> 
>> Actually there is - its called T and T&.
> 
> Not quite. Compare:
> 
> T& X = Factory (...);
> 
> with
> 
> X : T'Class := Factory (...);

ROFL. That is indeed the weekness of the C++ concept and the source of
countless problems. :-).

Martin

-- 
mailto://krischik@users.sourceforge.net
Ada programming at: http://ada.krischik.com



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

* Re: tagged record child: override constructor?
  2005-09-14 17:16                   ` Hyman Rosen
@ 2005-09-14 20:20                     ` Dmitry A. Kazakov
  2005-09-14 20:34                       ` Georg Bauhaus
  2005-09-14 20:56                       ` Hyman Rosen
  0 siblings, 2 replies; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-14 20:20 UTC (permalink / raw)


On 14 Sep 2005 10:16:16 -0700, Hyman Rosen wrote:

> Dmitry A. Kazakov wrote:
>> meaningful dispatch is rare. How often the specific type is really unknown?
> 
> This is just baffling. The whole point of object-oriented
> programming is that you have classes which conform to an
> interface but have differing implementations, and those
> implementations are dispatched to by making a call using a
> classwide type. That's the whole reason tagged types were
> implemented in Ada 95.

That does not invalidate my point. You need dispatch only if you have a
class-wide object. So if your newbie doesn't write T'Class s/he doesn't
need it.

> Without it, you just have ADTs.

Which is all I need! There is no magic in OO. Class is just an ADT.
Polymorphic object is just a value of that ADT. A polymorphic operation is
just an operation defined on that ADT, which body is composed out of parts
defined by specific types.

>> A goal of Ada design was safe software construction. Putting former C++
>> programmers on the right track wasn't. (:-))
> 
> How nice for Ada. But what does dispatching have to do
> with safety?

It was your point that for a newbie coming from C++ it might appear
difficult to understand Ada. But as it was pointed out many times before,
Ada's default choices are the safest ones. Thus by default an operation on
a tagged type is primitive and dispatching. C++ choices are at most
adventurous. The languages designers had different goals in mind. So for a
newbie it indeed might look as a paradigm shift. High time it is.

>> Because having the tag you can determine the object's type, which might
>> differ from what the contract states. Ada is based on contract model.
> 
> That's how tagged types work. That's the whole point of
> tagged types. You're supposed to be able to get at aspects
> of the object's real type, via dispatching.

That is how *class-wide* types work. Class-wide objects have the type
identity. Specific types need not, because in Ada you cannot dispatch on
them.

>> Because it is the contract to determine what's going on.
> 
> You've got a tagged type, so it's methods should dispatch.
> That's why you use a tagged type in the first place. When
> the method call doesn't dispatch that's surprising and
> unexpected. OO came to Ada late. Making its behavior weirdly
> different from other OO languages isn't helpful.

There is no point in adopting what other languages did wrong.

> The only difference is what type tag
> the object contains when the dispatching happens.

As I said before, it is equivalent to dispatch that does not happen. How
the compiler implements it, by either patching the dispatch table or not, I
don't care. It is a *consistent* behavior, and it is how Ada does it
everywhere.

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



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

* Re: tagged record child: override constructor?
  2005-09-14 20:20                     ` Dmitry A. Kazakov
@ 2005-09-14 20:34                       ` Georg Bauhaus
  2005-09-14 20:56                       ` Hyman Rosen
  1 sibling, 0 replies; 38+ messages in thread
From: Georg Bauhaus @ 2005-09-14 20:34 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

> There is no magic in OO. Class is just an ADT.

So why do cats not propose to dogs?



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

* Re: tagged record child: override constructor?
  2005-09-14 20:20                     ` Dmitry A. Kazakov
  2005-09-14 20:34                       ` Georg Bauhaus
@ 2005-09-14 20:56                       ` Hyman Rosen
  2005-09-15  7:31                         ` Dmitry A. Kazakov
  1 sibling, 1 reply; 38+ messages in thread
From: Hyman Rosen @ 2005-09-14 20:56 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> There is no point in adopting what other languages did wrong.

Except that Ada isn't doing things any differently than the
other languages, it's just adopted a notation that makes it
confusing. Ada *does* allow redispatch, type tags *are* in
every tagged object, you *can* convert from the type to the
classwide type, tagged objects are *always* passed by
reference. But where C++ and Java will make dispatching
calls where those are indicated by the class specification,
Ada forces the programmer to request such calls in a strange
way at call sites, and since there's nothing syntactically
wrong with making a non-dispatching call, the programmer won't
notice the mistake if that request isn't made.

> As I said before, it is equivalent to dispatch that does not happen.

You may say it all you like, but my code clearly demonstrates
that dispatching *does* happen. B::B() calls A::g() and that
dispatches to B::f(). The A::g() that's called by B::B() is
exactly the same A::g() that's called from outside constructors.




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

* Re: tagged record child: override constructor?
  2005-09-14 20:56                       ` Hyman Rosen
@ 2005-09-15  7:31                         ` Dmitry A. Kazakov
  2005-09-15 13:19                           ` Hyman Rosen
  0 siblings, 1 reply; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-15  7:31 UTC (permalink / raw)


On 14 Sep 2005 13:56:11 -0700, Hyman Rosen wrote:

> Dmitry A. Kazakov wrote:
>> There is no point in adopting what other languages did wrong.
> 
> Except that Ada isn't doing things any differently than the
> other languages, it's just adopted a notation that makes it
> confusing. Ada *does* allow redispatch, type tags *are* in
> every tagged object, you *can* convert from the type to the
> classwide type, tagged objects are *always* passed by
> reference.

Ada's model is the *only* model I know which allows:

1. A universal-purpose language (like C++ or Ada) to become a pure OO
language.

2. To support multiple dispatch

3. To avoid distributed overhead

4. To create a consistent construction/destruction model

OK, these opportunities weren't used, so what?

> But where C++ and Java will make dispatching
> calls where those are indicated by the class specification,
> Ada forces the programmer to request such calls in a strange
> way at call sites, and since there's nothing syntactically
> wrong with making a non-dispatching call, the programmer won't
> notice the mistake if that request isn't made.

Which mistake? Again, it is solely the actual object's *type* which
determines dispatch. It is 100% safe, intuitive and unambiguous. Ada is a
typed language! Is C++ one?

>> As I said before, it is equivalent to dispatch that does not happen.
> 
> You may say it all you like, but my code clearly demonstrates
> that dispatching *does* happen. B::B() calls A::g()

No. It calls B::g() which was inherited from A. I don't care if code of
B::g() and A::g() share memory or not. They are different functions because
their signatures are different.

> and that dispatches to B::f().

It does not. It calls B::f() from B::g()

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



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

* Re: tagged record child: override constructor?
  2005-09-15  7:31                         ` Dmitry A. Kazakov
@ 2005-09-15 13:19                           ` Hyman Rosen
  2005-09-15 13:45                             ` Maciej Sobczak
  2005-09-15 17:45                             ` Dmitry A. Kazakov
  0 siblings, 2 replies; 38+ messages in thread
From: Hyman Rosen @ 2005-09-15 13:19 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> Which mistake? Again, it is solely the actual object's *type* which
> determines dispatch.

The mistake of not calling the overridden method of the actual
type of the object rather than of the declared type. In OO
programming, by-reference objects have two types, their declared
base type and their actual created type. In OO programming, it is
expected by the programmers that a call to a potentially overridden
function using a base by-reference object will dispatch to the
overridden function. If the language claims to support OO but then
makes it easy to call these methods in such a way that overriding
doesn't happen, and without warning, then the language has introduced
a dangerous trap. That is supposedly antithetical to the spirit of
Ada.

> It is 100% safe, intuitive and unambiguous.

It is not intuitive at all, except to people whose intuition has
been warped. It's just plain wrong.

> Ada is a typed language! Is C++ one?

It's the Ada language that has caused itself this problem. C++
requires that you clearly distinguish between a reference to an
object, a pointer to an object, and the object itself. Thus,
when you see a pointer or reference to a base class, you know
that the referee object may be of a derived class, and that
dispatching may take place. Ada, in trying to be clever about
hiding parameter passing mode from the programmer, and by not
supporting a distinguished dispatching object syntax, let itself
in for a trap.

> No. It calls B::g() which was inherited from A. I don't care if code of
> B::g() and A::g() share memory or not. They are different functions because
> their signatures are different.

No, they are the same function. If, for example, I included a static
variable within it, calls to A::g() and B::g() would see the same
variable. And for that matter, simply write this:
    #include <iostream>
    #include <ostream>
    struct A { void g(); };
    struct B : A { };
    int main() { std::cout << (&A::g == &B::g) << "\n"; }
and you'll see whether the compiler thinks they're the same or not.

> It does not. It calls B::f() from B::g()

No. There is no B::g separate from A::g. Inherited functions are
not copies, they are the same function. There is no code you can
write which will demonstrate a difference.




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

* Re: tagged record child: override constructor?
  2005-09-15 13:19                           ` Hyman Rosen
@ 2005-09-15 13:45                             ` Maciej Sobczak
  2005-09-15 17:45                             ` Dmitry A. Kazakov
  1 sibling, 0 replies; 38+ messages in thread
From: Maciej Sobczak @ 2005-09-15 13:45 UTC (permalink / raw)


Hyman Rosen wrote:

>>It is 100% safe, intuitive and unambiguous.
> 
> It is not intuitive at all, except to people whose intuition has
> been warped. It's just plain wrong.

I agree with this.
It can be instructive to see how it is done in other languages which 
claim to support OO. Any examples supporting the "Ada way"?
If there are not many, then claiming that the whole world got it wrong 
whereas Ada got it right does not help, especially those who are 
learning or evaluating languages.

For me it is clear in C++ that whenever I call a function which is 
virtual I should expect dynamic dispatch if the call is made through the 
pointer or reference to the base class. If I want to suppress this 
behaviour, I still can (obj.Base::fun()), which means that the element 
of control is there and I can take advantage of it when it is what is 
really needed. It is the question of what is the default that we get 
implicitly. For me, the default in C++ is correct, because it is 
consistent with what I understand by dynamic polymorphism.
Note also that in C++ there is something like slicing, which actually 
"deprives" the object from its inherited properties, but people early 
learn to avoid it - for a reason.


-- 
Maciej Sobczak : http://www.msobczak.com/
Programming    : http://www.msobczak.com/prog/



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

* Re: tagged record child: override constructor?
  2005-09-15 13:19                           ` Hyman Rosen
  2005-09-15 13:45                             ` Maciej Sobczak
@ 2005-09-15 17:45                             ` Dmitry A. Kazakov
  2005-09-15 18:54                               ` Hyman Rosen
  1 sibling, 1 reply; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-15 17:45 UTC (permalink / raw)


On 15 Sep 2005 06:19:40 -0700, Hyman Rosen wrote:

> Dmitry A. Kazakov wrote:
>> Which mistake? Again, it is solely the actual object's *type* which
>> determines dispatch.
> 
> The mistake of not calling the overridden method of the actual
> type of the object rather than of the declared type.

There is no actual type, there is only the type specified by the contract.
That's the whole point.

> In OO
> programming, by-reference objects have two types, their declared
> base type and their actual created type.

That's your view on OO, which is clearly inconsistent with the notion of
types. Try to ask yourself simple questions: is "by-reference" a type
property? Why by-copy objects (of which type?) have one (which?) type while
by-reference objects (of which type?) have more than one? It is rubbish. In
a typed language an object has a type, only one type.

> In OO programming, it is
> expected by the programmers that a call to a potentially overridden
> function using a base by-reference object will dispatch to the
> overridden function. If the language claims to support OO but then
> makes it easy to call these methods in such a way that overriding
> doesn't happen, and without warning, then the language has introduced
> a dangerous trap.

No. Trap is when the declared type does not determine what's going on.

>> It is 100% safe, intuitive and unambiguous.
> 
> It is not intuitive at all, except to people whose intuition has
> been warped. It's just plain wrong.

You haven't presented anything to support this point, other than
assumptions about some hypothetical newbies.

I (again) formulate the properties of Ada's model:

1. The contract model

2. No space overhead

3. No run-time overhead caused by unnecessary re-dispatch

4. Pure OO (all types can have classes)

5. Compatible to multiple dispatch

6. Consistent with multi-methods

7. Compatible with class construction (dispatching from
constructors/destructors can be )

8. Compatible with assignment, abstract factory and all other cases, where
the result is dispatching.

9. Polymorphic object can be coped

10. Can return polymorphic objects on the stack

11. Can aggregate polymorphic objects in other objects

12. Covariant in all parameters

Now show me how C++ or Java could accomplish 1..12.

>> No. It calls B::g() which was inherited from A. I don't care if code of
>> B::g() and A::g() share memory or not. They are different functions because
>> their signatures are different.
> 
> No, they are the same function. If, for example, I included a static
> variable within it, calls to A::g() and B::g() would see the same
> variable. And for that matter, simply write this:
>     #include <iostream>
>     #include <ostream>
>     struct A { void g(); };
>     struct B : A { };
>     int main() { std::cout << (&A::g == &B::g) << "\n"; }
> and you'll see whether the compiler thinks they're the same or not.

What should that code prove? That pointers to members is a total mess in
C++? Everybody knows it.

>> It does not. It calls B::f() from B::g()
> 
> No. There is no B::g separate from A::g.

I don't know what "separate" members are. Do you mean memory location? Why
should I care of?

> Inherited functions are
> not copies, they are the same function.

They cannot be same because they act on different types. You cannot call
B::g() on A. The same question again, is C++ typed?

> There is no code you can
> write which will demonstrate a difference.

class A
{
public :
   void g();
};
class B : public A {};

B  *  Y = new B;
A  *  X = Y;

X->B::g (); // Error, how so? X is of B, or maybe not quite?
Y->B::g (); // OK

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



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

* Re: tagged record child: override constructor?
  2005-09-15 17:45                             ` Dmitry A. Kazakov
@ 2005-09-15 18:54                               ` Hyman Rosen
  2005-09-16  9:32                                 ` Dmitry A. Kazakov
  0 siblings, 1 reply; 38+ messages in thread
From: Hyman Rosen @ 2005-09-15 18:54 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> There is no actual type, there is only the type specified by the contract.

There is an actual type, and it can be recovered from the object
through redispatching (or even by simply extracting the type tag,
if I'm not mistaken).

> That's your view on OO, which is clearly inconsistent with the notion of
> types.

Perhaps with your notions of types.

> is "by-reference" a type property?

Yes. In C++ you have "type" and "reference to type" and the two
are completely different. A "reference to type" may have as its
referant a sub-object of the given type which is a base class of
a larger object. An object of "type" is exactly that. Both of
these concepts are useful.

> Why by-copy objects (of which type?) have one (which?) type while
> by-reference objects (of which type?) have more than one?

It's not the "by-copy" that gives something a type. When you declare
that a parameter has a class type, it has that type and nothing more.
It's a separate object within its lifetime, and when the function is
called, it's initialized with a copy of the argument. When you have
a parameter of reference type, when the function is called it's
initialized to refer to the appropriate subobject of the argument.
(Actually, newbies in C++ face the same confusion between paramaters
of T and T& as Ada newbies do between T and T'Class.)

> It is rubbish. In a typed language an object has a type, only one type.

You may not like it, but that's the way it's done, including in Ada.

> No. Trap is when the declared type does not determine what's going on.

In C++ the declared type does determine what's going on.
In Ada, you are permitted to go from the declared type to the
classwide type, so by your argument C++ reflects the contract
model better than Ada does.

> I (again) formulate the properties of Ada's model:

But no Ada compiler implements the model the way you would like
it to be. And I believe that allowing conversion from T to T'Class
is a requirement of Ada.

> Now show me how C++ or Java could accomplish 1..12.

C++ doesn't have polymorphic variables or copying, so some things
are out of reach. But the main problem with your approach is that
no one wants to implement differently sized pointers for T'Class
than for T. For all your posting on this, I've yet to see an
implementor so taken by your approach that he's willing to adopt it.

> What should that code prove? That pointers to members is a total mess in
> C++? Everybody knows it.

It proves that they're equal. *That's* what everyone knows.

> I don't know what "separate" members are. Do you mean memory location? Why
> should I care of?

I mean "different", or "distinct", or "not the same", or any other
synonym you care to pick.

> They cannot be same because they act on different types. You cannot call
> B::g() on A. The same question again, is C++ typed?

As I said, in OO languages like Ada, C++, and Java, when you have a
reference type, the object to which it refers may be a typed base
subobject of a larger object of different type. There is only an
A::g(),
and it operates on an A object which is standalone or which is a base
subobject of an object of a class derived from A. Because B inherits
g() from A, you may also refer to it as B::g, much as Ada lets you
rename one thing as another, but it is only a different name for the
same thing. And if you have a 'B b;' it's perfectly legal to say
'b.A::g();'.

> X->B::g (); // Error, how so? X is of B, or maybe not quite?

Because the rules of C++ (3.4.5/4 of the Standard, if you would like
to check) say that the name preceding the :: is first looked up in
context. There is no B in A, so it simply fails to compile without
ever getting to consider the g() part.




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

* Re: tagged record child: override constructor?
  2005-09-15 18:54                               ` Hyman Rosen
@ 2005-09-16  9:32                                 ` Dmitry A. Kazakov
  2005-09-16 14:52                                   ` Hyman Rosen
  0 siblings, 1 reply; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-16  9:32 UTC (permalink / raw)


On 15 Sep 2005 11:54:15 -0700, Hyman Rosen wrote:

> Dmitry A. Kazakov wrote:
>> There is no actual type, there is only the type specified by the contract.
> 
> There is an actual type, and it can be recovered from the object
> through redispatching (or even by simply extracting the type tag,
> if I'm not mistaken).

You are. Tag is said to be "associated", the standard does not require to
engrave it on the objects. Moreover, X'Tag is valid for class-wide objects
only! See 3.9 (11) 

>> is "by-reference" a type property?
> 
> Yes. In C++ you have "type" and "reference to type" and the two
> are completely different.

So T and T& are two different types, then why methods of T can be used with
T&? Is there a type conversion between them? When T is converted to T&, why
it dispatches to methods of T rather than to ones of T&? A poor C++ newbie
asks why the following does not compile:

class T& {}; // Isn't T& a type?

> A "reference to type" may have as its
> referant a sub-object of the given type which is a base class of
> a larger object. An object of "type" is exactly that. Both of
> these concepts are useful.

T != T& is useful, you say. Then why T /= T'Class is not? (:-))

>> Why by-copy objects (of which type?) have one (which?) type while
>> by-reference objects (of which type?) have more than one?
> 
> It's not the "by-copy" that gives something a type. When you declare
> that a parameter has a class type, it has that type and nothing more.

But the actual parameter may have another type. It leaks!

> It's a separate object within its lifetime, and when the function is
> called, it's initialized with a copy of the argument.

A copy, really? It is a strange copy, when the actual parameter is of a
derived type. Your theory leaks.

> When you have
> a parameter of reference type, when the function is called it's
> initialized to refer to the appropriate subobject of the argument.

Now you have a difficult job to explain what is a subobject. Which type it
has. Is it a by-reference or by-copy type, etc. Poor old C++ newbie!

> (Actually, newbies in C++ face the same confusion between paramaters
> of T and T& as Ada newbies do between T and T'Class.)

Huh, that's the minor confusion they face in C++...

>> It is rubbish. In a typed language an object has a type, only one type.
> 
> You may not like it, but that's the way it's done, including in Ada.

No, Ada is consistent here:

"A type is characterized by a set of values, and a set of primitive
operations which implement the fundamental aspects of its semantics. An
object of a given type is a run-time entity that contains (has) a value of
the type." 
   - ARM 3.2 (1)

>> No. Trap is when the declared type does not determine what's going on.
> 
> In C++ the declared type does determine what's going on.

Finally! (:-)) I.e. C++ is not a typed language. What else evidence you
need?

> In Ada, you are permitted to go from the declared type to the
> classwide type, so by your argument C++ reflects the contract
> model better than Ada does.

Allowing specific to class-wide conversion was in my view a mistake.
However, even though it contradicts to Ada's model, technically it is not a
big issue, because tagged types are by-reference anyway. When Ada will have
T'Class for non-tagged types as well, for those this conversion will not be
allowed.

>> I (again) formulate the properties of Ada's model:
> 
> But no Ada compiler implements the model the way you would like
> it to be.

There is a great difference between not yet implemented things and ones
which cannot be under any circumstances.

Alone to separate T and T'Class was a revolutionary breakthrough,
importance of which many people still do not grasp.

>> Now show me how C++ or Java could accomplish 1..12.
> 
> C++ doesn't have polymorphic variables or copying, so some things
> are out of reach. But the main problem with your approach is that
> no one wants to implement differently sized pointers for T'Class
> than for T.

Egh? Ada vendors have used fat pointers for decades. Even in C++, print
sizeof() of a member function pointer!

> For all your posting on this, I've yet to see an
> implementor so taken by your approach that he's willing to adopt it.

Come on, c.l.a. is a chat root. Do you think I have influence of Bjarne
Stroustrup, or what? (:-))

>> What should that code prove? That pointers to members is a total mess in
>> C++? Everybody knows it.
> 
> It proves that they're equal. *That's* what everyone knows.

Pointers are equal, so what?

>> They cannot be same because they act on different types. You cannot call
>> B::g() on A. The same question again, is C++ typed?
> 
> As I said, in OO languages like Ada, C++, and Java, when you have a
> reference type, the object to which it refers may be a typed base
> subobject of a larger object of different type.

See? It is of a different type. Now observe that B <: A, it is an
asymmetric relation. So B::g() cannot be same as A::g(), because that would
require equivalence B :=: A, which is wrong. This is why you cannot call
B::g() on A, even if B::g() was inherited, not overridden. My example shows
this. At least here C++ is consistent.

[my "theory" on]
The difference between A::g() and B::g() inherited from A is that B::g() is
a composition of three functions:

B::g() :=: (B *) o A::g() o (A *)

The argument "this" is first converted from B* to A*, then this->A::g() is
called, then it is converted back to B*. These conversions might be
identity ones (not always in presence of multiple inheritance, BTW), but
that gives no right to declare A::g() and B::g() same.
[my "theory" off]

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



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

* Re: tagged record child: override constructor?
  2005-09-16  9:32                                 ` Dmitry A. Kazakov
@ 2005-09-16 14:52                                   ` Hyman Rosen
  2005-09-16 15:33                                     ` Jean-Pierre Rosen
  2005-09-16 21:03                                     ` Dmitry A. Kazakov
  0 siblings, 2 replies; 38+ messages in thread
From: Hyman Rosen @ 2005-09-16 14:52 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> So T and T& are two different types, then why methods of T can be used with
> T&?

Because the language says so.

> Is there a type conversion between them?

No.

> When T is converted to T&, why
> it dispatches to methods of T rather than to ones of T&?

T& has no methods. It dispatches to methods of T and derived classes
because that's what the language says. It's not unlike the way Ada
sometimes gives you an implicit .all on an access type.

> A poor C++ newbie
> asks why the following does not compile:
>
> class T& {}; // Isn't T& a type?

Because T& cannot be used as the name of a class. All classes
are types, but not all types are classes.

> T != T& is useful, you say. Then why T /= T'Class is not? (:-))

Actually, you're right about that and I'm wrong. I still think
that Ada's requirement that tagged types be passed by reference
shows that it was a mistake for the language to try to conceal
the parameter passing mode from the programmer.

> But the actual parameter may have another type. It leaks!

Irrelevant. When the paramater is of a specific class type
and you pass a derived type as an argument, the parameter
simply receives a copy of the base part of the argument.
It's referred to as "slicing". And just as you claim to prefer,
once that happens there is no going back to the derived type.
The parameter is well and truly only of its declared type.

> A copy, really? It is a strange copy, when the actual parameter is of a
> derived type. Your theory leaks.

Nope. No leak. It really is a copy of just that subpart of the
argument. That's why most functions in C++ which take class types
as arguments get them by reference. And of course, the distinguished
calling object itself is passed by reference; it's address becomes
the value of the 'this' keyword in the member function.

> Now you have a difficult job to explain what is a subobject. Which type it
> has. Is it a by-reference or by-copy type, etc. Poor old C++ newbie!

Not difficult at all. When you declare a class, it is either standalone
or it inherits from one or more other classes. A reference to any class
in this inheritance hierarchy may be initialized to refer to an object
of the derived class. (It's slightly more complicated because of access
restrictions and the ability to have a class more than once in the
inheritance hierarchy, but that's details.) Classes in an inheritance
hierarchy are just that - classes. You cannot inherit from anything
else,
so there is no such thing as a base class which is a reference.

> "A type is characterized by a set of values..."

But what is the set of values for a tagged type?
Doesn't it include the values of all possible derived types?
After all, reading 4.6, it certainly seems that I'm allowed
to do view conversions back and forth between base and derived
tagged types. So it looks like I can have something that's
declared as a type 'B' but is really an object of a derived
type 'D', and I can take my 'B'-declared object and view-convert
it into a 'D'-declared object, all the while maintaining the
identity of the object.

> > In C++ the declared type does determine what's going on.
>
> Finally! (:-)) I.e. C++ is not a typed language. What else evidence you
> need?

Huh? I said "does", not "doesn't".

> There is a great difference between not yet implemented things and ones
> which cannot be under any circumstances.

The requirements for view conversion and by-refernce passing
make it seem to me that no Ada compiler will ever implement
your version of things.

> The argument "this" is first converted from B* to A*, then this->A::g() is
> called, then it is converted back to B*.

And when this->A::g() calls f(), it does so by dispatching, regardless
of whether the A::g() call originated in a constructor or not.




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

* Re: tagged record child: override constructor?
  2005-09-16 14:52                                   ` Hyman Rosen
@ 2005-09-16 15:33                                     ` Jean-Pierre Rosen
  2005-09-16 18:37                                       ` Hyman Rosen
  2005-09-16 21:03                                     ` Dmitry A. Kazakov
  1 sibling, 1 reply; 38+ messages in thread
From: Jean-Pierre Rosen @ 2005-09-16 15:33 UTC (permalink / raw)


Hyman Rosen a �crit :
> Irrelevant. When the paramater is of a specific class type
> and you pass a derived type as an argument, the parameter
> simply receives a copy of the base part of the argument.
> It's referred to as "slicing". And just as you claim to prefer,
> once that happens there is no going back to the derived type.
> The parameter is well and truly only of its declared type.
> 
Not at all. You receive a *view*, not a copy (as you noted above, tagged 
types are always passed by reference). If you convert it to T'Class, it 
will dispatch according to its run-time type (i.e. the tag is not 
changed by parameter-passing - even if the parameter is of a specific type).
-- 
---------------------------------------------------------
            J-P. Rosen (rosen@adalog.fr)
Visit Adalog's web site at http://www.adalog.fr



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

* Re: tagged record child: override constructor?
  2005-09-16 15:33                                     ` Jean-Pierre Rosen
@ 2005-09-16 18:37                                       ` Hyman Rosen
  0 siblings, 0 replies; 38+ messages in thread
From: Hyman Rosen @ 2005-09-16 18:37 UTC (permalink / raw)



Jean-Pierre Rosen wrote:
> Not at all. You receive a *view*, not a copy

Sorry, you misunderstood. I was talking to DK about C++, not Ada.
Ada works just as you describe, of course.




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

* Re: tagged record child: override constructor?
  2005-09-16 14:52                                   ` Hyman Rosen
  2005-09-16 15:33                                     ` Jean-Pierre Rosen
@ 2005-09-16 21:03                                     ` Dmitry A. Kazakov
  2005-09-16 21:33                                       ` Hyman Rosen
  1 sibling, 1 reply; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-16 21:03 UTC (permalink / raw)


On 16 Sep 2005 07:52:36 -0700, Hyman Rosen wrote:

> I still think
> that Ada's requirement that tagged types be passed by reference
> shows that it was a mistake for the language to try to conceal
> the parameter passing mode from the programmer.

Why do you think so? The program semantics should be independent from the
parameter passing mode. The type identity is another thing. Though IMO, it
is always suspicious from the design point view to have objects with
identity. Fortunately Ada does not re-dispatch by default. I say
fortunately, because it was a number of times were the compiler gave me the
message, that a class-wide was expected, but a specific type was found
instead. I can remember none of them, where the program were right. In C++
these errors would slip through.

>> But the actual parameter may have another type. It leaks!
> 
> Irrelevant. When the paramater is of a specific class type
> and you pass a derived type as an argument, the parameter
> simply receives a copy of the base part of the argument.
> It's referred to as "slicing". And just as you claim to prefer,
> once that happens there is no going back to the derived type.
> The parameter is well and truly only of its declared type.

But it is not a copy of the actual object. It is a copy of its part! The
point is that T and T& are no less different C++ than T and T'Class in Ada.
But in C++ T& is overloaded with two different meanings: closure of types
(class) vs. reference type. As a consequence T& is always a reference to
class (except from con/destructors (:-)) You cannot have a reference to a
specific type. Same with pointers. In Ada both renaming (a rough equivalent
of reference) and access types can be either specific or class-wide. Wasn't
Java's final types an unsuccessful attempt to mend this? Further, because
T& is not an object, you cannot do such elementary thing as to copy a
polymorphic object.

>> "A type is characterized by a set of values..."
> 
> But what is the set of values for a tagged type?
> Doesn't it include the values of all possible derived types?

No. It is the class rooted in the type which do. A T'Class serves as a
closure of all types derived from T.

> After all, reading 4.6, it certainly seems that I'm allowed
> to do view conversions back and forth between base and derived
> tagged types.

A view conversion is still a conversion. If all types had classes, then for
non-tagged types T<->T'Class were a full conversions constructing new
objects.

In Ada there are things much close to this pattern. Unconstrained array
types are similar to classes, their constrained subtypes are similar to
specific types. Here bounds play the same role as the tag does. Note that
the language does not define how bounds are passed back and forth. It also
allows to pass arrays by copy and throw known bounds away.

>> There is a great difference between not yet implemented things and ones
>> which cannot be under any circumstances.
> 
> The requirements for view conversion and by-refernce passing
> make it seem to me that no Ada compiler will ever implement
> your version of things.

This is required for tagged types only.

Let's wait until people finally turn sick of templates! (:-))

>> The argument "this" is first converted from B* to A*, then this->A::g() is
>> called, then it is converted back to B*.
> 
> And when this->A::g() calls f(), it does so by dispatching, regardless
> of whether the A::g() call originated in a constructor or not.

Don't you see that the following two interpretations are equivalent:

1. It dispatches, but the tag changes

2. It does not dispatch, and the tag is constant.

It is clear that you prefer (1) because your mental model is many types -
one object. But I stick to (2) because I am convinced that the multiple
types model will show itself infeasible, should somebody try to formalize
it.

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



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

* Re: tagged record child: override constructor?
  2005-09-16 21:03                                     ` Dmitry A. Kazakov
@ 2005-09-16 21:33                                       ` Hyman Rosen
       [not found]                                         ` <98ox2x9xvj9z.1uh92dslhvt4g.dlg@40tude.net>
  0 siblings, 1 reply; 38+ messages in thread
From: Hyman Rosen @ 2005-09-16 21:33 UTC (permalink / raw)


Dmitry A. Kazakov wrote:
> Why do you think so? The program semantics should be independent from the
> parameter passing mode.

If tagged types were not passed by reference then the program
semantics would be severely affected. That's why the language
requires that passing mode. But in that case, it should be part
of the program, not a hidden requirement.

> You cannot have a reference to a specific type. Same with pointers.

True (for class types).

> Further, because
> T& is not an object, you cannot do such elementary thing as to copy a
> polymorphic object.

No, that's not why. It's purely the pragmatic implementation
complexity of arranging for the space and copying of an object
of runtime-determined size. Even in Ada objects of T'Class type
are somewhat half-baked. They can't live inside records, you
can't have arrays of them, etc.

> No. It is the class rooted in the type which do. A T'Class serves as a
> closure of all types derived from T.
> A view conversion is still a conversion.

When I view convert from a derived class type to a base class type,
and then back again, I have the same contents (and the same object)
that I started with. The extra derived stuff cannot magically appear
from nowhere; therefore it must have been there all along. Therefore
even though I have an object of some declared type I know that it may
be carrying along a bunch of extra data.




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

* Re: tagged record child: override constructor?
       [not found]                                         ` <98ox2x9xvj9z.1uh92dslhvt4g.dlg@40tude.net>
@ 2005-09-17 12:47                                           ` Georg Bauhaus
  2005-09-17 15:56                                             ` Dmitry A. Kazakov
  0 siblings, 1 reply; 38+ messages in thread
From: Georg Bauhaus @ 2005-09-17 12:47 UTC (permalink / raw)


Dmitry A. Kazakov wrote:

> type T tagged record
>    Self : T_Ptr := T'Unchecked_Access;

This won't compile unless you make T limited.



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

* Re: tagged record child: override constructor?
  2005-09-17 12:47                                           ` Georg Bauhaus
@ 2005-09-17 15:56                                             ` Dmitry A. Kazakov
  0 siblings, 0 replies; 38+ messages in thread
From: Dmitry A. Kazakov @ 2005-09-17 15:56 UTC (permalink / raw)


On Sat, 17 Sep 2005 14:47:07 +0200, Georg Bauhaus wrote:

> Dmitry A. Kazakov wrote:
> 
>> type T tagged record
>>    Self : T_Ptr := T'Unchecked_Access;
> 
> This won't compile unless you make T limited.

Exactly. Limited implies by-reference.

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



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

end of thread, other threads:[~2005-09-17 15:56 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-09-13  5:58 tagged record child: override constructor? sean.gilbertson
2005-09-13  6:39 ` David Trudgett
2005-09-13  7:32 ` Dmitry A. Kazakov
2005-09-13  7:56   ` tmoran
2005-09-13 15:23   ` sean.gilbertson
2005-09-13 17:37     ` Martin Krischik
2005-09-13 19:29       ` Ludovic Brenta
2005-09-14  7:49         ` Dmitry A. Kazakov
2005-09-14  9:05           ` Maciej Sobczak
2005-09-14 13:20             ` Dmitry A. Kazakov
2005-09-14 13:52               ` Hyman Rosen
2005-09-14 16:47                 ` Dmitry A. Kazakov
2005-09-14 17:16                   ` Hyman Rosen
2005-09-14 20:20                     ` Dmitry A. Kazakov
2005-09-14 20:34                       ` Georg Bauhaus
2005-09-14 20:56                       ` Hyman Rosen
2005-09-15  7:31                         ` Dmitry A. Kazakov
2005-09-15 13:19                           ` Hyman Rosen
2005-09-15 13:45                             ` Maciej Sobczak
2005-09-15 17:45                             ` Dmitry A. Kazakov
2005-09-15 18:54                               ` Hyman Rosen
2005-09-16  9:32                                 ` Dmitry A. Kazakov
2005-09-16 14:52                                   ` Hyman Rosen
2005-09-16 15:33                                     ` Jean-Pierre Rosen
2005-09-16 18:37                                       ` Hyman Rosen
2005-09-16 21:03                                     ` Dmitry A. Kazakov
2005-09-16 21:33                                       ` Hyman Rosen
     [not found]                                         ` <98ox2x9xvj9z.1uh92dslhvt4g.dlg@40tude.net>
2005-09-17 12:47                                           ` Georg Bauhaus
2005-09-17 15:56                                             ` Dmitry A. Kazakov
2005-09-14 16:14           ` Martin Krischik
2005-09-14 16:57             ` Dmitry A. Kazakov
2005-09-14 18:35               ` Martin Krischik
2005-09-14  9:28         ` Alex R. Mosteo
2005-09-14 16:10         ` Martin Krischik
2005-09-13  9:33 ` Georg Bauhaus
2005-09-13 16:37 ` Jeffrey Carter
2005-09-13 18:55   ` Robert A Duff
2005-09-13 22:18     ` Jeffrey Carter

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