
On Mon, Sep 7, 2009 at 2:14 AM, Pierre Morcello<pmorcell-cppfrance@yahoo.fr> wrote:
Thanks Michael, I did not think sooner to the 'else if'. But there is one problem with the 'else if' : you can not do calculations between the tests.
When I work often my code looks like : ...
Another 'lifer' here (20+ years!) :-) I think, by now, we all know and have seen the type of code you are talking about. I saw it a lot with Windows GUI programming - get the HWND, check, Get the DC, check, do some stuff, make a HPEN, check,... I have yet to see a perfect solution. Some of the solutions I've seen: 1) try to do all the checks at the top: void func(P1 p1, P2 p2, ...) { if (!IsOK(p1)) return; if (!IsOK(p2)) return; etc now do stuff... } I think this is pretty good if all the checks are just checking inputs. but it gets hairy in those cases, as mentioned, when there is other code in between. An alternative to this is to 2) do checks, then pass it down to unchecked functions: void func(P1 p1, P2 p2, ...) { if (!IsOK(p1)) return; if (!IsOK(p2)) return; return unchecked_func(p1, p2, ...); } This actually CAN (somewhat) work with code in between - after every single check you call another smaller function. But you end up with a cascade of functions (hard to follow) and you quickly run out of good names or reuse the same name if the param types have changed. But it can also be helpful if the inner functions can be reused in cases where you, say, already had a DC or HPEN or something. ie void func(P1 p1, P2 p2, ...) { Q q = getQ(p1); if (!IsOK(q)) return; return foo(q, p2,...); // then foo(Q,...) then assumes Q, handles p2, maybe calls down another layer. } 3) use gotos Maybe gotos are evil, but I've seen 'goto cleanup' or 'goto endoffunction' and it doesn't really bother me that much. It is sort of a poor man's 'finally' construct. As long as you are not going to multiple labels, or going back to the top (ie only 'goto' to labels closer to the end of the function) I can live with it. 4) mulitple returns strewn about I think there are a few reasons to avoid multiple returns, but a few of them need revisiting. In particular, one reason was that multiple returns tended to cause resources to go un-destructed. At least in the C days. But now, if we are coding to be exception safe, then basically we are dealing with multiple return points whether we want to or not. ie if an exception happens in the middle of the function, then that's where we are 'returning' from. Or at least realize that if everything is RAII and exception safe, then the code is also 'return safe'. Multiple return points might still be harder to read and reason with (although not much different than multiple breaks), but at least it should now be 'safe'. P.S. multiple returns were once hard to debug, (you had to put breakpoints at every return if you didn't want to miss your exit) but now you can typically put a breakpoint at the closing '}' of the function itself, and catch them all (although you still might not know which one it was). 5) exceptions I've tried throwing exceptions, but found a few things a) some of the errors were not 'exceptional' b) it felt odd (to me) to catch exceptions thrown within the same function (as opposed to catching exceptions thrown from lower levels) c) it didn't look like less code than other solutions Of course, there are cases where the errors SHOULD be thrown up to a higher level, and that does make the code cleaner (particularly if there is NO catching in the current function) so it would make sense to use it in those situations. 6) Your 'Breakable' idiom Well, I haven't tried it. I really dislike macros. Probably that would be enough to stop me. Not sure I like for (bool executed=false;!executed;executed=true) any better. I'd have to comment that, and then why not use a macro, which would be a comment at the same time. 7?) Interestingly, I think I would be fine with it, if it was built into the language. ie why not allow a break out of 'unnamed' blocks? (besides breaking some existing code with these blocks within loops, of course :-) { if (bad(x)) break; y = ....; if (!y) break; } I think I recall proposals for 'named' breaks, and multilevel breaks as well? Anyhow, I think that is a long way for me to just say "I feel your pain", and I wish there was a nicer solution (that was part of the language). I might even try your style. If I DID start using it regularly, I'd definitely want it to be in boost, even if it was tiny - because then it would become a known and understood idiom. But I'd prefer better language support instead. Tony