Gem #11: Limited Types in Ada 2005 — Constructor Functions Part 2

by Bob Duff —AdaCore

Let's get started…

We've earlier seen examples of constructor functions for limited types similar to this:

   package P is
      type T (<>) is limited private;
      function Make_T (Name : String) return T; -- constructor function
   private
      type T is new Limited_Controlled with
         recordend record;
   end P;
   package body P is
      function Make_T (Name : String) return T is
      begin
         return (Name => To_Unbounded_String (Name), others => <>);
      end Make_T;
   end P;
   function Make_Rumplestiltskin return T is
   begin
       return Make_T (Name => “Rumplestiltskin”);
   end Make_Rumplestiltskin;

It is useful to consider the various contexts in which these functions may be called. We've already seen things like:

   Rumplestiltskin_Is_My_Name : T := Make_Rumplestiltskin;

in which case the limited object is built directly in a standalone object. This object will be finalized whenever the surrounding scope is left.

We can also do:

   procedure Do_Something (X : T);
    
   Do_Something (X => Make_Rumplestiltskin);

Here, the result of the function is built directly in the formal parameter X of Do_Something. X will be finalized as soon as we return from Do_Something.

We can allocate initialized objects on the heap:

   type T_Ref is access all T;
   Global : T_Ref;
   procedure Heap_Alloc is
      Local : T_Ref;
   begin
      Local := new T'(Make_Rumplestiltskin);
      ifthen
         Global := Local;
      end if;
   end Heap_Alloc;

The result of the function is built directly in the heap-allocated object, which will be finalized when the scope of T_Ref is left (long after Heap_Alloc returns).

We can create another limited type with a component of type T, and use an aggregate:

   type Outer_Type is limited
      record
         This : T;
         That : T;
      end record;
   Outer_Obj : Outer_Type := (This => Make_Rumplestiltskin,
                              That => Make_T (Name => “”));

As usual, the function results are built in place, directly in Outer_Obj.This and Outer_Obj.That, with no copying involved.

The one case where we _cannot_ call such constructor functions is in an assignment statement:

   Rumplestiltskin_Is_My_Name := Make_T(Name => ""); -- Illegal!

which is illegal because assignment statements involve copying. Likewise, we can't copy a limited object into some other object:

   Other : T := Rumplestiltskin_Is_My_Name; -- Illegal!