1. 24
  1. 18

    GOTOphobia is part of the anti-pattern culture – shaming someone for something allegedly bad without thinking about the actual matter and considering the pros and cons of various solutions.

    1. 5

      Part of this culture is the Lies Programmers Believe About X series of blog posts, which offer problems but no solutions, and implicitly refuse to consider that, in many cases, data is subject to constraints long before it hits the software. For example: If I’m writing a payroll application for a company, the names I consider will be the names acceptable to the country that company is based in, because payroll has to interface with that country’s tax authority. Therefore, if a name isn’t acceptable to that country’s government, my software can’t accept it. And some countries, like Finland and Denmark, have restrictive naming laws.

    2. 6

      One of the things I’ve really wanted for a long time – besides a lightsaber, of course – is for people to stop being needlessly dogmatic about style rules. Style rules are good, and it’s useful to be aware of them when writing code, but you know what works even better, and will absolutely make every single line you ever write better? Common sense.

      You can see just how needlessly divisive this whole thing is just based on how other dangerous constructs from C are treated. malloc is also dangerous in a lot of contexts, and complex lifetimes are harder to manage than label jumps, which are at least function-scoped. That’s one of the reasons – though not the main reason – why it’s not used in some contexts. But fortunately, Dijkstra never had to write a misunderstood note about memory leaks and double frees, so nobody badgers anyone about malloc being harmful, even though it’s a more dangerous construct than goto, it’s easier to misuse, and you can write perfectly functional C without it, too. continue and break within loops are also dangerous enough that MISRA C used to forbid them along with goto; MISRA has moved on, and allows all three with some constraints, but some of the world apparently hasn’t.

      Discussions about goto in C are just bloody ridiculous. They always go pretty much the same way. Like, someone always shows a code snippet where an innocuous use of goto really does result in shorter, more obvious code, and this sparks an endless discussion about how goto is actually dangerous in a lot of other contexts and so you should always avoid it, no matter what. Common sense would dictate – just as with malloc – to not use goto in those dangerous contexts.

      Rather than stubbornly clinging to some rule that was not even about C in the first place, how about just… following engineering common sense? If you ever find yourself writing a goto line, or encounter it in someone else’s code, instead of applying your favourite goto tribe – whether that’s “goto is bad and should be banned” or “goto is a language feature like any other, nothing to see here” – I would suggest asking yourself all four of these questions:

      1. Does the environment you’re targetting support better options (e.g. the __cleanup__ attribute, if its constraints make it applicable in your case)? If yes, great, you don’t even need a reason to use or not use it, you have a better alternative that makes this whole thing moot!

      2. If you rewrite the code without goto, does that make it easier for someone to understand how the code works and why it has to work that way? Because if it doesn’t, and the goto-less version is harder to understand (both in terms of behaviour and in terms of meaning), then rewriting it makes it more likely to end up buggy, not less likely.

      3. Is this a context in which the use of goto might be misunderstood, or where it might be a source of bugs in future refactoring efforts? I mean, if you’re goingto a label halfway through a six-screen function (why!?) then, sure, avoid it. If you’re goingto a one-line cleanup sequence in a ten-line function that implements the init sequence specified in some chip’s datasheet and thus consists of nothing but reading and writing a handful of memory locations which will never change unless the sillicon somehow spontaneously re-arranges itself, what’s the looming disaster here?

      4. Does the coding standard you’re coding by forbid the use of goto? If not, remember that may be deliberate, too, and consider whether you’re in one of the contexts where it’s considered correct/acceptable or not. MISRA, for example, forbade the use of goto at one point, not because of readability concerns but because it made some types of static analysis more difficult. Now it recognizes it’s actually useful in some cases, and just places some specific constraints. If you’re in one of those cases then… maybe, like, follow the coding standard?

      This is hardly rocket science, it’s just… how you’d treat any other construct. Some debates, like whether goto is good or bad, or whether Nickelback deserves being called a rock band, are best left to teenagers.

      1. 3

        I’d like to preface with: I’m staunchly on the side of using go to where sensible.

        The trouble is “common sense” isn’t as common as you’d like it to be.

        1. 1

          We’re pretty much on the same side :-). Maybe “common sense” was not the best way to put it. I don’t mean “common sense” as in “we don’t need no stinkin’ safe languages, you can write perfectly safe ASM if you just apply basic common sense”. I mean “common sense” as in “can learn a language and read code written in that language”.

          Other than #4, which is an exception only because not all constructs are contentious, all other three questions are things one routinely answers when writing code, since pretty much any non-trivial function can be implemented in more than one way, and we routinely choose the best one in terms of what’s simpler, more understandable, more obvious and so on. They apply just the same for if-else vs. switch instead of goto. It’s not advanced engineering know-how that greybeards employ to bend the compiler to their will, it’s just basic language comprehension and use.

          I guess what I’m trying to clarify is that I’m not advocating for more liberal use of goto, just for treating it the same as any other language construct.

          Coding standards that ban it for technical, or even purely compliance reasons are sensible IMHO. MISRA is probably a good example – avoiding it has advisory status. If, in the context of a particular project, you lack static analysis tools that handle it right, or you don’t trust whatever ancient proprietary compiler you’re forced to use with it, these are perfectly valid reasons to take the “ban it” advice. I’m even okay (and I’ve done that!) with enforcing a ban for continuity. A long time ago, when the MISRA04 -> MISRA12 transition happened, I proposed we keep goto banned in the codebase I was responsible for simply because a lot of our existing code was written with that rule, and allowing it going forward, even just in some branches in some projects, would make it difficult to backport fixes onto “LTS” branches, and result in review noise, as someone would have, sooner or later, committed goto code on branches where the linter insisted on no gotos just because they forgot.

          But IMHO banning it strictly for readability/trustworthiness issues is wishful thinking. If you don’t trust your team to know when a language construct is sensible to use, that team doesn’t have a “coding discipline” problem, it has a skill problem. Banning a particular feature, and especially one that – even counting non-sensible uses – is as rarely used as goto will have a strictly theoretical influence on code quality, because that team will absolutely screw up any subset of code constructs you decide to allow.

          1. 2

            Almost 20 years ago, I had one Linux kernel developer justify using the goto style instead of nested if statements because the compiler generated better code. I rewrote his code with nested ifs and shows him the assembly that GCC generated. It was identical. This did not stop him using goto to write code that went subtly wrong when someone modified it without fully understanding the shape of his ad-hoc control flow structures.

            Unfortunately, i think goto, like manual memory management, has become something that people use more often to prove that they are Real Programmers who can be trusted with power tools, rather than because they actually need it.

            1. 1

              Unfortunately, i think goto, like manual memory management, has become something that people use more often to prove that they are Real Programmers who can be trusted with power tools, rather than because they actually need it

              Oh, yeah. It’s an attitude they carry across to any language they touch. Once upon a time we used to joke that Real Programmers can write FORTRAN in any language. I think it’s time we replaced FORTRAN with C :-).

        2. 3

          One of the things I’ve really wanted for a long time – besides a lightsaber, of course – is for people to stop being needlessly dogmatic about style rules.

          I have a simple meta-rule: everything in your style guide should have a rationale. In most cases, that involves a bug class that is harder to introduce with a particular style. Exceptions are fine in any case where the rationale doesn’t apply.

          Note: in some cases the rationale can simply be that there are two valid options, either is fine but inconsistency is bad.

          1. 2

            I like that. That’s a good idea for any style guide. Style guides that go all “what” and no “why” perpetuate all sorts of cargo cults, and they makes for very unproductive reviews, too. You can make a case for almost anything, and rationales are really useful for gauging those. Otherwise, edge cases get decided based on how persuasive and how stubborn everyone is.

            I got to experience that with goto as well. Barring some Linux kernel code (where I don’t like the goto approach but it’s what the Romans do and it’s a bad idea to do otherwise when in Rome…) I think I used goto just once in the last five or six or God knows how many years. __cleanup__ and/or a little bit of structure in error handling make it unnecessary almost every time. In that case, it was in a short routine that implemented an algorithm given verbatim in the standard of an ancient network protocol I was implementing. I decided to keep my implementation as close to the (very C-like) pseudocode, gotos and all, in order to facillitate review against the standard’s text. That tends to be very useful for ancient protocols, where compliant reference implementations that expose all of a protocol’s functions are very hard to find, so it’s rarely the case that you get to validate your implementation just by getting it to talk to other implementations. Being able to walk through your implementation and the reference algorithm virtually line by line makes early validation a lot less stressful.

            The whole review ended up being about that – the style guideline said something uselessly vague, like “avoid it unless absolutely necessary”, and I woke up to a fourty-thread email flamewar in which half the team was debating whether it was absolutely necessary or not.

        3. 7

          Nah goto actually sucks and this article is missing both the main downside of using it like this, and the best alternative.

          You can’t jump over variable declarations with goto, which in practice forces you to write them all at the top of your function, which makes it harder to see where your variables actually get used and for how long and leads to accidental reuse etc.

          The best alternative is to compile as C++, use RAII for the common/easy cases, and roll a defer keyword for everything else:

          #define CONCAT_HELPER( a, b ) a##b
          #define CONCAT( a, b ) CONCAT_HELPER( a, b )
          #define COUNTER_NAME( x ) CONCAT( x, __COUNTER__ )
          template< typename F >
          struct ScopeExit {
                  ScopeExit( F f_ ) : f( f_ ) { }
                  ~ScopeExit() { f(); }
                  F f;
          struct DeferHelper {
                  template< typename F >
                  ScopeExit< F > operator+( F f ) { return f; }
          #define defer [[maybe_unused]] const auto & COUNTER_NAME( DEFER_ ) = DeferHelper() + [&]()
          void f() {
              void * p = malloc( 1 );
              defer { free( p ); };
              memset( p, 0, 1 );
          1. 15

            It’s easy to dislike goto if changing to another language is an option. But when strictly C is required, goto is very useful for error handling in complex functions. See driver probe functions in the Linux kernel for example.

            1. 8

              Not sure about other dialects, it GNU C has a cleanup attribute that lets you register a function to be called when a variable goes out of scope. This makes it possible to write RAII-style code with lexically scoped cleanups. The compiler is really good at inlining them and you end up with the same code generated as the goto version, but much harder to get wrong.

            2. 5

              Okay, but this is about C, not C++. What do you propose as the alternative to goto for C?

              Compiling C with a C++ compiler actually really sucks. C++ is basically C89, with some small improvements, but without implicit casting of void*. You don’t get compound literals, designated initializers, or any of the other many improvements since C89 which C++ hasn’t incorporated yet. If you’re gonna write C++, write C++.

              1. 3

                You can’t jump over variable declarations with goto, which in practice forces you to write them all at the top of your function, which makes it harder to see where your variables actually get used and for how long and leads to accidental reuse etc.

                I would argue if you need to define your variables not at the start of a scope, your scope is to big. Having variable declarations in the middle of the code makes the function less readable. So yes then you can’t just jump in an other scope, but this wouldn’t go with the argument the post is making. Because this wouldn’t be readable.

                Yes const is then a problem, but this problem is independent of goto.

                The best alternative is to compile as C++

                Best advice just use an other language. Hey in rust this is build in, or in java the GC will do the cleanup so just use one of these languages.

                By the way: The “Error-Checking Convenience Macros for C” of “Advanced UNIX Programming” are more or less the same what the author is suggesting and are a bit better to read.

                1. 6

                  Having variable declarations in the middle of the code makes the function less readable.

                  The problem with moving variable declarations to the start is that you move declaration away from initialisation. This make it harder to read to code because you need to look at three lines of code (declaration, initialisation, and use) to understand a use. In C, this is even worse because you can refer to a variable between the declaration and initialisation and you get an unspecified value.

                  1. 5

                    Why does it make the function less readable to have variable declaration in the middle? Maybe it’s me who tends to use type inference in other languages but I have honestly never encountered a situation where I lost readability because I declared on first assignment.

                  2. 1

                    Is that true in C or just in C++? I thought that was a C+-ism.

                    1. 3

                      It’s not possible in standard C, but very little that’s actually useful is. C compilers that support GNU C (gcc, clang, icc, xlc, and so on) provide __attribute__((cleanup)), which can do the same think. I’ve used it a lot in C codebases for things like acquiring a lock for a lexical scope or having lexical lifetimes on heap allocations. Probably the most fun use involved creating a linked list of on-stack structures across threads that let a thread see if a blocked thread had started doing something before sleeping.

                      The other benefit of this attribute is that it is exception-safe if you compile with -fexceptions. If your code is calling C++ or some other language that may throw exceptions, then throwing an exception will gracefully clean up the resources that you acquire, so you aren’t stuck leaking memory or locks in the presence of foreign exceptions.