c++reference-countingbinary-compatibility

Reference counted structure exposing a POD interface


I currently have a structure that declares a copy constructor, and a destructor, and keeps a reference count to a pimpl object. This allows me to pass this structure around by value. I need to refactor it because I want it to have a POD interface because it's part of a library that now needs to be used from code that uses an older C++ flavor. Because of this, I have to remove both the copy constructor and the desctructor. I can't figure out how I can keep this nice "pass by value", multiple ownership mechanism after I have removed that. Shared pointers are not an option because the structure is used as parameter to the method of other classes that also need to be seen as POD from the perspective of the user of the library.

struct Foo {
  Foo(const Foo&);
  const Foo& operator=(const Foo&);
  ~ Foo();
private:
  void* pimpl;
};

struct MainLibrary {
   void DoSomething(Foo param);
}

The user code now is like:

MainLibrary ml;
{
  Foo a;
  ml.doSomething(a);
}

at this point, the a variable can be kept for a long time inside the main library. For efficiency, the internals of Foo can't be deep copied each time, that's why the pimpl implementation keeps a reference counter that is incremented each time the instance of Foo is copied, decremented each time the instance of Foo is destroyed.


Solution

  • Too much for a comment... something like below. Client include foo03.h and their usage should remain unaffected. Your C++11 implementation is invoked via a "C" layer. You can find lots of examples if you search for "exposing C++ code to C" or similar....

    foo03.h:

    extern "C"
    {
        void* foo_constructor();
        void* foo_constructor2(void* rhs);
        void foo_assign(void* lhs, void* rhs);
        void foo_destructor(void* p_this);
    }
    
    struct Foo {
        Foo() { p_ = foo_constructor(); }
        Foo(const Foo& rhs) { p_ = foo_constructor2(rhs.p_); }
        const Foo& operator=(const Foo& rhs) { foo_assign(p_, rhs.p_); return *this; }
        ~Foo() { foo_destructor(p_); }
      private:
        void* p_;
    };
    

    foo11.h:

    // up to you whether you call this Foo (relying on mangling differences to avoid conflicts when
    // linking with C++03 objects), FooImpl, put it in a namespace ala Impl::Foo etc..
    struct FooImpl {
        FooImpl() { ... }
        FooImpl(const FooImpl&) { ... }
        const FooImpl& operator=(const FooImpl& rhs) { ... }
        ~FooImpl() { ... }
    };
    
    extern "C"
    {
        void* foo_constructor() { return new FooImpl(); }
        void* foo_constructor2(void* rhs) { return new FooImpl(*(FooImpl*)rhs); }
        void foo_assign(void* lhs, void* rhs) { *(FooImpl*)lhs = *(FooImpl*)rhs; }
        void foo_destructor(void* p_this) { delete (FooImpl*)p_this; }
    }