comp.lang.ada
 help / color / mirror / Atom feed
From: "Jean-Pierre Rosen" <rosen@adalog.fr>
Subject: Re: C++ virtual function mechanism in Ada ?
Date: Wed, 25 Jul 2001 15:43:37 +0200
Date: 2001-07-25T15:43:37+02:00	[thread overview]
Message-ID: <9jmif2$et3$1@s1.read.news.oleane.net> (raw)
In-Reply-To: IDz77.6315$ar1.18443@www.newsranger.com

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 4271 bytes --]


"Didier.pieroux" <dpieroux.no-spam@pi.be> a �crit dans le message news: IDz77.6315$ar1.18443@www.newsranger.com...
[snipped for brievity]

> One way to get the job done is to use:
> procedure F(A: in T1) is begin Put("F1:");     E(T1'Class(A)); end F;
> That switches the dynamical dispatching on and everybody is happy. But the bad
> point is that I have the impression that extending a base type can require
> modifications in that base type implementation. Too bad for code reliability,
> isn't it ?
This is the right way to do it.

>
> So, my first question is: knowing that a characteristic feature of primitive
> operations is to call them with objects of a derived type, why did the Ada
> designers choose not to use dynamical dispatching when calling a primitive
> function ?  I guess that most of the time Ada programmers (as C++ programmers)
> want to really call the primitive operation corresponding to the type of the
> actual parameter, no ?
A fundamental question, so let's go.
There is a fundamental difference between C++ and Ada: in C++, dispatching is a property of the function (virtual or not).
Second: Ada is a language driven by types. It is always the *type* of the parameters that determines what is called. If the
(declared) specific (i.e. real) type is known at compile time, the call is static. If it is not (i.e. a class-wide type), the
binding is dynamic.
The benefit of this approach is that when you read the code, if you know the types of the parameters (and in Ada, you'd better know
that), you know what will happen, you don't have to look for information in some distant place. It also avoids the risk of ruining a
program, as in C++ when you decide to change a function from non-virtual to virtual.It is the user (i.e. the caller) who decides on
the behaviour he wants.

Now, it is true that when you write a primitive operation on a tagged type, you need to think about whether you want to redispatch
or not. That's part of the design.

Once again, the Ada solution makes the code a bit more difficult to write (you have to convert to a class-wide type) for the benefit
of the reader.

> Second question... In order to simulate the virtual function mechanism for
> primitive operation, I found the following solution.  I declare (non-primitive)
> procedures with a class-wide formal argument of the Base type. The only thing
> they do is to call the real primitive functions. Elsewhere in the code, these
> primitive functions are never called directly but always through the class-wide
> procedures. Using this, the package above becomes:
> [snip]
>
> => Questions: is it the right way to simulate the C++ virtual mechanism in Ada
> or is there something more clever or more "in the Ada approach" ?
>
This works, but brings nothing to simply converting to the class inside the primitive operation.

Another fundamental difference in the Ada model is that it distinguishes a node (T) from a subtree rooted at that node (T'Class).
There is no such distinction in other languages that I know, where a T is at the same time a real T AND valid for all descendants of
T (through pointers in C++, but not in Java).
An operation with a parameter of type T is a fundamental behaviour of the type, that may need to be redefined by descendents. An
operation with a parameter T'Class operates on the subtree, and is not redefinable; it generally calls primitive operations, and
dispatching will take place. This way, you are sure that everybody uses the same operation.

To take a concrete example, suppose I have various kinds of windows, with Put and New_Line operations:
   procedure Put (Item : String; Into : Window);
   procedure New_Line (Into : Window);

A scrollable window will redefine these operations, because it needs to keep previous lines in some buffer. Now I want to add a
Put_Line; I want Put_Line to be always equivalent to calling Put then New_Line:
   procedure Put_Line (Item : String; Into : Window'class) is
   begin
      Put (Item, Into);
      New_Line (Into);
   end Put_Line;

This way, I am certain that nobody will redefine Put_Line to do something else!

--
---------------------------------------------------------
           J-P. Rosen (rosen@adalog.fr)
Visit Adalog's web site at http://www.adalog.fr





  reply	other threads:[~2001-07-25 13:43 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-07-25 13:15 C++ virtual function mechanism in Ada ? Didier.pieroux
2001-07-25 13:43 ` Jean-Pierre Rosen [this message]
2001-07-25 16:10   ` Warren W. Gay VE3WWG
2001-07-25 15:49 ` Larry Kilgallen
2001-07-26  2:00   ` DuckE
2001-07-26  3:14     ` Larry Kilgallen
2001-07-26 10:44       ` Robert Dewar
2001-07-26 12:52         ` Dmitry A. Kazakov
2001-07-26 17:18           ` Ted Dennison
2001-07-28  2:10           ` Robert Dewar
2001-07-30  8:09             ` Dmitry Kazakov
2001-07-26 12:57         ` Larry Kilgallen
2001-07-26 10:38   ` Robert Dewar
2001-07-26 17:31     ` Warren W. Gay VE3WWG
replies disabled

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