views:

441

answers:

9

Recently I've bumped into the following C++ code:

if (a)
{
  f();
}
else if (b)
{
  f();
}
else if (c)
{
  f();
}

Where a, b and c are all different conditions, and they are not very short.

I tried to change the code to:

if (a || b || c)
{
  f();
}

But the author opposed saying that my change will decrease readability of the code. I had two arguments:

1) You should not increase readability by replacing one branching statement with three (though I really doubt that it's possible to make code more readable by using else if instead of ||).

2) It's not the fastest code, and no compiler will optimize this.

But my arguments did not convince him.

What would you tell a programmer writing such a code?

Do you think complex condition is an excuse for using else if instead of OR?

+20  A: 

This code is redundant. It is prone to error.

If you were to replace f(); someday with something else, there is the danger you miss one out.


There may though be a motivation behind that these three condition bodies could one day become different and you sort of prepare for this situation. If there is a strong possibility it will happen, it may be okay to do something of the sort. But I'd advice to follow the YAGNI principle (You Ain't Gonna Need It). Can't say how much bloated code has been written not because of the real need but just in anticipation of it becoming needed tomorrow. Practice shows this does not bring any value during the entire lifetime of an application but heavily increases maintenance overhead.


As to how to approach explaining it to your colleague, it has been discussed numerous times. Look here:

How do you tell someone they’re writing bad code?

How to justify to your colleagues that they produce crappy code?

How do you handle poor quality code from team members?

“Mentor” a senior programmer or colleague without insulting

Developer Art
+1 for a correct, concise answer.
Justin Ethier
Not a very strong argument: just because a or b or c does the same for now does not necessarily mean they will do the same for ever (the chance for the change is even higher when a, b and c are complex logic). When the change comes, the old code is less error prone than the new version.
Codism
He may also forget to spell his name correctly. Seriously, do people really miss obvious things like this? Yes it's redundant but it's redundant in the same code block. It's not like you have to chase down a file here or there to find all the redundancies
Joe Philllips
+1 for being the correct answer to the question. In my own (very subjective) reasoning though, both the OP and developer should change their programming style and make use of a function, clearly stating what that function does.
Lieven
Codism, if you follow that logic, then you'd write code like this **everywhere**, on the off chance that someday you *might* have to introduce such a branch. Ridiculous. Code YAGNI style (You Ain't Gonna Need It) and produce only what you know you are (or are very likely) going to need.
Philip Kelley
@Philip Kelley: I agree. Lot of people writing code with the thinking pattern to prepare for every contingency. It has to be stopped.
Developer Art
@Developer Art: Yes, this goes along with excessive OOP and the "plugin" trend we see. Everything has it's place of course, but doing such things without a solid reason is wrong and leads to complexity and inefficiency.
phkahler
@phkahler: Not sure I know what you mean by "plugin trend", are you saying that this is a product of the issue, or a solution to it? Because personally, I think I understand Developer Art's comment, but I see the new wave of plugin-friendly architecture being a solution to the "we may want X someday, so we have to lay the foundation now"-problem by giving a modular "whatever, just write it later"-style option. Did I just misunderstand?
Lance May
@DeveloperArt, thank you for the answer. The motivation was: "in case of if (a || b || c) we cannot clearly see three different scenarios". The argument was about readability.
Lilit
@Lance May: You understand correctly - Plug-in architectures were meant by me as another example of "prepare for every contingency" as stated by Developer Art. I disagree with building frameworks ahead of time unless you know there will be more than one variation needed. One implementation can probably be transformed into a plugin when needed, but a plugin arch that only gets one thing built for it is a waste of time and code.
phkahler
A: 

Performance shouldn't really even come into question

Maybe later he wont call f() in all 3 conditons

sylvanaar
+2  A: 

In general I think you are right in that if (a || b || c) { f(); } is easier to read. He could also make good use of whitespace to help separate the three blocks.

That said, I would be interested to see what a, b, c, and f look like. If f is just a single function call and each block is large, I can sort of see his point, although I cringe at violating the DRY principle by calling f three different times.

Justin Ethier
+10  A: 

Replace the three complex conditions with one function, making it obvious why f() should be executed.

bool ShouldExecute; { return a||b||c};
...
if ShouldExecute {f();};
Lieven
Better solution.
Paul Nathan
+1  A: 

I think that both of your arguments (as well as Developer Art's point about maintainability) are valid, but apparently your discussion partner is not open for a discussion.

I get the feeling that you are having this discussion with someone who is ranked as more senior. If that's the case, you have a war to fight and this is just one small battle, which is not important for you to win. Instead of spending time arguing about this thing, try to make your results (which will be far better than your discussion partner's if he's writing that kind of kode) speak for themselves. Just make sure that you get credit for your work, not the whole team or someone else.

This is probably not the kind of answer you expected to the question, but I got a feeling that there's something more to it than just this small argument...

Anders Abel
A: 

Repeating code doesn't make things clearer, your (a||b||c) is much clearer although maybe one could refactor it even more (since its C++) e.g.

x.f()
Anders K.
It may be clearer but is it correct? We don't know unless we see the actual code
Joe Philllips
+5  A: 

Since the conditions are long, have him do this:

if ( (aaaaaaaaaaaaaaaaaaaaaaaaaaaa)
  || (bbbbbbbbbbbbbbbbbbbbbbbbbbbb)
  || (cccccccccccccccccccccccccccc) )
{
    f();
}

A good compiler might turn all of these into the same code anyway, but the above is a common construct for this type of thing. 3 calls to the same function is ugly.

phkahler
I do not think compiler should optimize if\else if(s) doing same thing. Translating stupid code into something logical should not be a compiler's concern. But I will check that tomorrow.
Lilit
@Lilit: you're probably right, but compilers are getting very smart these days. It seems like some combination of existing optimizations *might* actually produce only a single function call.
phkahler
+1  A: 

I very much doubt there will be any performance gains of this, except at least in a very specific scenario. In this scenario you change a, b, and c, and thus which of the three that triggers the code changes, but the code executes anyhow, then reducing the code to one if-statement might improve, since the CPU might have the code in the branch cache when it gets to it next time. If you triple the code, so that it occupies 3 times the space in the branch cache, there is a higher chance one or more of the paths will be pushed out, and thus you won't have the most performant execution.

This is very low-level, so again, I doubt this will make much of an impact.

As for readability, which one is easier to read:

  • if something, do this
  • if something else, do this
  • if yet another something else, do this
  • "this" is the same in all three cases

or this:

  • if something, or something else, or yet another something else, then do this

Place some more code in there, other than just a simple function call, and it starts getting hard to identify that this is actually three identical pieces of code.

Maintainability goes down with the 3 if-statement organization because of this.

You also have duplicated code, almost always a sign of bad structure and design.

Basically, I would tell the programmer that if he has problems reading the 1 if-statement way of writing it, maybe C++ is not what he should be doing.

Now, let's assume that the "a", "b", and "c" parts are really big, so that the OR's in there gets lost in lots of noise with parenthesis or what not.

I would still reorganize the code so that it only called the function (or executed the code in there) in one place, so perhaps this is a compromise?

bool doExecute = false;
if (a) doExecute = true;
if (b) doExecute = true;
if (c) doExecute = true;
if (doExecute)
{
    f();
}

or, even better, this way to take advantage of boolean logic short circuiting to avoid evaluating things unnecessary:

bool doExecute = a;
doExecute = doExecute || b;
doExecute = doExecute || c;
if (doExecute)
{
    f();
}
Lasse V. Karlsen
@Lasse, thank you for your reply. I assumed there will be 3 branching instead of one, but there can be only one of course, so performance is really not an argument here.
Lilit
+2  A: 

Performance is not an issue here.

Many people wrap themselves in the flag of "readability" when it's really just a matter of individual taste.

Sometimes I do it one way, sometimes the other. What I'm thinking about is -

"Which way will make it easier to edit the kinds of changes that might have to be made in the future?"

Don't sweat the small stuff.

Mike Dunlavey
Dirty as it may seem (for standards' sake), I would have to agree with Mike completely here. If performance, scalability, and portability are not an issue, it really should come down to what makes the most sense from a maintenance perspective.
Lance May
Performance issue is very low level here - subsequent instructions may be disposed from processor's pipeline and cache. IMHO it's not bad to take this into account if your target platform is a not a desctop computer but a microcontroller.
Lilit
@ Mike Dunlavey. I like this: "Many people wrap themselves in the flag of "readability" when it's really just a matter of individual taste".
Lilit