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.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,772ae8afc5db35f2 X-Google-Attributes: gid103376,public From: Matthew Heaney Subject: Re: Can't export object of private type Date: 1999/03/08 Message-ID: X-Deja-AN: 452498615 Sender: matt@mheaney.ni.net References: NNTP-Posting-Date: Mon, 08 Mar 1999 00:48:44 PDT Newsgroups: comp.lang.ada Date: 1999-03-08T00:00:00+00:00 List-Id: nospam@thanks.com.au (Don Harrison) writes: > Here is my test program based on your solution: > > > package T is > > type T_Type (<>) is abstract tagged private; > type Access_Class_T_Type is access all T_Type'Class; > > procedure Display (T: access T_Type) is abstract; > function Solo_T return Access_Class_T_Type is abstract; I don't understand why you declared function Solo_T as abstract. It is not a primitive type for T_Type. Even if it weren't abstract, why do you need it there in the root package? All the (singleton) instances live in child packages. > > package T.Child is > > type T_Child_Type (<>) is new T_Type with private; > type Access_Class_T_Child_Type is access all T_Child_Type'Class; > > procedure Display (TC: access T_Child_Type); > function Solo_T return Access_Class_T_Child_Type; Why does Solo_T return a pointer to a class-wide type? You want to have a function that returns a pointer to the _specific_ type, because you're pointing to an object of that type. There's no need for dispatching, so return the specific type: type T_Child_Access is access all T_Child_Type; function Solo_T return T_Child_Access; > with T; use T; > with T.Child; use T.Child; > with T.Other_Child; use T.Other_Child; > procedure Use_T is > > type T_Index_Type is new Integer range 1 .. 2; > type T_Array_Type is array (T_Index_Type) of Access_Class_T_Type; > > T_Array: T_Array_Type := ( > 1 => T.Child.Solo_T.all'Access, -- 1) > 2 => T.OTher_Child.Solo_T.all'Access); -- 1) > > begin > for I in T_Array'Range loop > Display (T_Array(I) ); -- 2) > end loop; > end; > > > 1) Can this ugly stuff be avoided? Yes. What you did here was to declare some data (an array of pointers to singletons) outside of any object, and perform an operation on that data (display each object). This isn't in the spirit of the object-oriented paradigm, in which data is encapsulated by an object, and you send the object a message to operate in that data. Your implementation is just the opposite, and the symptom is "ugly stuff." A better way to think of this problem is that you have a (singleton) collection object containing (pointers to) all the singleton instances in your type hierarchy. This super-object has a passive iterator to let you visit the items in the collection. Now, here's where we get back to our state-machine paradigm: the subsystem rooted at package T _is_ the collection object. You'll want to have a passive iterator to visit the items, something like this: package T is type T_Type (<>) is abstract tagged limited private; ... generic procedure Process (Item : access T_Type'Class; Done : in out Boolean); procedure For_Every_Item; end T; There is no more ugly stuff, because it's hidden behind the wall of the subsystem. To display all the singleton instances, just do this: procedure Display_Item (Item : access T_Type'Class; Done : in out Boolean) is begin Display (Item); -- dispatches end; procedure Display_Items is For_Every_Item (Display_Item); And so now you just call Display_Items. You the client don't have the array or even the loop. So how we implement this? What we do is declare a "register" operation in the private region of the root package, so that it's visible to child packages, but not to clients. During its elaboration, each child package registers its singleton instance with root package, which stores the pointer in an array inside the body of the root package. Something like: package T is pragma Elaborate_Body; ... private procedure Register (Item : in T_Class_Access); end T; package body T is Max : constant := 20; -- adjust during maintenance subtype Item_Array_Range is Positive range 1 .. Max; type Item_Array is array (Item_Array_Range) of T_Class_Access; Items : Item_Array; Last : Natural := 0; procedure Register (Item : in T_Class_Access) is begin Last := Item_Array_Range'(Last + 1); Items (Last) := Item; end; end T; Each time Register is called, it increments the Last index, and inserts the item into the array. (You don't have to worry about synchronization issues, because elaboration is linear.) So the passive iterator would be implemented as: procedure For_Every_Item is Done : Boolean := False; begin for I in Positive range 1 .. Last loop Process (Items (I), Done); exit when Done; end loop; end For_Every_Item; Each child package would be implemented something like: package body T.Child is ... begin Register (Solo); -- (may need type conversion) end T.Child; Be sure you use pragma Elaborate_Body everywhere. The root package holds the collection of singletons, and the collection itself gets populated (automatically) by calls from child packages. The client then just visits members in the collection object, which here happens to be implemented as a state machine. No more ugliness. > :We have used the features the language provides us to design a type > :hierarchy such that, the client must use the singleton instance(s). He > :has no other choice in the matter, which is the intended effect. > > That's what I'm after. One of the "features the language provides us" is the package, a tool for the organization of name-space. If you insist on only using objects-that-are-instances-of-a-type everywhere, you're limiting your range of potential solutions, and "ugly stuff" results. By using a package as a state machine, the ugly stuff is hidden. Which is the ultimate goal, really. Use all the name-space management tools the language offers.