2009-01-31 5 views
1

Я использую C++ с библиотекой OpenCV, которая представляет собой библиотечную обработку изображений, хотя это не относится к этому вопросу. В настоящее время у меня есть дизайнерское решение.Умные указатели с библиотекой, написанной на языке C

OpenCV, являющийся библиотекой C, имеет свои структуры данных (такие как CvMat), объявленные как структуры. Чтобы создать их, вы используете такие функции, как cvCreateMat, и для их выпуска вы используете такие функции, как cvReleaseMat. Будучи программистом на C++, я создал специальный класс cv_scoped, который автоматически вызывается cvReleaseMat, когда он выходит из сферы действия (например, boost::scoped_ptr).

То, что я понимаю сейчас, это то, что я хотел бы использовать auto_ptr и shared_ptr в случаях. Я просто чувствую, что писать код для моих собственных классов cv_auto_ptr и cv_shared_ptr будет плохой идеей, не говоря уже о пустой трате времени. Поэтому я искал решения, и у меня появились три возможности.

Первый, я мог бы использовать класс cv_scoped, который я уже сделал. Я бы переименовал его в cv_ptr, а затем использовал интеллектуальные указатели, например: std::auto_ptr<cv_ptr>. Раздражающая вещь об этом, хотя это, я всегда должен разыменованию дважды:

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 * - поэтому не будет вызвано неявное преобразование. Мне бы очень хотелось сделать это, когда мне не пришлось делать двойное разыменование.

Второй, я мог бы как-то переопределить operator delete. Я не хочу переопределять глобальный оператор delete, потому что я бы хотел, чтобы это применимо к CvMat (и нескольким другим) типам. Однако я не могу изменить библиотеку, поэтому я не могу добавить 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 
} 

Что бы вы сделали в моей ситуации? Пожалуйста, помогите мне разобраться в этом.

ответ

4

auto_ptr действительно предназначены для RAII на C++-классе с конструкциями/деструкторами, которые вы нажимаете на их использование здесь, чтобы вещи, которые они, вероятно, не должны использовать (но могут).

В любом случае, вы хотите иметь возможность использовать свой объект 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); 
} 
+0

Я думал об этом около 15 минут, и я думаю, что вы можете быть на лучшее решение здесь. Различия в этом и других решениях являются тонкими, но очень важными. Я подумаю об этом немного дольше, прежде чем принимать этот ответ. –

3

Если все, что вы заботитесь о безопасности исключений, делаю это каждый раз вы используете матрицы:

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; 
}; 

} 

Going среднего пути, используя Ходжа мешанину общих интеллектуальных указателей и «cv_ptrs» звучит как рецепт для головной боли и ненужных осложнения.

+0

Я вижу вашу точку зрения, но я думаю, что, возможно, я поеду своими умными указателями на это. Я думаю, потому что существует так много разных функций и типов, я чувствую, что я изобретаю колесо, переписывая свои подписи. Спасибо хоть! –

6

Один из подходов, который вы могли бы рассмотреть, заключается в том, чтобы использовать тот факт, что 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()); 
    // . . . 
} 

Поскольку Deleter является магазин в общем указателе вы можете просто использовать его как обычно, и когда общий сырой указатель, наконец, должен быть удален, cvReleaseMat будет называться по мере необходимости. Обратите внимание, что auto_ptr и scoped_ptr являются гораздо более легкими классами, поэтому у них нет функций для пользовательских удалений, но если вы готовы к небольшим накладным расходам, то вместо них можно использовать shared_ptr.

+0

Ничего себе это звучит как раз то, что мне нужно, для shared_ptr как минимум. Благодаря! –

+0

hmm Почему функтор? почему бы не пройти cvReleaseMat как отправитель напрямую? – 2009-01-31 10:28:21

+0

Нет настоящей причины; привычка, я полагаю. В некоторых случаях может быть несколько более эффективным использование функтора, так как его тело может быть встроено в точку использования. В этом случае я сомневаюсь, что это имеет какое-то значение. –