Boost logo

Boost :

Subject: Re: [boost] [Boost.Breakable] Any interest in a Boost Breakable library?
From: Gottlob Frege (gottlobfrege_at_[hidden])
Date: 2009-09-07 03:40:20


On Mon, Sep 7, 2009 at 2:14 AM, Pierre
Morcello<pmorcell-cppfrance_at_[hidden]> 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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk