comp.lang.ada
 help / color / mirror / Atom feed
From: Adam Beneschan <adam@irvine.com>
Subject: Re: Pointers explained?
Date: Mon, 30 Jul 2007 12:36:35 -0700
Date: 2007-07-30T12:36:35-07:00	[thread overview]
Message-ID: <1185824195.711745.136860@i13g2000prf.googlegroups.com> (raw)
In-Reply-To: <1185818189.689914.159900@x40g2000prg.googlegroups.com>

On Jul 30, 10:56 am, shaunpatter...@gmail.com wrote:

> Hi
>
>    I'm still relatively new to Ada -- coming from a strong C++
> background. Some of this may seem stupid - and feel free to point it
> out to me.

I don't have a strong C++ background, so some of my response may seem
stupid.

> I am using Ada 95 - GNAT Pro 3.16a1
>
> I have a message factory I've been screwing around with (converting
> it
> from Ada to C++).
>
> I basically have an abstract message type:
>
> type Message is abstract tagged null record;
> type Message_Class is access all Message'Class;
>
> then all other messages are derived from this.
>
> Now from my factory create method, I'm returning a Message_Class.
> This message_class
> is stored in another record to be handled again later:
>
> type CallbackEvent is
> record
>      msg : Message_Class;
>      ...
> end record;
>
> Now my basic problem -- or not really a problem -- is that to create
> the Message_Class I have to use
> "new" and allocate and deallocate memory.
>
> Now - my solution was to re-write my message factory to return a type
> Message'Class.  However, I went
> to change my callback structure to:
>
> type CallbackEvent is
> record
>     msg : Message'Class
> end record;
>
> and I get the message "unconstrained subtype in component declaration"
>
> So, I'm assuming the compiler just doesn't know how to big this
> message is.

That's pretty much correct.  Technically, "Message'Class" is an
"indefinite subtype"; other examples of indefinite subtypes would be
"string", which is an unconstrained array that you haven't given the
bounds for.  You can't have a variable or component declaration that
looks like

   X : String;

The reason is basically the same, though: the compiler wouldn't know
how much to allocate.  Of course the compiler can't know how big a
Message'Class is, because the object could be *any* type that you
derived from Message, including a type in a package that you haven't
even written yet.

> that's why I believe i should use a pointer (the Message_Class type)

I think you're right.  Others will probably point out how the fact
that Message_Class is a pointer ought to be hidden from users of your
Message package.  But at some point, a pointer will likely be needed.

> However,
> I cannot find a way of converting between a Message'Class to a
> Message_Class;
>
> It appears that the only way to get a Message_Class is by some where
> in the
> code using a "new"
>
> Is this assumption correct?

Not quite.  The other thing you can do is use 'Access on a variable
whose type in Message'Class, if the variable is declared "aliased":

   Z : aliased Message_1;

now Z'Access gives you a "pointer" to Z, which can used in a context
where a Message_Class is expected.  (Sort of like the & operator in C+
+.)  But this is unlikely to help you much.  It won't work to declare
Z as a local variable in your factory function---Ada won't let you do
that, and it shouldn't, because the memory used for Z will be on the
stack, and the stack will disappear when your factory function exits
and will be overwritten by the next subroutine you call.  C++ will of
course let you do that, with the likely result that any machine that
runs the program will end up screwing itself up and probably become
part of a botnet that will fill MY inbox with messages about fake
Rolexes and Canadian drugs and web sites that tell me how I can
enlarge my ... um ... bowling score.  So I'd definitely prefer that
you use Ada.

Anyway, there may be some cases where returning an access to a global
variable may be appropriate, but those are probably not the norm.  So
some sort of dynamic allocation (via "new") will likely be necessary,
at some point.

A couple things I might point out: First, if you have a factory
function [say Create_Message] that returns Message'Class (i.e. *not* a
pointer), you can declare variables of type Message'Class as long as
you use the function (or some other function call, maybe) as an
initializer:

    Var1 : Message'Class := Create_Message (...);

When you do this, Create_Message can return any type derived from
Message.  At that point, the program will determine how much space is
needed for Var1 and allocate it.  Var1's type can't be changed after
that, though; if Create_Message returns a Message_3, Var1's type will
be fixed at Message_3, and you can't, say, assign

    Var1 := Y;

unless Y also happens to have type Message_3.  (If it's the wrong
type, a Constraint_Error exception will be raised.)  This applies to
record components also; by default, the function will be called to set
up the component when any variable of that record type is created.
The variable will be allocated dynamically, but you won't need a "new"
to allocate it and you won't have to worry about deallocation---that
will all be taken care of automatically.

Second, if there's some place in the program where you *know* what
type that factory function will return, you can take advantage of
that, e.g.:

   type Record_With_Message_4 is record
       Msg : Message_4;
   end record;

   R : Record_With_Message_4;
   ...

   R.Msg := Create_Message (...);

Now the code will call Create_Message but then check to make sure the
type returned is actually Message_4.  If it isn't, a Constraint_Error
exception will be raised.  If it is, the message will be set up, and
Msg will probably not be dynamically allocated.

Anyway, those are a couple cases where you can use your factory
function without "new".  Because of that, I'd recommend writing an
overloaded function:

   function Create_Message (...) return Message'Class;
   function Create_Message (...) return Message_Class;

where the body of the second function would be

   function Create_Message (...) return Message_Class is
   begin
      return new Message'Class (Create_Message (...));
   end;

or something to that effect.  The compiler will be able to tell from
context which Create_Message, the one that returns the Message'Class
or the one that returns the pointer, is being called, unless the
context is ambiguous.

Anyway, I'm sure others have some other ideas they can give you.  In
places where "new" will be necessary somewhere, you may want to look
into controlled types to get the deallocation to be done
automatically.  But this message is already real long, so I'll let
others enlighten you with the details of that.

Hope at least some of this is helpful,

                    -- Adam





  parent reply	other threads:[~2007-07-30 19:36 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-07-30 17:53 Pointers explained? shaunpatterson
2007-07-30 17:56 ` shaunpatterson
2007-07-30 19:04   ` Ed Falis
2007-07-30 19:05   ` Ludovic Brenta
2007-07-30 19:36   ` Adam Beneschan [this message]
2007-07-30 21:23     ` Maciej Sobczak
2007-07-30 22:36       ` Adam Beneschan
2007-07-31  7:33         ` Dmitry A. Kazakov
2007-07-31  1:15       ` Anh Vo
2007-07-30 22:20     ` Adam Beneschan
2007-07-30 21:31   ` Jeffrey R. Carter
2007-07-31 17:21   ` Simon Wright
2007-07-31 18:06     ` Dmitry A. Kazakov
2007-07-31 22:38       ` shaunpatterson
2007-08-01  7:53         ` Martin Krischik
2007-08-01  8:21         ` Dmitry A. Kazakov
2007-08-10 19:52         ` Simon Wright
2007-07-30 20:11 ` Dmitry A. Kazakov
2007-07-31  8:28 ` Martin Krischik
  -- strict thread matches above, loose matches on Subject: below --
2007-07-30 17:53 shaunpatterson
replies disabled

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