15

I've read an article about the "Named Loop Idiom" in C++ : http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Named_Loop

This idiom allows us to write things like that :

named(outer) 
for(int i = 0 ; i < rows ; ++i) {

   named(inner) 
   for(int j = 0 ; j < cols ; ++j) {

        if(some_condition)
            break(outer);   // exit the 'outer' loop 

   }
}

Such constructs already exists as core feature in many languages, like Java for instance.

According to the article, it can be implemented in C++ by defining two evil macros :

#define named(blockname) goto blockname; \
                         blockname##_skip: if (0) \
                         blockname:

#define break(blockname) goto blockname##_skip;

I know that many people would like to banish the use of goto. I personally found it helpful in very rare cases, especially when I wanted to break a bunch of nested loops. This idiom appears to me as a cleaner solution for that, but is it ok to use it in real code ?

On the discussion page of the article, one can read :

"Do not do this. You'll end up in hell"

So my questions are : What are the drawbacks of using the named loop idiom ? Is it dangerous ? If yes, why ?

Bonus question : is it possible to implement named continue similarly ? (I think it's not possible using the named(...) for(...;...;...) {} syntax, but who knows ?)

EDIT : I agree with you, redefining a keyword is nasty. What about using #define breakLoop() instead?

Frédéric Terrazzoni
  • 2,190
  • 19
  • 25
  • 10
    `#define break` is a complete dealbreaker for me, personally. – ildjarn Jun 12 '12 at 22:35
  • 3
    #define break(something) is not the same as #define break, it should not replace the normal break statements. – Frédéric Terrazzoni Jun 12 '12 at 22:35
  • What difference do you perceive there? You're still turning a keyword into a macro. That's the very definition of evil IMO. – ildjarn Jun 12 '12 at 22:36
  • 6
    `#define break` lies somewhere between undefined behavior and ill-formed. It doesn't matter that you've defined it as a function-like macro, the only thing that matters is that the name of the macro is lexically identical to a keyword. – James McNellis Jun 12 '12 at 22:37
  • The preprocessor doesn't care about keywords, it just performs string substitution before the real code analysis. As long as no one will ever use break(something) since it won't compile, what is the problem ? Can it break something ? – Frédéric Terrazzoni Jun 12 '12 at 22:40
  • 5
    It can break anything because [it is forbidden](http://stackoverflow.com/questions/2726204/c-preprocessor-define-ing-a-keyword-is-it-standards-conforming) (at least in source files that use the Standard Library headers). – James McNellis Jun 12 '12 at 22:43
  • 11
    If you want to use `goto` then do it and keep it visible instead of obscuring it with macros. – Blastfurnace Jun 12 '12 at 22:43
  • 2
    @Frédéric : That's an issue of technically right vs. morally wrong, which is always subjective (i.e. "not constructive"). – ildjarn Jun 12 '12 at 22:43
  • Defining macros that use keywords as names is explicitly forbidden by the standard. – Cat Plus Plus Jun 12 '12 at 22:50
  • 1
    @ildjarn : My original question was about technical correctness & what it could effectively break / is it standard compliant. I agree with you about the "morally wrong", since redefining a keyword + using a macro + using goto all at the same time is cleary more than evil :) @ CatPlusPlus & James McNellis : #define break is forbidden, I'm okay with that. But is it the same for #define break() ? Last thing : what about changing the macro name ? #define breakLoop(name) for instance ? – Frédéric Terrazzoni Jun 12 '12 at 22:53
  • Take a look at Known Uses section of the article you linked to - it's empty :P – jrok Jun 12 '12 at 22:53
  • @CatPlusPlus Do you have a quote of that? [I asked a while ago in chat and it seems it's half-okay.](http://chat.stackoverflow.com/transcript/10?m=3594354#3594354) – Pubby Jun 12 '12 at 22:56
  • If Java has this it's perhaps partly because it lacks the alternatives you have in C++! – leftaroundabout Jun 12 '12 at 23:33
  • @leftaroundabout What alternative are you talking about for instance ? – Frédéric Terrazzoni Jun 12 '12 at 23:34
  • @CatPlusPlus Someone on the chat – Pubby Jun 12 '12 at 23:36
  • 1
    I am confused as to why this questions was closed. I certainly think that this "idiom" is a very bad idea, but that doesn't make the question any less valid. It's a "practical, answerable question." – James McNellis Jun 13 '12 at 00:29
  • @JamesMcNellis -- it seems more like a discussion question to me, and that's why it was closed. Even in the comments above, I see some differing opinions about *why* it's bad. – Ernest Friedman-Hill Jun 13 '12 at 00:41
  • 1
    This question will be useful to future users who learn of this idiom. They will find the legitimate concerns and see examples of alternative approaches, allowing them to make an informed decision about how and if to use this pattern in their code. – Adrian McCarthy Jun 13 '12 at 12:57
  • @FrédéricTerrazzoni I mean mostly goto, obviously. But also lambdas, which are IMO the superior solution in most situations; but obviously these were added to C++ when Java long had named loops. – leftaroundabout Jun 13 '12 at 14:08

2 Answers2

10

As covered in the comments, #defining break is problematic. Let's assume you use something else.

I'd still argue that this is dangerous. It's an extremely unusual idiom (to C++ programmers), so they're less likely to understand, and thus they might make breaking changes. Given that there are less-surprising--and therefore less-dangerous--ways to accomplish the same thing, I would advise against it.

Consider putting the loops in a function or a lambda. Then you can return to break out of the outer loop. As a benefit, you can return information about the premature exit, which may be useful to the outer code.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • Indeed lambda are pretty good for that. I'm wondering if they won't prevent some kind of optimization (loop invariants), but I'm just guessing... – Frédéric Terrazzoni Jun 12 '12 at 23:08
  • @Frédéric : `std::for_each` + lambdas was _made_ for this. ;-] (And add [`boost::counting_iterator`](http://www.boost.org/libs/iterator/doc/counting_iterator.html) if you really need indices rather than iterators.) – ildjarn Jun 12 '12 at 23:10
  • Indeed :p But this solution is not as flexible as "named for loops", since you cannot jump to any outer loop : you can only exit the lambda scope with `return`. – Frédéric Terrazzoni Jun 12 '12 at 23:17
  • @Frédéric : Which also happens to be easier to reason about. Whoever ends up maintaining your code later will thank you. ;-] – ildjarn Jun 12 '12 at 23:21
  • I know I'm wrong, but I love exploring the dark side of C++ ... Poor maintainers :) – Frédéric Terrazzoni Jun 12 '12 at 23:51
  • There exists several proposals to add a similar feature to C++, so stay tuned for the 2020 edition of the standard. – PlasmaHH Jun 15 '12 at 08:16
2

I find a couple of problems with this.

First, you're defining a macro with the same name as one of the language's reserved words. Even if your compiler doesn't gripe about that, it's error-prone and not and (IMO, at least) dangerous.

Second, I'm always hesitant to create labels programmatically. Even though your compiler will probably complain if you accidentally create two labels with the same name in the same scope, the error message it generates will probably not be easily understood without the programmer dissecting these macros (which partially defeats the purpose of the extra abstraction).

Probably my main problem is that the macros introduce something that is unlike anything in the normal language syntax. The named(...) lines don't end in semicolons nor are they followed by a { ... } block. Adding any sort of new syntax opens the door for developer confusion and accidental misuse.

Overall, I kind of like the idea of named loops, but this isn't the sort of thing that you'd want to create using macros. It's a mechanism that would really need to be provided by the language itself. When using C or C++, it's cleaner, safer, and more maintainable to use a manually-created label and a goto. It's almost always better to be explicit than to hide what's going on behind macros.

bta
  • 43,959
  • 6
  • 69
  • 99
  • 1
    Creating two labels with the same name isn't more likely than creating two variables with the same name :) It's a shame for C++ to not have a built-in labeled break/continue... – Frédéric Terrazzoni Jun 12 '12 at 23:13
  • @FrédéricTERRAZZONI- True. However, hiding the creation of something behind a macro that builds the name at compile time is more likely to result in a duplicate definition than declaring it out in the open. – bta Jun 12 '12 at 23:18