5

いくつかのcライブラリをc++でラップすることを検討していますが、不透明なポインタをラップするための最良の方法がわかりません。

C構造体がパブリックAPIの一部である場合

typedef struct _SomeType
{
    int a;
    int b;
} SomeType_t;

いくつかの「メンバー」機能がある場合:

void SomeTypeFoo( SomeType_t* obj, ... );
void SomeTypeBar( SomeType_t* obj, ... );

これらの「メンバー」関数を実際のクラスメンバーとして単純に関連付けるために、ベースから派生するアプローチが好きです。すなわち:

class SomeTypeWrapper:
    public SomeType_t
{
    void foo( ... );
    void bar( ... );
};

私の理解では、これは、c++メソッドの実装が次のようになるようにキャストできるものSomeTypeWrapperと「バイナリ互換」であると信じています。struct _SomeTypeSomeTypeWrapper*SomeType_t*

SomeTypeWrapper::foo( ... )
{
    SomeTypeFoo( (SomeType_t*)this, ... );
}

注[1]を参照してください。

ただし、一部のライブラリでは、構造の定義を非公開にしたいと考えています(これは、ヘッダー/ APIを変更せずに実装を変更できるようにするためだと思います)。したがって、ヘッダーには次のようなものがあります。

typedef struct _SomeType SomeType_t;

そして、すべての「メンバー」関数はこれらの不透明なポインターを処理します。これらのメソッドをインターフェースに「ラップ」するために、これまでに行ったことは、ポインターを含むクラスを提供することです。

class SomeTypeWrapper
{
    private:
        SomeType_t* m_data;

    public:
        SomeTypeWrapper( SomeType_t* data ): m_data(data){}
        void foo(...);
        void bar(...);
};

これは、そのような不透明なポインターを返すcライブラリ内の関数に対して、そのポインターを運ぶために新しいオブジェクトを割り当てる必要があることを意味します。多くのライブラリではおそらく問題ありませんが、この設計ではラッパーの実装が複雑になり、多くの小さなオブジェクトの割り当てを行う高速なcライブラリの場合、この余分なオブジェクトのオーバーヘッドがパフォーマンスの大幅な低下につながる可能性があります。さらに、基になるオブジェクトは参照カウントされる可能性があるため、場合によっては、SomeWrapperTypeが参照カウントを増やすか、コピーコンストラクターを実装するか、プライベートコンストラクターを使用するかなどを決定する必要があります。

ほとんどの場合、私はより良いインターフェースのためにそのペナルティを喜んで支払いますが、私は他のオプションを探しています。C ++ラッパーライブラリで不透明なCポインタを処理するためのよりクリーンな方法はありますか?関連:c ++ヘッダーにc-libraryヘッダーを含める必要がなく、ラッパーオブジェクトの割り当てを必要としないように、ラッパークラスを実装する良い方法はありますか?

SomeTypeWrapper注[1]:同じコンパイラが使用され、追加のメンバーがなく、仮想メソッドがない限り、このキャストは安全だと思います。同じコンパイラで、gccによってコンパイルされたc-libraryは、同じgccによってコンパイルされたc++ラッパーで動作すると想定しています。おそらくそれは保証されていませんか?

4

3 に答える 3

5

(質問の)の最終バージョンSomeTypeWrapperは、前進する方法です。

これは、そのような不透明なポインターを返すcライブラリ内の関数に対して、そのポインターを運ぶために新しいオブジェクトを割り当てる必要があることを意味します。多くのライブラリではおそらく問題ありませんが、この設計ではラッパーの実装が複雑になり、多くの小さなオブジェクトの割り当てを行う高速なcライブラリの場合、この余分なオブジェクトのオーバーヘッドがパフォーマンスの大幅な低下につながる可能性があります。

オーバーヘッドはありません。classデータメンバーが1つで、メソッドがないaのインスタンスは、virtualその唯一のメンバーよりも大きくはありません。ヘッダーでメソッドをインラインで定義すると、関数呼び出しのオーバーヘッドも発生しません。newしたがって、これらのインスタンスをで割り当てず、スタックに割り当てる限り、次のようになります。

SomeTypeWrapper some_object(make_some_object());  // one allocation on the heap

次に、オーバーヘッドゼロのソリューションがあります。

于 2012-07-20T20:26:17.163 に答える
3

不透明なポインタをラップしている場合、それから派生することはできません。一部の機能を無料で入手するための一見魅力的な方法ですが、集約が意図されている場所で継承を使用すると、すぐに面倒になります。ラッパーはライブラリのオブジェクトの1つではなく、代わりに...ライブラリオブジェクトをラップします

ライブラリオブジェクトを処理するのと同じ注意を払ってラッパーオブジェクトを処理する場合、不透明なポインタごとに常に1つのラッパー(最大で)があります。ラッパーオブジェクトをたとえばaと共有できますunique_ptr(C ++ 11にあります!)。ラップされたオブジェクトが貴重なリソースであることを考えると、これは良いことです。

共有ポインターと言えば、deleterコンストラクター引数として関数をとることができます。

struct Wrapper : boost::non_copyable 
{
private:
   std::unique_ptr<SomeType> wrapped;
public:
   Wrapper()
   :wrapped(SomeType_create(), &SomeType_destroy)
   // upon destruction of wrapped, use SomeType_destroy
   {
   }

   void foo() { SomeType_foo(wrapped.get()); }
   int bar(int i) { return SomeType_bar(wrapped.get(),i); }
};

SomeTypeはい: ;の各メンバー関数のメソッドラッパーが必要になります。C ++はCとは異なる呼び出し規約を使用し、各Cスタイルのメンバー関数をC ++のものに置き換える必要があるため、これは完全に正常です。

また、ライブラリオブジェクトごとに1つのラッパーを作成するように注意する必要があります。このオブジェクトはあなたの管理下にあり、信頼できる人なら誰とでも共有できますが、これは1つのオブジェクトにすぎません。

于 2012-07-22T10:43:48.930 に答える
0

一般的に、上記で提案するのは、あらゆる種類の問題につながる可能性のある醜いハックです。継承階層を上に(そして意味がある場合は下に)キャストしても問題ありませんが、ポインターからラッパークラスにキャストし、最初のメンバーが指すオブジェクトへのポインターにキャストする場合は未定義です)。最初のメンバーのアドレスがラッパーアドレスと一致するという保証はまったくありません。

私の意見では、あなたはここで間違った問題を解決しようとしています。たぶん、適切な戦略ではない、あらゆる種類の小さなC「オブジェクト」のC ++ラッパーを作成しますか?個々のオブジェクトをラップするのではなく、基盤となるCライブラリの機能をラップするライブラリがないのはなぜですか?そしてそれを行うとき、それは意味のある、倹約的な方法で(すなわち、RIAA、共有ポインタなどを使用することによって)基礎となるCオブジェクトを管理します。

于 2012-07-20T20:22:13.810 に答える