Search code examples
c++coperator-overloadingraii

Create a transparent wrapper class in C++ for a C-style object


I want to implement a class in C++ whose purpose is implementing RAII mechanism for a C-style object.

Then, I need to be able to pass an instance of this class to all the C-style functions that receive the mentioned C-style object as argument. I know that this should be solved with unique_ptr, but I cannot use C++11 for now. Anyway, I would like to understand how this should be made, regardless that there are better solutions.

I have several doubts regarding to which operators I must overload, and the difference beteween some of them.

Below is an example of code with the implementation I have made. I am specially confused with the operators 1 and 2 (what is the difference?)

I want to know if the code I have implemented covers all the use cases, I mean, all the scenarios where the C library could use the object. In addition, I would like to understand the difference between the operators 1 and 2.

C-Style object

// Example: a C-style library called "clib" which use an object called "cobj":

struct cobj {
    int n;    
};

void clib_create_cobj(struct cobj **obj) {
    *obj = (struct cobj*)malloc(sizeof(cobj));
    (*obj)->n = 25;
}

void clib_release_cobj(struct cobj *obj) {
    free(obj);
}

void clib_doSomething(struct cobj *obj) {
    std::cout << obj->n << std::endl;
}

C++ "transparent" wrapper

// My wrapper class for implementing RAII

class CobjWrapper {
    public:
        CobjWrapper(struct cobj *obj) : m_obj (obj) { }

        ~CobjWrapper() {
            if(m_obj != NULL) {
                clib_release_cobj(m_obj);
                m_obj = NULL;
            }
        }


        operator struct cobj* () const {    // (1)
            return m_obj;
        }

        struct cobj& operator * () {        // (2)
            return *m_obj;
        }

        struct cobj** operator & () {       // (3)
            return &m_obj;
        }

        struct cobj* operator->() {     // (4)
            return m_obj;
        }

    private:
        struct cobj *m_obj;

};

The main method

// The main method:

int main() {
  struct cobj *obj = NULL;

  clib_create_cobj(&obj);

  CobjWrapper w(obj);
  clib_doSomething(w);

  return 0;
}

The above source can be tested here:

http://cpp.sh/8nue3


Solution

  • The following is an implicit cast

    operator struct cobj* () const {    // (1)
    

    with example usage

    CobjWrapper wrapper = /**/;
    
    struct cobj* obj = wrapper;
    

    whereas the following is the unary operator *

    struct cobj& operator * () {        // (2)
    

    with example usage

    CobjWrapper wrapper = /**/;
    
    struct cobj& obj = *wrapper;
    

    BTW, I would completely hide the C struct, something like:

    class CobjWrapper {
    
        struct ObjDeleter
        {
            void operator()(cobj *obj) const { clib_release_cobj(obj); }
        };
    
        public:
            CobjWrapper()
            {
                 cobj *obj = nullptr;
                 clib_create_cobj(&obj);
                 m_obj.reset(obj);
            }
    
            void doSomething() { clib_doSomething(m_obj.get()); }
    
        private:
            std::unique_ptr<cobj, ObjDeleter> m_obj;
    };