GOTO, why are you adverse to using it

Started by
74 comments, last by SmkViper 9 years, 7 months ago

Personally, I don't mind the use of "goto" when it fits. There are very few cases where I can justify using it, but I do use it once in a blue moon.

"The code you write when you learn a new language is shit.
You either already know that and you are wise, or you don’t realize it for many years and you are an idiot. Either way, your learning code is objectively shit." - L. Spiro

"This is called programming. The art of typing shit into an editor/IDE is not programming, it's basically data entry. The part that makes a programmer a programmer is their problem solving skills." - Serapth

"The 'friend' relationship in c++ is the tightest coupling you can give two objects. Friends can reach out and touch your privates." - frob

Advertisement

Do you have any examples of the sorts of circumstances under which more modern compilers are untrustworthy?

It took them years to get the compilers right on the last gen of consoles, at least. Sony spent at least half of the generation simultaneously shipping two independent compilers (common linker) as part of the PS3 dev kit.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

Do you have any examples of the sorts of circumstances under which more modern compilers are untrustworthy?


On Android (and in extremely rare circumstances) the Mono JIT compiler can produce corrupted interface method tables which result in undefined behavior instead of just crashing.

On iOS, Mono's AOT compiler inconsistently generates either correct or incorrect code when using highly important generic interfaces such as IEnumerable<T> (especially when an array is involved).

(Both of these may be Unity-specific as Unity uses a very old version of Mono)

Let's not forget that goto fail; happened.

Braceless conditionals are eeeeevuuuuul!

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Promit and Nypyren both jumped in with examples I was going to use. :-)

We used to live in a world where everything was manually validated, people had to use a phone call, postal service, or a private BBS to report defects in tools, and each individual had to identify the errors and the workarounds on their own. Every computer system had its own unique compiler dedicated just for that chipset. Defective tools were the norm, you needed to validate everything.

Today we live in a world with automated testing. Compiled code can run on billions of compatible chips. Instant global communications mean defects are reported quickly, and patches or work-arounds are quickly available to everyone. Serious compiler bugs are fairly rare and quickly become well-known.

The few cases where we cannot trust the tools make industry news. They are so rare that when they happen they stick in people's memory. It is quite a good situation.

We can trust our tools quite a lot. The major compilers have an incredible amount of automated and manual testing that takes place before the builds are marked as stable and released. If you're running off a nightly build or otherwise experimental build then of course you should trust it less. If you are using a new compiler for an obscure chip you should probably be a little cautious. But if you're using GCC/Visual Studio/clang or similar major compilers, yeah, trust them.

Even so, we shouldn't completely trust our tools. Bugs still exist. I've run in to my share of ICE (internal compiler error) messages over the years. There are also rare cases where some combination of actions happens to not do the proper thing. While almost always you should assume it is a bug on your part, there are still occasional defects even in the major compilers. Since I'm often considered a compiler expert at places I've worked, people have brought me weird issues; I've found and reported bugs in major compilers, with the latest being found and reported last year. (Following a peculiar bit of code coupled with high optimization levels and particular compiler options, an optimization rule was incorrectly being applied. There were many workarounds, and simply breaking apart a statement into two lines was enough to avoid the bug.)

Defects happen, even in compilers.

Getting back on goto, they can also still be misused and I haven't seen one in years outside of assembly/disassembly where jumps are the implementation detail for structure. I remember the frustration of seeing a function labeled something like "mandatory_cleanup()" that started with a comparison of setjmp followed by a bunch of seemingly unrelated commands that someone was using a longjmp for. Fortunately we have so many good alternative constructs that goto and other unstructured jumps are practically gone from the lexicon of high level languages.

Let's not forget that goto fail; happened.

Braceless conditionals are eeeeevuuuuul!


This is true. Though with the goto fail example, shouldn't the compiler have produced an "unreachable code detected" warning? (Or do they still not have that kind of static analysis there?)

Maybe I'm just spoiled by C# compilers...

Let's not forget that goto fail; happened.

Braceless conditionals are eeeeevuuuuul!


This is true. Though with the goto fail example, shouldn't the compiler have produced an "unreachable code detected" warning? (Or do they still not have that kind of static analysis there?)

Maybe I'm just spoiled by C# compilers...

Who's to say the project doesn't compile with a bajillion warnings, or that particular warning is disabled?

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

goto breaks the flow of the program and makes significantly harder to track or predict how a program will execute by just glancing at the code. It also makes silly bugs and mistakes much easier to introduce.
It basically reverts any language back to assembly-level difficulty where you have to follow instruction by instruction to see how the execution jumps around; except that its even harder because in assembly at least you've got an instruction per line; whereas in other languages one expression could be expanded to multiple statements.
I cannot simply place my finger at a random place, start from there, and understand what's going on. Code with goto forces me to go to the start.

Comparing with continue and break is not fair, because continue and break have very clear flow of the program: Just jump your eyes to the bottom of the ending bracket '}' (unless you have deeply nested break and/or continue statements, which is also hard to track and also considered a bad practice anyway). If the code inside the brackets is too long and you have to scroll down too much, again... you're doing it wrong.

goto on the other hand, can jump anywhere, like a flea. It may jump backwards a couple lines. It may jump backwards a thousand lines, then jump again forward another couple thousand lines. You never know where it's going to land and it's very distracting. Not to mention you have to keep a mental model of the state of the program before each jump.
May be you think writing code with goto is easy and convenient. And indeed it is. But when you have to maintain that code a year after you wrote it, or collaborate with other people who need to read and understand what you wrote; goto becomes a nightmare.

You speak as if you never programmed anything of more than trivial complexity. BREAK and CONTINUE can be less clear where there are (many) nested loops or where your 'just jump your eyes' might be to along ways off the screen far below.

GOTO has a label which can be made VERY visible and understandable (I usually place the goto destination LABELS to the far left no matter what the indenting so they stand out VERY obviously) and use a meaningful phrase which makes it quite 'clear' where its expected to be (we should be assuming that it would be used RIGHT not assuming it to automaticly to be used poorly -- as ANY part of the programming language can be use wrong -- actually far wronger than the simple GOTO statement).

I use GOTOs more than a little (where appropriate) and BREAKS and CONTINUE (where appropriate) as well as comments in significant ways to make the code more readable, which is quite an advantage when the programs are many tens of thousands of lines and for situations where a thousand line subroutine is not unlikely.

GOTOs can flatten out/clarify certain patterns of code which otherwise would be a mess of nestings and escape logic (and not just for error recovery/handling)

--------------------------------------------[size="1"]Ratings are Opinion, not Fact

BREAK and CONTINUE can be less clear where there are (many) nested loops or where your 'just jump your eyes' might be to along ways off the screen far below.

Which is why well written code doesn't do that either. It's a false duality. Goto doesn't excuse other ways of writing shitty, hard to follow code.


GOTOs can flatten out/clarify certain patterns of code which otherwise would be a mess of nestings and escape logic (and not just for error recovery/handling)

This is certainly the case in C or code that is forced to follow similar flow patterns, where a jump to a cleanup stage is often required in lieu of a return. The RAII crowd will claim that this is a fundamental failing of the language. I have mixed feelings on the matter, but it's the damndest thing: hundreds of thousands of lines in our current project, a number of subroutines in the thousands of lines, and no need for goto statements. So I can't really agree with you that its use is a common structural pattern, particularly in situations where you do use RAII constructs (ie locally scoped destructors) to deal with cleanup.

Here's the thing, though. Maybe it's goto. Maybe it's a break/continue on deeply nested looping structures (which we definitely have in our codebase). It sorta doesn't matter which one - most of these direct-jump commands are representative of fundamental design issues, or simple poor code quality in need of refactoring. You've got a big attitude about the wrong thing. If you have a lot of non-local control flow, especially nested, then your code sucks. Avoiding a specific keyword has no effect on that. These are endemic problems. If your use of goto makes completely badly written code only tolerably badly written, that is not an argument in favor of goto, nor is it an argument in favor of other long distance jump instructions.

SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
BREAK and CONTINUE can be less clear where there are (many) nested loops or where your 'just jump your eyes' might be to along ways off the screen far below.

If you have loops or conditionals so deeply nested that this is something you need to actively worry about, then your problem isn't "which language construct shall I use?"

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement