Successive checks on success
Most of you have probably run into a situation where several things have to be checked and in a specific order before the application can continue, for example in the very simple case of creating a listening socket (socket, bind, listen, accept, etc.) ... There are at least two obvious ways (don't take this 100% literal):
if (1st_ok)
{
if (2nd_ok)
{
...
or
if (!1st_ok)
{
return;
}
if (!2nd_ok)
{
return;
}
...
Have you ever done anything smarter, do you prefer one of them above, or do you use (if the language provides for it) use exceptions?
a source to share
I prefer the second method. The main problem with the first is that it increases the depth of code nesting, which is a significant problem when you have a significant amount of preconditions / allocs to test, since the business part of a function ends up buried deep behind a wall of conditions (and often loops also). In the second case, you can simplify the conceptual logic so that "we have it here and everything is in order" that are much easier to work with. Keeping the normal case as straightforward as possible is just easier, especially when doing technical coding.
a source to share
It depends on the language - for example. in C ++ you can use exceptions, and in C you can use one of several strategies:
-
if
/else
blocks -
goto
(one of the few cases where a single labelgoto
for exception handling might be warranted - use
break
in a loopdo { ... } while (0)
Personally, I don't like multiple statements return
in a function - I prefer to have a common cleanup block at the end of the function, followed by a single statement return
.
a source to share
This is usually a matter of style. Some people only like to come back at the end of the procedure, others prefer to do it wherever necessary.
I am a fan of the second method as it allows for clean and concise code and the ease of adding documentation on what it does.
// Checking for llama integration
if (!1st_ok)
{
return;
}
// Llama found, loading spitting capacity
if (!2nd_ok)
{
return;
}
// Etc.
a source to share
I am avoiding the first solution because of the nesting.
I avoid the second solution because of the corporate coding rules that prohibit several return
in the body of the function.
Of course, the coding rules also prohibit goto
.
My workaround is to use a local variable:
bool isFailed = false; // or whatever is available for bool/true/false
if (!check1) {
log_error();
try_recovery_action();
isFailed = true;
}
if (!isfailed) {
if (!check2) {
log_error();
try_recovery_action();
isFailed = true;
}
}
...
It's not as pretty as I would like, but it's the best I've found to fit my constraints and write readable code.
a source to share
For what it's worth, here are some of my thoughts and experiences on the matter.
Personally, I prefer the second case you listed. I find it easier to follow (and debug) the code. That is, as the code progresses, it becomes "more correct". In my experience this seems to have been the preferred method.
I don't know how often this happens in the field, but I have also seen condition testing written as ...
error = foo1 ();
if ((error == OK) && test1)) {
error = foo2 ();
}
if ((error == OK) && (test2)) {
error = foo3 ();
}
...
return (error);
Even though it is readable (always a plus in my books) and avoids deep nesting, I always had to use a lot of unnecessary tests to achieve these goals.
The first method, I see, is used less frequently than the second. In those days, the vast majority of time was associated with the fact that there was nothing good around. For the remaining few instances, this was justified on the basis of better performance if successful. The argument was that the processor would predict the forward branch as not accepted (which corresponds to the else clause ). It depended on several factors including architecture, compiler, language, necessity ... Obviously, most projects (and most aspects of a project) did not meet these requirements.
Hope it helps.
a source to share