Search code examples
typesadaprotected

How to finalize an Ada protected type


Given an Ada protected type:

protected type A is
    procedure Foo;
    ...
private
    M : Map;
    ...
end A;

How would you implement or emulate a Finalize procedure that is called when the protected object is finalized?

Basically I need to do some house keeping using the private members of the protected type (iterating over some map and so on).


Solution

  • Wrap the private members that have a part in the finalization in one or more records derived from Ada.Finalization.Controlled or Limited_Controlled. When the protected object is finalized, those private members will be correspondingly finalized as well.

    Here's a quick, working(!) example:

    with Text_IO; use Text_IO;
    with Ada.Finalization;
    with Ada.Containers.Ordered_Maps;
    with Ada.Unchecked_Deallocation;
    
    procedure Protected_Final is
    
       Instance_Counter : Natural := 1;
    
       package Int_Map is new Ada.Containers.Ordered_Maps (Integer, Integer);
       subtype Map is Int_Map.Map;
    
       type Map_Wrapper is new Ada.Finalization.Controlled with record
          ID : Natural;
          M  : Map;
       end record;
    
       overriding procedure Initialize(Item : in out Map_Wrapper);
    
       overriding procedure Finalize(Item : in out Map_Wrapper);
    
       procedure Initialize(Item : in out Map_Wrapper) is
       begin
          Item.ID := Instance_Counter;
          Instance_Counter := Instance_Counter + 1;
          Put_Line("Initialize the Map part as instance" & Natural'Image(Item.ID));
       end Initialize;
    
       procedure Finalize(Item : in out Map_Wrapper) is
       begin
          Put_Line("Clean up the Map stuff for instance" & Natural'Image(Item.ID));
       end Finalize;
    
       protected type A is
          procedure Foo;
       private
          M : Map_Wrapper;
       end A;
    
       protected body A is
    
          procedure Foo is 
          begin
             null;
          end Foo;
       end A;
    
       Test_Item : A;
    
       type A_Handle is access A;
    
       procedure Free is new Ada.Unchecked_Deallocation(A, A_Handle);
    
       Test_Item_Handle : A_Handle;
    
    begin
       Test_Item_Handle := new A;
    
       Free(Test_Item_Handle);
    end Protected_Final;
    

    Running this I get:

    C:\sandbox\protected_final
    Initialize the Map part as instance 1
    Initialize the Map part as instance 2
    Clean up the Map stuff for instance 2
    Clean up the Map stuff for instance 1
    [2011-03-04 08:37:29] process terminated successfully (elapsed time: 00.21s)
    

    The "outer" Initialize/Cleanup messages come are a result of the statically declared Test_Item instance, while the inner pair are from the dynamically allocated and deallocated Test_Item_Handle.