Search code examples
c++boostshared-ptrobject-lifetime

Managing C type lifecycle using boost's shared_ptr?


I have a question similar to How to manage object life time using Boost library smart pointers? but, in my case, the "object" isn't a C++ object at all, but an opaque type returned/passed out from a C API. The type does not have pointer semantics, i.e., there is no dereferencing; it is, however, passed as an argument to other functions in the C API. The type also has a definitive close API which must be called in order to clean up internal resources.

So, I have a C API that's something along the lines of

opaque_legacy_type_t x;
XXopen(..., &x); // allocates/opens resource and fills out 'x' to be used later
XXdoSomethingWithResource(x, ...); // do something with resources related to 'x'
...more actions...
XXclose(x); // closes and cleans up resources related to 'x'

For various reasons, in my C++ code I would like to manage "instances" of opaque_legacy_type_t much like I would manage heap-allocated object instances, i.e. with similar sharing semantics as boost::shared_ptr<>. It seems that shared_ptr offers enough that I can manage calling XXclose by doing this:

opaque_legacy_type_t x;
XXopen(..., &x);
boost::shared_ptr<opaque_legacy_type_t> managed(x, XXclose);

But, since opaque_legacy_type_t doesn't have pointer semantics, the usage of managed is a bit clumsy.

What I'd like to do is have something like a managed_type that is similar to shared_ptr, and am looking for ideas that don't require me to write it all.

EDIT: I corrected my original screw-up in the example. The legacy API takes the opaque type by value rather than by pointer.


Solution

  • You could use boost smart pointers together with the pimpl idom:

    class shared_opaque_legacy_type_t {
        struct impl {
            opaque_legacy_type_t t;
            impl(...) { XXOpen(..., t); }
            ~impl(...) { XXClose(t); }
        }
        boost::shared_ptr<impl> _impl;
    public:
        shared_opaque_lagacy_type_t(...) : _impl(new impl(...)) {}
    
        opaque_legacy_type_t* get() {
            return _impl->t;
        }
    };
    
    
    shared_opaque_legacy_type_t x(...);
    XXdoSomethingWithResource(x.get(), ...);
    

    The drawback is that you could still call XXclose(x.get()) and invalidate your object.

    UPDATE: Fixed it. :-)