I just hope the following doesn't seem to you like redundant jabber :)
Anyway, there is that:
for (p = fmt; *p; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
switch (*++p) {
/* Some cases here */
...
}
}
And I wondered why the writer (Kernighan / Ritchie) used the continue in the if statement.
I thought it was for the mere reason that he deemed it would be more elegant than indenting the whole switch under an else statement, what do you think?
Probably. The human brain has limited stack space, making it difficult to deal with deeply nested structures. Anything that flattens the information we're expected to parse makes it easier to understand.
Similarly, I normally prefer this:
bool foo(int arg)
{
if(!arg) {
/* arg can't be 0 */
return false;
}
/* Do some work */
return true;
}
To this:
bool foo(int arg)
{
if(!arg) {
/* arg can't be 0 */
return false;
} else {
/* Do some work */
return true;
}
}
Or worse, to this:
bool foo(int arg)
{
if(arg) {
/* Do some work */
return true;
} else {
/* arg can't be 0 */
return false;
}
}
In the last example, the part that does the work might be quite long. By the time the reader gets to the else clause, he may not remember how he got there.
Putting the bail out conditions as close to the beginning helps to assure that people who try to call your functions will have a good idea of what inputs the function expects.
Also, as others pointed out, the continue makes it clear that there's no need to read further into the code inside the loop to determine whether any more processing is done after that point for this case, making the code easier to follow. Again, the fewer things you force the reader to keep track of, the better.
Because with the continue it is clear that the code is done for this loop iteration. If a else would have been used you had also to check if there is no code after the else.
I think it is general a good habit to exit a context as soon as possible because this leads to much clearer code.
For example:
if(arg1 == NULL)
return;
if(arg2 == NULL)
return;
//Do some stuff
vs.
if(arg1 != null)
{
if(arg2 != null)
{
//Do some stuff
}
}
It is just so much easier to read when it's put like this.
Are we done here with this iteration through the loop? Yes? So let us continue with the next iteration.
I think that he would have reasons enough to indent the code under the switch, and indenting the entire meat of the function is quite wasteful of horizontal space. At the time the code was written, I imagine 80 character widths were still popular.
I don't think it is difficult to understand, but I do think that it's quite nice to mention what you DON'T do immediately, and then GTFO.
There are always many ways to write code like this -
Putting the entire switch inside an else statement would be perfectly valid. I suppose the reason they did it this way ~may~ have been just the way they were thinking at the time:
"if the value at p does not equal '%', put then continue on."
If you have switch under an else, it may not have been as obvious to the writer that you were jumping to the next iteration in that specific case.
This is completely personal style choices, though. I wouldn't worry too much - just write it in a way that makes the most sense to you and your team.
I agree.
But you can't look at it as a "mere reason", it's actually a pretty good reason, because it reduces the over all complexity of the code. Making it shorter and easier to read and understand.
If you use an else then everything inside the else needs to be indented:
if ()
{
doA();
}
else
{
doB();
if ()
{
doC();
}
else
{
doD()
}
}
If you use continue then you don't need to indent:
if ()
{
doA()
continue;
}
doB();
if ()
{
doC();
continue;
}
doD();
Also, continue means that I can stop thinking about that case: for example, if I see else then perhaps there'll be more processing of the '%' case later in the loop, i.e. at the end of the else statement; whereas on seeing continue I know instantly that the processing of the '%' case in the loop is completely finished.
The most probable reason is that the switch that follows is rather long - this looks like printf format parsing.
There could be more that one reason to continue/break a loop. So it would look next:
loop
{
if (cond1)
{
if (cond2)
{
if (cond2)
{
more conditions...
}
}
}
else
{
the loop action
}
}
IMHO it's not so elegant and readable as the loop in your example, e.g:
loop
{
if (cond1)
continue;
if (cond2)
continue;
if (cond2)
continue;
if( more conditions...)
continue;
the loop action
}
And you don't even need to understand all structure of all "if"s (it could be much more complex) to understand the loop logic.
P.S. just for the case: I don't think the authors thought about how to write this loop, they just wrote it:)
I stick to Dijkstra's teachings: goto is harmful. And continue/break are goto's little brothers.
If the problem is that you're indenting the code too much, the solution is not putting a continue in the loop, but reducing the complexity by separating the code in different functions or thinking about a better way of organizing it.
For example, #Kamarey snippet would be even clearer like this:
loop
{
if (!(cond1 ||
cond2 ||
cond2 ||
...))
{
the loop actions;
}
}
or #Ori Pessach example could be expressed like:
bool foo(int arg)
{
if(arg) {
/*do some work*/
}
return arg != 0;
}
In brief, usually I can't find a good enough reason to use non structured programming constructs (maybe for some cleanup codes in a very limited ocasions...)
Well, I wrote C programs for about 11 years and I had to read 5 times your piece of code to understand it !
Kernighan and Ritchie were active in the sixties. At that time, being able to understand a piece of code was not relevant. Being able to write code that fit in 16 Ko was.
So I'm not suprised. C is a terrible language when your teatchers are K & R. Just look at realloc : who would know code something like that today ? In the '60ies, it was all the rage, but it is now appalling, at least :o)
Related
Recently, I was reviewing some code I maintain and I noticed a practice different than what I am used to. As a result, I'm wondering which method to use when performing an early return in a function.
Here's some example:
Version 1:
int MyFunction(int* ptr)
{
if(!ptr) { // oh no, NULL pointer!
return -1; // what was the caller doing? :(
}
// other code goes here to do work on the pointer
// ...
return 0; // we did it!
}
Version 2:
int MyFunction(int* ptr)
{
if(!ptr) { // oh no, NULL pointer!
return -1; // what was the caller doing? :(
} else { // explicitly show that this only gets call when if statement fails
// other code goes here to do work on the pointer
// ...
return 0; // hooray!
}
}
As a result, I'm wondering which is considered the "best practice" for those of you who have endured (and survived) many code reviews. I know each effectively does the same thing, but does the "else" add much in terms of readability and clarity? Thanks for the help.
The else would only add clarity if the else clause is short, a few lines of code at best. And if you have several initial conditions you want to check, the source gets cluttered very quickly.
The only time I would use an else if it is a small function with a small else, meaning less than about 10 source lines, and there are no other initial checks to make.
In some cases I have used a single loop so that a series of initial checks can use a break to leave.
do {
...
} while (0);
I am loathe to use a goto which is practically guaranteed to get at least one true believer of goto less programming up in arms.
So much would depend on any code standards of your organization. I tend to like minimalism so I use the first version you provide without the else.
I might also do something like the following in a smaller function say less than 20 or 30 lines:
int MyFunction(int* ptr)
{
int iRetStatus = -1; // we have an error condition
if (ptr) { // good pointer
// stuff to do in this function
iRetStatus = 0;
}
return iRetStatus; // we did it!
}
The only problem with returns in the body of the function is that sometimes people scanning the function do not realize that there is a return. In small functions where everything can be pretty much seen on a single screen, the chance of missing a return is pretty small. However for large functions, returns in the middle can be missed especially large complex functions that have gone through several maintenance cycles and had a lot of cruft and work arounds put into them.
I've been messing around with C++ SDL for a few days now and I've come across an interesting problem.
SDL_Event event1;
while(SDL_WaitEvent(&event1))
{
for(size_t i = 0; i < MainMenuOptions.size();i++)
{
if(event1.button.x > MainMenuOptions.at(i).GetX() && event1.button.x < (MainMenuOptions.at(i).GetX() + MainMenuOptions.at(i).GetWidth())
&& event1.button.y > MainMenuOptions.at(i).GetY() && event1.button.y < (MainMenuOptions.at(i).GetY() + MainMenuOptions.at(i).GetHeight()))
{
break;
}
}
}
When I use break in the for loop its going to break out of the for loop instead of the while loop. How would I break out the while loop instead without using the goto statement? (the goto statement is bad programming, I heard)
The common solution is to put this stuff into its own function and return from that:
inline SDL_Event do_it()
{
SDL_Event event;
while(SDL_WaitEvent(&event))
for(std::size_t i = 0; i < MainMenuOptions.size(); ++i)
if(/*...*/)
return event;
return event; // or whatever else suits, I know too little about your code
}
There's another answer to that, and I think I should say it before everyone will downvote me.
Using a variable is certainly a "good" way to do it. However, the creating additional variable just to jump out of the loop seems a bit of overkill, right?
So yes, this time goto is perfect solution. It's perfectly clear what you are doing with it, you are not using another variable and the code remains short, maintainable and readable.
The statement goto is bad practice is mostly a remnant of the BASIC times, when it was quite the only way of changing code flow. However, now we "know better", and saying that the goto or any other construction is bad, just doesn't cut it. It can be bad for one particular problem you are trying to solve with it (and it's the case with most of the problems that people try to solve with goto). However, given the right circumstances (like here) it's OK. I don't want to start a debate here, of course. Goto is like a very powerful tool (sledgehammer, for example). It has its uses and you can't say a tool is totally bad; it's the user using it in the wrong way.
Use a variable to indicate the need to exit:
bool exit_program = false;
while( !exit_program && SDL_WaitEvent(&event1) )
{
for( /* ... */ )
{
exit_program = true;
}
}
First point: IMO, you're trying to wrap too much up into a single place, and ending up with something that's fairly difficult to understand -- somebody has to read through that entire long set of comparisons before they can understand any of what this is supposed to accomplish at all.
Second point: using an explicit loop to iterate over a standard collection is usually a mistake -- and this is no exception. The standard library already has an algorithm to accomplish the same basic thing as your loop. It's better to use that than write it again yourself.
template <class T>
bool in_range(T a, T b, T c) {
return (a > b) && (a < b+c);
}
class in_rect {
point p;
public:
in_rect(point const &p) : p(p) {}
// Not sure of the type of objects in MainMenuOptions, so just T for now.
//
bool operator()(T const &m) {
return in_range(p.x, m.GetX(), m.GetWidth())
&& in_range(p.y, m.GetY(), m.GetHeight());
}
};
SDL_Event event1;
while (SDL_WaitEvent(&event1))
if (std::any_of(MainMenuOptions.begin(), MainMenuOptions.end(),
in_rect(event1.button))
break;
Once we fix the other problems, there's simply no longer any need (or even use) for the goto. We haven't taken any steps explicitly intended to remove it, but when the other problems have been fixed (especially, replacing the loop with an appropriate algorithm), the use for it has disappeared.
I suppose I should preemptively comment on the increase in the total number of lines of code: yes, there are more lines of code. What of it? If we really wanted to, we could use the same basic approach, but instead of defining in_rect and in_range, we'd basically just take the condition from the original if statement and stuff it into a lambda. While I'm very happy that lambdas have been added to C++, in this case I'm not excited about using it. It would get rid of the goto, but in general the code would be almost as unreadable as it started out.
Simply put, the number of lines isn't a good way to measure much of anything.
A solution without additional variable and goto:
while(SDL_WaitEvent(&event1))
{
size_t i;
for(i = 0; i < MainMenuOptions.size();i++)
{
if(/* ... */)
{
break;
}
}
if (i < MainMenuOptions.size())
break;
}
do{...} while(0);
the usage of do{}while(0); in my coding is used because, i do not want to use long if else nested conditional statements. I eventually give an break at the time of failure and move out of the loop, with a assurance that my function would have been traversed at least 1 time.
Now, the problem comes with the code warning tools, I am getting a warning at the usage of do{...}while(0);
the usage of nested if(){} else{} is less readable, high complex. and lets the code to be having dead code.
if i exclude nested if(){} else{} and do{} while(0); , do we left part with some other way to make code readable with understandable logic;
if(status_of_funcA_ok != funcA())
{ //failure}
else if (status_of_funcB_ok != funcB())
{//failure}
else if (status_of_funcC_ok != funcC())
else
{//Great}
do{
if(status_of_funcA_ok != funcA())
break;
if (status_of_funcB_ok != funcB())
break;
if (status_of_funcC_ok != funcC())
break;
}while(0);
Move the complete logic of the do while{0} loop to a function, and replace the break with return. And call the function, instead of the loop.
You will not have to worry about the beauty.
The compiler also doesn't have to complain about the do while{0}.
All the more, by adding a bit of modularity, the program might be a little more readable.
In any case, before doing this, it would be nice to check whether your compiler is in an extremely pedantic mode, and you might want to turn that off. That might take the warning away.
ss.
PS: You don't seem to need a return value for the function, but you could have that to get a clue of which function was successful.
I am using this pattern too, for those who wonder, here's an abstract example:
do // while(0) for break
{
state1 = 0;
if (cond1())
{
if (cond2())
break;
state1 = opA();
}
if (cond3() || state1 && state1->cond4())
break;
...
Triumph(state1, ...);
// often here: return
}
Failure(state1, ...);
I consider this valid in the following circumstances:
you have a long-ish sequence (say, >~half a dozen of conditions)
the conditions are complex, and you use / build up significant state, so you can't
isolate the elements into functions
you are in an exception-unfriendly environment, or your break-ing code path is
not actually an exception
What you can do about it:
Silence the warning. It is just a warning, after all; and I don't see a "typical mistake" (like typing 0 instead of your condition) that would be caught by this warning.
[edit] Now, that was silly. the typical mistake that you catch with the warning is e.g. while (a1!=a1) instead of while (a1!=a2).[/edit]
Break into functions, move state to a class
this would transform above code to:
struct Garbler
{
State1 state1;
bool Step1()
{
state1 = 0;
if (cond1())
{
if (cond2())
return false;
state1 = opA();
}
return true;
}
bool Step2()
{
return cond3() || state1 && state1->cond4();
}
..
void Run()
{
if (Step1() && Step2() && ... && Step23())
Triumph(state1, ...);
else
Failure(state1, ...);
}
}
This is arguably less readable, worse is that you pull apart the sequence, which might lead to a very questionable class (where members may be called only in a certain order).
Scopeguards
This may allow to transform the breaks into early returns, which are more acceptable:
state1 = 0;
ScopeGuard gFailure = MakeGuard(&Failure, ByRef(state1), ...);
if (cond1())
{
if (cond2())
return;
state1 = opA();
}
if (cond3() || state1 && state1->cond4())
return;
// everything went ok, we can dismiss the scopeguard
gFailure.Dismiss();
Triumph(state1, ...);
They can be more elegantly written in C++0x, preserve the flow, but the solution isn't that flexible either, e.g. when Failure() cannot be isolated easily into a single function.
Nested nested if-else statements can become quite unreadable, but I think using do {..} while(0); as a replacement would be much worse. It is very unconventional and anybody else reading it would not really associate it with if-else statements.
There are a few things you can do to make nested if-else statements more readable. A few suggestions are:
optimize your logic - sometimes you can do away with a lot of if clauses when you 'refactor' your logic ex. grouping identical items.
use switch() - switch is generally more readable compared to if-else statements. You can associate an enum to each case and you can switch this.
encapsulate complicated logic with functions
You can use goto instead of do {} while(0) and break. This is not readable and not good practice either though. I think for each specific case there is a better way to avoid deep if/else structures. For example, sometimes using function calls can help:
for example instead of:
if(status_of_funcA_ok != funcA())
{ //failure}
else if (status_of_funcB_ok != funcB())
{//failure}
else if (status_of_funcC_ok != funcC())
else
{//Great}
you can write:
if (check_funcs() == 0) {
great();
}
int check_funcs() {
if (status_of_funcA_ok != funcA())
return -1;
if (if(status_of_funcB_ok != funcB()))
return -2;
if (if(status_of_funcC_ok != funcC()))
return -3;
return 0; /* great */
}
Sometimes, you can use exit().
Also, in c++ you can use throw() and try/catch:
try {
/* */
throw (this error);
/* */
throw (that error);
} catch (this error) {
} catch (that error) {
}
If there are more conditions to check avoid using if{} else{},
best practice is to Replace if else conditions with switch case
Like we all know, it's not that easy to break from a nested loop out of an outer loop without either:
a goto (Example code.)
another condition check in the outer loop (Example code.)
putting both loops in an extra function and returning instead of breaking (Example code.)
Though, you gotta admit, all of those are kinda clumsy. Especially the function version lacks because of the missing context where the loops are called, as you'd need to pass everything you need in the loops as parameters.
Additionally, the second one gets worse for each nested loop.
So, I personally, still consider the goto version to be the cleanest.
Now, thinking all C++0x and stuff, the third option brought me this idea utilizing lambda expressions:
#include <iostream>
bool CheckCondition(){
return true;
}
bool CheckOtherCondition(){
return false;
}
int main(){
[&]{while(CheckCondition()){
for(;;){
if(!CheckOtherCondition())
return;
// do stuff...
}
// do stuff...
}}();
std::cout << "yep, broke out of it\n";
}
(Example at Ideone.)
This allows for the semantic beauty of a simple return that the third option offers while not suffering from the context problems and being (nearly) as clean as the goto version. It's also even shorter (character-wise) than any of the above options.
Now, I've learned to keep my joy down after finding beautiful (ab)uses of the language, because there's almost always some kind of drawback. Are there any on this one? Or is there even a better approach to the problem?
Please don't do that in a project I'm managing. That's an awkward abuse of lambdas in my opinion.
Use a goto where a goto is useful.
Perfectly valid in my opinion. Though I prefer to assign mine with names, making the code more self documenting, i.e.
int main(){
auto DoThatOneThing = [&]{while(CheckCondition()){
for(;;){
if(!CheckOtherCondition())
return;
// do stuff...
}
// do stuff...
}};
DoThatOneThing();
std::cout << "yep, broke out of it\n";
}
In which way is that an improvement over
void frgleTheBrgls()
{
while(CheckCondition()) {
for(;;) {
if(!CheckOtherCondition())
return;
// do stuff...
}
// do stuff...
}
}
int main()
{
frgleTheBrgls();
std::cout << "yep, broke out of it\n";
}
This is much well-known (functions, you know, as in BASIC), clearer (the algorithm's got a nice name explaining what it does), and does exactly the same as yours does.
Especially the function version lacks because of the missing context where the loops are called, as you'd need to pass everything you need in the loops as parameters.
I see that as an advantage. You see exactly what is needed to frgle the brgls. Explicity, when programming, often is a good thing.
One drawback with your proposed syntax: you cannot have more than 2 nested loops. The 'goto' syntax allows this:
int main()
{
for (;;)
{
for (;;)
{
for (;;)
{
if (CheckCondition1()) goto BREAK_ON_COND1;
if (CheckCondition2()) goto BREAK_ON_COND2;
if (CheckCondition3()) break;
// Do stuff when all conditions are false
}
// Do stuff when condition 3 becomes true
}
BREAK_ON_COND2:
// Do stuff when condition 2 becomes true
}
BREAK_ON_COND1: // When condition 1 becomes true
std::cout << "yep, broke out of it\n";
}
In a situation where a variable could have two different values, and you do something if its one, something differnent if its the other, would you just do:
if(myVariable == FIRST_POSSIBLE_VALUE) { ... }
else { ... }
or would you do:
if(myVariable == FIRST_POSSIBLE_VALUE) { ... }
else if (myVariable == SECOND_POSSIBLE_VALUE) { ... }
for clarity, in a situation where a reader wouldn't necessarily be able to tell that they do the same thing (but the else if does a "needless" expression)?
So what would you do?
Thanks!
EDIT: There is actually about a lot more different options for something like this: ternary operator, if-else, if-elseif, if-elseif-else, -if-else(with assert), switch. Each one has its place, but its hard to decide..
I always prefer just plain else when there is no other possible state of the variable(ie, checking for null and all that). I may add a comment saying what the variable is if it isn't the first conditional, but that is only in cases where its like
if(color==red){
....
}else{ //our theme only allows for red and yellow, so the color must be yellow.
....
}
Also, this saves some time for the processor cause it won't have to check a useless variable(or worse in OOP, where checking that variable can take quite a few dereferences, function calls, and memory reads)
I never do something like
if(file.is_open==1){
....
}else if(file.is_open==0){
....
}
as is_open is a boolean, it is pointless to specify that because the only option left is 0, also this can save a little bit of typing when you must refactor your code to use is_open() instead, as now you only must change one line instead of two.
and 'else if' statements I think should be turned to switches if there is more than 1 'else if', unless of course the language makes it impossible(such as how C can't handle strings in switches)
Else is a default. Meaning that there are either a large number of possibilities for the data, or that it is unexpected data.
I go by the basic rule: If there is a parameter that can be met, use else if, and if there isn't, use else. I normally use else's for errors.
I only use the if-else for boolean checks, that means that if the expression doesn't match there only can be the else. Or i want to take everything with the else: think of it like default.
If you want to check enumeration or something, you should try check this via switch statement, if possible in your language.
In Java it's not possible to use a switch for Strings. So you could use something like this:
if(string.equals("foo")) {
// first case
} else if(string.equals("bar")) {
// second case
} else {
throw IllegalArgumentException(" ... ");
// or log it
}
If you're not sure that your check can't be extended, you should if you can provide an default way.
Isn't this what assert was made for?
if (condition1) { ... }
else { assert(condition2); }
This can be expanded for three-state logic, too.
if (condition1) { ... }
elsif (condition2) { ... }
else { assert(condition3); }
Using assert makes your code readable, easy to maintain, and clear. That being said, asserts and comments are almost interchangeable.
Sometimes the condition of the else statement is very obvious. For example
if(user.IsNew) { } else { /*in this case user.IsNew != true*/ }
But in some other cases the else isn't that obvious and it is better to clarify the else condition. This is also more future proof in case some other possible conditions are added.
Furthermore you are able to insert an exception in the (last) else to inform about unimplemented cases. This can be very useful when the for example backend and frontend are separated and somebody adds a new value to an enumerator (or when using text keys a new key is introduced) you will receive an error when the new value is first used. When not using if else if you won't see what happened and that could make debugging pretty hard.
if(user.SelectedStyle == Styles.Red) {
} else if(user.SelectedStyle == Styles.Basic) {
} else {
throw new Exception("Not implemented");
}
In the case above a new Style (for example Style.Blue) will cause your application to throw an exception.
It's really a matter of style and your own mental view of the world. The glass is BOTH half empty and half full, but you can get the darnedest arguments going about it.
If the boolean tests are all of the same type, a switch statement is best.
If not, I'd recommend leaving out the additional test but insert a comment about the operational meaning of falling through into that last statement. See Gertjan's comment, above.
When your input can be clearly separate into distinct cases, I feel it is mostly nicer to explicitly state what those cases are, for example if you are expecting 'n' to be a number between 0 and 100, and you have 3 cases:
if (n >= 0 && n < 30) {
case1();
} else if (n >=30 && n < 70) {
case2();
} else if (n >=70 && n < 100) {
case3();
}
in some situations the 'else' case is good for error checking
} else {
error("n should be between 0 and 100");
}
if your data is checked for erroneous values earlier, then there may be a case to use else for the final case, to provide a small performance improvement in languages like C:
} else { // (n >= 70 && n < 100)
case3();
}
but this is only necessary due to some languages inability to express the domain of a function, in languages where the domain can be expressed clearly, the optimiser should add this performance benefit for you, allowing you to be specific in your code, and making it easier to add more cases later
... of course this is an art, not a science, and in some cases you can't follow the hard-and-fast rule, I frequently find myself writing code like:
if (p == NULL) {
doSomething();
} else {
doSomethingElse();
}
...justified by the fact that is very obvious and implicit from the first if condition what the else is used for.
else was invented and is used for good reason. Where you use it should be dictated by the logic that you trying to achieve, not a contrived sense of style.
Some argue that it is more self-documenting by using explicit conditions in an else if; however, this may lead to gaps in logic with default or catch all conditions.
Also, some say that it is easier to modify in the future. This argument is bunk. Using design patterns and writing modular code is something that is easier to modify in the future, writing one line shouldn't qualify for these kinds of statements.
It depends on the situation. Do you only want to take action if certain criteria are met, or is there a special case for one value and another set of logic for any other value?