Search code examples
c++opencvsmart-pointersraii

Smart pointers with a library written in C


I'm using C++ with the OpenCV library, which is a library image-processing although that's not relevant for this question. Currently I have a design decision to make.

OpenCV, being a C library, has its data structures (such as CvMat) declared as structs. To create them, you use functions like cvCreateMat, and to release them, you use functions like cvReleaseMat. Being a C++ programmer, I created a special cv_scoped class which would automatically call cvReleaseMat when it went out of scope (like boost::scoped_ptr).

What I'm realising now is that I wish I could use auto_ptr and shared_ptr in cases as well. I just feel that writing code for my own cv_auto_ptr and cv_shared_ptr classes would be a bad idea, not to mention a waste of time. So I've been looking for solutions, and I've come up with three possibilities.

First, I could use the cv_scoped class I've already made. I'd rename it to cv_ptr and then use smart pointers like so: std::auto_ptr<cv_ptr>. The annoying thing about this though is, I'd always have to dereference twice:

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

I know it looks like I could declare an implicit conversion, but I couldn't actually - most of OpenCV's functions have the parameter void* - so no implicit conversion would be called. I would really like a way of doing this where I didn't have to do the double dereference.

Second, I could somehow override operator delete. I don't want to override the global operator delete because I'd only want this to apply to CvMat (and a few other) types. However, I can't change the library, so I can't add operator delete to the CvMat struct. So I don't know how this would work.

Third, I could just rewrite my own auto_ptr, scoped_ptr, and shared_ptr. They're not large classes so it wouldn't be too difficult, but I just feel like this is bad design. If I were to do this, I would probably do something along these lines:

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
}

What would you do in my situation? Please help me figure this one out.


Solution

  • The auto_ptr are really designed for RAII on C++ class with constructs/destructors you are pushing their uses here to things they probably should not be used for (but can).

    Anyway don'y you want to be able to use your C++ object as if it was a normal stack variable without dynamically allocating each time?

    The standard solution to your problem is to create a wrapper with constructor/destructor.
    But to make it usable by the C functions just add an internal cast operator so it auto-magically converts itself back to the C object when passed to a C function

    Write a wrapper class.

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