13

Like we all know, it's not that easy to break from a nested loop out of an outer loop without either:

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?

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • I'm really tempted to vote it down, but perhaps because I always have been using `goto` for this and I don't like my habits to get changed. I would have found this ok in Javascript, so why not here. – Alexandre C. May 20 '11 at 21:34
  • 3
    The only drawback I can see is that it'll undoubtedly confuse some readers. :) – jalf May 20 '11 at 21:37
  • 1
    @jalf: Didi you say "the only"? That's a very serious drawback, that needs to be outweighed by clear advantages. For >15 years, I have tortured cow-workers with advanced C++ stuff that was painful for them to learn. I think template meta-programming is much harder to grok than this lambda wizardry, and I did not hesitate to force it down their throats. _But that was because I saw a clear advantage in using it._ (If it fails, it fails compiling, and what doesn't compile, can never fail on a customer's machine.) I fail to see any advantage in this one here. See my answer. – sbi May 20 '11 at 21:52
  • 5
    @sbi: I said nothing about how serious the drawback is. Just that it was the only one I could see. :) – jalf May 20 '11 at 21:59
  • 4
    If this catches on we will have a new resource to earn easy reps answering questions from people forgetting the `()` at the end. – Bo Persson May 21 '11 at 09:12
  • @Bo: Nah, only once. Others get closed off as duplicates. :P – Xeo May 21 '11 at 18:54
  • If you think this is abuse of lambda sxpressions, then you should check out Continuation Passing Style :) – hugomg Nov 01 '12 at 21:09

4 Answers4

16

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.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 2
    I can't agree more. It is 1) clear, 2) "idiomatic", 3) taught in C++ kindergarten as the only proper use of "evil `goto`" in C++ – Alexandre C. May 21 '11 at 11:02
5

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";
}
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • Got to love C++11 or 0x, well... :) – Diego Sevilla May 20 '11 at 21:46
  • 4
    Now that you even named the thing, what is so good about doing it this way (except for that you know how to do cool lambda stuff), rather than with a normal function?? I dislike this. – sbi May 20 '11 at 21:49
  • 1
    The namind kinda destroys the advantage over having it in a normal function. :/ – Xeo May 20 '11 at 21:51
  • @Xeo: Yeah. of course. But still, the first answer you got was suggesting giving it a name. What does that tell you? ___If an algorithm is so complex that you're hesitating how to implement it, give it a name and call it by that name.___ You do that by putting it into its own function. – sbi May 20 '11 at 21:56
  • 2
    @Xeo,@sbi: Because it's local, and it doesn't pollute the namespace. – Benjamin Lindley May 20 '11 at 22:02
  • 1
    @Xeo: What advantages do you see in lambdas that are lost by giving them names? – Benjamin Lindley May 20 '11 at 22:05
  • 1
    None, but I certainly see no advantage in giving them names if it's a one-shot lambda. If I need it multiple times, I either name it or make it a real function. – Xeo May 20 '11 at 22:08
  • @Xeo: The advantage is that it clarifies the purpose of the lambda, like I said in my answer, it makes it self documenting. – Benjamin Lindley May 20 '11 at 22:09
  • 4
    @Xeo: You seriously underestimate names. Names are something very important and incredibly powerful in programming. I sometimes think longer over a function's name than over its body, and I wish more programmers would do so. – sbi May 20 '11 at 22:10
  • 2
    @sbi : Names, like comments, can be deceiving; code is self explanatory. You seriously overestimate names. – ildjarn May 20 '11 at 22:45
  • 2
    @ildjam: Code says exactly what it does, not the summary (or the programmer's intentions) which is better suited to the name (and the comments). If you're in the habit of lying to yourself in your code, I'd strongly suggest you _stop that_. Refactoring to introduce sane names is really easy… – Donal Fellows May 21 '11 at 07:46
  • @DonalFellows : I'm not in the habit of lying to myself, I'm in the habit of working with (gasp) other human beings who are imperfect and forget to update their variable names and comments when maintaining code. I.e., relying on names is idealistic -- I'm being realistic. – ildjarn May 21 '11 at 16:39
  • 1
    @ildjarn: Realistic? Single letter variable names. Single letter function names. Chaos and confusion. (I remember working on merging C codebases with multiple global functions called `doit` and `exec`…) – Donal Fellows May 24 '11 at 15:30
4

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.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • Well, and now do the same with local variables in `main`. You'd need to pass them to `frgleTheBrgls`, and I find it cleaner to just auto-capture them. Also, this is one of the `one-off` examples I kinda dislike, like writing functors in C++98/03 for stdlib algorithms. – Xeo May 20 '11 at 21:43
  • @Xeo: I see it as an advantage to have to pass those variables explicitly. See my answer. – sbi May 20 '11 at 21:46
  • Lambdas aren't functions now? – ildjarn May 20 '11 at 21:46
  • 1
    @Xeo: but usually, the inner loop function doesn't depend on all that much external state. If it does, perhaps you should tighten up your dependencies. That's kind of the argument behind avoiding globals too. Pass your dependencies explicitly, don't just allow them to be implicitly accessed all over the place ;) – jalf May 20 '11 at 21:48
  • 1
    @jalf: Both loops are inside the function, not only the inner. – Xeo May 20 '11 at 21:49
  • @Xeo: Yes, but I'm referring to the code inside the loops. The "do stuff" part. – jalf May 20 '11 at 22:00
  • 3
    Ignoring parameter passing, sometimes (not always) creating one-shot functions like this results in unnecessary clutter. You lose a lot of context this way. If you only call the function in one place, why not implement it in that same place? – Dennis Zickefoose May 20 '11 at 22:01
  • 2
    @Dennis: While yours clutters the function's scope. Look, Pascal had local functions, nested as deep as you wanted to. In C++ you don't have those, and abusing some language construct to swindle them in is like doing `#define begin {`: It's raping the language. Don't try to write Pascal in C++. – sbi May 20 '11 at 22:08
  • 7
    @sbi : "*In C++ you don't have those"* Clearly, in C++11, you do. Get used to it. – ildjarn May 20 '11 at 22:49
  • 2
    Lambdas were added because people were abusing language features to make C++ something it wasn't, so that argument doesn't sit well with me. Sometimes functions are easier to understand in context, and sometimes limiting who can call a function makes maintenance easier. Inner functions provide both. Is this the right time to use them, maybe not. – Dennis Zickefoose May 20 '11 at 23:01
1

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";
}
anatolyg
  • 26,506
  • 9
  • 60
  • 134