Is this a valid (ab)use of lambda expressions? - c++

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";
}

Related

How do you re-write a C++ empty if statement with side-effects

I have code that does something like this:
//datareader.cpp
if (populateFoo(dataReader, foo))
else {
// Do other things with the reader.
}
//foo.cpp
bool populateFoo(const DataReader &dataReader, Foo &foo)
{
if (dataReader.name() == "bar") {
foo.bar() = dataReader.value();
return true;
} // More similar checks.
return false;
}
I feel like it's misleading to have an if statement with conditions that have side-effects. However, I can't move the body of the populateFoo function into datareader.cpp. Is there a good way to restructure this code so we get rid of this misleading if statement, without duplicating the body of populateFoo()?
Do you have a strong hatred of local variables? If not:
bool populated = populateFoo(dataReader, foo);
if (populated)
{
// Do things
}
else
{
// Do other things
}
The compiler will almost certainly emit exactly the same code, so performance shouldn't be an issue. It's a readability/style choice, ultimately.
The obvious solution seems like storing the result of populateFoo and using it for determining whether populateFoo was successful:
bool fooPopulated = populateFoo(dataReader, Foo);
if (!fooPopulated)
//Do other things with reader.
However, I don't find the original difficult to understand, and it's a fairly well-established practice to both modify values and test the success of the modification in the same line. However, I would change it to:
if (!populateFoo(dataReader, Foo)
//Do other things with reader.
How about:
if (!populateFoo(dataReader, foo)) {
// Do other things with the reader.
}
Edit: The title of the question suggests it is the fact the if statement is empty that bothers you but the body seems more that it is the side effect that is the concern. I think it's fine in C++ to have conditions in if statements that have side effects but this won't solve your issue if you want to avoid that.
Having conditions with side-effects is quite common - think about calling a C API and checking its return code for errors.
Usually, as long as it's not buried in a complicated expression where it may be missed by the casual bystander, I don't bother to do particular refactorings, but, in case you wanted to make it extra clear (or document what the return value is, which is particularly useful in case of booleans) just assign it to a variable before the branch - or even just a few comments may help.
You could split the populateFoo function into two, a const check function (shouldPopulateFoo) that checks the condition, and another non-const function that performs the actual modifications (populateFoo):
//datareader.cpp
if (shouldPopulateFoo(dataReader)) {
populateFoo(dataReader, foo);
}
else {
// Do other things with the reader.
}
//foo.cpp
bool shouldPopulateFoo(const DataReader &dataReader) /* const */
{
return (dataReader.name() == "bar");
}
void populateFoo(const DataReader &dataReader, Foo &foo) /* non-const */
{
assert(shouldPopulateFoo(dataReader));
foo.bar = dataReader.value();
}
Note that when using these functions as class methods, you could declare the check function const.
How about:
if (populateFoo(dataReader, foo) == false) {
// Do other things with the reader.
}
It is very readable, I often see code where the returned value from function is a signal to the caller for branching in the caller. The else block with empty if block bothers me more then the side effects inside the if (). There is a sense of reverse logic, which is alway less readable.

How to avoid GOTO in C++

I have read that GOTO is bad, but how do I avoid it? I don't know how to program without GOTO. In BASIC I used GOTO for everything. What should I use instead in C and C++?
I used GOTO in BASIC like this:
MainLoop:
INPUT string$
IF string$ = "game" THEN
GOTO game
ENDIF
Consider the following piece of C++ code:
void broken()
{
int i = rand() % 10;
if (i == 0) // 1 in 10 chance.
goto iHaveABadFeelingAboutThis;
std::string cake = "a lie";
// ...
// lots of code that prepares the cake
// ...
iHaveABadFeelingAboutThis:
// 1 time out of ten, the cake really is a lie.
eat(cake);
// maybe this is where "iHaveABadFeelingAboutThis" was supposed to be?
std::cout << "Thank you for calling" << std::endl;
}
Ultimately, "goto" is not much different than C++'s other flow-control keywords: "break", "continue", "throw", etc; functionally it introduces some scope-related issues as demonstrated above.
Relying on goto will teach you bad habits that produce difficult to read, difficult to debug and difficult to maintain code, and it will generally tend to lead to bugs. Why? Because goto is free-form in the worst possible way, and it lets you bypass structural controls built into the language, such as scope rules, etc.
Few of the alternatives are particularly intuitive, and some of them are arguably as ambiguous as "goto", but at least you are operating within the structure of the language - referring back to the above sample, it's much harder to do what we did in the above example with anything but goto (of course, you can still shoot yourself in the foot with for/while/throw when working with pointers).
Your options for avoiding it and using the language's natural flow control constructs to keep code humanly readable and maintainable:
Break your code up into subroutines.
Don't be afraid of small, discrete, well-named functions, as long as you are not perpetually hauling a massive list of arguments around (if you are, then you probably want to look at encapsulating with a class).
Many novices use "goto" because they write ridiculously long functions and then find that they want to get from line 2 of a 3000 line function to line 2998. In the above code, the bug created by goto is much harder to create if you split the function into two payloads, the logic and the functional.
void haveCake() {
std::string cake = "a lie";
// ...
// lots of code that prepares the cake
// ...
eat(cake);
}
void foo() {
int i = rand() % 10;
if (i != 0) // 9 times out of 10
haveCake();
std::cout << "Thanks for calling" << std::endl;
}
Some folks refer to this as "hoisting" (I hoisted everything that needed to be scoped with 'cake' into the haveCake function).
One-shot for loops.
These are not always obvious to programmers starting out, it says it's a for/while/do loop but it's actually only intended to run once.
for ( ; ; ) { // 1-shot for loop.
int i = rand() % 10;
if (i == 0) // 1 time in 10
break;
std::string cake = "a lie";
// << all the cakey goodness.
// And here's the weakness of this approach.
// If you don't "break" you may create an infinite loop.
break;
}
std::cout << "Thanks for calling" << std::endl;
Exceptions.
These can be very powerful, but they can also require a lot of boiler plate. Plus you can throw exceptions to be caught further back up the call stack or not at all (and exit the program).
struct OutOfLuck {};
try {
int i = rand() % 10;
if (i == 0)
throw OutOfLuck();
std::string cake = "a lie";
// << did you know: cake contains no fat, sugar, salt, calories or chemicals?
if (cake.size() < MIN_CAKE)
throw CakeError("WTF is this? I asked for cake, not muffin");
}
catch (OutOfLuck&) {} // we don't catch CakeError, that's Someone Else's Problem(TM).
std::cout << "Thanks for calling" << std::endl;
Formally, you should try and derive your exceptions from std::exception, but I'm sometimes kind of partial to throwing const char* strings, enums and occasionally struct Rock.
try {
if (creamyGoodness.index() < 11)
throw "Well, heck, we ran out of cream.";
} catch (const char* wkoft /*what kind of fail today*/) {
std::cout << "CAKE FAIL: " << wkoft << std::endl;
throw std::runtime_error(wkoft);
}
The biggest problem here is that exceptions are intended for handling errors as in the second of the two examples immediately above.
There are several reasons to use goto, the main would be: conditional execution, loops and "exit" routine.
Conditional execution is managed by if/else generally, and it should be enough
Loops are managed by for, while and do while; and are furthermore reinforced by continue and break
The most difficult would be the "exit" routine, in C++ however it is replaced by deterministic execution of destructors. So to make you call a routine on exiting a function, you simply create an object that will perform the action you need in its destructor: immediate advantages are that you cannot forget to execute the action when adding one return and that it'll work even in the presence of exceptions.
Usually loops like for, while and do while and functions have more or less disposed the need of using GOTO. Learn about using those and after a few examples you won't think about goto anymore.
:)
Edsger Dijkstra published a famous letter titled Go To Statement Considered Harmful. You should read about it, he advocated for structured programming. That wikipedia article describes what you need to know about structured programming. You can write structured programs with goto, but that is not a popular view these days, for that perspective read Donald Knuth's Structured Programming with goto Statements.
goto is now displaced by other programming constructs like for, while, do-while etc, which are easier to read. But goto still has it's uses. I use it in a situation where different code blocks in a function (for e.g., which involve different conditional checks) have a single exit point. Apart from this one use for every other thing you should use appropriate programming constructs.
goto is not inherently bad, it has it's uses, just like any other language feature. You can completely avoid using goto, by using exceptions, try/catch, and loops as well as appropriate if/else constructs.
However, if you realize that you get extremly out of your way, just to avoid it, it might be an indiaction that it would be better to use it.
Personally I use goto to implement functions with single entry and exit points, which makes the code much more readable. This is the only thing where I still find goto usefull and actually improves the structure and readabillity of the code.
As an example:
int foo()
{
int fd1 = -1;
int fd2 = -1;
int fd3 = -1;
fd1 = open();
if(fd1 == -1)
goto Quit:
fd2 = open();
if(fd2 == -1)
goto Quit:
fd3 = open();
if(fd3 == -1)
goto Quit:
... do your stuff here ...
Quit:
if(fd1 != -1)
closefile();
if(fd2 != -1)
closefile();
if(fd3 != -1)
closefile();
}
In C++ you find, that the need for such structures might be drastically reduced, if you properly implement classes which encapsulate access to resources. For example using smartpointer are such an example.
In the above sample, you would implement/use a file class in C++, so that, when it gets destructed, the file handle is also closed. Using classes, also has the advantage that it will work when exceptions are thrown, because then the compiler ensures that all objects are properly destructed. So in C++ you should definitely use classes with destructors, to achieve this.
When you want to code in C, you should consider that extra blocks also add additional complexity to the code, which in turn makes the code harder to understand and to control. I would prefer a well placed goto anytime over a series of artifical if/else clauses just to avoid it. And if you later have to revisit the code, you can still understand it, without following all the extra blocks.
Maybe instead of
if(something happens)
goto err;
err:
print_log()
one can use :
do {
if (something happens)
{
seterrbool = true;
break; // You can avoid using using go to I believe
}
} while (false) //loop will work only one anyways
if (seterrbool)
printlog();
It may not seem friendly because in the example above there is only one goto but will be more readable if there are many "goto" .
This implementation of the function above avoids using goto's. Note, this does NOT contain a loop. The compiler will optimize this. I prefer this implementation.
Using 'break' and 'continue', goto statements can (almost?) always be avoided.
int foo()
{
int fd1 = -1;
int fd2 = -1;
int fd3 = -1;
do
{
fd1 = open();
if(fd1 == -1)
break;
fd2 = open();
if(fd2 == -1)
break:
fd3 = open();
if(fd3 == -1)
break;
... do your stuff here ...
}
while (false);
if(fd1 != -1)
closefile();
if(fd2 != -1)
closefile();
if(fd3 != -1)
closefile();
}
BASIC originally is an interpreted language. It doesn't have structures so it relies on GOTOs to jump the specific line, like how you jump in assembly. In this way the program flow is hard to follow, making debugging more complicated.
Pascal, C and all modern high-level programming languages including Visual Basic (which was based on BASIC) are strongly structured with "commands" grouped into blocks. For example VB has Do... Loop, While... End While, For...Next. Even some old derivatives of BASIC support structures like Microsoft QuickBASIC:
DECLARE SUB PrintSomeStars (StarCount!)
REM QuickBASIC example
INPUT "What is your name: ", UserName$
PRINT "Hello "; UserName$
DO
INPUT "How many stars do you want: ", NumStars
CALL PrintSomeStars(NumStars)
DO
INPUT "Do you want more stars? ", Answer$
LOOP UNTIL Answer$ <> ""
Answer$ = LEFT$(Answer$, 1)
LOOP WHILE UCASE$(Answer$) = "Y"
PRINT "Goodbye "; UserName$
END
SUB PrintSomeStars (StarCount)
REM This procedure uses a local variable called Stars$
Stars$ = STRING$(StarCount, "*")
PRINT Stars$
END SUB
Another example in Visual Basic .NET
Public Module StarsProgram
Private Function Ask(prompt As String) As String
Console.Write(prompt)
Return Console.ReadLine()
End Function
Public Sub Main()
Dim userName = Ask("What is your name: ")
Console.WriteLine("Hello {0}", userName)
Dim answer As String
Do
Dim numStars = CInt(Ask("How many stars do you want: "))
Dim stars As New String("*"c, numStars)
Console.WriteLine(stars)
Do
answer = Ask("Do you want more stars? ")
Loop Until answer <> ""
Loop While answer.StartsWith("Y", StringComparison.OrdinalIgnoreCase)
Console.WriteLine("Goodbye {0}", userName)
End Sub
End Module
Similar things will be used in C++, like if, then, for, do, while... which together define the program flow. You don't need to use goto to jump to the next statement. In specific cases you can still use goto if it makes the control flow clearer, but in general there's no need for it

C/C++ (Other Languages Too?) Conditional Early Return Good Code Practice

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.

C++ SDL Breaking out of while loop

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;
}

Style question about existing piece of code (C/C++)

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)