Related
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
What are the best practices for writing C or C++ functions that return an int that represents a status code?
Specifically, I want to know about the client usage but other tips are welcome.
For example, can I write something like this:
int foo() {
return 0; // because everything was cool
}
And then use it like this?
if (foo()) {
// what to do if false, e.g. non-zero, e.g. not OK
} else {
// what to do if true, e.g. zero, e.g. OK
}
This should work because best practices typically dictate that a status code of 0 means everything was OK and also 0 means false in a boolean statement.
However, this wouldn't be good, right:
if (!foo()) {
// what to do if true
} else {
// what to do if false
}
We use this in C where I work:
int err = foo();
if (err) {
// armageddon
}
The assignment and if could be combined, but with more complicated function calls it gets more confusing and some people are confused by assignment in a conditional (and gcc hates it).
For C++, I would prefer exceptions if available, otherwise the above.
Edit:
I would recommend returning 0 on success and anything else on error. This is what unix command line utilities do.
If you really want to use status codes that way, use them with an enum or block of #define statements that describe the intention of the status code.
For example:
enum
{
kSuccess = 0,
kFailure = -1,
}
function foo()
{
return kSuccess;
}
if (kSuccess == foo())
{
// Handle successful call to foo
}
else
{
// Handle failed call to foo
}
This way, the intention is clear and there's no error-prone guesswork when someone wants to use or maintain your code in the future.
if (foo()) {
// what to do if false
} else {
// what to do if true
}
The problem with this approach is excess nesting. Suppose you have three functions you want to call:
if(foo1()) {
if(foo2()) {
if(foo3()) {
// the rest of your code
} else {
// handle error
}
} else {
// handle error
}
} else {
// handle error
}
To solve the excess nesting problem, invert the return value:
if(!foo1()) {
// handle error
return;
}
if(!foo2()) {
// handle error
return;
}
if(!foo3()) {
// handle error
return;
}
This solution suffers from another problem. It mixes the program logic with the error handling code. This complicates everything. Ideally, you want the program logic and error handling separated. This problem can be fixed with the goto
if(!foo1())
goto error1;
if(!foo2())
goto error2;
if(!foo3())
goto error3;
return;
error1:
// handle error
return;
error2:
// handle error
return;
error3:
// handle error
return;
Much cleaner.
Also, the goto can solve the problem of resource deallocation. See Using goto for error handling in C by Eli Bendersky for more details.
The return statuses should be defined in your interface and known to the caller. Some return 0 on failure (because it's easy to check with !), some return 0 on success (because they have enum of error codes, with OK being the first item).
There's no law or standard, each interface defines its own conventions. In C++ - use exceptions.
Best practice is to document your code so that yourself and others can quickly look up what the return codes will be when doing error checking.
Just jumping on board with another option that may be appropriate in your circumstances:
enum fooret { GOOD, BAD, UGLY, WORSE };
fooret foo(); // defined elsewhere
switch(foo())
{
case BAD:
case UGLY:
// maybe a recoverable failure(s)...
// take appropriate actions
break;
case WORSE:
// maybe non-recoverable
break;
case GOOD:
// successful, take appropriate actions
break;
}
int foo() {
try{
...
return 1
}
catch
{
return 0; // because everything was cool
}
}
I would start by wrapping everything in a try/catch block. Also instead of using and int it might make more scene to return a Boolean value. This is just a little more intuitive when testing in the if statement.
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?
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)