13

In my code I have a if statement, which looks like:

if(someFunction1(a) || someFunction2(b->b1,c) || *d == null || somefunction3(e) > f * g || !e->e1 || ...){
   return 0;
} else {
   do_something;
}

In my code with real variable and function names are conditions nearly in three lines and it looks very overlook. So I decided to rewrite it into form:

if(someFunction1(a)){
   return 0;
} else if(someFunction2(b->b1,c)){
   return 0;
} else if(*d == null){
   return 0;
} else if(somefunction3(e) > f * g){
   return 0;
} else if(!e->e1){
   return 0;
} else if(...){
   return 0;
} else{
   do_something;
}

Is there any argument why I should not do it?

Marek Čačko
  • 980
  • 2
  • 21
  • 31
  • possible duplicate of [How to indent long conditionals for 'if' statements?](http://stackoverflow.com/questions/1059166/how-to-indent-long-conditionals-for-if-statements) – moooeeeep May 04 '15 at 10:14
  • Apparantly this question is borderline to being a matter of individual style (primarily opinion based). – moooeeeep May 04 '15 at 10:15
  • This is on the borderline of primarily opinion-based, but I think the question is narrow enough for the SO format. In addition, I think programmers need to vent these things now and then, as we can tell by the number of up-votes all threads like this get. So there is no need to close the question, although there might very well be subjective _answers_ popping up. – Lundin May 04 '15 at 11:39
  • These days any question gets into Hot Network Questions list.... also answer is no different from what is written in question except for it follows a better code formatting practice – Mr. Alien May 04 '15 at 14:59

6 Answers6

19

From a purely semantic-syntactical point of view there's no effective difference between them. But if readability is your concern, why don't you use the "datenwolf" formatting style – I came to develop that style over the course of my past 5 projects or so:

if( someFunction1(a)
 || someFunction2(b->b1,c)
 || *d == null
 || somefunction3(e) > f * g
 || !e->e1
 || ...
){
   return 0;
} else {
   do_something;
}

Do you see how beautiful everything lines up? It really looks like a tube the program is falling down through until it hits a met condition. And if you have && it looks like a chain of operations that must not be broken.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • 11
    "a tube the program is falling down through"... "a chain of operations that must not be broken"... And people still laugh at me when I say programming is an art... –  May 04 '15 at 10:42
  • On a side note, you can use the logic booelan operators (`||`, `&&`) outside of a conditional to implement this kind of flow control. However I strongly advise against doing so and in fact I strongly recommend to surround every call to a non-trivial function (i.e. functions that may fail) with error checks. – datenwolf May 04 '15 at 10:47
  • I'd do like this too (though I place the "tube" on the right side of the expressions), if I didn't need to place extensive comments for every error case. In that case you need to split the code up. – Lundin May 04 '15 at 11:35
  • 1
    @Lundin: I know it sounds counterintuitive at first, but I figured out, that placing and operators to the left side has several advantages. I suggest to try it for a few weeks/months. The past two years I did *a lot* of crunch phase coding and after 12 hours straight looking onto source code you want it to be formatted in the most easy to read manner possible. – datenwolf May 04 '15 at 12:07
  • Consider making the first operand `false` so that all lines can be formed equally. – usr May 04 '15 at 14:47
  • 1
    I use similar semantics with SQL and other languages. The Pipe/Comma's/And/etc on the left makes it easier to comment out a single line without affecting the flow - I'm more likely to comment out the last in a list... than I am to comment out the first. If I comment out the last and the pipe is on the right, it'll error... If the Pipe is on the left, it'll simply ignore that line. Great for quick testing. – WernerCD May 04 '15 at 14:53
  • @usr Now that's just obfuscation. Don't. – Lundin May 05 '15 at 06:14
6

As you're asking because of readability you may want to rearrange the long conditional into predicate variables that say why zero must get returned.

bool unnecessary = someFunction1(a) || someFunction2(b->b1,c);
bool beyondTolerance = somefunction3(e) > f * g;
bool invalidInput = *d == nullptr || !e->e1;

if (unnecessary || beyondTolerance || invalidInput)
    return 0;
else 
    ...

This is Martin Fowler's Decompose Conditional refactoring.

acraig5075
  • 10,588
  • 3
  • 31
  • 50
  • 8
    You need to be careful if you do this. The original code may rely on __short-circuit evaluation__ which this code changes. – Blastfurnace May 04 '15 at 10:35
  • Personally, I would not attempt to add even more complexity to already complex expressions. – Lundin May 04 '15 at 11:37
5

Option 1:

  • Terseness
  • One exit point to avoid redundancy of return statement.

Option 2:

  • Exact failure point can be diagnosed easily i.e logs can be added to each branch to detect the failure.
thepace
  • 2,221
  • 1
  • 13
  • 21
3

I don't think there is any other problem in this code other than the redundancy involved. If at all you have to make change to the return statement, you have to change it at 6 places,according to your implementation.

But that redundancy does not occur in the first implementation.

Both are similar otherwise.

Aswin Murugesh
  • 10,831
  • 10
  • 40
  • 69
  • 1
    It would, of course, be trivial to factor that out to e.g. `const int retval = 0;` before the first `if`, and then do `return retval;` as needed. – unwind May 04 '15 at 10:03
  • @unwind: It would. But still is not so efficient because there is redundancy involved. OP is extending a single line code to almost 15 lines :P – Aswin Murugesh May 04 '15 at 10:06
2

First of all, you can't answer this question without providing some rationale, or the answer will become completely subjective. I would be wary of people answering "do like this, because I like this best", with no rationale provided.


Looking at the code, it is obviously a number of error checks done inside a function. In a real code example, all such error handling usually requires plenty of comments, to describe each individual error condition, as functions with extensive error handling tend to be complex.

Given that, it is not a good idea to write the code as one statement at all, because if you have to squeeze in comments in the middle of the statement, the code will become a mess.

With the above rationale, the best way to write such is perhaps:

/* comments here */
if(someFunction1(a)){
   return 0;
} 

/* comments here */
if(someFunction2(b->b1,c)){
 return 0;
}

...

/* if we got here, then there are no errors */
do_something();

This also have the advantage of being maintainable, should you need to execute code in between the error checks. Or if you wish to split some of the more complex expressions into several lines for readability.

Even though there are plenty of cases where multiple return statements have the potential to create messy code, this is not one of them. In for this case, multiple return statements actually improve readability/maintainability. You shouldn't dogmatically avoid multiple return statements just because some coding standard tells you to do so.

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

You can do it the following way

int not_valid = someFunction1(a) || 
                someFunction2(b->b1,c) || 
                *d == null || 
                somefunction3(e) > f * g || 
                !e->e1 || ...;

if ( !not_valid )
{
   do_something;
}

return !not_valid;

Instead of not_valid you can select a more appropriate name.:)

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335