Maxim Reznik wrote: > Randy Brukardt wrote: > [skip] > > > > > But the *really* best solution is an OOP-based solution. This is extensible > > and type-safe and doesn't require unsafe compares and doesn't even need > > (visible) access types. Admittedly, there is a bit more work up front, but I > > think access types should avoided unless there is really dynamic allocation > > (which can't happen with subprograms in Ada). One of the really nice things > > about Ada 95 is that you can do useful OOP without using any access types. > > > > Randy Brukardt. > > > > Could you explain please how to avoid visible access type? > I tought we should write something like > > package Subscriber is > > type Listener is abstract tagged null record; > procedure Action (L : Listener) is abstract; > > type Listener_Ptr is access all Listener'Class; > > procedure Register (P : Listener_Ptr); > procedure Unregister (P : Listener_Ptr); > > end Subscriber; I would use something like: with Ada.Finalization; package Subscriber is type Listener is abstract tagged limited private; procedure Action (L : Listener) is abstract; procedure Register (L : in out Listener'Class); procedure Unregister (P : in out Listener'Class); private type Listener_Ptr is access all Listener'Class; type Listener is abstract new Ada.Finalization.Limited_Controlled with record Next_Listener : Listener_Ptr; end record; procedure Finalize (L : in out Listener); end Subscriber; Then the body would be something like: package body Subscriber is Subscriber_List : Listener_Ptr; procedure Register (L : in out Listener'Class) is begin -- Possibly check for already registered here. L.Next_Listener := Subscriber_List; Subscriber_List := L'Unchecked_Access; end Register; procedure Unregister (P : in out Listener'Class) is ... remove P'Unchecked_Access from the list. No deallocation ... is needed, that is the caller's responsibility. procedure Finalize (P : in out Listener) is ... remove P'Unchecked_Access from the list. end Subscriber; This is limited so that we know for sure that the registered and unregistered objects are the same (thus we can compare the access values, which *is* guaranteed for objects). This is a controlled type so it isn't possible to have an Action pointing at something that no longer exists. That has the nice property of allowing action (objects) to be declared in nested scopes, and have them automatically deregistered when they go out of scope. Given that this has to be a controlled type to be safe, we could use Initialize to register it, and Finalize to unregister it. That would look something like: with Ada.Finalization; package Subscriber is type Listener is abstract tagged limited private; -- Declaring an object of a type derived from Listener registers -- it, letting it finalize deregisters it. procedure Action (L : in out Listener) is abstract; private type Listener_Ptr is access all Listener'Class; type Listener is abstract new Ada.Finalization.Limited_Controlled with record Next_Listener : Listener_Ptr; end record; procedure Initialize (L : in out Listener); procedure Finalize (L : in out Listener); end Subscriber; Then you can use objects declared in blocks and subprograms to control the lifetime. But that might be too confining in some systems. To go back to the original question of this thread, the big advantage of the OOP solution is the ability to have state data packaged along with the callback. A smaller advantage is to eliminate the possibility of using something that is no longer meaningful. To see this, consider using this mechanism to do something on windows. You need somewhere to hold onto the window object that you're going to act on. This could look something like: with Claw, Subscriber; package Window_Actions is type Window_Action_Type is new Subscriber.Listener with record My_Window : Claw.Any_Window_Access_Type; end record; procedure Action (Item : in out Window_Action_Type); end Window_Actions; In this case, Action simply has to look at the data object in order to find what to act on. No unsafe global variables needed. And then this can used as a component in a window object, so we don't have to worry about lifetime issues. (We could have made My_Window an access discriminant if that was the primary intended use of this package.) type App_Window is new Claw.Frame_Window.Frame_Window_Type with record Action : Window_Action_Type; end record; When initializing App_Window, we only have to set Action.My_Window: App : App_Window; App.Action.My_Window := App'Unchecked_Access; (This could be done automatically if My_Window was an access discriminant.) I hope it is obvious why the O-O solution should be preferred over the unstructured, direct use of 'Access one. Randy Brukardt.