Using std::unique_ptr with “Legacy” output APIs

A basic tenet of modern C++ programming; for leak-free code, you must use std::unique_ptr  and/or std::shared_ptr  by default.  Any time you allocate memory, it should be placed immediately into a smart pointer.  Ideally, the smart pointer’s initialization will also occur on the same line of code as the memory allocation:

There are times, however, when your application must interact with – *gasp!* – legacy APIs. Legacy APIs which might allocate memory before handing off ownership to you, the client.  This article will discuss clean, convenient ways to ensure that memory allocated from a ‘legacy’ API is reliably placed into a smart pointer.

When dealing with a legacy API which allocates memory, it will typically take one of two forms:

Type 1: Directly return a pointer to the allocated memory

This form of API is the easiest to deal with; you can directly store the return value into a smart pointer:

One caveat to watch out for; if your legacy function is returning an array of items in memory, your unique_ptr  declaration needs to reflect that;

If you fail to catch this distinction, your unique_ptr  will only delete the first element of the array during destruction, resulting in a memory leak.

Type 2: Allocate the memory into a user-provided pointer

This type of API is the entire reason this post exists.  This type of API takes either a raw pointer reference, or a raw pointer-to-a-pointer, as one of its function parameters.  The function allocates the memory and directly assigns the user-provided pointer to it.  You could call this an “output pointer parameter”;

Either way; after calling this type of legacy function, it is the caller’s responsibility to clean up the allocated memory, pointed to by pResult .

Smart pointers – std::unique_ptr  and std::shared_ptr  – are not designed to be used in place of ‘output’ raw pointer parameters, such as this.  In order to get this memory safely into a smart pointer, we need to temporarily use a legacy/raw pointer, and then move it to the smart pointer, after the function returns.  This is not ideal:

Okay, this is a serviceable approach, but I’m not a fan. If there are a lot of legacy API calls, then you have this extra ‘temporary’ raw pointer used everywhere, potentially confusing readers and introducing potential areas for error.  You might even be tempted to forgo the smart pointer entirely, to avoid the hassle of the extra keystrokes.

It would be much more useful to have an ‘adapter’ class.  Such a class might be passed directly into the legacy API, and automatically assign the result into a given smart pointer, on the way back out.

Such a class exists – because I wrote it – and I’ve already used it a number of times!  Introducing the out_ptr_adapter  class, defined in the space below:

What’s happening here?  The out_ptr_adapter  takes a single constructor argument – your desired ‘target’ pointer.  This is the unique_ptr  which will receive the resulting allocated memory, upon the legacy API’s return.  The out_ptr_adapter  also privately holds a raw, temporary pointer.  This is the actual pointer which is provided to the legacy API, and it supports both of the previously-described parameter types: ‘reference-to-a-pointer’ and ‘pointer-to-a-pointer,’ via implicit casting operators.

Upon destruction, the resulting memory in the raw pointer is assigned to the constructor-provided unique_ptr .

For this to work correctly, the out_ptr_adapter  must be a ‘temporary’ variable.  You create it within the call to the function itself, which guarantees it will be destroyed automatically (and thus, the memory ownership transferred) when the legacy function returns.

Underneath the class definition, the make_out_ptr_adapter  function is provided as a convenience.  It allows us to save some typing through C++11’s type inference capabilities.

Enough talk, let’s see how it’s used.

Nice and short, right?  This block of code creates a temporary out_ptr_adapter  object, passes it into the legacy API, and assigns the resulting memory to our unique_ptr  variable as soon as the function returns.

Go forth and make leak-free code.

Leave a Reply

Your email address will not be published. Required fields are marked *