3

Ada95 での C++ クラスの使用 / コンストラクタと制御される型

Ada コードで C++ クラスを使用できるようにしたいと考えています。私の目標は、Ada コードを Ada95 仕様に移植できるようにすることです。GNAT または Ada05 固有の方法論は使用したくありません。

インターフェイスを実現するために C のラッパー関数で pragma Import (C) を使用しています。しかし、C++ Ctors/Dtors を自動的に呼び出す方法がわかりません。私が最初に考えたのは、Ada Controlled Types を使用し、Initialize が Ctor を呼び出し、Finalize が Dtor を呼び出すことでした。これは、パラメーターを渡す必要がある Ctor を取得するまでは、すべて問題ありませんでした。

フー。

class Foo
{
public:
    Foo();
    Foo(long x, long y, long z);
    Foo(const Foo& that);
    ~Foo();

    Foo& operator=(const Foo& that);

    long getX() const;
    long getY() const;
    long getZ() const;

    void setX(long x);
    void setY(long y);
    void setZ(long z);

private:
    long mX;
    long mY;
    long mZ;    
};

Foo_Exports.cpp

#include "foo.h"
#include <new>

extern "C"
{
    void extFoo_New    (Foo* foo) { new (foo) Foo(); }  
    void extFoo_NewXYZ (Foo* foo, long x, long y, long z)   { new (foo) Foo(x,y,z); }
    void extFoo_Delete (Foo* foo) { foo->~Foo(); }  

    long extFoo_getX(const Foo& foo) { return foo.getX(); }
    long extFoo_getY(const Foo& foo) { return foo.getY(); }
    long extFoo_getZ(const Foo& foo) { return foo.getZ(); }

    void extFoo_setX(const Foo& foo, long x) { foo.setX(x) };
    void extFoo_setY(const Foo& foo, long y) { foo.setY(y) };
    void extFoo_setZ(const Foo& foo, long z) { foo.setZ(z) };
}

cpp.foo.ads

with Ada.Finalization;
with Interfaces.C;
use Interfaces.C;

package Cpp.Foo is

    type Obj_t is new Ada.Finalization.Controlled_Type with private;

    procedure Initialize (This : in out Obj_T);
    procedure Adjust     (This : in out Obj_T);
    procedure Finalize   (This : in out Obj_T);

    function Get_X (This : access Obj_T) return Long;
    function Get_Y (This : access Obj_T) return Long;
    function Get_Z (This : access Obj_T) return Long;

    procedure Set_X(This : access Obj_T; 
                    X    : in     Long );
    procedure Set_Y(This : access Obj_T; 
                    Y    : in     Long );
    procedure Set_Z(This : access Obj_T; 
                    Z    : in     Long );

private
    type Obj_t is new Ada.Finalization.Controlled_Type with null record;
    for Obj_T'Size use 192;
    for Obj_T'Alignment use 8;

    pragma Import (C, Get_X, "extFoo_getX");
    pragma Import (C, Get_Y, "extFoo_getY");
    pragma Import (C, Get_Z, "extFoo_getZ");

    pragma Import (C, Set_X, "extFoo_setX");
    pragma Import (C, Set_Y, "extFoo_setY");
    pragma Import (C, Set_Z, "extFoo_setZ");
end Cpp.Foo;

cpp.foo.adb

with System;

package body Cpp.Foo is

    procedure Initialize (This : in out Obj_T) is
        procedure ExtFoo_New(Addr : in System.Address);
        pragma Import (C, ExtFoo_New "extFoo_New");

        procedure ExtFoo_NewXYZ(Addr  : in System.Address,
                                X     : in Long;
                                Y     : in Long;
                                Z     : in Long);
        pragma Import (C, ExtFoo_NewXYZ "extFoo_NewXYZ");
    begin

        null; -- **WHAT DO I DO HERE?!**

    end Initialize;

    procedure Adjust     (This : in out Obj_T) is
    begin
        null; -- TBD copy ctor
    end Adjust;

    procedure Finalize   (This : in out Obj_T) is
        procedure ExtFoo_Delete(Addr : in System.Address);
        pragma Import (C, ExtFoo_Delete, extFoo_Delete);
    begin
        ExtFoo_Delete(This'address);
    end Finalize;


end Cpp.Foo;
4

3 に答える 3

1

Ada 95 内から C++ クラス メソッドを呼び出すための移植可能な (言語標準) 方法は、実際にはありません。これは、C、Fortran、および C++ とのインターフェイスが必要な場合がある他のほとんどの主要なコンパイル済みシステムのプログラミング言語に当てはまります。

これに対処するには、C プログラムに接続するときに通常使用するのと同じ手法を使用する必要があります。基本的に、他の言語から呼び出す必要があるものには、C リンク可能な C++ 関数が関連付けられている必要があります。C++ では、これは通常、ネイキッド関数または静的メンバー関数のいずれかを意味し、それにextern "C"適用されます。

Gnat は、GCC と緊密に統合されているため、C++ コードを処理するためのより優れた機能を備えています。ただし、それは標準ではありません。

このような非 OO 呼び出しを OO メソッド呼び出しに「ブートストラップ」するには、オブジェクトをパラメーターとして渡し、非 OO 呼び出しにそのオブジェクトで適切なメソッド呼び出しを行わせることができることに注意してください。それでも、メソッドごとにそのようなブートストラップ関数が 1 つ必要であることを意味します。

于 2012-07-12T15:39:09.603 に答える
1

Initializeデフォルトの初期化のためにのみ呼び出されます。

O : Obj_t;

and は、 を呼び出すサブプログラムですExtFoo_New。初期値を持つを作成するには、おそらくObj_tを呼び出す別の関数が必要ですExtFoo_NewXYZ

function Create (X, Y, Z : Long_Integer) return Obj_T;

その後

O : Obj_T := Create (41, 42, 43);

Obj_tそうは言っても、Adaと C++をオーバーレイするというあなたのスキームは良い考えではないと思います。Fooなぜなら、両方の言語が隠しフィールドを使用する資格があるからです。C++ がディスパッチ テーブルへのポインターを格納する可能性があることは想像できますが、(GNAT では) の子にAda.Finalization.Controlledファイナライズ チェーンを実装するリンクが含まれていることは確かです。他のコンパイラは別の方法でそれを行う可能性があります (GNAT は GCC 4.7 で戦略を変更しました)。したがってObj_t、C++ オブジェクトへの参照 (つまり、によって返される値Foo()) が含まれていることになります。

于 2012-07-13T19:52:16.227 に答える
0

コード サンプルを拡張し、BitBucketのリポジトリにアップロードしました

デフォルト コンストラクター、コピー コンストラクター、カスタム コンストラクターを含む ctor ラッパーが含まれています。デバッグ出力を提供するコードを追加しました。私が見る限り、すべてが期待どおりに機能しますが、作成 (カスタム ctor ラッパー) により、コピー/削除の 2 つの冗長なホップが発生します。

Foo()
Foo(long, long, long)
Foo(const Foo&)
~Foo()
Foo(const Foo&)
~Foo()
Foo()
Foo(const Foo&)
Uninitialized A, should be   0   0   0:    0   0   0
Initialized B,   should be   4   5   6:    4   5   6
Copied B to C,   should be   4   5   6:    4   5   6
Modified C,      should be   4   7   6:    4   7   6
~Foo()
Foo(const Foo&)
Copied C to A,   should be   4   7   6:    4   7   6
~Foo()
~Foo()
~Foo()
~Foo()

私のラッパーは、実際のコンストラクターによって作成された本物のオブジェクトからコピーするのではなく、オブジェクトのコンテンツを移動し、ビットごとにコピーされたオブジェクトのコンテンツからコピー コンストラクターを呼び出すことが安全であるという前提に依存しています。

このアプローチは、メモリ内の C++ オブジェクトの場所が何らかの方法で追跡されるまれなケースでは機能しません。この方法では、Adjust.Temp_Copy は不明な孤立状態になります。

Flat_Get_X はアクセスの代わりに System.Address を使用します。これは、アクセス定数の引数が Ada 2005 の機能であり、読み取り専用の This ではプレーンなアクセスが利用できないためです。

于 2012-07-31T16:59:44.470 に答える