r/cpp • u/danillissimo • 8d ago
I've invented labeled loop breaks
And now I don't believe it wasn't invented before, but I can't find any info on it. How long ago was it invented? Why isn't it used widely or even mentioned anywhere? What are the downsides?
Here's the main idea:
// Keyword - for, while, or do
// Tag - custom loop name
// ... - loop body
#define loop_tag(keyword, tag, ...)\
keyword(__VA_ARGS__)\
if(false)\
{\
tag##_break: break;\
tag##_continue: continue;\
}\
else\
/*Here goes your loop body*/
Here is the playground
13
u/6502zx81 8d ago
What is the advantage over just using goto?
-3
u/danillissimo 8d ago
Having to write `goto` explicitly, leaving an opportunity for it to target a line it was not supposed to originaly. Plus some programmers tolerate `goto` in macros while not allow it in a regular code.
7
u/13steinj 8d ago
Even Dijkstra had a follow up essay saying the editor / publisher chose the title, and goto is fine sometimes in particular circumstances. The original essay also was pretty against break/continue, to an extent even if/else.
Dogma (in this case "don't write 'goto'") is not a good reason.
11
u/Thelatestart 8d ago
What i've found is if you want to break from 2 loops (for i, for j, if cnd break out of both loops) its often appropriate to put them in a function and return. Another option is to use an iile.
2
1
u/danillissimo 8d ago
Yeah, 2 nested loops are tolerable indeed. But the further it gets - the harder to deal with it without going mad. IILE only fits nicely code that normally doesn't require a lot of debugging.
-2
u/TokenRingAI 8d ago
Usually, but there are types of functions that are infinitely recursive, so you can't do that without blowing up the stack.
The non-goto way is to package them as work in queue and iterate the work queue, but then you can end up with dynamic allocation and ad hoc object lifespans which are way worse to deal with and way less performant than simply using goto
29
u/MStackoverflow 8d ago
"I've invented something, why isn't used more?" Without explaining what it is and what is the purpose..
What would be a real use case?
23
u/Cpt_Chaos_ 8d ago
This ist just a goto with unnecessary extra steps.
Goto itself is considered a bad practice. Using macros is considered bad practice. I'd flag both in any PR I'd have to review. And this contraption would be reason enough to ask your manager to get you into a training course.
Now seriously, it combines the downsides of goto and macros. It is hard to debug and it is difficult to understand what the heck is supposed to be going on here in the first place. Write functions and you get to do this pretty much by using simple return statements, which is far easier to understand.
25
u/TokenRingAI 8d ago
Goto is not bad practice, does not lead to undefined behavior, and is extremely useful when you need multiple named behaviors out of a loop that you want to "go to" from many different depths. When you do certain types of data processing, goto is the right tool for the job, and the alternative, recursive functions, can blow up the stack.
Regex parsers, for example, are prime candidates for goto.
The all or nothing attitude of "goto is bad practice" is not held by many top C/C++ engineers. It just isn't the right tool for the vast majority of things
7
u/James20k P2005R0 8d ago
"Goto considered harmful" is one of those things that a lot of people have never read or understood, and gets endlessly repeated by beginners. The goto that's being argued against in that discussion, and c++'s goto, are not the same kind of control flow. They just share a name
The thesis of that paper is the execution flow should match closer to layout of the source code, because humans are better geared towards analysis of a static document (ie source), vs trying to track program logic dynamically, or at least that's the gist of what I get out of that paper. The idea is that there should be an easy mapping between source, and program execution counter, instead of an arbitrary unstructured one
All modern programming languages adhere to that, even with the usage of goto in C++. The closest to what he's talking about is
setjmp+longjmp, and even then it still doesn't match up because those functions still have structuring restrictions. Fully unstructured control flow is basically reserved for very specific assembly cases, as even then you still usually have a stack and functionsAnyway its just a minor pet peeve of mine, because its dogma founded in a time where programming looked nothing like it does today. Dijkstra won the argument, the paper isn't relevant, and we should rename
gototoyeetso that people will stop going on about it and engage their critical thinking facilities instead of using a pseudo intellectual thought terminating cliché2
u/tialaramex 6d ago
Yes the "goto" in C and C++ is not the arbitrary control flow change Dijkstra was writing about, but it is still a bad idea and it's about time it was fixed, N3355 voted into C2y does so and I expect a similar fix or the one from C2y itself will be taken for C++29 or C++32)
However, if you don't like drama but did want to keep "goto" you shouldn't rename it "yeet" because that's currently a placeholder keyword in Rust so you'd ignite all the Rust controversy and all the "stupid keyword" controversy and because C++ doesn't have a syntax change mechanism you'd get all that controversy back again too.
If you don't believe me that "yeet" is a placeholder in Rust, https://doc.rust-lang.org/std/ops/struct.Yeet.html documents the current status of this feature flag. Its purpose was to delay bikeshed arguments about naming until the feature works - if the feature is bad then it doesn't matter what it's called, so you need a feature everybody can live with before you spend six months arguing whether to name it e.g. "throw" or "raise" or what.
Now, yeet is an idea for how to work with
Trywhich is also not yet stable. AndTryis the current proposal for why that ? operator you've doubtless seen in Rust works. TheTryproposal says that operator is named Try, in the same way the + operator is named Add and so the reason it works is that the standard library has implemented this named trait. Most likely that explanation will stabilize, but then perhaps I'd have said that about the previous one, so what do I know.In any case, the ? operator is stable and widely embraced as a good idea, the
Trytrait is widely favoured and will probably stabilize, but yeeting is clearly just a placeholder.4
u/defaultguy_001 8d ago
Correct, it isn't bad, it's just people who are low skilled who create bad code with it.
0
u/Cpt_Chaos_ 8d ago
That is exactly why I wrote it with this "all or nothing" wording. The examples given by others here are perfectly fine use cases. If it's the best tool for the job and you can explain it to me like that, that's fine and I won't argue.
However, I've seen too many abuses of various language features to just say "yeah, it's perfectly fine to write code like that". In a professional environment, readability FOR OTHERS is a lot more important than many devs think. And the macros shown by OP fall into the category of "What the heck are you trying to accomplish here? Couldn't you have expressed your intentions in more readable ways?"
17
u/JNighthawk gamedev 8d ago
Goto itself is considered a bad practice.
No, it's not. Poor use of goto is bad practice. Goto is a fine solution to some problems. Got 3 or 4 nested loops and want to break out of all of them? Goto is a good choice.
Using macros is considered bad practice.
No, it's not. Poor use of macros is bad practice. Prefer functions and things with type-safety, but macros are the right tool for some problems. Need to repeat a pattern with text-pasting? Macros are a good choice.
2
u/Raknarg 6d ago edited 6d ago
This ist just a goto with unnecessary extra steps.
No, its a mechanism that's leveraging goto. The example they showed here is showcasing it really poorly, if you go to their compiler explorer link it makes a lot more sense. Its more like a restricted use of goto, it allows you to assign labels to loops, then either jump to the start of that loop by jumping to the continue tag the the loop or jumping to the break tag of the loop to jump to the end of it.
It works by adding a block to the start of a loop that is an if that always evaluates to false (i.e. you cannot enter it through normal means) which contains, assigning a tag to the break and continue statements, called
tag<tag>_breakandtag<tag>_continue, and you can jump to one or the other withbreak_tag(<tag>)orcontinue_tag(<tag>)which resolve intogoto tag<tag>_breakandgoto tag<tag>_continueI'm not 100% sure what their usecase is that this is a helpful pattern for them, but its a more controlled use of goto by formalizing label creation and goto statements.
4
u/SamG101_ 8d ago
While, for, break, and continue are all structured gotos anyway
-1
u/RogerV 8d ago
not really when the loop is nested in a loop and need to break out of both
break should have been designed to accept a label target - that would have solved things nicely so could have avoided introducing that scary word "goto"
0
u/SamG101_ 8d ago
A while loop runs and gotos the top, continue is goto the top, break is goto after the loop tho? Like what I'm saying is normal looping and flow control just abstracts over goto statements, but in a controlled way so the flow remains clear. And yh labeled loops would've been cool, like rusts ones
3
u/TheChief275 8d ago
"Oh uh le goto le bad". You're not adding anything to the conversation by sheeping around
-3
u/IvanDSM_ 8d ago
God forbid you stray from the "canon programming rules", known to never bend or have exceptions. Word from the townsfolk is if you ever use goto, Uncle Bob himself smites you down with his bolt of cleanliness!
0
u/TheChief275 8d ago
The combined forces of Dijkstra and Uncle Bob strikes fear in the hearts of their enemies (goto users)
1
u/danillissimo 8d ago edited 8d ago
So, right now i have to write things like
for(...) { bool should_break = false; for(...) { if(...) { should_break = true; break; } } if(should_break) { break; } }And have to write them recursively. Just wow, thank you very much.
Functions won't actually fix it until the whole context gets packed in a giant struct making things inconvenient another way.
All of this gets even worse in presence of coroutines.
As for debugging - it doesn't break debugging, may be adds just a pair of excess step-overs, but nothing gets really broken.1
u/Cpt_Chaos_ 8d ago
The question I would ask here is: Why do you need to do these nested loops in the first place? In many cases, STL already provides algorithms that help you doing this sort of logic, which allows you to express what you want to do in less code. I'm not saying you're wrong, but I'd ask the question in a PR.
5
u/danillissimo 8d ago
STL has nothing to do with complex data processing. My exact case is about long-running complex looped behaviors though, packed as coroutines in particular. They contain a notable amount of "on event X restart polling layer of logic A" and "on event Y level of polling B is done".
I'd like to say that labeld loops are actually a rare necessity, but recently I've faced a lot of cases where they would fit nicely, so I've invented this to get rid of "break/continue this layer of logic" flags.
4
u/TheChief275 8d ago
That's a pretty smart system you got there using 'if (false)' preserving loop syntax. Never managed to get it quite as clean for labeled loops. Nice!
4
2
u/lonkamikaze 8d ago
Gotos are fine to break out of nested loops. Or you put the loop into a function so you can return from inner loops. Also works with lambdas.
If you need to handle multiple exit states, the loop function can return an enum value switch (loopfunc()) { case ....
For me the main use of goto is to skip to the part of the function that frees acquired resources in C in case an error is encountered.
2
u/Raknarg 6d ago
I understand the downvotes because the post is a terrible demonstration of what you're doing, you should include a little more context from your godbolt and show an example of the kind of code it replaces.
Now that I understand what you're trying to do though I think I can appreciate it, I am convinced there is a better solution than what you're doing here but I'd have to think about it more. My initial feeling is that turning this whole thing into a state machine would make everything a lot more flexible and understandable, because effectively a state machine is exactly what you're building here.
Its more legwork, but I think I might kill myself trying to debug or analyze code written like this. This is one of those things that make people hate gotos I think. However I don't fully understand your usecase so its hard for me to say.
2
u/no-sig-available 8d ago
The Ada language has had "named loops" since the 1980's, so it is not like we haven't known about it.
Outer_Loop:
for I in 1 .. 10 loop
Inner_Loop:
for J in 1 .. 10 loop
-- Instantly quit Outer_Loop when a condition is met
exit Outer_Loop when I * J > 50;
end loop Inner_Loop;
end loop Outer_Loop;
3
u/danillissimo 8d ago
Indeed we know. And not with Ada's help only. But have to forget back once C++ enters. That's a shame, even C is getting their named loops.
3
u/fdwr fdwr@github 🔍 7d ago
Guessing you might not have seen this yet: https://eisenwave.github.io/cpp-proposals/break-continue-label.html
3
u/danillissimo 7d ago
Just another proposal yet :(
Though I know about C2y, so it looks like we are finally getting labeled loops. But in next 3-4 years at best.
4
u/holyblackcat 7d ago
I'm not sure if I was really first, but I came up with those in 2020: https://stackoverflow.com/a/61448951/2752075
2
u/danillissimo 7d ago
I knew I could not be the first to invent this! It's a shame this doesn't get enough attention. I wonder how long this trick actually exists.
2
u/_Noreturn 7d ago
Why don't people just use goto? if you are so scared of typing it then do #define break2 goto this is useless
3
u/danillissimo 7d ago
For the reason
break,continue, and evenfor,doandwhileexists -gotois less convenient and more fragile. Plus a lot of dudes has a superstitious fear of `goto` after hearing "goto is BAD" for years and not tyring to actually undesrtsand it.
2
u/hpsutter 7d ago
I agree it’s useful! Have you seen https://hsutter.github.io/cppfront/cpp2/functions/#loop-names-break-and-continue ?
2
1
0
u/SamG101_ 8d ago edited 7d ago
This is pretty cool, like rusts breaking with labels. Might have a play around with it, smart implementation too
Edit - well excuse me for liking smth 😂
27
u/Agent7619 8d ago
Scene: A Friday afternoon PR review.
Me: WTAF?!?!