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:
Initialize
is called when an object of typeT
is declared without initialization expression.Adjust
is called after an object of typeT
is assigned a new value.Finalize
is called when an object of typeT
goes 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;