From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.3 required=5.0 tests=BAYES_00,INVALID_MSGID autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,2e589dbfe40b0d25,start X-Google-Attributes: gid103376,public From: "Matthew Heaney" Subject: Re: Deallocating an object referenced via a classwide access type. Date: 2000/01/10 Message-ID: <387aa062_4@news1.prserv.net>#1/1 X-Deja-AN: 571006121 Content-transfer-encoding: 7bit References: Content-Type: text/plain; charset="US-ASCII" X-Complaints-To: abuse@prserv.net X-Trace: 11 Jan 2000 03:15:46 GMT, 32.101.8.14 Organization: Global Network Services - Remote Access Mail & News Services Mime-version: 1.0 Newsgroups: comp.lang.ada Date: 2000-01-10T00:00:00+00:00 List-Id: In article , 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. 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 := ; 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 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; 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 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. 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