Related
I have a question about good coding practices. I understand the differences between doing an if-else if and multiple ifs (that is, when a condition is met in an if-else if, the rest of the checks are skipped). I've found a piece of code along these lines:
if (A == 5) {
do_something();
} else if (B == 7) {
do_something_else();
}
I understand that this code won't check B == 7 if A == 5. The code works, so that means that B is only 7, if A is not 5, but I think this is just waiting to break when the code changes. What I would do is:
if (A == 5) {
do_something();
return or continue or break;
}
if (B == 7) {
do_something_else();
return or continue or break;
}
My question is, when I have multiple exclusive cases that depend on different, exclusive variables, what's the best way to tackle the flow control? I have the impression that the first code (with else ifs) depends a lot on other pieces of code to work, and that changes in other areas might break it. The second one seems to be a bit clunky. A switch could be a third option, but I would need to create another structure to hold the case and the logic to assign its value, and I think that it would be a bit clunky and counter-intuitive.
You asked about "exclusive" cases, but the issue with the conditions A == 5 and B == 7 is that they are not exclusive; they are independent.
For full generality you may need to test and handle all four cases:
if(A == 5) {
if(B == 7) {
/* case 1 */
} else {
/* case 2 */
}
} else {
if(B == 7) {
/* case 3 */
} else {
/* case 4 */
}
}
This is the notorious "bushy" if/else block. It's notorious because it can almost immediately become nearly impossible for a reader to follow, especially if the cases are involved, or more levels are introduced. (I think most style guides will tell you never to use an if/else tree that's 3 or more levels deep. I'd certainly say that.)
I have occasionally used these two alternatives:
(1) Fully decouple the cases:
if(A == 5 && B == 7) {
/* case 1 */
} else if(A == 5 && B != 7) {
/* case 2 */
} else if(A != 5 && B == 7) {
/* case 3 */
} else if(A != 5 && B != 7) {
/* case 4 */
} else {
/* can't happen */
}
The point here is to make it maximally clear to a later reader exactly which conditions go with cases 1, 2, 3, and 4. For this reason, you might as well list the last, else if(A != 5 && B != 7) case explicitly (as I've shown), even though by that point it's basically an "else".
(2) Contrive a "two level" switch. I can't say this is a common technique; it has a whiff of being "too clever", but it's robust and readable, in its way:
#define PAIR(b1, b2) (((b1) << 8) | (b2))
switch(PAIR(A == 5), (B == 7)) {
case PAIR(TRUE, TRUE):
/* case 1 */
break;
case PAIR(TRUE, FALSE):
/* case 2 */
break;
case PAIR(FALSE, TRUE):
/* case 3 */
break;
case PAIR(FALSE, FALSE):
/* case 4 */
break;
}
I wouldn't recommend this when the conditions are A == 5 and B == 7, because when you're down in the switch, it's not obvious what "TRUE" and "FALSE" mean, but sometimes, this sort of thing can read cleanly. It's also cleanly amenable to 3 or more levels of nesting, unlike "bushy" if/else trees, which as I said are notoriously unreadable.
The most robust way of programming this,
while avoiding the assumption that either A==5 or B==7 is to consider all the four cases:
if ((A == 5) && (B == 7))
{
do_somethingAB();
/* or */
do_somethingA();
do_somethingB();
} else if (A == 5)
{
do_somethingA();
} else if (B == 7)
{
do_somethingB();
} else
{
do_somethingNeither();
/* or
do nothing */
}
As I think you know, the two pieces of code are not equivalent. (They're equivalent IF they both contain "return or continue or break", which makes the question more interesting, but that's a different answer.)
In general, which one you choose (or how you choose to rewrite it) has to depend on precisely what you want the program to do.
When you write
if (A == 5) {
do_something();
} else if (B == 7) {
do_something_else();
}
you're additionally saying you want to do_something_else only if A is not equal to 5. That might be just what you want, or it might be a bug. If you wanted to achieve the same effect without an else, it would have to look like this:
if (A == 5) {
do_something();
}
if (A != 5 && B == 7) {
do_something_else();
}
The second piece of code you wrote in your question, on the other hand, has the potential to execute both do_something and do_something_else.
In general, it's best (clearest and least confusing) if all the conditions in an if/else chain test variations on the same condition, not some unusual mixture involving, for example, both A and B.
You use an if/else block when the alternatives are truly and deliberately exclusive, and when you want to emphasize this fact. You might choose to use separate if blocks (not chained with else) when the alternatives are not exclusive, or when they're only coincidentally or accidentally exclusive. For example, I have deliberately written code like
if(A == 5) {
do_something();
}
if(A != 5) {
do_some_unrelated_thing();
}
I might do this when the two things have nothing to do with each other, meaning that in some future revision of the program's logic, they might be not be exclusive after all. Or, I might do this if do_something is not a single like, but is a long, elaborate block, at the end of which I'm concerned that the reader might not have remembered why we were or weren't doing something, and that on the other hand we might want to do something else. For similar reasons, I've occasionally written
if(A == 5) {
do_something();
}
if(A == 5) {
do_some_unrelated_thing();
}
in the case that, again, the two things to be done had nothing to do with each other, and the reasons for doing them might diverge.
[This is now my third answer. The fact that I keep misreading your question, and failing to grasp the essential point you're asking about, suggests that maybe I shouldn't be answering at all.]
I think the essential point you're asking about concerns the case where the cases are independent, but you get the effect of an else due to the fact that each clause contains a control-flow statement which "goes out": a break, or a continue, or a return, or something like that.
In this specific case, my preference today would be not to use the else. When we write
if(A == 5) {
do_something();
return or continue or break;
}
if(B == 7) {
do_something_else();
return or continue or break;
}
it's clear that the two conditions have nothing to do with each other, other than that they're both cases that do something to "finish" the subtask being done, and leave the block of code that's responsible for performing that subtask.
When we write the two cases separately (without an else), we make clear not only that they're independent, but that they could be reordered, or that another case could be introduced in between them, etc.
But then again, could they be reordered? How likely is it that both cases A == 5 and B == 7 will both be true? And in that case, how important is it that do_something be done, as opposed to do_something_else? If the two cases can't be reordered, if it would be wrong to test B first and maybe do do_something_else, I suppose the explicit else is preferable, to tie the two cases together and make even more clear the requirement that A be tested first.
Like any question of style, the arguments for and against this sort of thing end up being pretty subjective. You're not likely to find a single, overwhelmingly convincing answer one way or the other.
One way to handle this is to use a do { ... } while (0); technique.
Here is your original code:
if (A == 5) {
do_something();
} else if (B == 7) {
do_something_else();
}
Doing else if on the same line is [IMO] a bit of a hack because it hides the true indentation:
if (A == 5) {
do_something();
}
else
if (B == 7) {
do_something_else();
}
Using the aformentioned technique, which I've used quite a lot is:
do {
if (A == 5) {
do_something();
break;
}
if (B == 7) {
do_something_else();
break;
}
} while (0);
This becomes even more evident when we increase the number of levels in the if/else ladder:
if (A == 5) {
do_something();
} else if (B == 7) {
do_something_else();
} else if (C == 9) {
do_something_else_again();
} else if (D == 3) {
do_something_for_D();
}
Once again, this is indented to:
if (A == 5) {
do_something();
}
else
if (B == 7) {
do_something_else();
}
else
if (C == 9) {
do_something_else_again();
}
else
if (D == 3) {
do_something_for_D();
}
Using the do/while/0 block, we get something that is simpler/cleaner:
do {
if (A == 5) {
do_something();
break;
}
if (B == 7) {
do_something_else();
break;
}
if (C == 9) {
do_something_else_again();
break;
}
if (D == 3) {
do_something_for_D();
break;
}
} while (0);
Note: I've been programming in c for 35+ years, and I've yet to find a case where a more standard use of do/while (e.g. do { ... } while (<cond>)) can't be replaced more cleanly/effectively with either a standard for or while loop. Some languages don't even have a do/while loop. Thus, I consider the do loop to be available for reuse.
Another use of do/while/0 is to allow things defined by a preprocessor macro to appear as a single block:
#define ABORTME(msg_) \
do { \
printf(stderr,"ABORT: %s (at line %d)\n",msg_,__LINE__); \
dump_some_state_data(); \
exit(1); \
} while (0)
if (some_error_condition)
ABORTME("some_error_condition");
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
Preface
This question is meant as a canonical collection of the most frequent (beginner) mistakes using conditional statements like if() ... else or similar.
Answers are meant to describe unexpected behaviors at runtime, syntactical flaws and misconceptions like
if(x) {}
else (y) {}
should not be addressed here.
Addressed issues
Misconceptions of conditional expressions
Formatting and scoping errors
Misconceptions of conditional expressions
if(x = 1) // ...
Equality comparisons are expressed using the ==. = is an assignment, and the result is evaluated as a cast to bool. I.e. any value evaluated to != 0 results in true. As a prevention mechanism consider the below expressions:
if(1 = x) // invalid assignment compilation error!
if(1 == x) // valid equality comparison
A wrong use of an assignment operator can be avoided by always placing the constant on the left hand side of the expression. The compiler will flag any mistake triggering an invalid assignment error.
if(answer == 'y' || 'Y')
Variations: if(answer == 'y','Y')
Conditions must be tested with separate comparisons. The || operator binding doesn't do what's expected here. Use if(answer == 'y' || answer == 'Y')instead.
if (0 < x < 42)
Valid syntax in Python, with expected behaviour, that syntax is valid in C++, but parsed as if ((0 < x) < 42) so false/true converted to 0/1 and then tested against < 42 -> always true.
Condition must be tested with separate comparisons: if (0 < x && x < 42)
Formatting and scoping errors
if(mycondition);
{
// Why this code is always executed ???
}
There's a superfluous ; after the if() statement.
if(mycondition)
statement1();
statement2(); // Why this code is always executed ???
The code is equivalent to
if(mycondition) {
statement1();
}
statement2();
statement2(); is outside the scope of the conditional block. Add {} to group statements.
if (mycondition)
if (mycondition2)
statement1();
else
statement2();
The code is equivalent to
if(mycondition) {
if (mycondition2)
statement1();
else
statement2();
}
else apply on previous if. Add {}:
if (mycondition) {
if (mycondition2)
statement1();
}
else
statement2();
The same applies for any wrongly placed ; in loop statements like
for(int x = 0;x < 5;++x);
// ^
{
// statements executed only once
}
or
while(x < 5);
// ^
{
// statements executed only once
}
if (north) {
} else if (south) {
} else if (west) {
} else if (east) {
}
potential missing else-clause for wrong direction.
Funny placement of {} because they add them, misplace them and then delete the wrong ones.
if (a) {
}
if (b) {
}
if (c) {
}
missing else as only one of them should be done even if more is true.
Clean coding principles generally include a rule that functions must be small and single-purpose.
From Robert Martin's book, Clean Code: "The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that."
This is difficult to adhere to if I have a function with a complicated loop that contains branches that can break the loop. For example, this code in a chess variant is meant to allow a unit to attack by leaping over another unit on the board:
for (arow = row, acol = col - 1, found_obstacle = false; acol >= 0; --acol) {
if (!found_obstacle) {
if (cellHasUnit(arow, acol)) found_obstacle = true;
continue;
} else {
if (cellHasUnit(arow, acol)) {
if (!cellHasAlly(arow, acol))
_dangerzones.insert(std::make_pair(arow, acol));
break;
}
}
}
I understand that you cannot break a loop from inside a function, where the function is called inside that loop.
What is a good way to handle complicated break conditions inside loops in order to maintain clean code with short functions? I can imagine using a special function with a return value to indicate if breaks are necessary, but that still means that each break needs its own function. If I have many break conditions that means the function containing the main loop would still be quite big.
Edit: I am asking the general question of whether it is practical and desirable (from a clean coding perspective) to modularize within-loop code that has a number of break conditions.
As a programmer with above 15 years of programming in several programming languages, I can first tell you that the quote you brought is very nice, you should follow it in order to make modular code, but it doesn't mean each function should be 10 lines of code. That's impossible.
Regarding your code, it is OK. Not complicated. You use functions inside the loop and it looks modular. A break is also OK.
I have one comment, though, using continue looks redundant. You could do:
if (cellHasUnit(arow, acol)) {
found_obstacle = true;
else {
...
Some discourage continue altogether because it can confuse. I don't follow this recommendation and sometimes use continue but I do try to avoid both break and continue on the same loop because they have somewhat opposite meanings.
Maybe at the break; statement, set acol = -1 and then continue; so that on the next iteration, you break out of the loop?
You may find this form more expressive. It inverts the tests and therefore avoids 2 call sites for the cellHasUnit test:
#include <map>
std::map<int, int> _dangerzones;
bool cellHasUnit(int, int);
bool cellHasAlly(int, int);
void handleAlly(int arow, int acol)
{
if (!cellHasAlly(arow, acol))
_dangerzones.insert(std::make_pair(arow, acol));
}
void test(int row, int col)
{
int arow = row;
bool found_obstacle = false;
for (int acol = col - 1 ; acol >= 0 ; --acol)
{
if (cellHasUnit(arow, acol))
{
if (found_obstacle)
{
return handleAlly(arow, acol);
}
else // not strictly necessary
found_obstacle = true;
}
}
}
The return statement is used to indicate that in this example, the breaking of the loop is also necessarily the end of the test function.
If the real function is longer, then you could write this instead:
if (found_obstacle)
{
handleAlly(arow, acol);
break;
}
else ...
I am very new to C++.
How I can "redirect" code to certain position?
Basically, what should I put instead of comments lines here:
if ( N>1 ) {
// What should be here to make the code start from the beginning?
}
else {
// What should be here to make the code start from certain point?
}
I understand that C++ is not scripting language, but in case the code is written as script, how I can make redirect it?
Thank you
A goto command will do what you want but it's generally frowned on in polite circles :-)
It has its place but you would be possibly better off learning structured programming techniques since the overuse of goto tends to lead to what we call spaghetti code, hard to understand, follow and debug.
If your mandate is to make minimal changes to code which sounds like it may already be badly written, goto may be the best solution:
try_again:
n = try_something();
if (n > 1)
goto try_again;
With structured programming, you would have something like:
n = try_something();
while (n > 1)
n = try_something();
You may not see much of a difference between those two cases but that's because it's simple. If you end up with your labels and goto statements widely separated, or forty-two different labels, you'll beg for the structured version.
Use functions, loops etc to control the "flow" of your application. Think about code as reusable pieces, anything that is going to be reused should be placed in a function or looped through.
Here is an example:
void main()
{
int i = 0;
SayHello();
if (i < 1)
{
SayHello();
i++;
}
else
{
SayGoodbye();
}
}
void SayHello()
{
cout << "Hello" << endl;
}
void SayGoodbye()
{
cout << "Goodbye" << endl;
}
I'm not entirely certain what you mean by "redirect", but consider the following:
if (N > 1) {
speak();
} else {
do_something_else();
}
as paxdiablo has already stated the goto method isn't good practice. It would be better to use functions that do a specific thing, this way debugging is easier and someone can actually follow what your code is doing (or at least what it is supposed to do).
I have an if statement that [obviously] only runs if the condition is true. After this if statement there is some code that should always run, after that is another if statement that should run under the same condition as the first.
The code in the middle is performing an operation using a particular element of a stack, the ifs on either side perform a push/pop on the stack before and after the operation respectively.
so the logic is something like this:
Do I need to push the stack? yes/no
perform operation on top of stack
Was the stack pushed? (if yes then pop)
items 1 and 3 are the same condition.
This is the code that I first wrote to do this in c++
#include <stdio.h>
#include <stdlib.h>
int somefunction(){
return rand() % 3 + 1; //return a random number from 1 to 3
}
int ret = 0;
//:::::::::::::::::::::::::::::::::::::::
// Option 1 Start
//:::::::::::::::::::::::::::::::::::::::
int main(){
bool run = (ret = somefunction()) == 1; //if the return of the function is 1
run = (run || (ret == 2)); //or the return of the function is 2
if (run){ //execute this if block
//conditional code
if (ret == 1){
//more conditional code
}
}
//unconditional code
if (run){
//even more conditional code
}
}
//:::::::::::::::::::::::::::::::::::::::
// Option 1 End
//:::::::::::::::::::::::::::::::::::::::
After writing this I thought that it might be more efficient to do this:
//:::::::::::::::::::::::::::::::::::::::
// Option 2 Start
//:::::::::::::::::::::::::::::::::::::::
int main(){
bool run;
if (run=(((ret = somefunction()) == 1)||ret == 2)){ //if the return of the function is 1 or 2 then execute this if block
//conditional code
if (ret == 1){
//more conditional code
}
}
//unconditional code
if (run){
//even more conditional code
}
}
//:::::::::::::::::::::::::::::::::::::::
// Option 2 End
//:::::::::::::::::::::::::::::::::::::::
I prefer the first method for readability as it is split into several lines whereas the second has two assignments (=) and two comparisons (==) in the same line.
I want to know if it is better to use the second method (for reasons of efficiency or executable size) or if there is a better method than both.
Before anyone says it will only make an almost immeasurable difference, this is in a huge loop that has to run many thousands of times within 1/50 of a second so I would like to save as much time as possible.
Performance should not be your concern: the modern compilers are usually smart enough to optimize the code in any case. The results will be the same if the code is doing essentially the same thing.
So you should prefer the variant which is more readable (and therefore better maintainable).
I would write something like that:
ret = somefunction();
// I don't know what is the semantics of ret == 1, so let's imagine some
bool operationIsPush = (ret == 1);
bool operationIsOnTop = (ret == 2);
if (operationIsPush || operationIsOnTop)
{
//conditional code
}
if (operationIsPush)
{
//more conditional code
}
//unconditional code
if (operationIsPush || operationIsOnTop)
{
// ...
}
I believe there will be no difference in the performance here. The first reason is that your compiler will probably optimize the code in each case. The second is that you just change the place where operations take place (like "I do A->B->C or A->C->B"), not the amount of operations, so it's always the same amount of computing (1 function call, a couple of == and so on).
However consider that this
(run=(((ret = somefunction()) == 1)||ret == 2))
is pretty hard to read.
Correctness is more important than whether you fold two operations assigning a bool into one (which the compiler will probably do anyway).
For pushing/popping a stack, you should use a scopeguard (original article here). This will ensure that if something throws in the "unconditional bit", which you never really know for sure, then it still runs correctly. Otherwise you get funny a surprise (stack off by one, or overflowing).
if theres a situation that you can split "if-else" to distinct huge loops, it will be faster
rather than
loop { if_1 {some work} if_2 {some other work} }
you can
if_1 { loop {work }} if_2 {loop{same work}}
even more extremely, if you can split the most inner "if" sentences, you can have 10-20(dpending on your situation) distinct huge loops that runs x2 x3 faster (if it is slow bacause of "if")