comp.lang.ada
 help / color / mirror / Atom feed
From: "Matthew Heaney" <matthew_heaney@acm.org>
Subject: Re: Deallocating an object referenced via a classwide access type.
Date: 2000/01/10
Date: 2000-01-10T00:00:00+00:00	[thread overview]
Message-ID: <387aa062_4@news1.prserv.net> (raw)
In-Reply-To: slrn87l2rt.15e.aidan@skinner.demon.co.uk

In article <slrn87l2rt.15e.aidan@skinner.demon.co.uk> , 
aidan@skinner.demon.co.uk (Aidan Skinner) wrote:

> with Ada.Finalization;
> package Foo_Package is
>
>   type Foo is new Ada.Finalization.Controlled with private;
>
>   type Foo_Access is access all Foo'Class;
>
>   type New_Foo is new Foo with private;
>
> private
>
>   type Foo is new Ada.Finalization.Controlled with record
>      My_Thing : Foo_Access := null;
>   end Foo;
>
>   type New_Foo is new Foo with null record;
>
>   procedure Deallocate (Object : in out Foo);
>
> end Foo_Package;
>
> How do I deallocate My_Thing?
>
> I can't use Ada.Unchecked_Deallocation, since My_Thing might be a Foo
> or it might be a New_Foo.
>
> A quick search through the RM and various text books has proven
> fruitless. :((

I show how to do this in the design patterns archive.  Do a search for
"free" and see what drops out.

<http://www.acm.org/archives/patterns.html>

Method #1

Yes, you can deallocate an object, designated by an access-to-classwide
access object, by using an instantiation of Unchecked_Deallocation.
(Note carefully that in the declaration of UC, formal type Object is
indefinite, which means a class-wide type is acceptable.)


  type T is abstract tagged null record;

  type TA is access all T;

  procedure Free is
    Unchecked_Deallocation (T, TA);

  O : TA := <some object in T'Class>;
declare
  Free (O);  -- yes, this is perfectly legal


Method #2

However, realize that the RTS is going to have to carry around a lot of
baggage whenever you declare an access type that designated a class-wide
type.  It may be better to simply assume this responsibility yourself,
by using a template method.

The basic idea is to declare a private operation (similar to what you
have in your example) that does the actual deallocation of the specific
type, and to call this operation by dispatching on the tag of the object
whose type is class-wide.  Like this:

package P is

  type T (<>) is abstract tagged limited null record;  --0

  type TA is access all T'Class;
  for TA'Storage_Size use 0;  -- avoid RTS baggage

  <primitive ops for types in T'Class>

  procedure Free (O : in out TA);     --1

private

  procedure Do_Free (O : access T);  --2, 3

end P;


Points about above spec:

0) Declare the type (class) as limited and indefinite.  This prevents
clients from declaring their own instances, and forces them to create
instances by using a constructor provided by each specific type in the
class.


1) Free is a "template method," otherwise known as a "class-wide
operation."  It takes an access object, which designates a class-wide
type, and deallocates the object (by calling Do_Free -- see body below).


2) Do_Free is a "hook method."  It is overridden by each specific type
in the class, and deallocates the object according to the storage
management policy specific to that type.

3) No, this can't be declared as abstract, because it's private.  So we
declare it as non-abstract, and give it a null body.


package body P is

  procedure Do_Free (O : access T) is
  begin
    null;
  end;

  procedure Free (O : in out TA) is
  begin
    if O /= null then
      Do_Free (O);      -- dispatch according to O's tag
      O := null;
    end if;
  end;

end P;


package P.C is

  type NT is new T with private;

  <override primitive ops>

  function New_NT return TA;     --1

private

  type NT is new T with record ...;

  procedure Do_Free (O : access NT);  --2

end P.C;


Comments about above spec:

1) Function New_NT is the constructor for specific type NT.  A client
*must* call this function in order to create a new instance.  The
abstraction (here, P.C.NT) is free to create and delete instances using
whatever storage management policy is appropriate for the type.


2) Do_Free is the hook method.  It can be implemented as an
instantiation of UC, or whatever.


package body P.C is

  type NTA is access all NT;  -- pointer to specific type

  procedure Deallocate is
    new Unchecked_Deallocation (NT, NTA);

  procedure Do_Free (O : access NT) is

    OA : NTA := NTA (O);
  begin
    Deallocate (OA);
  end;

  function New_NT return TA is

    O : constant NTA := NTA'(new NT);
  begin
    <init O>
    return TA (O);   -- or O.all'Access?
  end;

end P.C;



One more point: you seem to be trying to implement a smart pointer in
your example.  If you are, then you can read the Smart Pointers post in
the design patterns archive.  Search for "smart pointer" in the title.

<http://www.acm.org/archives/patterns.html>

Basically, instead of constructors returning an access object directly,
they return a "handle", which is a private type, implemented as a
private derivation of Controlled, and which contains an access object.
Clients don't actually manipulate access objects directly, only handles.

Send me some email if any of this is unclear.

Matt
<mailto:matthew_heaney@acm.org>




  reply	other threads:[~2000-01-10  0:00 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2000-01-11  0:00 Deallocating an object referenced via a classwide access type Aidan Skinner
2000-01-10  0:00 ` Matthew Heaney [this message]
2000-01-11  0:00   ` Matthew Heaney
2000-01-11  0:00 ` Tucker Taft
2000-01-11  0:00 ` Laurent Guerby
replies disabled

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