I'm trying to use a C library which basically only exposes typedefs
to structs that are used internally. The issue is that I want to use smart pointers to manage the lifetime of the raw pointers which are the interface to the library, but I cannot create the smart pointers because of an incomplete_type
error. Note I have asked a previous question previous question trying to solve the same problem but the question turned out to be a poor representation of my actualy problem:
I cannot figure out how to use a smart pointer to the type that I need from the C library. Instead I've been using smart pointers to the underlying raw pointers (see below) but this isn't exactly what I'd like, and probably not ideal.
Here is some code:
#include "librdf.h" // the library I need to use
int main() {
// call necessary to use the librdf library.
librdf_world *world = librdf_new_world();
librdf_world_open(world);
/*
* Error: In instantiation of function template specialization
* 'std::make_unique<librdf_world_s, librdf_world_s *&>'
* allocation of incomplete type 'librdf_world_s'
*/
std::unique_ptr<librdf_world> w1 = std::make_unique<librdf_world>(world);
/* // errors:
* 1) Template argument for template type parameter must be a type // (on librdf_free_world left)
*
* 2) No matching function for call to 'make_unique' // (on std::make_unique<librdf_world, librdf_free_world>, right)
*
*/
std::unique_ptr<librdf_world, librdf_free_world> w2 = std::make_unique<librdf_world, librdf_free_world>(world);
/* Error:
* In instantiation of template class 'std::unique_ptr<librdf_world_s, void (librdf_world_s *)>'
* data member instantiated with function type 'void (librdf_world_s *)' // (on w3)
*
* No matching function for call to 'make_unique' candidate function template not viable:
* cannot convert argument of incomplete type 'librdf_world *'
* (aka 'librdf_world_s *') to 'void (&&)(librdf_world_s *)' for 1st argument // (on std::make_unique<librdf_world, decltype(librdf_free_world)>)
*/
std::unique_ptr<librdf_world, decltype(librdf_free_world)> w3 = std::make_unique<librdf_world, decltype(librdf_free_world)>(world);
/* No error:
* This version actually works and has been my strategy for a while now. Note,
* I've been using shared_ptr because I need other objects to have a reference
* to the `world` to create other objects from the library. An example call to the library would be:
* librdf_new_node(world, ... other arguments ... );
* However using a smart pointer to a raw pointer doesn't solve the problem of automatically
* managing the lifetime of the underlying raw pointer (according to valgrind). My attempt at
* overcoming this issue is to wrap the smart pointer in a class and have the shared_ptr as
* the single private variable. Then, in the destructor, when the shared_ptr::use_count gets to 1
* I call the C destructor.
* i.e.
* ~LibrdfWorld(){ // wrapper class name
* if (world_.use_count == 1){ // world_ is private instance of shared pointer to librdf_world*
* librdf_free_world(*world_.getWorld()); // call the c library destructor when ref count gets to 1
* }
*
*/
std::shared_ptr<librdf_world*> w4 = std::make_shared<librdf_world*>(world);
}
So what is the best way to use this library from c++? Do I use the raw pointers and manage the life times manually? Do I use the smart pointer to the raw pointer? Or is there another way I haven't thought of.