Print

Print


Robert I. Eachus ([log in to unmask]) wrote:

>Heath White ([log in to unmask]) said:
>
>> One of the main headaches this causes occurs when you already have
>> a tagged hierarchy of types. You want to derive off from it, but
>> you want the new type to have controlled behavior. There is no
>> good way to do this.
>
> Of course there is, but in part it is non-intuitive ...
>
> If you are adding a component that needs to be controlled, the
> controlledness is a property of the component, and the language
> supports that correctly. It has to, since you might not be aware
> that the component you are adding to a non-controlled class is
> controlled.
>
> The more "troubling" case is when the addition of a component
> creates a need to finalize the containing structure. Access
> discriminants allow the component to see the containing structure.
>
> I keep meaning to create an abstraction which deals with the most
> common sort of such extensions separate from other "building block"
> tagged components. It is less than twenty lines long -- but
> choosing some of those lines correctly takes thought. Hmmm...
>
> This version has the side effect of making the extended type
> limited. The body for a non-limited version is much more complex
> because you have to use a record with an access value as a non-
> discriminant, then track assignment and copying using Initialize
> and Adjust. (Left as an exercise for the reader.)
>
> There is however one "can't be avoided" problem. The order in which
> the finalization of two extensions will be called is determined in
> part by the subclassing order (see RM 7.6.1(9)) and in part by
> whether or not there is an access discriminant. If you have too
> many components trying to finalize things, the ordering can get to
> be a problem.
>
>                                       Robert I. Eachus
>
> with Standard_Disclaimer;
> use  Standard_Disclaimer;
> function Message (Text: in Clever_Ideas) return Better_Ideas is...
>
>with Ada.Finalization;
>
>generic
>
>    type Uncontrolled (<>) is abstract tagged limited private;
>
>    with procedure Finalize (Object : in out Uncontrolled);
>
>package Add_Finalization is
>
>    type Controlled is new Uncontrolled with private;
>
>private
>
>    type Component (Parent : access Controlled) is new
>       Ada.Finalization.Limited_Controlled with null record;
>
>    type Controlled is new Uncontrolled with record
>      Controller: Component (Controlled'Access);
>    end record;
>
>    procedure Finalize (Object : in out Component);
>
>end Add_Finalization;

Now I've really tried hard to find a solution for the unlimited type, but
the only one I found is an ugly solution to the problem.
Using a global pointer to intermediately store the access value defeats the
idea to keep everything hidden inside the object.
What is more: In a multi-tasking environment, this cannot safely work!

But the big problem is: How to get the access value of the object just
finalized before into Adjust?

See the remarks in the AARM 7.6 (17a..e). Adjust just knows NOTHING about
the target.


with Ada.Finalization;

generic

  type Uncontrolled (<>) is abstract tagged private;

  with procedure Finalize (Object: in out Uncontrolled);

package Add_Finalization is

  type Controlled is new Uncontrolled with private;

private

  type Controlled_Ptr is access all Controll

  type Component is new Ada.Finalization.Controlled with record
    Parent: Controlled_Ptr;
  end record;

  type Controlled is new Uncontrolled with record
    Controller: Component := (Ada.Finalization.Controlled with
                              Controlled'Unchecked_Access);
  end record;

  procedure Adjust   (Object: in out Component);
  procedure Finalize (Object: in out Component);

end Add_Finalization;

package body Add_Finalization.To_Uncontrolled is

  Ugly_Store: Controlled_Ptr;

  procedure Adjust (Object: in out Component) is
  begin
    Object.Parent := Ugly_Store;  -- follows directly a call of Finalize
  end Adjust;

  procedure Finalize (Object: in out Component) is
  begin
    Ugly_Store := Object.Parent;  -- in an assignment A := B; this just precedes
    Finalize (Uncontrolled (Object.Parent.all));  -- the call of Adjust
  end Finalize;

end To_Uncontrolled;

I've just some more questions about the whole concept. Let's assume the above
works. Then:


with Add_Finalization;

package Generations is

  type Root is abstract tagged null record;
  procedure Identify_Yourself (X: in out Root) is abstract;

  type Parent is new Root with record
    ...
  end record;
  procedure Identify_Yourself (X: in out Parent);

  type Derived is new Parent with record
    ...
  end record;
  -- Inherited:
  -- procedure Identify_Yourself (X: in out Derived);
  procedure Finalize (X: in out Derived);
  package Add_Finalization_To_Derived is
     new Add_Finalization (Derived, Finalize);
  use Add_Finalization_To_Derived;
  -- Is this inherited?
  -- procedure Identify_Yourself (X: in out Controlled);

  -- By what mechanism does Ada manage to derive Identify_Yourself for the types
  -- Add_Finalization_To_Derived.Controlled and Final?
  -- Inside package Add_Finalization, Uncontrolled is abstract and no operations
  -- except Finalize are known to the generic.

  type Final is new Controlled with record
    ...
  end record;
  -- Is this inherited?
  -- procedure Identify_Yourself (X: in out Final);

end Generations;


Christoph Grein
[log in to unmask]