I've seen people code the way they do because they didn't know an important detail. A common one among new C++ programmers (or some C programmers) is checking the return value of new
.
int *array_of_ints = new int[100];
if(!array_of_ints)
{
// handle error here
}
This is utterly useless as new
throws an exception when memory allocation fails.
Another one, this time for all programmers, is the lack of asserts in their code.
An assert, if you happen to not know, is a statement that must be true or the program does a predetermined action, usually exiting the whole application. Bundled with a message detailing the problem before exiting and now you can clean up your code base.
All my examples will in C++, but these concepts can apply to any language.
Currently, I run the series called Using SDL2 in C++. The series focuses around a library called SDL2 which is a library that can do graphics, audio, input from the keyboard and mouse, etc. For an example where we could use an assert, let's see how we initialize SDL2.
In SDL2, a function called SDL_Init()
that takes flags OR'd together and returns a value. If this value is less than 0, then an error occurred and we have to handle it. SDL2 also provides a function called SDL_GetError()
that gets the error message from the most recently failed function.
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
{
std::cout << "SDL2 Error: " << SDL_GetError() << "\n";
exit(1);
}
// Continue on if successful
This isn't just the only place where the use asserts can be helpful. Before you can do any graphics operations, you have to create a window and a renderer. Both of the functions that create them both return 0 on failure.
SDL_Window *window = SDL_CreateWindow(/* Arguments */);
if(window == 0)
{
// print an error message and exit
}
SDL_Renderer *renderer = SDL_CreateRenderer(/* Arguments */);
if(renderer == 0)
{
// again, print an error message and exit
}
Ugh, already getting out of hand. Wouldn't it be nice to have something to check the result and print an error message before exiting? Now is the time to start the creation of it.
We'll be using a macro since it will be a one-liner.
#define assert(expression, message) if(expression == false) std::cout << message; exit(1)
And now we can use it:
assert(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) > -1, SDL_GetError());
SDL_Window *window = SDL_CreateWindow(/* Arguments */);
assert(window != 0);
SDL_Renderer *renderer = SDL_CreateRenderer(/* Arguments */);
assert(renderer != 0);
Much cleaner, right? We can enhance this by adding what line, function, and file it occurred in. In C++, there are macros provided by the standard/compilers called __LINE__
, __func__
and __FILE__
that we will use for this purpose. We'll have to modify the macro where it fills the arguments of a function that we will create in a moment that does the actual work of printing the message along with the other information and exit.
#define assert(expression, message) if(expression == false) assert_func(message, __LINE__, __func__, __FILE__)
void assert_func(char const *message, int line, char const *function, char const *file)
{
std::cout << "ASSERTION FAILED: \n" <<
"Message: " << message << "\n" <<
"Line: " << line << "\n <<
"Function: " << function << "\n" <<
"File: " << file << "\n";
}
And there we have it! Something so simple yet now can hopefully make codebases cleaner.
Of course, asserts shouldn't be used everywhere. For example, if someone is working on a game and they need to load an image and it fails, they might choose to load a default image instead of exiting the entire game.
Everyone has their preferences and opinions on everything and programming is no different. Maybe you can leave a comment to tell how you like to handle errors?
Top comments (0)