C で書かれたライブラリを備えたスマート ポインター
-
20-08-2019 - |
質問
私は C++ と OpenCV ライブラリを使用しています。これは画像処理ライブラリですが、この質問には関係ありません。現在、デザインを決定する必要があります。
OpenCV は C ライブラリであり、そのデータ構造 (CvMat など) が構造体として宣言されています。作成するには cvCreateMat などの関数を使用し、解放するには cvReleaseMat などの関数を使用します。C++ プログラマーである私は、特別なプログラムを作成しました。 cv_scoped
スコープ外になったときに cvReleaseMat を自動的に呼び出すクラス (例: boost::scoped_ptr
).
今気づいたのは、使えたらいいのにということです auto_ptr
そして shared_ptr
場合にも同様に。自分でコードを書いているだけだと思う cv_auto_ptr
そして cv_shared_ptr
授業を受けるのは時間の無駄であるだけでなく、悪い考えです。そこで解決策を探していたところ、3つの可能性を思いつきました。
初め, すでに作成した cv_scoped クラスを使用できました。に名前を変更します cv_ptr
次に、次のようにスマート ポインターを使用します。 std::auto_ptr<cv_ptr>
. 。ただし、これの厄介な点は、常に 2 回逆参照する必要があることです。
std::auto_ptr<cv_ptr> matrix(cv_ptr(cvCreateMat(320, 240, CV_32FC3)));
cvPow(matrix.get()->get()); // one get for the auto_ptr, one for the cv_ptr
暗黙的な変換を宣言できるように見えますが、実際にはできません。OpenCV の関数のほとんどにはパラメータ void* があるため、暗黙的な変換は呼び出されません。二重逆参照を行わなくても済む方法が本当に欲しいです。
2番, 、何とかオーバーライドできました operator delete
. 。これを CvMat (およびその他のいくつかの) 型にのみ適用したいため、グローバル演算子の delete をオーバーライドしたくありません。ただし、ライブラリを変更できないため、追加することはできません operator delete
CvMat 構造体に追加します。したがって、これがどのように機能するかわかりません。
三番目, 、自分で書き直すこともできます auto_ptr
, scoped_ptr
, 、 そして shared_ptr
. 。大人数のクラスではないのでそれほど難しくはないと思いますが、これは設計が悪いような気がします。私がこれを行うとしたら、おそらく次のようなことをするでしょう。
class cv_auto_ptr {
public:
cv_auto_ptr();
~cv_auto_ptr();
// each method would just be a proxy for the smart pointer
CvMat* get() { return this->matrix_.get()->get(); }
// all the other operators/methods in auto_ptr would be the same, you get the idea
private:
auto_ptr<cv_ptr> matrix_; // cv_ptr deletes CvMat properly
}
私の状況ならどうしますか?これを理解するのを手伝ってください。
解決
auto_ptr は実際には C++ クラスの RAII 用に設計されており、コンストラクト/デストラクターを使用して、おそらく使用すべきではない (しかし使用できる) ものに使用をプッシュしています。
とにかく、毎回動的に割り当てることなく、C++ オブジェクトを通常のスタック変数であるかのように使用できるようにしたいと思いませんか?
問題に対する標準的な解決策は、コンストラクター/デストラクターを使用してラッパーを作成することです。
ただし、C 関数で使用できるようにするには、内部キャスト演算子を追加するだけで、C 関数に渡されたときに魔法のように自動的に C オブジェクトに変換されます。
ラッパークラスを書きます。
class Mat
{
CvMat* impl;
public:
Mat(/* Constructor Arguments */)
{
impl = cvCreateMat(/* BLAH */);
}
~Mat()
{
cvReleaseMat(impl);
}
operator CvMat*()
{ // Cast opertator. Convert your C++ wrapper object into C object
// when you use it with all those C functions that come with the
// library.
return impl;
}
};
void Plop(CvMat* x)
{ // Some C function dealing with CvMat
}
int main()
{ // Don't need to dynamically allocate
Mat m; // Just create on the stack.
Plop(m); // Call Plop directly
std::auto_ptr<Mat> mP(new Mat);
Plop(*mP);
}
他のヒント
考えられる 1 つのアプローチは、次の事実を利用することです。 std::tr1::shared_ptr
カスタム削除機能を提供する機能があります。私は OpenCV に詳しくないので、あなたの書いた内容から推測しています。
struct CvMatDeleter
{
void operator( CvMat* p ) { cvReleaseMat( p ) ; }
};
void test()
{
std::tr1::shared_ptr< CvMat > pMat( cvCreateMat(320, 240, CV_32FC3), CvMatDeleter() );
// . . .
}
デリータは共有ポインタに保存されているため、通常どおり使用することができ、最終的に共有生ポインタを削除する必要があるときに、 cvReleaseMat
必要に応じて呼び出されます。ご了承ください auto_ptr
そして scoped_ptr
これは非常に軽量なクラスなので、カスタム デリーターの機能はありませんが、多少のオーバーヘッドを覚悟できれば、 shared_ptr
代わりに使用できます。
例外の安全性だけを気にする場合は、行列を使用するたびにこれを実行してください。
void f() {
try {
CvMat* mat = cvCreateMat(320, 240, CV_32FC3));
// ...
} catch(...) {
cvReleaseMat(mat);
throw;
}
cvReleaseMat(mat);
}
一方、次のことが必要な場合は、 正気の 解決策としては、さらに頑張って完全なラッパーを作成してください。
namespace cv {
class Mat {
public:
enum Type { /* ... */ };
Mat(int w, int h, Type type) {
impl = cvCreateMat(w, h, intFromType(type));
}
~Mat() {
cvReleaseMat(impl);
}
void pow() { // wrap all operations
cvPow(impl);
}
private:
CvMat* impl;
};
}
中道を行く、汎用スマート ポインタと「cv_ptrs」の寄せ集めを使用するのは、頭痛の種と不必要な複雑さのレシピのように思えます。