2016-10-13 16 views
9

CのC++ライブラリにいくつかのAPIをラップする必要があります。私はこれまで、クラスオブジェクト、extern "C"などの不透明ポインタを使用して、hereと記述しました。しかし、私が扱っているこの新しいライブラリは、リファレンスカウントのスマートポインタを広範に使用しています。私はスマートポインタの存在下でラッピングを行う方法がわかりません。Cでスマートポインタを返すC++関数をどのようにラップするのですか?

SmartPointer<MyClass> foo() { 
    SmartPointer<MyClass> ret(new MyClass); // Create smart pointer ret 
    ret->DoSomething(); // Do something with ret 
    return ret; 
} 

どのように私はCでfoo()をラップすることができます。たとえば、のは、C++ライブラリは、以下の機能を持っているとしましょうか?明らかに、MyClassオブジェクトを参照するC関数で使用できる不透明なポインタ(void*または空の構造体ポインタなど)が必要です。私が考えた最初の選択肢は、retからMyClassオブジェクトを抽出し、それをvoid*にキャストすることでした。しかし、このvoid*は、retがスマートポインタによって行われる自動削除のために範囲外になったときにぶら下がります(私が間違っていれば私を修正してください)。

もう1つの方法は、スマートポインタ(たとえばretPtr)にポインタを割り当て、*retPtr=retとし、retPtrへの不透明なポインタを作成することです。私はこのオプションがうまくいくと思っていますが、これが最善の方法ですか?

何か助けていただければ幸いです。

+3

私はあなたの最後の提案に同意します。 –

+3

どのような種類のスマートポインタで、私はstdlibにSmartPointerというクラスがないことを知っています。これは問題のスマートポインタのセマンティクスに大きく依存します... – Vality

+0

Cライブラリは実際に 'ObjectType'のメンバにアクセスする必要がありますか?それとも、しばらくの間ポインタを保持し、後でそれを解放するのですか?また、 'ObjectType'と' MyClass'の関係は何ですか? –

答えて

1

コメントは、Cコードがスマートポインタを保持する必要があることを示していますが、別のC++関数にそのまま渡す以外に何もする必要はありません。そのためのコードは次のようになります。

// Shared header 
#ifdef __cplusplus 
extern "C" { 
#endif 

void * foo_acquire(void); 
int foo_bar(void *); 
void foo_release(void *); 

#ifdef __cplusplus 
} 
#endif 

// C++ implementation 
extern "C" void *foo_acquire() 
{ 
    return new SmartPointer<MyClass>(foo()); 
} 

extern "C" int foo_bar(void *s) 
{ 
    auto& sp = *static_cast< SmartPointer<MyClass> * >(s); 
    return bar(sp); // bar represents some function expecting the smart pointer 
} 

extern "C" void foo_release(void *s) 
{ 
    delete static_cast<SmartPointer<MyClass> *>(s); 
} 

これはSmartPointerの移動・コンストラクタ(またはコピーコンストラクタを、それが動き、コンストラクタを持っていなかった場合)、スマートポインタによってサポートされるべき操作を使用しています。

Cコードでの暗黙的な変換を防止する場合は、void *の代わりに不透明なハンドルを使用できます。 (例えば、メンバーとしてvoid *を有する構造体)。

5

Cコードには不透明なポインタを返す必要があります。だから、newを使用してポインタを返すことができる新しいスマートポインタを割り当てる必要があります。

あなたの唯一の選択肢については、共有ポインタのコレクションを使用してオブジェクトを存続させることです。 Cコードでオブジェクトが終了したことが示されると、コレクションから共有ポインタが削除されます。これにより、Cコードに必要なあらゆる種類の識別子を返すことができます。これは、コレクション内のオブジェクトを見つけるためのハンドルとして使用されています。

+1

ハンドルの考え方は良いことです。結局のところ、 'FILE'がCでどのように動作するのかということです。 –

2

David Schwartzの回答に加えて、COMのIUnknownに似たアプローチを検討することもできます。関数ポインタ(純粋なC言語でC++インタフェースをシミュレートする)を含む構造体を定義し、AddRefやReleaseのようなメソッドを公開してリフ数を増やしたり解放したりすることができます。

呼び出し元は、その構造体へのポインタを取得するので、AddRefとReleaseを使用して、返されたオブジェクトの適切な有効期間を制御できます。

さらに、他のメソッド(関数ポインタ)を構造体に追加して、返されたオブジェクトの他の機能を公開することもできます。

This article on COM in plain Cは詳細を説明します。

0

構造体をポインタだけでなくCコードに公開します。前記構造体において、ポインタとその状態のインジケータを有する。構造体を認識できるように、スマートポインタにビヘイビアを追加します。構造体には、ポインタとその割り当てられたオブジェクトの状態の両方が含まれます。したがって、スマートポインタの余分な動作は、構造体の割り当てられたオブジェクトの状態を更新する必要があります(たとえば、スマートポインタが割り当てを解除するときに値を設定するなど)。

0

コメントは、Cコードが何かを保持し、C++ APIにSmartPointerを渡す必要があることを示していますが、別のC++関数にそのまま渡す以外に何もする必要はありません。

私はのはEnableSharedFromThisを言わせ、あなたには、いくつかのstd::enable_shared_from_thisように考える作成する必要があると思う:

EnableSharedFromThisからあなたMyClass inheriteを行います

struct MyClass : public EnableSharedFromThis, public AnotherBaseClass { 
//... 
}; 

共有ヘッダー:

#ifdef __cplusplus 
extern "C" { 
#endif 

struct MyClass; 
MyClass * foo_acquire(void); 
int foo_bar(MyClass *); 
void foo_release(MyClass *); 

#ifdef __cplusplus 
} 
#endif 

C++の実装:

List<SmartPointer<MyClass> > listToEnsureLifeTime; 
extern "C" MyClass * foo_acquire() 
{ 
    SmartPointer<MyClass> ptr = foo(); 
    listToEnsureLifeTime.Add(ptr); 
    return ptr.get(); 
} 

extern "C" int foo_bar(MyClass *s) 
{ 
    // bar represents some function expecting the smart pointer 
    return bar(s->SharedFromThis()); 
} 

extern "C" void foo_release(MyClass *s) 
{ 
    // I suppose this list can take difference SmartPointer with same 
    // inner have the same hash or something like that 
    listToEnsureLifeTime.erase(s->SharedFromThis()); 
} 
関連する問題