Interfacing with C++ constructors (GNAT User’s Guide for Native Platforms)

#### 3.11.3.4 Interfacing with C++ constructors ¶

To interface with C++ constructors GNAT provides the `pragma CPP_Constructor` (see the GNAT_Reference_Manual for additional information). In this section, we present some common uses of C++ constructors in mixed-languages programs in GNAT.

Let us assume we need to interface with the following C++ class:

    class Root {
    public:
      int  a_value;
      int  b_value;
      virtual int Get_Value ();
      Root();              // Default constructor
      Root(int v);         // 1st non-default constructor
      Root(int v, int w);  // 2nd non-default constructor
    };

For this purpose, we can write the following package spec (further information on how to build this spec is available in Interfacing with C++ at the Class Level and Generating Ada Bindings for C and C++ headers).

    with Interfaces.C; use Interfaces.C;
    package Pkg_Root is
      type Root is tagged limited record
         A_Value : int;
         B_Value : int;
      end record;
      pragma Import (CPP, Root);

      function Get_Value (Obj : Root) return int;
      pragma Import (CPP, Get_Value);

      function Constructor return Root;
      pragma Cpp_Constructor (Constructor, "_ZN4RootC1Ev");

      function Constructor (v : Integer) return Root;
      pragma Cpp_Constructor (Constructor, "_ZN4RootC1Ei");

      function Constructor (v, w : Integer) return Root;
      pragma Cpp_Constructor (Constructor, "_ZN4RootC1Eii");
    end Pkg_Root;

On the Ada side, the constructor is represented by a function (whose name is arbitrary) that returns the classwide type corresponding to the imported C++ class. Although the constructor is described as a function, it’s typically a procedure with an extra implicit argument (the object being initialized) at the implementation level. GNAT issues the appropriate call, whatever it is, to get the object properly initialized.

Constructors can only appear in the following contexts:

- On the right side of an initialization of an object of type.
- On the right side of an initialization of a record component of type.
- In an Ada 2005 limited aggregate.
- In an Ada 2005 nested limited aggregate.
- In an Ada 2005 limited aggregate that initializes an object built in place by an extended return statement.

In a declaration of an object whose type is a class imported from C++, either the default C++ constructor is implicitly called by GNAT or you must explicitly call the required C++ constructor in the expression that initializes the object. For example:

    Obj1 : Root;
    Obj2 : Root := Constructor;
    Obj3 : Root := Constructor (v => 10);
    Obj4 : Root := Constructor (30, 40);

The first two declarations are equivalent: in both cases the default C++ constructor is invoked (in the former case the call to the constructor is implicit and in the latter case the call is explicit in the object declaration). `Obj3` is initialized by the C++ non-default constructor that takes an integer argument and `Obj4` is initialized by the non-default C++ constructor that takes two integers.

Let’s derive the imported C++ class in the Ada side. For example:

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

In this case, you must initialize the components `DT` inherited from the C++ side 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 := (Constructor (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++. That is, 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 := Constructor (10);
       Value : Natural := 1000;
    end record;

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

The initialization of an object of type `Rec2` calls the non-default 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 => Constructor (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,30`.

In Ada 2005, we can use the extended return statement to build the Ada equivalent to C++ non-default constructors. For example:

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

In this example, we use the extended return statement construct to build in place the returned object whose components are initialized by means of a limited aggregate. We could also place any further action associated with the constructor inside the construct.