Re: Inherited Methods and such



On 24 Wrz, 21:20, "Dmitry A. Kazakov" <mail...@xxxxxxxxxxxxxxxxx>
wrote:

OK. Let's forbid to call virtual functions altogether until the full
object is ready. I'm fine with such restriction.

No, because:

1. It introduces another type T w/o primitive operations;

Indeed.

2. This degenerate is anyway inviable, as you can call primitive operations
from non-primitive ones.

3. There already exists a type that has the desired property of
non-dispatching, it is T (specific).

2. and 3. are inconsistent.


You are in an unresolvable contradiction by trying to mix class and its
truncations. It is untyped, just because mixing types in untyped. Ada 95
solved that by naming the truncation as T and the class of as T'Class. The
rest was easy.

Yes. Like here:

-- p.ads:
with Ada.Finalization;
package P is

type T is new Ada.Finalization.Controlled with null record;

overriding procedure Initialize (X : in out T);
procedure Some_Operation (X : in T);

type R is range 10 .. 20;

type S is new T with record
I : R;
end record;

overriding procedure Initialize (X : in out S);
overriding procedure Some_Operation (X : in S);

end P;

-- p.adb:
with Ada.Text_IO;
use Ada.Text_IO;

package body P is

procedure Dispatch (X : in T'Class) is
begin
Put ("Calling Some_Operation with T'Class");
New_Line (2);
Some_Operation (X);
end Dispatch;

procedure Initialize (X : in out T) is
begin
Put ("P.Initialize for T");
New_Line;
Put ("-> doing some operation with the object");
New_Line (2);
Dispatch (X);
end Initialize;

procedure Some_Operation (X : in T) is
begin
Put ("P.Some_Operation for T");
New_Line (2);
end Some_Operation;

procedure Initialize (X : in out S) is
begin
Put ("P.Initialize for S, but first let's initialize T" &
" and then we will take care of our (S) components");
New_Line (2);
Initialize (T (X));
Put ("OK, finished with the supertype (T)");
New_Line;
Put ("-> let's initialize our own (S) components...");
New_Line;
X.I := 15;
Put ("-> components initialized and now I =" & R'Image (X.I));
New_Line (2);
end Initialize;

procedure Some_Operation (X : in S) is
begin
Put ("P.Some_Operation for S");
New_Line;
Put ("-> here we are SURE that all components are correct");
New_Line;
Put ("-> for example I =" & R'Image (X.I));
Put (" which is SURELY in the range " &
R'Image (R'First) & " .." & R'Image (R'Last));
New_Line;
Put ("-> otherwise it would be UNTYPED MESS");
New_Line (2);
end Some_Operation;

end P;

-- a.adb:
with P;
procedure A is
X : P.S;
begin
null;
end;

$ gnatmake a
gcc -c a.adb
gcc -c p.adb
gnatbind -x a.ali
gnatlink a.ali
$ ./a
P.Initialize for S, but first let's initialize T and then we will take
care of our (S) components

P.Initialize for T
-> doing some operation with the object

Calling Some_Operation with T'Class

P.Some_Operation for S
-> here we are SURE that all components are correct
-> for example I = 0 which is SURELY in the range 10 .. 20
-> otherwise it would be UNTYPED MESS

OK, finished with the supertype (T)
-> let's initialize our own (S) components...
-> components initialized and now I = 15

$

The above program prints 0 as the value of an object which type is
range 10 .. 20.

What's even more funny, I can add the following inside Some_Operation
for S:

declare
J : R := X.I; -- at this point X.I = 0
begin
Put (R'Image (J));
end;

Guess what will happen. No constraint_error and 0 is assigned to J.
Why? Because the poor compiler assumed that if X.I is of type R, then
no checks are necessary.

Wow.

Let's say I want to register my new object from T's constructor in
some map or somewhere. I don't want to hook to complete T'Class,
because I cannot force users deriving from my T to call be back so
that I can register. I want the registration process to be
encapsulated and invisible and I want to do it from T's constructor.
If you restrict the possibility to use T'Class inside the constructor,
you have to way to register T'Class reference/pointer from T's
constructor. Too bad.

I don't understand your example - if you don't derive from T, its
constructor will not be called. What do you want, to register objects of
the type T or objects of the type T'Class? Answer this and you will know
from where to do the registration of what.

I want to register T'Class, but I don't want to involve those who
derive from T.


Another part is dispatch into
vacuum.

Not in Ada. You cannot dispatch in Initialize because its argument is
specific.

See above example.

What about growing up?

The attributes do.

No. Children and Adults have different interfaces. It's not even the
case-block-pretending-dispatch. There are different primitive
operations, some of them not even rooted in Human.

You could say it simpler: Children and Adult are different types. Period.

They are both in Human'Class. You try to escape the problem instead of
solving it.


My point is, a view is just a new object of a *new* type.

It does not have to be, but I find it acceptable except that I don't
think you need a *new* type for them.

We need it to prevent infinite recursion. More generally, here the
behavioral principle applies: if there is a difference between object and a
reference to => that should be observable through the behaviors of => they
have different behaviors => they have different types.

What if they don't have different behaviors?
There are two ways to achieve it:
1. Prevent anybody from interacting with the object directly, so there
is nothing to compare (Java).
2. Make references just names, which are syntax entities (C++, in some
contexts).


Control question: what is the identity of the view? :-)

There is nothing special in identity. A view can have many identities:

1. The name of the view
2. The type of the view
3. The target if the type of is comparable
4. The tag of the view if it is polymorphic
5. The tag of the target if that is polymorphic
...

6. The identity of the target.

Ada is consistent because Initialize is not a constructor. It washes its
hands: if we don't know how to do it properly, we better don't.

Leaving the problem open? I don't call it consistent.

Incomplete, but consistent. Absence of solution is better than a wrong one.

A wrong solution is exploited in the example above.
This is *really* *untyped* *mess* (tm).

Let's also not forget that Initialize is useless as a constructor (and
thus incomparable to C++) anyway due to the lack of parameters. But so-
called "constructor functions" will not solve this problem.

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

.



Relevant Pages

  • Re: Inherited Methods and such
    ... Initialize converts the object X to T'Class. ... a hook on the constructor of T'Class which is officially constructed, ... re-dispatch is inherently inconsistent. ... you have to way to register T'Class reference/pointer from T's ...
    (comp.lang.ada)
  • Re: Inherited Methods and such
    ... what I see in Initialize is ... Now, what you want is to get in the constructor of some S derived from T, ... The base types and registry are part of the framework and the user ... provides the concrete factories. ...
    (comp.lang.ada)
  • Re: Strange member pointer error - even the debugger is confused.
    ... Initialize m_pHead in your constructor to NULL. ... "Aaron Lawrence" wrote in message ... > class ATL_NO_VTABLE CIfsf: ...
    (microsoft.public.vc.language)
  • Re: Initialization & Inheritance
    ... for a recent project I was trying to initialize members of a derived ... class from the constructor of the super class (done using an abstract ... initialize, but double initialization. ... if you used the pointer as it stood, ...
    (comp.lang.java.programmer)
  • Re: Initializing static readonly methods
    ... public class TestClass ... //So assigning a value to readonly is perfectly fine. ... //readonly values are has to be initialized in constructor. ... There is no other place to initialize them. ...
    (microsoft.public.dotnet.languages.csharp)