I have written an extensible c++ wrapper around a very hard to use but also very useful c library. The goal is to have the convience of c++ for allocating the object, exposing its properties, deallocating the object, copy semantics etc…
The problem is this: sometimes the c library wants the underlying object (a pointer to the object), and the class destructor should not destroy the underlying memory. While most of the time, the destructor should deallocate the underlying object. I have experimented with setting a bool hasOwnership
flag in the class so that the destructor, assignment operator, etc… will know whether or not it should free the underlying memory or not. However, this is cumbersome for the user, and also, sometimes there is no way to know when another process will be using that memory.
Currently, I have it setup where when the assignment comes from a pointer of the same type as the underlying type, then I set the hasOwnership flag. I do the same when the overloaded constructor is called using the pointer from the c library. Yet, this still does not handle the case when the user has created the object and passed it to one of my functions which calls the c_api and the library stores the pointer for later use. If they were to delete their object, then it would no doubt cause a segfault in the c library.
Is there a design pattern that would simplify this process? Maybe some sort of reference counting?
3
If the responsibility for cleaning up dynamically allocated stuff shifts between your wrapper class and the C library based on how things get used, you are either dealing with a badly designed C library, or you are trying to do too much in your wrapper class.
In the first case, all you can do is keep track of who is responsible for the cleanup and hope no mistakes are made (either by you or by the maintainers of the C library).
In the second case, you should rethink your wrapper design. Does all the functionality belong together in the same class, or can it be split into multiple classes. Perhaps the C library uses something similar to the Facade design pattern and you should keep a similar structure in your C++ wrapper.
In any case, even if the C library is responsible for cleaning up some stuff, there is nothing wrong with keeping a reference/pointer to that stuff. You just need to remember that you are not responsible for cleaning up what the pointer refers to.
3
Often you can use a pattern like this:
class C {
public:
void foo() {
underlying_foo(handle.get());
}
void bar() {
// transfers ownership
underlying_bar(handle.release());
}
// use default copy/move constructor and assignment operator
private:
struct deleter {
void operator()(T* ptr) {
deleter_fn(ptr);
}
};
std::unique_ptr<T, deleter> handle;
};
By using release
you can explicitly transfer ownership. However this is confusing and you should avoid it if at all possible.
Most C libraries have a C++-like object life cycle (object allocation, accessors, destruction) that maps nicely onto the C++ pattern without ownership transfer.
If users need shared ownership, they should use shared_ptr
with your classes. Don’t try to implement any ownership sharing yourself.
Update: If you want to make the transfer of ownership more explicit, you can use a reference qualifier:
void bar() && { ... }
Then users must call bar
on lvalues like this:
C o;
std::move(o).bar(); // transfer of ownership is explicit at call site
6
There is a straightforward answer to your issue, smart pointers. By using a smart pointer to hold the C library’s memory and adding a reference when the pointer is also in the library (and dropping the reference when the C library returns) then you will automatically free the memory when the reference count drops to zero (and only then)
3
If the library can free things internally, and the scenarios where this can happen is well documented, then all you can do is set a flag like you’re doing already.
1