Gem #62: C++ constructors and Ada 2005

by Javier MirandaArnaud Charlet —AdaCoreAdaCore

Let's get started…

Continuing from the example discussed in the last Gem, let's derive the imported C++ class on the Ada side. For example:

 type DT is limited new Root with record
    C_Value : Natural := 2009;
 end record;

In this case, the components DT inherited from the C++ side must be initialized by a C++ constructor, and the additional Ada components of type DT are initialized by GNAT. The initialization of such an object is done either by default, or by means of a function returning an aggregate of type DT, or by means of an extension aggregate:

 Obj5 : DT;
 Obj6 : DT := Function_Returning_DT (50);
 Obj7 : DT := (New_Root (30, 40) with C_Value => 50);

The declaration of Obj5 invokes the default constructors: the C++ default constructor of the parent type takes care of the initialization of the components inherited from Root, and GNAT takes care of the default initialization of the additional Ada components of type DT (that is, C_Value is initialized to value 2009). The order of invocation of the constructors is consistent with the order of elaboration required by Ada and C++, meaning that the constructor of the parent type is always called before the constructor of the derived type.

Let's now consider a record that has components whose type is imported from C++. For example:

 type Rec1 is limited record
    Data1 : Root := New_Root (10);
    Value : Natural := 1000;
 end record;

 type Rec2 (D : Integer := 20) is limited record
    Rec   : Rec1;
    Data2 : Root := New_Root (D, 30);
 end record;

The initialization of an object of type Rec2 will call the nondefault C++ constructors specified for the imported components. For example:

 Obj8 : Rec2 (40);

Using Ada 2005 we can use limited aggregates to initialize an object invoking C++ constructors that differ from those specified in the type declarations. For example:

 Obj9 : Rec2 := (Rec => (Data1 => New_Root (15, 16),
                         others => <>),
                 others => <>);

The above declaration uses an Ada 2005 limited aggregate to initialize Obj9, and the C++ constructor that has two integer arguments is invoked to initialize the Data1 component instead of the constructor specified in the declaration of type Rec1. In Ada 2005, the box in the aggregate indicates that unspecified components are initialized using the expression (if any) available in the component declaration. That is, in this case discriminant D is initialized to value 20, Value is initialized to value 1000, and the non-default C++ constructor that handles two integers takes care of initializing component Data2 with values 20 and 30.

In Ada 2005, we can use the extended return statement to build the Ada equivalent of a C++ nondefault constructor. For example:

 function New_Rec2 (V : Integer) return Rec2 is
    return Obj : Rec2 := (Rec => (Data1  => New_Root (V, 20),
                                  others => <>),
                          others => <>) do
       --  Further actions required for construction of
       --  objects of type Rec2
    end record;
 end New_Rec2;

In this example, the extended return statement construct is used to build in place the returned object, whose components are initialized by means of a limited aggregate. Any further actions associated with the constructor can be given within the statement part of the extended return.