When using SDL, I have been handling errors (e.g. during SDL initialisation) by returning false
whenever an error is encountered in a function, and true
otherwise. The cleaning-up is then performed by calling a close()
function at the very end of the main()
function, as such:
int main(){
if(!init()){
// do some stuff
}
...
close();
return 0;
}
close()
function:
void close(){
SDL_DestroyRenderer(g_Renderer);
SDL_DestroyWindow(g_Window);
IMG_Quit();
SDL_Quit();
}
However, I have realised that this method of cleaning-up means I can't really throw an exception, as close()
function would not be called.
After doing a bit of reading around, I have decided that I would try not to throw exceptions, unless needed, for performance-related reasons (I heard that this is important, especially in game development).
Also, this would mean I need to add more functions for cleaning up onto close()
as the program grows, which seems impractical and easy to forget.
So the questions I have are:
unique_ptr
(or other smart pointers) by initialising SDL object normally and provide a deleter class for cleaning-up.atexit()
to handle SDL_Quit()
and IMG_Quit()
?I was thinking of implementing a wrapper class, but then realised I would probably need to throw an exception in case of error during initialization, e.g. from SDL_CreateWindow()
,which I'm trying to avoid. Any help or advice?
Use
atexit()
to handleSDL_Quit()
andIMG_Quit()
?
I wouldn't use atexit
. Other alternatives give you a better control over when the cleanup happens.
Use
unique_ptr
(or other smart pointers) by initialising SDL object normally and provide a deleter class for cleaning-up.
It could work, but I don't like this approach. It would work for cleaning up pointers such as SDL_Window *
, but what if the thing you need to clean up is not a pointer? (E.g. if you want to call SDL_Quit()
automatically.)
Using unique_ptr
to clean up pointers and other approaches to clean up other things would introduce inconsistency into your code for no good reason, so I'd avoid it completely (except for memory management).
Create a C++ wrapper class to handle both initialisation in the constructor and cleaning up in its destructor
This is a good idea.
If you're going to do it, don't forget to follow the rule of three. Either delete the copy constructor and assignment operator:
MyWindow(const MyWindow &) = delete;
MyWindow &operator=(const MyWindow &) = delete;
Or make the class movable and not copyable, to make it easier to use.
There's yet another option: the scope guards. The idea is to put the cleanup code into a destructor of a local variable.
Those local variables are often created using a macro. This is what I use:
#include <exception>
#include <utility>
namespace Macro
{
template <typename T> class FinallyObject
{
T func;
public:
FinallyObject(T &&func) : func(std::move(func)) {}
FinallyObject(const FinallyObject &) = delete;
FinallyObject &operator=(const FinallyObject &) = delete;
~FinallyObject()
{
func();
}
};
}
#define FINALLY_impl_cat(a, b) FINALLY_impl_cat_(a, b)
#define FINALLY_impl_cat_(a, b) a##b
#define FINALLY(...) \
::Macro::FinallyObject FINALLY_impl_cat(_finally_object_,__LINE__) ([&]{ __VA_ARGS__ });
And here's how you use them:
int main(int, char **)
{
if (SDL_Init(...))
/*throw or exit with an error*/;
FINALLY( SDL_Quit(); )
SDL_Window *window = SDL_CreateWindow(...);
if (!window)
/*throw or exit with an error*/;
FINALLY( SDL_DestroyWindow(window); )
// Your code here.
}
For the resources that are used until the program exits, then cleanup shouldn't be necessary. The OS will clean them up automatically. (SDL_Quit
, SDL_DestroyWindow
, etc.)
In general, when should we raise exceptions when using SDL? Is it considered good practice to generally avoid them unless needed?
If you do cleanup properly (using destructors and/or scope guards, rather than manually), exceptions will make things easier in this regard, not harder.
They shouldn't impact the performance too much until you actually throw, so you should be fine if you only throw on rare occasions. But they do increase the size of your binaries.