comp.lang.ada
 help / color / mirror / Atom feed
From: mheaney@ni.net (Matthew Heaney)
Subject: Re: about abstract data types and pointer
Date: 1996/09/24
Date: 1996-09-24T00:00:00+00:00	[thread overview]
Message-ID: <mheaney-ya023180002409962311140001@news.ni.net> (raw)
In-Reply-To: R.3244B03D.32EA@er.uqam.ca


In article <R.3244B03D.32EA@er.uqam.ca>, jk791840@er.uqam.ca wrote:

>Reposting article removed by rogue canceller.
>
>Hi! I'm new with ADA but i'm a programmer since a long time.

Ada is not an acronym, thus you need not capitalize its name.  The language
was named in honor of Ada Loveless, the assistant of Charles Babbage, and
the world's first programmer.

>
>I use the ADA83 standard (but with the gnat compiler than can compile
>ADA95).
>
>I have some questions to start with:
>
>1- Is there a way to define a procedure type like in MODULA-2 in
>   a way such:
>
>     TYPE PROC IS PROCEDURE(IN E:ELEMENT);
>
>   Or like in C where we can define a pointer to such a fonction?
>   (how can i define an access type in ADA that "points" to such
>   a procedure?)

(Note that my Ada 95 syntax is a little shaky...)

   type Procedure_Access is access procedure (E : in Element);

For example,

   procedure Process (The_Element : in Element);

   PA : Procedure_Access := Process'Access;

To invoke procedure Process through the access object:
 
   E : Element := ...;
   PA (E);


>   This would be very helpful in the case of a list type (for example),
>   having an operation defined like that:
>
>     PROCEDURE Map(L: IN List; P: PROC);
>
>   Which map the P Procedure to each element of the list. P would
>   obviously cause a side effect (like printing an element).
>
>   OR can I define procedure Map something like that:
>
>     PROCEDURE Map(L: IN List; P: PROCEDURE(IN ELEMENT));
>
>   Please tell me how i can call the p procedure passed as a parameter
>   to Map in the code; maybe something like:


   type List is private;

   type Procedure_Access is access procedure (E  : in Element);

   procedure Map (L : in List; P : in Procedure_Access);

>
>     P(E);

That is how you call the actual argument P, in fact.

>   Please don't suggest me to use a generic proc, because it's NOT
>   what i want; Map could be called for MANY procedures, not just one
>   for each instance of a List. example:
>
>     Map(L, Put);    -- Print each element on stdout.
>     Map(L, CountE); -- Count the number of elements (side effect).
>     Map(L, Draw);   -- Draw the value of L on a graph.
>
>   Of course; Draw and Put have the side effect of "writing".

   procedure Put (E : Element);
   procedure CountE (E: Element);
   procedure Draw (E : Element);

   L : List;

   Map (L, Put'Access);
   Map (L, CountE'Access);
   Map (L, Draw'Access);

>
>   Count could be written like this:
>
>   FUNCTION Count(L: IN List) RETURN Natural IS
>     Nb : Natural := 0;
>     PROCEDURE CountE IS
>     BEGIN
>       Nb := Nb + 1;
>     END CountE;
>   BEGIN
>     Map(L, CountE);
>     RETURN Nb;
>   END Count;

I'm not sure about the exact legality rules for doing this.

Either

Map (L, CountE'Access);

is allowed, or if not, then

Map (L, Count'Unchecked_Access);

for use when you know something about the lifetime of the subprogram you're
accessing that the compiler doesn't.


>2- Is there a way to totally hide the representation of an abstract
>   data type in the specification package? I really don't understand
>   why we should specify the representation of such a type in the
>   PRIVATE part of the specification package (except for compiler
>   needs -- that is simpler for the compiler :-) ). Even in C we
>   can hide the representation of an abstract data type from the 
>   header file. Why not in ADA which is supposed to be better for 
>   the information hiding principle?

Well, I'm not sure what you mean there about C.  For example, in C I might do

   typedef struct StackRep* Stack;

to declare a _pointer_ to stack in the header, and hide the representation
of its structure in the body.  That's a bit different from what you said,
though.

And yes, you can so the same thing in Ada:

package Stacks is

   type Stack is private;
   ...
private

   type Stack_Representation;
   type Stack is access Stack_Representation;

end Stacks;

This allows one to defer the implementation of the Stack_Representation to
the package body.

However, this is an probably an improper use of access types.  One should
use access types because the abstraction itself requires reference rather
than value semantics.

To implement an abstract data type as an access type to _really_ hide the
representation is a not such a great idea (at least in Ada 83), because of
all the attendant problems one has with  memory leaks.

And no, putting the representation in the private region of the
specification does *not* violate information hiding (nor does it violate
information hiding in C++ or Eiffel or...).  Information hiding does not
mean where the full view of the type appears (I could just read the body in
that case), but rather how clients are able to manipulate the abstraction.


>3- Is there a way to "add" or change attributes to a new type that i 
>   have elaborated as an abstract data type (in a package).
>
>   Ex: L'First could have another meaning here if L is a List.
>       List'Nb could denote the number of elements of a List L.

None that I know of (though I think it's a good idea).  The only attributes
I think you can change in Ada 95 are the T'Input and T'Output, for use with
Streams_IO.

>
>   I know i can (and sure i must) code these as functions but i just
>   want to use them as attribute; which could be much more natural here.
>
>   Is that what RENAMES is for?

A great technique that should be in every Ada programmer's arsenal!  It's
Ada's alias feature.  For example, I can rename elements of a record if the
dot notation gets out of hand:

package Bounded_Stacks is

   type Stack (Max_Depth : Positive) is limited private;

   procedure Push (The_Item : in Item; On : in out Stack);

private

   type Item_Array  is array (Positive range <>) of Item;

   type Stack (Max_Depth : Positive) is
      record
         The_Items : Item_Array (1 .. Max_Depth);
         The_Depth : Natural := 0;
      end record;

end Bounded_Stacks;

...

   procedure Push (The_Item : in Item; On : in out Stack) is

      The_Stack : Stack renames On;   -- gives The_Stack a better name than "On"

      The_Depth : Natural renames The_Stack.The_Depth;
   begin
      The_Depth := The_Depth + 1;
      The_Stack.The_Items (The_Depth) := The_Item;
   end;

compare that to this

   procedure Push (The_Item : in Item; On : in out  Stack) is
   begin
      On.The_Depth := On.The_Depth + 1;
      On.The_Items (On.The_Depth) := The_Item;
   end;

So you can rename objects.  But you can also rename subprograms

   with Bounded_Stacks;
   package P is

      function Push (The_Item : in Item;  On : in out Stack)
      renames Bounded_Stacks.Push;

and rename exceptions (or any other object, really)

   with IO_Exceptions;
   package Text_IO is

      type File_Type is limited private;
      ...
      Use_Error : exception renames IO_Exceptions.Use_Error;
   end;

You can even rename a task entry as a procedure:

   task T is
      entry E (The_Item : in Item);
   end T;

   procedure Put (The_Item : in Item) renames T.E;

You  can even rename an array slice as an object:

   procedure P (S : String) is
      O : String renames S (1..4);

Cool, huh?


>4- Is there a way to overload the ":=" operator if List is a
>   limited private type?
>
>   ex:
>
>       L1 := L2;
>
>   could make a copy of all elements of L1 into L2. This is much more
>   natural than
>
>       Copy(L1, L2);

Technically, the assignment operator ":=" isn't really an operator (I don't
know the exact reasons, you'll have to ask the language theory gods...). 
You can't "add" an "assignment operator" to a type that is already declared
as limited private, but you can get control of assignment if your type is
(non-limited) private by deriving from type Finalization.Controlled:

   with Finalization;
   package Lists is
  
      type List is private;
      ...
   private

      type Node;
      type Node_Access is access Node;

      type List is new Finalization.Controlled with
          record
              Head : Node_Access;
         end record;

   end Lists;

That way 

   L1 := L2;

can have value (copy) semantics rather than reference semantics.


>
>5- Do the ADA83 standard specify that the deallocation of memory is
>   automatically done? Or must I ensure that my program does it?

You must make sure no memory leaks occur.  There are pragmatic reasons for
not mandating that Ada herself take care of the clean-up, specifically that
the use of a garbage collector can cause unpredictable run-time behavior.

But you don't have to worry about memory leaks if you inherit from
Finalization.Controlled, which will invoke a subprogram to finalize the
object (reclaim its storage, in this case) when its lifetime ends.

>
>   ex:
>   
>   PROCEDURE Test IS
>     L: List;
>   BEGIN
>     Add(L, 3);
>     Add(L, 4);
>   END Test;
>
>   If L is a pointer to some NEW element created; will it be freed
>   automaticaly on return of the PROCEDURE Test? Or must i provide
>   an operation to free memory (like Dispose)?

If List is implemented as inheriting from Controlled, then all is well, and
you (the client of L) don't have to free the memory yourself.

>   The next problem is that probably i would have allocated some extra
>   memory for the elements of L; will it be also freed automaticaly?
>   Some language do it (SML, SMALLTALK), some don't (MODULA-2, C, C++)
>
>   However, we could define a destructor in C++; is it possible in ADA
>   that the destructor would be automatticaly called when the
>   object fall out of scope?

The "deconstructor" is a primitive operation (called Finalize) of types
that inherit from Controlled,  that gets invoked automatically when the
scope of an object closes.


>6- Now the easy one.
>   Is there a function or procedure to read a character on the
>   keyboard without the need to press enter WHICH IS DEFINED ON THE
>   STANDARD (independant of the used compiler and machine).

Yes.  It's called Text_IO.Get_Immediate.

>I know that's a lot of questions to answer but that would be VERY
>appreciated.
>
>Please reply by mail. Thank you.

Your very welcome!  Keep the posts a-comin'!

>Benoit Rochefort
>jk791840@er.uqam.ca

Matt

--------------------------------------------------------------------
Matthew Heaney
Software Development Consultant
mheaney@ni.net
(818) 985-1271




  reply	other threads:[~1996-09-24  0:00 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1996-09-21  0:00 about abstract data types and pointer Benoit Rochefort
1996-09-24  0:00 ` Matthew Heaney [this message]
1996-09-24  0:00 ` Norman H. Cohen
1996-09-24  0:00 ` Stephen Leake
  -- strict thread matches above, loose matches on Subject: below --
1996-09-21  0:00 Benoit Rochefort
replies disabled

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