Gnat Pro. Powerful tools. Frontline Support. Ada expertise.

Gem #62: C++ constructors and Ada 2005

Author: Javier Miranda, Arnaud Charlet, AdaCore

Abstract: Ada Gem #62 — In the previous Gem, we explored how to interface and make simple use of C++ constructors on the Ada side.

In this Gem, we detail more advanced constructs related to Ada 2005 and C++ constructors.

« Previous Gem | Next Gem » | Gems Menu

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
 begin
    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.

 

Posted by Posted in Ada / Ada 2005 / Ada 2012, Development Log, Devt log - Gem of the Week

Have your own idea for a Gem?

If you have an idea for a Gem you would like to contribute please feel free to contact us at: gems@adacore.com

Discussion

2 responses to “Gem #62: C++ constructors and Ada 2005”


  1. Paul F. Pearson said:

    Will a future gem discuss destructors? I presume that they are handled in a similar fashion.


  2. Brad Moore said:

    function New_Rec2 above, if declared as a primitive operation of type Rec2, would be a dispatching operation, and if a new type were derived from Rec2, (say Rec3) it would require that Rec3 provide an overloaded version of New_Rec2. This may not be appropriate (naming issues aside). Supposing the Rec3 type adds further discriminants. The New_Rec2 function may not be able to determine the values to use for the new discriminants of Rec3. It would be helpful to show how to declare the New_Rec2 function so that it is not inherited by derivations of Rec2.
    My understanding is that one of the best ways to do this is to declare such constructor functions in a nested package. This way they cannot be primitive functions, and cannot be inherited. eg
    package Recs is
    type Rec1 is limited …
    type Rec2 is limited …
    package Constructors is
    function New_Rec2 (V : Integer) return Rec2;
    end Constructors;
    end package Recs;

    Are there other approaches to consider?

Leave a Reply