The Finalizable aspect can be applied to any record type, tagged or not,
to specify that it provides the same level of control on the operations of
initialization, finalization, and assignment of objects as the controlled
types (see RM 7.6(2) for a high-level overview). The only restriction is
that the record type must be a root type, in other words not a derived type.
The aspect additionally makes it possible to specify relaxed semantics for
the finalization operations by means of the Relaxed_Finalization setting.
Here is the archetypal example:
type T is record
...
end record
with Finalizable => (Initialize => Initialize,
Adjust => Adjust,
Finalize => Finalize,
Relaxed_Finalization => True);
procedure Adjust (Obj : in out T);
procedure Finalize (Obj : in out T);
procedure Initialize (Obj : in out T);
The three procedures have the same profile, with a single in out parameter,
and also have the same dynamic semantics as for controlled types:
Initializeis called when an object of typeTis declared without initialization expression.Adjustis called after an object of typeTis assigned a new value.Finalizeis called when an object of typeTgoes out of scope (for stack-allocated objects) or is deallocated (for heap-allocated objects). It is also called when the value is replaced by an assignment.
However, when Relaxed_Finalization is either True or not explicitly
specified, the following differences are implemented relative to the semantics
of controlled types:
Finalize is only called when such an object
is explicitly deallocated, or when the designated object is assigned a new
value. As a consequence, no runtime support is needed for performing
implicit deallocation. In particular, no per-object header data is needed
for heap-allocated objects.
Heap-allocated objects allocated through a nested access type will therefore ‘not’ be deallocated either. The result is simply that memory will be leaked in this case.
Adjust and Finalize procedures are automatically considered as
having the No_Raise aspect specified for them. In particular, the
compiler has permission to enforce none of the guarantees specified by the
RM 7.6.1 (14/1) and subsequent subclauses.
Simple example of ref-counted type:
type T is record
Value : Integer;
Ref_Count : Natural := 0;
end record;
procedure Inc_Ref (X : in out T);
procedure Dec_Ref (X : in out T);
type T_Access is access all T;
type T_Ref is record
Value : T_Access;
end record
with Finalizable => (Adjust => Adjust,
Finalize => Finalize);
procedure Adjust (Ref : in out T_Ref) is
begin
Inc_Ref (Ref.Value);
end Adjust;
procedure Finalize (Ref : in out T_Ref) is
begin
Def_Ref (Ref.Value);
end Finalize;
Simple file handle that ensures resources are properly released:
package P is
type File (<>) is limited private;
function Open (Path : String) return File;
procedure Close (F : in out File);
private
type File is limited record
Handle : ...;
end record
with Finalizable (Finalize => Close);
end P;