Search code examples
c++raii

How can I wrap a C object in a custom RAII Class in C++?


I am using the library igraph and the library uses a lot of allocating and freeing memory using custom functions. I want to remove the possibility of forgetting to free memory by using RAII.

The code beforehand looks something like this:

void doSomething() {
   igraph_vector_t shortest_path;

   igraph_get_shortest_path_dijkstra(
      someargs...,
      &shortest_path
      someargs...
   )

   igraph_vector_destroy(&shortest_path);
};

I want to replace igraph_vector_t with a custom C++ class that has a destructor that calls igraph_vector_destroy so I can just do:

void doSomething() {
   raii_igraph_vector_t shortest_path;

   igraph_get_shortest_path_dijkstra(
      someargs...,
      &shortest_path
      someargs...
   )
}

I can make a custom class/struct like:

struct raii_igraph_vector_int {
    igraph_vector_int_t vec;
    ~RAIIVectorInt(){
        igraph_vector_int_destroy(&vec);
    }
};

And I can pass it into the function like:

   igraph_get_shortest_path_dijkstra(
      someargs...,
      &shortest_path.vec
      someargs...
   )

However, I was wondering if there was a way to not need to add on the .vec and just treat it as if it were the previous type.


Solution

  • This should do the trick (totally untested, though):

    struct DeleteIGraphVector {
      void operator() (igraph_vector_t *vec) const {
        igraph_vector_destroy(vec);
        delete vec;
      }
    };
    
    using raii_igraph_vector_t = std::unique_ptr<igraph_vector_t, DeleteIGraphVector>;
    
    void doSomething() {
      raii_igraph_vector_t shortest_path{new igraph_vector_t};
      
      igraph_get_shortest_path_dijkstra(
        someargs...,
        shortest_path,
        someargs...
      );
    }
    

    It uses unique_ptr with a custom deleter to destroy and then delete the igraph type appropriately. One disadvantage of this approach is that the igraph_vector_t will be heap-allocated, though. This could be avoided with unique_resource, which hasn't quite made it into the C++ standard yet but is available in third-party libraries.

    If you don't want to write different deleters for each igraph type, you could try using templates, like this:

    template<typename T, void (*f_del)(T *)>
    struct GenericIGraphDeleter {
        void operator() (T *obj) {
            f_del(obj);
            delete obj;
        }
    };
    
    using raii_igraph_vector_t = std::unique_ptr<
        igraph_vector_t,
        GenericIGraphDeleter<igraph_vector_t, &igraph_vector_destroy>>;
    
    using raii_igraph_vector_int_t = std::unique_ptr<
        igraph_vector_int_t,
        GenericIGraphDeleter<igraph_vector_int_t, &igraph_vector_int_destroy>>;