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?

+2


a source to share


8 answers


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.



+4


a source


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 label goto

    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

.

+2


a source


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.

      

+1


a source


I prefer the second version.

In the normal case, all code is executed sequentially between checks, so I like to see them at the same level. Usually none of the branches if

are executed, so I want them to be as unobtrusive as possible.

+1


a source


I use the second one because I think it is better and easier to read the logic. They also say that exceptions should not be used for flow control, but for exceptional and unexpected cases. I love to see what the professionals have to say about it.

+1


a source


What about

if (1st_ok && 2nd_ok) { }

or if some work needs to be done like in your socket example

if (1st_ok()  &&  2nd_ok()) { }

      

+1


a source


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.

0


a source


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.

0


a source







All Articles