8

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?

Alexis King
  • 43,109
  • 15
  • 131
  • 205
  • 1
    Just to clarify; you want a shared_ptr to wrap a raw pointer obtained from `object_create`, and have it call `object_release` at the relevant time? – Oliver Charlesworth Jun 15 '13 at 17:38
  • @OliCharlesworth No, I'm reimplenting `Object` as a class (instead of a struct) in C++, and I'm using `shared_ptr` within the C++ code. I'm interested if it's possible to get a raw pointer from a shared_ptr and then somehow still make it work with shared_ptr's refcount. – Alexis King Jun 15 '13 at 17:40
  • 3
    You might be better off using [Boost.intrusive_ptr](http://www.boost.org/doc/libs/1_53_0/libs/smart_ptr/intrusive_ptr.html). – Angew is no longer proud of SO Jun 15 '13 at 17:41
  • 2
    Ah ok. Well you can get a raw pointer from a shared_ptr no problem, (see http://stackoverflow.com/questions/505143/getting-a-normal-ptr-from-shared-ptr), but as you might expect, there is nothing tracking where that raw pointer goes (it's completely outside the reference-counting mechanism at that point). – Oliver Charlesworth Jun 15 '13 at 17:41
  • @OliCharlesworth Understandable, but I guess I'm wondering if there's *any* way of modifying shared_ptr's internal reference count so I can just manually reference count from the C side. – Alexis King Jun 15 '13 at 17:44
  • You can make shared_ptr use a custom deleter. – Antimony Jun 15 '13 at 17:45
  • For *any* way you can use static list of `shared_ptr`s hold by C code. – zch Jun 15 '13 at 17:46
  • 1
    Implementing as a struct in c and class in c++? This way less great pain. – kfsone Jun 15 '13 at 17:48
  • 1
    std::enable_shared_from_this might work for you, but unfortunately you need to have at least one shared_ptr reference from C++ code: see http://stackoverflow.com/questions/4428023/enable-shared-from-this-c0x-what-am-i-doing-wrong – han Jun 15 '13 at 18:02

3 Answers3

7

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.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
1

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.

Uflex
  • 1,396
  • 1
  • 13
  • 32
  • The reference count is not maintained automatically on the C side. If the last `shared_ptr` to an `Object` is destroyed but there's still an `Object*` pointing to it, the object is destroyed and the C pointer is left dangling. – Fred Foo Jun 15 '13 at 17:50
  • True, I was thinking of a raw pointer used as a viewer of the object, assuming that the `shared_ptr` would handle the lifetime of the object. – Uflex Jun 15 '13 at 17:55
0

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);
}
Calmarius
  • 18,570
  • 18
  • 110
  • 157