|
Indeed. And should we question the order/precedence of the other operators as well?
Can we trust 3 + 4 * 5 to return 23? or might it return 35 once in a while?
I'm all for adding parentheses for clarity, but I don't get that confused with necessity.
|
|
|
|
|
You are depending on the order of evaluation of your if-statement for your logic to work. Will the PromptUser() operand be evaluated before the entire set of && operations are performed? I think C++ has an "undefined order of evaluation". Who knows. It should be irrelevant. Writing code that depends on order of evaluation is bad programming practice, regardless of language.
Sorry, but not knowing how your programming language works is bad practice (operator priority is very important in every language!). And depending on the order of your evaluations is the usual case in real world programming-logic. (I think you are mixing up here with the | and & operators and something a teacher told you once...).
I think C++ has an "undefined order of evaluation". Who knows
No - and I!!!
|
|
|
|
|
The second certainly looks cleaner; but sometimes you need to do cleanup (dispose objects, free memory, release handles, etc.) before exiting a function / method. If you add code later that needs cleanup, and already coded the second way, you'll have a lot to change... in addition, code that is already out there running stable is more valuable than new code, since you risk introducing bugs by changing from your second example back to the first. So I'd definitely go for the first - good habits breed consistency.
|
|
|
|
|
I've got to say, i don't agree with that rule.
I will aim to return out the function either very early, or at the end. Trivial conditions should be treated as such, and I'd rather they returned at the top, than add a unnecessary level of nested bracers.
I have seen the result of blindly following that practice to it's conclusion - a mountain of if / else statements with embedded switches that was 12 deep in places. I would rather see:
<br />
<br />
if (guardCondition1)<br />
return guard1Default;<br />
<br />
if (guardCondition2)<br />
return guard2Default;<br />
<br />
if (guardCondition3)<br />
return guard3Default;<br />
<br />
<br />
<br />
<br />
return result;<br />
<br />
That said, throwing return statements in without any real eye on program flow can cause just as many problems.
Regards
Tris
-------------------------------
Carrier Bags - 21st Century Tumbleweed.
|
|
|
|
|
Maybe WF fit such situation
The God created the world.
The programer made the world easy.
|
|
|
|
|
I completely agree with your sentiment. But in this specific example, it looks like processes generating FlagA, FlagB, and FlagC should always execute, regardless of whether any of the previous processes fail. Your guardConditional s don't allow for that.
|
|
|
|
|
Having been brought up on 'Structured Programming' I used to think that one-entry and one-exit point was the only way to write procedures.
Now I'm in favour of your approach - i.e. use guard conditions and exit early if need be. Oh and keep the procedure small, roughly no more than can fit on an A4 page.
One example of how not to do it that I came across was a C++ function that had around 500 lines of code with about 15 returns scattered throughout. It also had a couple of places where it could throw an exception!
David Rice
|
|
|
|
|
Having one return statement is still a worthwhile goal.
You just have to look at it both ways and decide which is "better" in each individual case.
I don't recall encountering any situations that required multiple return statements.
|
|
|
|
|
You can't make a rule like that because there is no one correct rule that works for every single function you're going to write in your life. Well, you can make the rule, but doing so will be counter-productive. Why create a state machine with local variables in a function (and don't forget to test every possible state!), just to avoid using a perfectly valid language feature?
|
|
|
|
|
it all depends on how much coffe i've had for the day...
do or do not, there is no try
|
|
|
|
|
Drinking coffee leads to good code. Drinking "energy drinks" leads to poor code.
|
|
|
|
|
Single exit , single entry . Its a mantra that leads to clarity
how about
if( !FlagA || !FlagB || !FlagC) // Select Variant on !Flagx or Flagx == false depending on language
DoOtherThing();
else
{
if(PromptUser())
DoSomething();
}
Not a horror though. My use of single entry and exit points in functions leads to more debates than anything. (My how the winter nights fly by)
|
|
|
|
|
Andrew Torrance wrote: if( !FlagA || !FlagB || !FlagC) // Select Variant on !Flagx or Flagx == false depending on language
DoOtherThing();
else
{
if(PromptUser())
DoSomething();
}
...except if PromptUser() fails, you also have to execute DoOtherThing() . So you need to add to your code:
if( !FlagA || !FlagB || !FlagC)
DoOtherThing();
else
{
if(PromptUser())
DoSomething();
<code> else
DoOtherThing();</code>
}
|
|
|
|
|
How is that better than what I posted?
|
|
|
|
|
Yes, he just inverted the boolean logic and made it worse in my opinion.
if(!FlagA || !FlagB) is essentially the same as if(FlagA && FlagB) but I'd rather see the latter and that's how I'd do it in my code.
Keep It Simple Stupid! (KISS)
|
|
|
|
|
It's not. Your code was how I would have done it...
|
|
|
|
|
Assuming that there is more processing going on than this example indicates, I would use a do-while-false loop:
do{
if(!flagA)break;
if(!flagB)break;
if(!flagC)break;
if(!PromptUser())break;
}while(0);
DoOtherThing(); This is the sort of thing that I routinely do with COM programming where I need to check the success of pretty much every single method call. It means that I don't have a crazy amount of indenting, and it's much cleaner for me or somebody else to understand and modify later.
I can highly recommend it.
None
|
|
|
|
|
Where's the call to DoSomething?
I'm pretty sure that's not applicable to the original post.
|
|
|
|
|
Doh - I was looking at the second code listing.
Assuming that there is lots of other stuff going on between the flag tests, and there is a need for clean-up code (thus don't want to use return), I'd use the same system with a flag:
bool doSomething = false;
do{
if(!flagA)break;
if(!flagB)break;
if(!flagC)break;
if(!PromptUser())break;
doSomething = true;
}while(0);
if(doSomething)
DoSomething();
else
DoOtherThing();
None
|
|
|
|
|
Still looks like a potentially infinite loop.
|
|
|
|
|
I can assure you that it isn't!
This technique is usually called a do-once block, and commented as such to make it really obvious. Look again at just the 'loop' bit and see if you think that this block of code will still run more than once:
do{
}while(0); What you basically end up with, is a block of code that you can jump to the end of early with a 'break', without having to resort to a 'goto'. That block of code isn't a loop, as it will never run more than once. Get it?
None
|
|
|
|
|
Oh, right, that's a 0, my mistake.
(Though I still say it looks like an infinite loop.)
And that technique is a horror unto itself -- a Weasel-Goto.
|
|
|
|
|
PIEBALDconsult wrote: And that technique is a horror unto itself -- a Weasel-Goto.
Yeah, and writing
if (condition)
{
do_something();
}
else
{
do_something_else();
} is really using "weasel-gotos" to obscure the fact that you're really writing:
if (condition) goto TRUE_CASE;
do_something_else();
goto END_OF_IF;
TRUE_CASE:
do_something();
END_OF_IF:
The goto statement itself is not inherently evil, but gets a bad rap for a couple reasons:
-1- There is nothing inherent in the GOTO which gives any clue about where to find its target, or what the significance of its target might be; having the target of a branch always be either the first statement of an indented block that is being executed or has just completed, or the first statement following such a block (and having the control-flow instruction clearly imply which target applies) makes it much easier to see where the program flow is going.
-2- Programs which use gotos (they are unavoidable when coding in things like assembly language), but where the overall program structure is consistent with the style indicated in (1) are generally easy for both humans and computers to analyze. If a program can clearly be divided into nested blocks such that there are no GOTOs into any block from anywhere outside it, program-flow analysis will generally be pretty easy. One of the major problems, in the days before structured programming, was that there were many pieces of code that had to go somewhere, and there wasn't any perceived reason not to put them anywhere that happened to be convenient.
For example, consider the code I listed above; notice that a conditional branch will be taken in the true case, and an unconditional branch will be taken in the false case. If one case occurred much more frequently than the other, the code could be rewritten:
if (rare_condition) goto TRUE_CASE;
handle_false_case();
END_OF_IF:
TRUE_CASE:
handle_true_case();
goto END_OF_IF; That approach will result in two branches taken in the true case, and none taken in the false case. In some situations, I've written code like that (when writing assembly code, and when performance was critical) but it's nasty to work with. Such code was the norm prior to the development of structured programming, and was the basis for much of the hatred related to GOTO.
Incidentally, I wish languages had a structure equivalent to "do{}while(0);" It can be very useful.
|
|
|
|
|
supercat9 wrote: prior to the development of structured programming
Agreed. When I first learned programming it was in BASIC, which didn't have the wealth of control statements that many other languages have.
Since learning Pascal and then C I haven't written a goto (other than within a switch statement).
supercat9 wrote: a structure equivalent to "do{}while(0);"
Borland C/C++, in C++ mode will handle this:
# define once for ( int i##__line__ = 1 ; i##__line__ ; i##__line__-- )
once
{
printf ( "Howdy" ) ;
if ( argc < 3 )
{
break ;
}
printf ( "Howdy" ) ;
}
Now I'll boot up one of my OpenVMS systems and try it there.
Later:
%CC-I-DECLINFOR, Placing a declaration in a for loop is a new feature in the C99 standard. Other C compilers may not support this extension.
Later yet:
I tried it with gcc version 3.2 (mingw special 20020817-1) , which I use to pre-process C#, and it wouldn't work.
It seems the others will replace __line__ (or __LINE__) before concatenating, but gcc wants to concatenate first and the replacement doesn't happen at all.
I guess I need to find a new C pre-processor.
modified on Monday, December 15, 2008 9:52 PM
|
|
|
|
|
The for-loop version is just plain nasty. It wastes a variable and a lot of code. Doing #define once while(0) and then
do {
some_code;
} once; would avoid the wasted code. If one wanted to tag both ends of the loop to make clear what was going on, instead of just the tail, one could define some other word as a synonym for "do". Personally, though, I don't like using macros to alter the syntax of a language doing so will make practical something that would otherwise be impractical. To people familiar with do{}while(0); as a means of doing something once, that form will be as readable as anything else. For those unfamiliar with it, the macro would just add confusion. That it would be desirable for a language to include a feature to avoid silly always-true or always-false conditionals does not imply that it is desirable to kludge such features.
BTW, if you really want your syntax, and if your code doesn't have to be thread-safe, you can limit the total number of extra variables program-wide to one:
#define once for(once_var=2;once_var>>=1; ) The global variable once_var will always be zero or one (doesn't matter which) except between the initial assignment and the initial test, so nested loops and recursion shouldn't pose problems. Still a horrible waste of code, but it should achieve the desired result.
|
|
|
|