Reputation: 43862
I have a C library that I'm porting to C++ that makes heavy use of manually reference-counted structs. I've considered using shared_ptr
to automatically handle the reference counting, but I also want to maintain the C API. The old signatures look something like this:
Object* object_create(void);
Object* object_retain(Object* o);
void object_release(Object* o);
If I use shared_ptr
, is there any way to effectively expose this manual reference counting in a C API?
Upvotes: 8
Views: 4538
Reputation: 19441
Since there is no RAII in C you will need to manage the shared pointers yourself via the create/destroy functions. So the handles you return should be shared_ptr-s, and your API would look like this:
// Opaque pointer wrapped in structure for type safety (in header)
typedef struct
{
void *ptr;
} ObjectHandle;
// Hide the gory details of resolving the shared ptr.
static std::shared_ptr<Object>* resolveHandle(ObjectHandle objectHandle)
{
return static_cast<std::shared_ptr<Object>*>(objectHandle.ptr);
}
// Just a sample on how to fully resolve the object for internal use.
static Object* resolveObject(ObjectHandle objectHandle)
{
return resolveHandle(objectHandle)->get();
}
// Public API functions.
ObjectHandle object_create(void)
{
return ObjectHandle{new std::shared_ptr<Object>(new Object())};
}
ObjectHandle object_retain(ObjectHandle o)
{
return ObjectHandle{new std::shared_ptr<Object>(*resolveHandle(o))};
}
void object_release(ObjectHandle o)
{
delete resolveHandle(o);
}
Upvotes: 0
Reputation: 1426
You can use std::shared_ptr::get
to retrieve the value of your pointer in your object_create
.
I'm not sure that you should maintain object_retain
or object_release
as it is already automatically handled by the shared_ptr
.
Do you want your library to be used by C code? If so then as @Angew pointed out in his comment, have a look at Boost.intrusive_ptr, it seems to be the best choice.
If you can assume that client code written in C will use the C library (which I think kind of make sense), then you can completely drop these functions and handle all internally. You can provide a raw pointer for compatibility with C api if you need to but all the lifetime management could be handled automatically with shared_ptr
.
Upvotes: 1
Reputation: 363737
The problem with shared_ptr
, as you'll have figured out already, is that you can't modify the reference count except by constructing or destroying instances. So no, there's no way to get this to work except by keeping a shared_ptr
to every constructed Object
around until its reference count drops to zero, but doing that right entails redoing much of the reference counting, so you gain very little.
Perhaps boost::intrusive_ptr
is a better option.
Upvotes: 7