NOTE: Every post ends with "END OF POST". If you don't see it then open the full post in a separate page!

Using std::unique_ptr (RAII) with malloc() and free()


This is a short post about using std::unique_ptr with malloc() and free(). Although, it can be used with other resource management functions too (files, sockets, etc.).

Often, when working with legacy C and C++ code, we see usages of malloc() and free(). Just like new and delete, explicit memory management should be hidden in the guts of libraries whenever possible and never be exposed to the casual programmer.

It would be great if the legacy code could be easily changed to use std::make_unique() to make it nice clean and safe at the same time. Unfortunately, std::make_unique() uses new and delete operators internally. So, if our legacy code uses some custom functions to allocate and deallocate memory then we may be forced to do more refactoring than we might have time for (e.g. to use new expressions instead of custom or malloc based allocators).

Luckily, we can still get the benefit of RAII by using std::unique_ptr by trading off some cleanliness.
But std::unique_ptr takes a deleter type. No problem, we have decltype() to the rescue!

#include <memory>

int main()
{
    auto Data =
        std::unique_ptr<double, decltype(free)*>{
            reinterpret_cast<double*>(malloc(sizeof(double)*50)),
            free };
    return 0;
}

The decltype(free) gives back a function type of “void (void*)” but we need a pointer to a function. So, we say “decltype(free)*” which gives us “void (*)(void*)”. Excellent!

A bit awkward, but it is still nice, since it does RAII (automatic free) and both the allocator (malloc()) and the deallocator (free()) is clearly visible to the reader.

With decltype() we don’t have to write our own deleter functor like in this example:

#include <memory>

struct MyDeleter
{
    void operator()(double *p) { free(p); }
};

int main()
{
    auto Data =
        std::unique_ptr<double, MyDeleter>{
            reinterpret_cast<double*>(malloc(sizeof(double) * 50)) };
    return 0;
}

Also, with decltype() we don’t have to spell out the type of the deleter like in this example:

#include <memory>

int main()
{
    auto Data =
        std::unique_ptr<double, void(*)(void*)>{
            reinterpret_cast<double*>(malloc(sizeof(double) * 50)),
            free };
    return 0;
}

So, with std::unique_ptr you can quickly hack RAII into legacy code. However, as a general guideline, prefer refactoring legacy code using modern C++.

END OF POST

Advertisements


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s