API が行う必要があることの 1 つは、ハンドルを前後に渡すことです。
わかりました
(例: 参照またはポインターを含む構造体)
なんで?「ハンドル」は、オブジェクトを識別するための単なる方法です。必ずしも参照またはポインターを保持する必要があるとは限りません。
1 つは、boost::shared_ptr や std::shared_ptr などのスマート ポインター パラダイムで、オブジェクトへの参照がなくなった場合にのみオブジェクトが破棄されるようにします。
確かに、
map<int, boost::shared_ptr<my_object>>
メモリ解放メカニズムに使用する場合は、ここで問題なく機能する可能性があります。
ライブラリ内のオブジェクトを破棄するだけで、
これは、スマート ポインターで存在する可能性がありますが、いずれかではありません。
入力としてそのオブジェクトへのハンドルを渡す後続の関数呼び出しは、単にエラー コードを返します。
確かにいいですね。
ライブラリがメモリの割り当てを担当する場合は、メモリの割り当て解除を担当する必要があります。
ライブラリの _GetNewObject() メソッドから単純な整数の「ハンドル」を返します。
ライブラリには、内部オブジェクトへのハンドルのマップが必要です。ライブラリの外にいる人は、C インターフェースからのオブジェクトを見るべきではありません。
すべてのライブラリ メソッドは、最初のパラメーターとしてハンドルを取る必要があります。
マルチスレッドの場合、2 つのスレッドが同じオブジェクトにアクセスする必要がありますか? その場合は、C API 関数に入ったときに発生し、終了する前に解放される、ある種のロックを組み込む必要があります。ライブラリ外のコードにこのロックを認識させたい場合は、決定を下す必要があります (おそらく認識しません)。ライブラリ関数を呼び出す C 関数は、おそらく戻り値を取得するだけで、施錠開錠。
したがって、ライブラリには次のものが必要です。
- 外部からはハンドルと見なされる、オブジェクトの割り当てと割り当て解除を行うインターフェース
- ハンドルを指定して何かを行うためのインターフェース。
編集: 詳細情報
ライブラリ内では、Factory パターンを使用して新しいオブジェクトを作成します。Factory は、オブジェクトの割り当て後に shared_ptr を配布する必要があります。このように、ライブラリ内の他のすべては単に shared_ptr を使用し、クリーンアップはかなり自動化されます (つまり、ファクトリはクリーンアップを覚えておくために作成されたもののリストを格納する必要がなく、明示的に delete を呼び出す必要はありません)。ハンドルを使用してマップに shared_ptr を格納します。次に利用可能なハンドルを取得し、ラップアラウンドを処理するために、GetNextHandle() 関数と一緒にある種の静的カウンターが必要になるでしょう (実行中のプログラムの存続期間内に作成および破棄されるオブジェクトの数によって異なります)。
次に、共有ポインタをプロキシに配置します。プロキシは非常に軽量である必要があり、実際のオブジェクトごとに多くの Proxy オブジェクトを持つことができます。各プロキシは、プライベート shared_ptr と、使用することを選択したスレッド/ミューテックス オブジェクトを保持します (これに関する情報を提供していないため、これ以上特定するのは困難です)。プロキシが作成されると、ミューテックスを取得し、破壊時に解放する必要があります (つまり、ロックを解放するための RAII)。
新しいオブジェクトを作成するか既存のオブジェクトを検索するかを決定する方法、および 2 つの異なるスレッドが同じオブジェクトを「検索」する方法に関する情報は含まれていません。ただし、オブジェクトが存在する場合は、各オブジェクトを一意に識別し、マップからハンドルを返すのに十分なパラメーターを持つ GetObject() があると仮定します。
この場合、可視の extern C ライブラリ関数のそれぞれがオブジェクト ハンドルを受け入れ、次のようになります。
指定されたハンドルの新しいプロキシを作成します。Proxy コンストラクターでは、Proxy はマップを調べてハンドルを見つけます。ハンドルが存在しない場合は、Factory にハンドルを作成するように依頼します (または、ここで選択したエラーを返します)。次に、プロキシがロックを取得します。次に、関数はプロキシからポインターを取得して使用します。関数が終了すると、プロキシはスコープ外になり、ロックを解放し、参照カウンターをデクリメントします。
2 つの関数が異なるスレッドで実行されている場合、関数の 1 つにプロキシが存在する限り、オブジェクトは引き続き存在します。他の関数は、マップから参照を削除するオブジェクトを削除するようにライブラリに要求できます。アクティブなプロキシ オブジェクトを持つ他のすべての関数が終了すると、最終的な shared_ptr が範囲外になり、オブジェクトが削除されます。
このほとんどは、テンプレートを使用して一般的に行うか、具象クラスを作成することができます。
編集: 詳細情報
プロキシは小さなクラスになります。これには shared_ptr があり、ロックがあります。クライアントによって呼び出される extern C 関数のスコープ内で Proxy をインスタンス化します (注: これは実際には、C++ クラスを使用できるなどのすべての利点を持つ C++ 関数です)。プロキシは小さく、スタックに配置する必要があります (これを新規作成して削除しないでください。その価値よりも問題が多くなります。スコープ付き変数を作成し、C++ に作業を任せてください)。プロキシは RAII パターンを使用して shared_ptr のコピーを取得し (shared_ptr の参照カウントをインクリメントします)、構築時にロックを取得します。Proxy がスコープ外になると、それが持っている shared_ptr が破棄されるため、参照カウントが減少します。Proxy デストラクタはロックを解放する必要があります。ところで、ブロッキングと、スレッドミューテックスをどのように機能させたいかについて考えたいと思うかもしれません。
マップには、他のすべてのコピー元の「マスター」shared_ptr が含まれます。ただし、プロキシがマップから shared_ptr を取得すると、「それを返す」ことを心配する必要がないため、これは柔軟で分離されています。マップ内の shared_ptr は削除できます (つまり、オブジェクトは Factory に「存在しません」) が、shared_ptr を持つ Proxy クラスがまだ存在する可能性があるため、実際のオブジェクトは、何かがそれを使用している限り存在し続けます。