I happen to use the SDL library when i need to display graphics on the screen, but want to do it simpler than with OpenGL.
SDL is easy to use and portable, but it is a C-style library.
Because of that C nature, the library does not really help the user to write elegant, modern code.
Acquiring and Releasing SDL resources often ends up in ugly old school resource management.
It is not hard to fix that, and this article shows how useful shared_ptr
from the STL is in such cases.
The approach is easily extendable to other kinds of resources, too.
Typical C-Style SDL Code
#include <SDL2/SDL.h>
int main() {
if (SDL_Init(SDL_INIT_VIDEO) != 0){
return 1;
}
*win {SDL_CreateWindow("Window Title",
SDL_Window , SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, height, SDL_WINDOW_SHOWN)};
widthif (win == nullptr){
();
SDL_Quitreturn 1;
}
*ren {SDL_CreateRenderer(win, -1,
SDL_Renderer | SDL_RENDERER_PRESENTVSYNC)};
SDL_RENDERER_ACCELERATED if (ren == nullptr){
(win);
SDL_DestroyWindow();
SDL_Quitreturn 1;
}
*bmp {SDL_LoadBMP("some_bitmap.bmp")};
SDL_Surface if (bmp == nullptr){
(ren);
SDL_DestroyRenderer(win);
SDL_DestroyWindow();
SDL_Quitreturn 1;
}
*tex {SDL_CreateTextureFromSurface(ren, bmp)};
SDL_Texture if (tex == nullptr) {
(bmp);
SDL_FreeSurface(ren);
SDL_DestroyRenderer(win);
SDL_DestroyWindow();
SDL_Quitreturn 1;
}
// ...
(tex);
SDL_DestroyTexture(bmp);
SDL_FreeSurface(ren);
SDL_DestroyRenderer(win);
SDL_DestroyWindow();
SDL_Quit}
This code contains a list of resource release code which is also quite error prone to write/maintain.
Using shared_ptr
to Automate Resource Handling
shared_ptr
from the STL is a useful helper class, which wraps a pointer to some resource payload.
As soon as a shared pointer is released, it automatically calls delete
on the payload instance being pointed to.
The resources which SDL gives us, cannot be release using delete
, as the SDL provides specific functions for every resource type.
In order to be able to use shared_ptr
’s automatic resource management capability, it needs to be customized to call different code than the default delete
call.
This is indeed possible:
shared_ptr
’s constructor takes a second, optional parameter, which can be a lambda expression.
This lambda expression will then be called (with the payload pointer as its only parameter) by the shared pointer class instead of its default delete
call.
The following code provides a function sdl_shared
, which takes a pointer to an SDL resource, and wraps it into a shared_ptr
instance which automatically calls the right SDL specific release function at its end of life cycle:
#include <memory> // shared_ptr
#include <SDL2/SDL.h>
static void SDL_DelRes(SDL_Window *r) { SDL_DestroyWindow(r); }
static void SDL_DelRes(SDL_Renderer *r) { SDL_DestroyRenderer(r); }
static void SDL_DelRes(SDL_Texture *r) { SDL_DestroyTexture(r); }
static void SDL_DelRes(SDL_Surface *r) { SDL_FreeSurface(r); }
template <typename T>
std::shared_ptr<T> sdl_shared(T *t) {
return std::shared_ptr<T>(t, [](T *t) { SDL_DelRes(t); });
}
sdl_shared
is a template function which deduces the pointer type from the input parameter.
This helper function can be wrapped around any SDL resource allocating function, as long as one SDL_DelRes
overload is defined for the specific SDL resource type.
Using this helper, the resource management can be implemented much cleaner:
int main() {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
return 1;
}
{ SDL_Quit(); };
ON_EXIT
auto win (sdl_shared(SDL_CreateWindow("Hello World!",
, SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, height, SDL_WINDOW_SHOWN)));
widthif (win == nullptr) return 1;
auto ren (sdl_shared(SDL_CreateRenderer(win.get(), -1,
| SDL_RENDERER_PRESENTVSYNC)));
SDL_RENDERER_ACCELERATED if (ren == nullptr) return 1;
auto bmp(sdl_shared(SDL_LoadBMP("hello.bmp")));
if (bmp == nullptr) return 1;
auto tex (sdl_shared(SDL_CreateTextureFromSurface(
.get(), bmp.get())));
renif (tex == nullptr) return 1;
// ...
}
Also have a look at the other article, which describes the ON_EXIT
macro.
All resources are automatically released, and the programmer does not need to define which resource is to be released in which case and what order.