Recall in the aligned_malloc
article that we noted the need to pair aligned_malloc
with aligned_free
. Using the wrong free
call can cause serious problems, as we have modified the pointer that malloc
originally returned to us.
This problem of pairing allocators and deleters also applies in other situations: new
must be paired with delete
, while new[]
must be paired with delete[]
.
But what if I free the memory at the later stage and don’t realize it’s an aligned pointer (or was allocated with new[]
)? What if I just have a brain fart? How can we protect against using the incorrect free
or delete
call?
In my earlier article about C++11 smart pointers, I highlighted that we can specify a deleter when declaring our smart pointers. When the pointer goes out of scope or is reset
, the correct deleter is automatically called. Let’s use this to protect ourselves from introducing unnecessary errors.
std::unique_ptr
If you are using a deleter with std::unique_ptr
, you must specify the prototype for your deleter function in the pointer type.
std::unique_ptr<uint8_t, decltype(&aligned_free)>
This can be quite tedious to type, so instead I recommend creating an alias. We can define a unique_ptr_aligned
type that includes our aligned_free
prototype while leaving the type as templated value:
template<class T> using unique_ptr_aligned = std::unique_ptr<T,
decltype(&aligned_free)>;
The primary benefit from the alias is that you can use it in multiple locations and function prototypes without the pesky decltype(&aligned_free)
being typed everywhere.
Now that we have our alias, we can use it to create a new aligned pointer. Note that you need to use aligned_malloc
rather than new
to generate the memory to pass into the constructor. We also must still specify the specific deleter function we need to call.
unique_ptr_aligned<uint8_t[]> p(
static_cast<uint8_t*>(aligned_malloc(32, 1024)),
&aligned_free);
That’s still quite a bit of typing. Wouldn’t it be better if I could just type the following?
auto p = aligned_uptr<uint8_t>(32, 100);
Luckily, with templates, we can:
template<class T>
unique_ptr_aligned<T> aligned_uptr(size_t align, size_t size)
{
return unique_ptr_aligned<T>(
static_cast<T*>(aligned_malloc(align, size)),
&aligned_free);
}
We have just hidden the details within our templated function. We can make sure all aligned_uptr
calls pass through aligned_malloc
and specify aligned_free
as the detail, leaving us to simply worry about the type, the alignment, and the memory allocation size.
std::shared_ptr
std::shared_ptr
is an easier case to handle than std::unique_ptr
. While std::unique_ptr
requires the deleter to be part of the pointer type, std::shared_ptr
does not. You simply need to include the deleter in the constructor call for your std::shared_ptr
:
std::shared_ptr<uint8_t> p(
static_cast<uint8_t*>(aligned_malloc(32, 128)),
&aligned_free);
Like with the std::unique_ptr
example, we can shorten this typing significantly with a templated function:
template<class T>
std::shared_ptr<T> aligned_sptr(size_t align, size_t size)
{
return std::shared_ptr<T>(
static_cast<T*>(aligned_malloc(align, size)),
&aligned_free);
}
Resulting in this much simpler declaration:
auto p = aligned_sptr<uint8_t>(64, 100);
Putting it all together
I have uploaded a “smart pointer aligned” example to the embedded-resources github repo. To build the example in examples/cpp
, simply run:
make aligned_ptr
I also added memory.h
to examples/c
, where the aligned_malloc
and aligned_free
prototypes live.
The smart pointer example uses malloc_aligned.c
, so I have moved the COMPILE_AS_EXAMPLE
definition to the C examples Makefile. This change allows the C++ examples to use malloc_aligned.c
as a library by removing the main
function.
Further Reading
- Generating Aligned Memory: implementation of
aligned_malloc
andaligned_free
- C++ Smart Pointers
Using C++ Without the Heap
Want to use C++, but worried about how much it relies on dynamic memory allocations? Our course provides a hands-on approach for learning a diverse set of patterns, tools, and techniques for writing C++ code that never uses the heap.
Learn More on the Course Page