2

(注:これは、D言語の構造体とクラスの間の使用設定に関連していますが、より具体的な使用例です)

たとえば、C ++コードへのDインターフェイスを作成する場合、SWIGなどは次のようなことを行います。

class A{
   private _A*ptr;//defined as extern(C) elsewhere
   this(){ptr=_A_new();}//ditto 
   this(string s){ptr=_A_new(s);} //ditto
   ~this(){_A_delete(ptr);} //ditto
   void fun(){_A_fun(ptr);}
}

継承は必要ないと仮定しましょう。

私の質問は、これにはクラスの代わりに構造体を使用する方が望ましいのではないかということです。

長所は次のとおりです。

1)効率(スタック割り当て)

2)使いやすさ(どこにでも新しいものを書く必要はありません。例:auto a=A(B(1),C(2))vs auto a=new A(new B(1),new C(2)))?

短所は次のとおりです。postblitを介してエイリアシングを処理するには、追加のフィールドis_ownが必要です。

そうするための最良の方法は何でしょうか? 他に気になることはありますか?これが試みです:

struct A{
   private _A*ptr;
   bool is_own;//required for postblit
   static A opCall(){//cannot write this() for struct
       A a;
       a.ptr=_A_new();
       a.is_own=true;
       return a;
   }
   this(string s){ptr=_A_new(s); is_own=true;} 
   ~this(){if(is_own) _A_delete(ptr);}
   void fun(){_A_fun(ptr);}
   this(this){//postblit; 
       //shallow copy: I don't want to call the C++ copy constructor (expensive or unknown semantics)           
       is_own=false; //to avoid _A_delete(ptr)
   }
}

次のような関数を呼び出す場合は、postblitが必要であることに注意してください。

myfun(A a){}
4

1 に答える 1

4

このページを読むことをお勧めします。Dで呼び出すことができるC++クラスの関数は、仮想関数のみです。つまり、

DはC++の特殊メンバー関数を呼び出すことはできません。その逆も同様です。これらには、コンストラクタ、デストラクタ、変換演算子、演算子のオーバーロード、およびアロケータが含まれます。

また、DでC ++クラスを宣言するときは、を使用しextern(C++) interfaceます。したがって、クラス/構造体は次のようになります

extern(C++) interface A
{
   void fun();
}

ただし、Dコードはコンストラクターにアクセスできないため、C ++コードがそれを実行する必要があるためextern(C++)、タイプのオブジェクトを割り当てるには別の関数が必要になります。Aまた、C ++コードに戻して、使い終わったら削除する方法も必要です。

extern(C++)ここで、そのインターフェイスを、関数を呼び出して構築し、関数を削除する型でラップextern(C++)する場合(手動で行うことを心配する必要がないように)、クラスを使用するかどうかまたは構造体は、それを使って何をしようとしているかに完全に依存します。

クラスは参照型であり、C++クラスが実際に何であるかを反映しています。したがって、特別なことをしなくても、それを渡すことができます。ただし、ラップされたC ++オブジェクトが解放されることを保証したい場合は、手動で行う必要があります。これは、Dクラスのファイナライザーが実行される保証がないためです(おそらく、ここにコードを配置します)。 C++関数を呼び出してC++オブジェクトを削除します)。clearDオブジェクトを破棄するには(実際にはコンパイラの次のリリースで名前が変更される-dmd destroy2.060)を使用する必要があります(つまり、ファイナライザを呼び出して、値型であるメンバー変数の破棄を処理します) 、またはC ++オブジェクトを削除するには、C++関数を呼び出したDオブジェクトの関数を呼び出す必要があります。例えば

extern(C++) interface A
{
   void fun();
}

extern(C++) A createA();
extern(C++) void deleteA(A a);

class Wrapper
{
public:
    this()
    {
        _a = createA();
    }

    ~this()
    {
        deleteA(_a);
    }

    auto opDispatch(string name, Args...)(Args args)
    {
        return mixin("_a." ~ name ~ "(args)");
    }

private:

    A _a;
}

void main()
{
    auto wrapped = new Wrapper();

    //do stuff...

    //current
    clear(wrapped);

    //starting with dmd 2.060
    //destroy(wrapped);
}

clearただし、これには、 /destroyを呼び出さず、ガベージコレクターがラッパーオブジェクトを収集しdeleteAない場合、C++オブジェクトで呼び出されないという欠点があります。それは問題かもしれないし、問題ではないかもしれません。これは、プログラムが終了する前にC ++オブジェクトが本当にデストラクタを呼び出す必要があるかどうか、またはプログラムが終了したときにGCがラッパーを収集する必要がない場合にメモリをOSに戻すことができるかどうかによって異なります。物体。

決定論的破壊が必要な場合は、構造体が必要です。つまり、構造体を参照型にすることについて心配する必要があります。それ以外の場合、コピーされた場合、一方が破棄されるとC ++オブジェクトが削除され、もう一方の構造体はガベージをポイントします(破棄されると削除を試みます)。これを解決するには、を使用できますstd.typecons.RefCounted。次に、あなたは次のようなものを手に入れます

extern(C++) interface A
{
   void fun();
}

extern(C++) A createA();
extern(C++) void deleteA(A a);

struct Wrapper
{
public:
    static Wrapper opCall()
    {
        Wrapper retval;
        retval._a = createA();
        return retval;
    }

    ~this()
    {
        if(_a !is null)
        {
            deleteA(_a);
            _a = null;
        }
    }

    auto opDispatch(string name, Args...)(Args args)
    {
        return mixin("_a." ~ name ~ "(args)");
    }

private:

    A _a;
}


void main()
{
    auto wrapped = RefCounted!Wrapper();
    //do stuff...
}

参照カウントロジックが含まれるようにラッパーを定義して回避することもできますが、RefCountedそれは間違いなくより複雑になります。

boolとにかく、ラッパーがC ++オブジェクトを所有しているかどうかをマークするためにを使用するという提案には絶対に反対します。これは、すべてのコピーが破棄される前に元のラッパーオブジェクトが破棄されると、コピーがガベージを指すためです。

C ++オブジェクトのコピーコンストラクターを使用したい場合(したがって、C ++オブジェクトを値型として扱う場合)の別のオプションは、C ++オブジェクトextern(C++)を取得してそのコピーを返し、ポストブリットで使用する関数を追加することです。 。

extern(C++) A copyA(A a);

this(this)
{
    if(_a !is null)
        _a = copyA(a);
}

うまくいけば、それは物事を十分に明確にするでしょう。

于 2012-06-16T02:14:58.377 に答える