Welcome to Boring Gamedev, a hopefully recurring series of articles where I dive deeper into the boring aspects of AAA gamedev, namely wrangling C++ into something resembling a useful language.
Suppose you have a third-party library; let's call it CookieJar. You're perfectly happy with its interface, but the code is starting to show its age, and the library uses raw pointers everywhere. You would prefer to use smart pointers, which guarantee that memory is freed when the instance goes out of scope, but this doesn't work with a library that takes raw pointers.
Or does it?
Before you can use this fictional CookieJar library, you must call the CookieJar_Initialize() function to initialize it, which will return a CookieJar*, a raw pointer to memory that the library initializes. If, for whatever reason, initialization fails, the function will return nullptr.
Because you are a good and proper programmer, you check that the initialization succeeded, and log the error from the library when it fails.
CookieJar* cookieJar = CookieJar_Initialize(params);
if (cookieJar == nullptr)
{
LOG_ERROR(CookieJar, "Failed to initialize: %s", CookieJar_GetLastError());
return EXIT_FAILURE;
}
Now that the cookie jar is initialized, you can use it to manage your cookies:
Cookie* choccy = cookieJar->Add("chocolate");
Cookie* minty = cookieJar->Retrieve("mint");
cookeJar->Consume(choccy, minty);
Tasty!
The library makes it your responsibility to free the memory. Failing to do so will result in a memory leak in your application. To free the memory correctly, you must call CookieJar_Destroy() with your library pointer before you actually delete it. But you also want to avoid trying to free the memory twice, which results in a dreaded heap corruption, so you first check if the library hasn't already been destroyed:
if (cookieJar != nullptr)
{
CookieJar_Destroy(cookieJar);
cookieJar = nullptr;
}
But this is obviously slightly brittle code. Because where should you put it? At the end of our main loop? Or, heaven forbid, in the destructor of our CookieJarManager class? It would be much better if the library memory lifetime could manage itself.
Good news
And here's where today's Boring Gamedev Tip comes in. The std::unique_ptr class can take a custom "deleter" function that is called right before the memory is freed. It's a bit cumbersome, but we can wrap it in a custom type with the using keyword:
template <typename T>
using TLibraryPtr = std::unique_ptr<T, std::function<void(T*)>>;
TLibraryPtr<CookieJar> cookieJar{
CookieJar_Initialize(params),
[](CookieJar* cookieJar) {
CookieJar_Destroy(cookieJar);
}
);
if (cookieJar == nullptr)
{
LOG_ERROR(CookieJar, "Failed to initialize: %s", CookieJar_GetLastError());
return EXIT_FAILURE;
}
And now the third-party library is always destroyed correctly when you go out of scope!
