comp.lang.ada
 help / color / mirror / Atom feed
From: "Robert I. Eachus" <rieachus@comcast.net>
Subject: Re: Question about OO programming in Ada
Date: Wed, 26 Nov 2003 12:58:52 -0500
Date: 2003-11-26T12:58:52-05:00	[thread overview]
Message-ID: <GpednY19wYJAdFmiRVn-gg@comcast.com> (raw)
In-Reply-To: <bq0qfs$3ja$1@online.de>

Ekkehard Morgenstern wrote:

> Ok. What I'd like to know is if I can view Ada record types as classes of
> objects.

Yes.  In Ada every type is a member of one or more classes of types. 
But for dispatching, read on.

> I.e. if I declare a record, like
> 
>         type My_Type1 is
>         record
>         Field1 : Integer;
>         Field2:  Integer;
>         end record;
> 
> Can I extend the record type like
> 
>         type My_Type2 is new My_Type with
>         record
>         Field3 : Integer;
>         Field4 : Integer;
>         end record;
> 
> such that the procedures declared to operate on type My_Type1 will operate
> on the My_Type1 fields of My_Type2.

To extend a record type, the parent type needs to be tagged.  Of course, 
the parent can be explicitly derived from a third tagged type, and so 
on.  The rule is that to be tagged, a non-derived type must include the 
reserved word tagged in its declaration, and a derived type must include 
a extension part, which may be null.

> I gathered from what I read that I need to declare My_Type1 as a tagged
> type.

See above.

> Now, I read that method dispatching (with static dispatching corresponding
> to method overloading in C++, and runtime dispatching corresponding to
> virtual methods in C++), would be possible only by using a class-wide type,
> such as My_Type1'Class in the first parameter of a procedure or in the
> return value of a function.

This may be correct for C++ but it is definitely not correct for Ada. 
In Ada, a primitive operation can be dispatched to if it has a parameter 
or return type that is a tagged type.  If there is more than one such 
parameter or result type, they must all resolve to the same specific 
type at the point of the call.  If you want to write a dispatching 
operation that takes more than one tagged operand, and of different 
specific types you have to select one such parameter as the one that you 
dispatch on.  The non-dispatching parameters or result type must be 
class wide.  Inside the operation you can further dispatch on the 
classwide arguments.

That is complex enough to deserve an example.  Let's say you want to 
write a registration system for your state's DMV.  For driver's 
licenses, you have an operand of type Person.  To allow for special 
processing in some cases, you decide to make this type tagged.  Since 
you don't want multiple copies of the same person in the system, let's 
make Person limited:

type Person is tagged limited private;

To deal with the complexities of registering truck, motorcycles, etc., 
you create a vehicle type:

type Vehicle is tagged limited private;

Of course, we need a registration type which will also have several 
variants, and you decide to make this type tagged as well:

type Registration is tagged limited private;

Since I made these limited, there will be a need for access types so 
that for example a registration can contain a vehicle reference and an 
owner reference:

type Person_Ref is access all Person'Class;
type Vehicle_Ref is access all Vehicle'Class;
type Registration_Ref is access all Registration'Class;

Whew!  Got all that out of the way.  Incidently, even though the _Ref 
types are needed in the actual record declarations, there is not 
necessarily a need for the user of the abstractions to be aware of the 
types.  My usual habit is to put them in the private part to start, and 
move them out later if necessary.

Now we can start to declare our registration function:

function Vehicle_Registration (Owner: in out Person;
                                The_Vehicle: in out Vehicle)
                         return Registration;

The compiler promptly slaps our hand.  You can't have a function that 
dispatches on three separate classes.  But in reality, that is not 
really what we wanted anyway.  Let me work this both forward and 
backward.  First, we don't really want to register Vehicles, we want to 
register cars, trucks, motorcycles, buses, boats, etc.  And there may be 
"generic" people, but the base types for Vehicles and Registrations 
should be abstract.

type Vehicle is abstract tagged limited private;
type Registration is abstract tagged limited private;

(It is sort of amazing to me that you often use one or two of abstract, 
tagged, limited, or private in a declaration, but if you need three, you 
usually end up adding the fourth.)

Now in our base package we can see that we want an abstract function for 
registration.  The type of registration returned will be determined 
within the specific function dispatched to, and any special cases 
involving people can be handled by dispatching within the actual 
function.  So what we really wanted was:

abstract function Register(Owner: in out Person'Class;
                            The_Vehicle: in out Vehicle)
                     return Registration'Class;

Perhaps when you finish, you will end up returning a Registration_Ref, 
but so far it isn't necessary to make that type public.  (Notice that 
you can always write Register(Someone, Car)'Access, if you really need 
to get an access value to assign to a Registration_Ref, or in some cases 
Register(Someone, Car)'Unchecked_Access.)

Later you will write (child) packages to deal with cars, trucks, buses, 
and so on.  In cars you will have:

function Register(Owner: in out Person'Class
                   The_Car: in out Automobile)
            return Registration'Class;

You know that you will always return an Automobile_Registration when 
called with an Automobile unless there is a separate 
Temporary_Automobile_Registration.  (You haven't decided on that issue 
yet.)  But AT THE POINT OF THE CALL to the abstract Register, it is not 
known at compile time what type of Registration will be returned.

There is a lot to learn in the above.  Notice that we have used 
classwide parameters or return values for two different reasons in the 
declaration of Register above.  The first occurs in Owner, where the 
owner may be any type derived from Person, and the Register function may 
not need to use internal dispatching on the parameter.  Or, for example, 
  registering a motorcycle may require that the Owner have a license to 
ride motorcycles.  This can be determined by nested dispatching, or more 
usually by "if Owner in Motorcycle_Driver'Class then..."

But the second instance, the use of a classwide result type is more 
interesting.  This is not to cause dispatching, but to actually return 
an object whose class is not known at compile time.  A typical usage is:

procedure Something (...) is
   The_Registration: Registration'Class
              := Register(The_Owner, The_Vehicle);
begin
   -- further operations on The_Registration, which may involve
   -- dispatching calls.
end Something;

> First of all, how do I accomplish returning a reference in Ada? What is the
> default behaviour of parameter passing and return in Ada? Someone said here
> that if I use an "in" parameter in a procedure or function definition, the
> object will be passed by reference. What about the returing of objects? Are
> they returned by copy or returned by reference?

Whether an object is passed by value, value-return, or reference depends 
on the object's class, not on whether the parameter is in, in out, or 
out.  In parameters can be referenced but not changed, in out parameters 
can be referenced and changed, and for out parameters the attributes are 
defined at the point of the call, but there may not be an initial value.

There are tricks you can play in Ada to modify in parameters, and of 
course, if you have an in access value, you may be able to modify the 
value designated. (The may is because the view may be limited.) In 
general, whether a parameter is in, in out, or out is decided to reflect 
the expectations that a user of the subprogram should expect.

> If I use a class-wide type, like My_Type1'Class, what kind of object is
> that? Is it similar to Java's "object.class"?

An object of a class-wide type always has a specific type.  It is just 
that the type may not be determined until the object is created at 
run-time.  Of course, if a class-wide object declaration is nested 
inside a subprogram, the type of the class-wide object--or any 
class-wide parameters--can change each time the subprogram is called.

> What's the difference between passing a class-wide type as an in/out
> parameter of a procedure, and passing an access to it?
> 
> i.e. the difference between
> 
>     procedure Proc( Param : in out Type'Class );
> 
> and
> 
>     type Type_Class_Access is access Type'Class;
>     procedure Proc( Param : in Type_Class_Access );

Not much.  Or rather, using the second form unnecessarily is a sign that 
you are new to Ada.  There are cases where the access parameter won't 
cause any additional difficulties, and other cases where it will mean 
that you need to explicitly free the storage to avoid memory leaks.

If you pass objects instead of pointers, you eliminate the potential 
memory leaks.  This doesn't mean you shouldn't use pointers (access 
types) where appropriate.  But in Ada a good rule is that you only use 
explicit access types to create data structures.  And since you normally 
use abstract data types to implement these structures, the notation is 
limited to the private parts and bodies of a few packages.  (And you 
probably chose those packages from a library instead of "rolling your 
own.")  But sometimes you need to create your own interlinked data 
structures, as in the example of Persons, Vehicles, and Registrations. 
In those cases, you can use different data structure packages, written 
as generics to implement some of the data structures you need.

Incidently, this is my problem with a set of data structure packages as 
a part of Ada.  There are several ways to implement these structures in 
Ada, and which one you want to use depends on intimate details of the 
problem you are trying to solve.  So a limited set of official solutions 
IMHO would be worse than the current situation.  (A core set of data 
structure packages in the standard, plus an indication that compiler 
vendors and others are encouraged to extend the set is a different story.)

> I think I know what run-time dynamic dispatching, class-wide types and
> derived types refer to, but I'm not sure about implicit type conversions
> that I can use.

Don't cheat yourself.  It is possible to subset Ada's support for 
object-oriented programming to match what you currently know about the 
subject from C++ or some other OO languages.  But the Ada model is much, 
much richer than that.  A lot of this richness involves information 
hiding.  In Ada you constantly find yourself asking two questions:

    What is the minimum required information to use this subprogram, 
data-type, abstraction, and so on?

and:

    Is there a different way to express this which allows more 
information hiding?

Why this emphasis on information hiding?  The converse of information 
hiding is coupling.  The more coupling you have, the more bugs you have, 
the bugs are harder to find, and fixing bugs is more likely to create 
additional bugs.  So maximizing information hiding is the easy way to 
bug free programming.

> Like, I'd want to do something similar like this:
> 
>     C := access A;
> 
> In C++, I can just write " C = &A" (if C is a pointer to B). How do I do
> that in Ada?

C := A'Access;

> I tried to use the pointer-like semantics of access to class-wide types, but
> I ended up with doing manual type conversions (or casting) all over. Is that
> normal?

Yes.  Ada is trying to tell you something.  In Ada the intent is that 
cleaner means better.  If you are converting back and forth all over the 
place, it means that your programming model is not well thought out. 
Note that if you really are programming in a scope where you need to do 
such conversions, you can declare your own conversion functions, or 
overload subprograms to take both types of operands:

    type Bar_Ref is access Bar;
    ...
    procedure Foo(in out Bar_Ref) is
    begin Foo(Bar'Access); end Foo;
    pragma Inline(Foo);

But again, if you want or need something like that, it is usually an 
indication of a design problem.

> I thought by using this newsgroup, I could be spared from reading in the Ada
> 95 Rationale. Which is very explicit, but also verbose, and I'd like to make
> my learning process quicker. ;-)

Quicker is possible, but tough.  We used to say that learning Ada was 5% 
learning syntax and 95% learning software engineering.  Then along came 
Ada 95.  It cleaned up a lot of special cases to make learning how to 
program in Ada easier, and added support for several powerful new 
programming paradigms.  My guess is that Ada 95 made learning the 
language somewhat easier, but tripled the amount of software engineering 
you need to know to use the whole language.  You can see that above, 
with delegation of dispatching, multiple dispatch, and returning 
classwide objects.  Those are just from the object-oriented extensions 
in Ada 95.

I could probably teach a one semester course on how to use child 
packages in Ada, and then follow it with a semester on generic package 
parameters in Ada, or a semester on real-time programming in Ada, or a 
semester on high integrity programming, oops make that at least two 
semesters!  I could also probably create a year long course on how to 
use access discriminants in Ada, but IMHO, maybe 30 people world-wide 
need to know all the tricks of using them.  (Maybe a course on when it 
looks like you need to use access discriminants, but don't?)

>               English is a foreign language to me, and I'd rather not
> read umpteen pages of Ada language theory when I can avoid it! ;-) )

Hmmm.  There should be some good Ada reference material auf Deutch, but 
I don't know what offhand.  (And at this point I'm not going to go back 
and rewrite the above. ;-)

-- 
                                           Robert I. Eachus

100% Ada, no bugs--the only way to create software.




  parent reply	other threads:[~2003-11-26 17:58 UTC|newest]

Thread overview: 109+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-11-25 19:04 Question about OO programming in Ada Ekkehard Morgenstern
2003-11-25 20:17 ` Randy Brukardt
2003-11-26  0:34   ` Ekkehard Morgenstern
2003-11-26  6:17     ` Vinzent 'Gadget' Hoefler
2003-11-26  9:29     ` Dmitry A. Kazakov
2003-11-26 15:54     ` Stephen Leake
2003-11-26 20:07       ` Randy Brukardt
2003-11-26 21:36         ` Stephen Leake
2003-11-26  8:56   ` Peter Hermann
2003-11-25 20:55 ` Martin Krischik
2003-11-26  0:22   ` Ekkehard Morgenstern
2003-11-26  1:00     ` Jeffrey Carter
2003-11-26 16:36     ` Martin Krischik
2003-11-26 18:09       ` Robert I. Eachus
2003-11-27 13:45         ` Jean-Pierre Rosen
2003-11-25 21:48 ` Stephen Leake
2003-11-26  0:01   ` Ekkehard Morgenstern
2003-11-26  1:16     ` Jeffrey Carter
2003-11-26 15:10     ` Georg Bauhaus
2003-11-26 15:48     ` Stephen Leake
2003-11-26 16:24       ` Hyman Rosen
2003-11-26 17:58     ` Robert I. Eachus [this message]
2003-11-27  2:10       ` Ekkehard Morgenstern
2003-11-27 10:15         ` Ludovic Brenta
2003-11-27 18:35         ` Jeffrey Carter
2003-11-28  4:35           ` Hyman Rosen
2003-11-28  7:28             ` Vinzent 'Gadget' Hoefler
2003-11-28  8:46               ` Dale Stanbrough
2003-11-28 10:16                 ` Vinzent 'Gadget' Hoefler
2003-12-01 15:57             ` Martin Krischik
2003-12-01 16:47               ` Hyman Rosen
2003-12-03 18:35                 ` Martin Krischik
2003-12-01 21:13               ` Jeffrey Carter
2003-12-02  8:47               ` Dmitry A. Kazakov
2003-12-03  9:29                 ` Pascal Obry
2003-12-03 11:26                   ` Dmitry A. Kazakov
2003-12-03 12:49                     ` Ludovic Brenta
2003-12-03 13:41                       ` Dmitry A. Kazakov
2003-12-03 14:11                         ` Ludovic Brenta
2003-12-03 14:45                           ` Dmitry A. Kazakov
2003-12-03 15:44                         ` Hyman Rosen
2003-12-03 16:11                           ` Dmitry A. Kazakov
2003-12-03 18:20                           ` David C. Hoos
     [not found]                           ` <28eb01c3b9ca$25b18870$b101a8c0@sy.com>
2003-12-03 18:35                             ` Hyman Rosen
2003-12-03 20:05                           ` Randy Brukardt
2003-12-03 20:57                             ` Hyman Rosen
2003-12-03 21:16                               ` Hyman Rosen
2003-12-03 22:04                           ` Pascal Obry
2003-12-03 22:34                             ` Hyman Rosen
2003-12-04  1:23                               ` Robert I. Eachus
2003-12-04  7:15                                 ` Hyman Rosen
2003-12-04 17:43                                   ` Warren W. Gay VE3WWG
2003-12-04  8:55                                 ` Dmitry A. Kazakov
2003-12-04 19:13                                   ` Randy Brukardt
2003-12-04 19:29                                     ` Hyman Rosen
2003-12-04 21:32                                   ` Robert I. Eachus
2003-12-05  8:43                                     ` Dmitry A. Kazakov
2003-11-27 22:12         ` Robert I. Eachus
2003-11-28  6:37           ` Simon Wright
2003-11-30  2:51             ` Robert I. Eachus
2003-12-06  7:48 ` Chad Bremmon
2003-12-06 13:33   ` Jeff C,
2003-12-06 22:44   ` Hyman Rosen
2003-12-07  3:02     ` Chad Bremmon
2003-12-07  7:53       ` Hyman Rosen
2003-12-07 15:34         ` James Rogers
2003-12-07 18:30           ` Martin Krischik
2003-12-07 20:25             ` James Rogers
2003-12-08  3:36               ` Hyman Rosen
2003-12-08  4:42                 ` Chad Bremmon
2003-12-08  8:42                   ` Hyman Rosen
2003-12-08  9:34                     ` Dmitry A. Kazakov
2003-12-08 13:25                       ` Hyman Rosen
2003-12-08 15:05                         ` Dmitry A. Kazakov
2003-12-09  4:38                           ` Hyman Rosen
2003-12-09  8:19                             ` Dmitry A. Kazakov
2003-12-09 13:29                               ` Hyman Rosen
2003-12-09 14:36                                 ` Dmitry A. Kazakov
2003-12-09 15:05                                   ` Hyman Rosen
2003-12-09 15:59                                     ` Dmitry A. Kazakov
2003-12-09 16:41                                       ` Hyman Rosen
2003-12-10 11:32                                         ` Dmitry A. Kazakov
2003-12-10 15:27                                           ` Hyman Rosen
2003-12-10 17:15                                             ` Dmitry A. Kazakov
2003-12-08 17:55                       ` Chad Bremmon
2003-12-08 23:09                         ` Hyman Rosen
2003-12-09  8:26                         ` Dmitry A. Kazakov
2003-12-08 19:33                       ` Martin Krischik
2003-12-09  4:41                         ` Hyman Rosen
2003-12-08 17:27                     ` Chad Bremmon
2003-12-08 18:44                       ` Georg Bauhaus
2003-12-08 19:27                         ` Martin Krischik
2003-12-08 19:36                         ` Chad Bremmon
2003-12-09  4:43                           ` Hyman Rosen
2003-12-08 23:23                       ` Hyman Rosen
2003-12-08 19:25                     ` Martin Krischik
2003-12-07 21:29           ` Peter C. Chapin
2003-12-08  3:44             ` Hyman Rosen
2003-12-08  3:46           ` Hyman Rosen
2003-12-08  5:54             ` James Rogers
2003-12-08  8:45               ` Hyman Rosen
2003-12-07 17:39         ` Chad Bremmon
2003-12-08 23:39           ` Hyman Rosen
2003-12-09  2:36             ` Chad Bremmon
2003-12-09  4:52               ` Hyman Rosen
2003-12-09 11:24               ` Georg Bauhaus
2003-12-09 18:42                 ` Chad Bremmon
2003-12-09 20:11                   ` Hyman Rosen
2003-12-08 23:40           ` Hyman Rosen
replies disabled

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