26

We often write some functions which have more than one exit point (that is, return in C). At the same time, when exiting the function, for some general works such as resource cleanup, we wish to implement them only once, rather than implementing them at every exit point. Typically, we may achieve our wish by using goto like the following:

void f()
{
    ...
    ...{..{... if(exit_cond) goto f_exit; }..}..
    ...
    f_exit:
    some general work such as cleanup
}

I think using goto here is acceptable, and I know many people agree on using goto here. Just out of curiosity, does there exist any elegant way for neatly exiting a function without using goto in C?

ACcreator
  • 1,362
  • 1
  • 12
  • 29
  • 2
    Why do you ask? Curiosity about bizarre control flow operations? And what is your criteria for elegance? – Basile Starynkevitch Sep 10 '14 at 14:10
  • @BasileStarynkevitch Personally speaking, I think the goto solution is okay, but not that elegant. Now I do use the goto solution in my programs where there exist exit points in nested loops/case so that neither `do {..} while(0)` nor `switch() {}` solution helps, but I really wish to know whether there are better solutions. – ACcreator Sep 10 '14 at 14:18
  • 3
    But elegance is a matter of opinion! `goto` is used (with care) in several programs. – Basile Starynkevitch Sep 10 '14 at 14:19
  • 1
    @BasileStarynkevitch Obviously I know elegance is a matter of opinion, but most of us do have some basic judgement in elegance in common and we do have many common agreements in designing so called ``elegant" program. – ACcreator Sep 10 '14 at 14:22
  • 1
    Adding an extra level of {} and indentation, or even a function (or macro!) is harder to read and more accident-prone than an explicit jump to a named exit-point. IMnsvHO – wildplasser Sep 10 '14 at 14:31
  • 15
    That use of `goto` is actually harmless. Don't forget that `return`, `continue`, `break` etc. are just idiomatic uses of `goto` that got their own syntax, to prevent errors and make the code clearer. In this case you have just discovered the `try: ... finally: ...` idiom found in many other languages. Since C doesn't have such a syntax, `goto cleanup` is the statement that more carefully provides the meaning of that operation. IMHO the `while (0)` tricks is just a hack, much less readable than using `goto`. – Bakuriu Sep 10 '14 at 18:19
  • @Bakuriu The advantage (in my opinion) of the `try/finally` idiom is that it uses syntax to clearly demarcate the section of code that can be broken out of and cleaned up using the `finally` block, whereas with a `goto` label that restriction is only enforced by convention (i.e. you cannot immediately see by looking at that section of the code whether there are any other `GOTO` statements that cause control to enter the cleanup block). Granted, since `GOTO` statements can't jump between functions, keeping a sane maximum function length more or less obviates this issue. – Kyle Strand Sep 10 '14 at 18:29
  • There's also [GOTO still considered harmful](http://stackoverflow.com/questions/46586/goto-still-considered-harmful/) that covers a lot of the same ground. – Jonathan Leffler Sep 11 '14 at 14:09
  • Although the general discussions about GOTO do cover similar ground, this question is specifically about executing some common code before the function returns to its caller. There are reasonable solutions to do so that don't involve GOTO. – jxh Sep 12 '14 at 18:28

7 Answers7

33

Why avoid goto?

The problem you want to solve is: How to make sure some common code always gets executed before the function returns to the caller? This is an issue for C programmers, since C does not provide any built in support for RAII.

As you already concede in your question body, goto is a perfectly acceptable solution. Never-the-less, there may be non-technical reasons to avoid using it:

  • academic exercise
  • coding standard compliance
  • personal whim (which I think is what is motivating this question)

There are always more than one way to skin a cat, but elegance as a criteria is too subjective to provide a way to narrow to a single best alternative. You have to decide the best option for yourself.

Explicitly calling a cleanup function

If avoiding an explicit jump (e.g., goto or break) common cleanup code can be encapsulated within a function, and explicitly called at the point of early return.

int foo () {
    ...
    if (SOME_ERROR) {
        return foo_cleanup(SOME_ERROR_CODE, ...);
    }
    ...
}

(This is similar to another posted answer, that I only saw after I initially posted, but the form shown here can take advantage of sibling call optimizations.)

Some people feel explicitness is more clear, and therefore more elegant. Others feel the need to pass cleanup arguments to the function to be a major detractor.

Add another layer of indirection.

Without changing the semantics of the user API, change its implementation into a wrapper composed of two parts. Part one performs the actual work of the function. Part two performs the cleanup necessary after part one is done. If each part is encapsulated within its own function, the wrapper function has a very clean implementation.

struct bar_stuff {...};

static int bar_work (struct bar_stuff *stuff) {
    ...
    if (SOME_ERROR) return SOME_ERROR_CODE;
    ...
}

int bar () {
    struct bar_stuff stuff = {};
    int r = bar_work(&stuff);
    return bar_cleanup(r, &stuff);
}

The "implicit" nature of the cleanup from the point of view of the function that performs the work may be viewed favorably by some. Some potential code bloat is also avoided by only calling the cleanup function from a single place. Some argue that "implicit" behaviors are "tricky", and therefore more difficult to understand and maintain.

Miscellaneous...

More esoteric solutions using setjmp()/longjmp() can be considered, but using them correctly can be difficult. There are open-source wrappers that implement try/catch exception handling style macros over them (for example, cexcept), but you have to change your coding style to use that style for error handling.

One could also consider implementing the function like a state machine. The function tracks progress through each state, an error causes the function to short circuit to the cleanup state. This style is usually reserved for particularly complex functions, or functions that need to be retried later and be able to pick up from where they left off.

Do as the Romans do.

If you need to comply to coding standards, then the best approach is to follow whatever technique is most prevalent in the existing code base. This applies to almost all aspects of making changes to an existing stable source code base. It would be considered disruptive to introduce a new coding style. You should seek approval from the powers that be if you feel a change would dramatically improve some aspect of the software. Otherwise, as "elegance" is subjective, arguing for the sake of "elegance" is not going to get you anywhere.

Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
  • This is very similar to the answer I wrote. You can indeed make the cleanup function such that you call it by `return cleanup(result);` where the result isn't necessarily related to the cleanup. But it is bad program design to pass a variable to a function, which isn't related to the function's actual task. Which is why I proposed a 2-liner instead, keeping the function result and the cleanup separate. – Lundin Sep 10 '14 at 14:23
  • @Lundin:The cleanup function may customize its behavior according to the value being returned. – jxh Sep 10 '14 at 14:26
  • I've used this approach many times in embedded applications rather than calling `cleanup()` from multiply points in `work()` as suggested by @Lundin. By calling `work()` and `cleanup()` only once, the compiler can optimize and put `work()` and `cleanup()` code into `bar()`, reducing the need for a level of function call. When the function stack is limited to 32 (or only 8!), this style of programming is a well needed efficiency _and_ it is a clean style +1. – chux - Reinstate Monica Sep 10 '14 at 14:59
  • +1 for `bar_stuff`/`bar_work`/`bar` separation. If the task has two distinct phases, make them into distinct procedures. Doing them both together overcomplicates the procedure, adding clever control flow to move between them exacerbates the complexity. – Warbo Sep 11 '14 at 08:35
6

For example

void f()
{
    do
    {
         ...
         ...{..{... if(exit_cond) break; }..}..
         ...
    }  while ( 0 );

    some general work such as cleanup
}

Or you could use the following structure

while ( 1 )
{
   //...
}

The main advantage of the structural approach contrary to using goto statements is that it introduces a discipline in writing code.

I am sure and have enough experience that if a function has one goto statement then through some time it will have several goto statements.:)

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Beat me by 11 secs ... ;-) – alk Sep 10 '14 at 13:42
  • Thanks for your answers. It has some limitations when `if(exit_cond) break;` is in `while`, `for` or `switch`, doesn't it? – ACcreator Sep 10 '14 at 13:46
  • @ACcreator Yes, it may be used only in these statements. But it is a general practice to write code like while ( 1 ) { /*,,,*/ } – Vlad from Moscow Sep 10 '14 at 13:48
  • So does there exist any more general solutions? :) – ACcreator Sep 10 '14 at 13:50
  • @ACcreator Each solution depends on details. You should design a function such a way that it would not have the goto statement. – Vlad from Moscow Sep 10 '14 at 13:51
  • 15
    A goto by any other name, is still a non-conditional branch. This adds nothing compared to the goto example. It is not better in any way. – Lundin Sep 10 '14 at 13:53
  • @Lundin You are mistaken. A loop and break has one exit point One entry one exit. while goto can have any exit points. So as I wrote in my post then a function has one goto bstatement that you may be sure that through some time it will have several goto statements. – Vlad from Moscow Sep 10 '14 at 13:57
  • @VladfromMoscow I know. The fact is that I used to take the solution as your answer for many problems which the `if(exit_cond)` statements are not in `while`, `for`, `switch`. But I haven't found a good way when meeting with more complex situations such as in nested loops/cases. – ACcreator Sep 10 '14 at 13:57
  • @VladfromMoscow If you make several gotos, then you are obviously lost - you don't know what you are doing - you are creating created spaghetti. It seems mighty strange to me that you would write code in a certain manner to protect the programmer from themselves. Your loop can easily be made as obscure as multiple gotos, just toss in some continues here and there and you've made some equally severe spaghetti. – Lundin Sep 10 '14 at 14:04
  • 1
    @Lundin You are very naive. The problem is not that somebody was lost. The problem is that initially bad design of a program dictates its evolutian in bad way. – Vlad from Moscow Sep 10 '14 at 14:07
  • 11
    @VladfromMoscow And how does your code prevent this? Lets evolve it a bit: `do { if(something) continue; if(exit_cond) break; if(something_else) ; /* do nothing */ if(this_or_that) break; else if (the_sun_shines) { do_stuff(); if(something) break; } else continue; do_stuff(); } while(0);` And so on. Just because your code doesn't contain the goto keyword doesn't mean it is safe from spaghetti. – Lundin Sep 10 '14 at 14:11
  • @Lundin First of all my code has a structure. So it dictates to write a structured code. – Vlad from Moscow Sep 10 '14 at 14:13
  • 4
    Structure? I only see control flow operators abuse. – Cthulhu Sep 11 '14 at 08:11
  • @Cthulhu It is your personal problem and nothing more. – Vlad from Moscow Sep 11 '14 at 16:43
  • 1
    While tastes are of course subjective, it's pretty difficult to deny that the origin of this "style" is the anti-goto cargo cult that arose after Dijkstra's paper (which arguably was wrongly interpreted by many). For this reason alone, I think some extra thinking is warranted before adopting it. If one wants to enforce the restrictions suggested by Vlad (which are definitely *good*), one should look at static analysis first, personal discipline second, and control flow statement abuse *last*. The horrors of `goto` *abuse* are long-forgotten, no reason to start abusing other statements instead. – tne Aug 20 '15 at 13:13
5

I've seen a lot of solutions how to do this and they tend to be obscure, unreadable and ugly at some degree.

I personally think the least ugly way is this:

int func (void)
{
  if(some_error)
  {
    cleanup();
    return result;
  }

  ...

  if(some_other_error)
  {
    cleanup();
    return result;
  }

  ...

  cleanup();
  return result;
}

Yes, it uses two rows of code instead of one. So? It is clear, readable, maintainable. This is a perfect example of where you have to fight your knee-jerk reflexes against code repetition and use common sense. The cleanup function is written only once, all clean up code is centralized there.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks for your answer. The same comment as I give to John Hascall, one limitation of the answer is that, sometimes we need to handle many variables when exiting the function, so that `cleanup()` may need to have many parameters. So it is not so neat. – ACcreator Sep 10 '14 at 14:09
  • @ACcreator Why isn't it "neat"? You have to clean up what you have to clean up. It will always be the same variables. If you have a flood of variables inside one single function, the core problem is likely that the function is to complex and needs to get split up into several. – Lundin Sep 10 '14 at 14:16
  • No, it is ugly: it introduces a new name at file scope + the arguments have to be passed to the cleanup-function (or even worse: accessed via globals) – wildplasser Sep 10 '14 at 15:45
  • 2
    In the currently running review/rewrite of OpenSSL, that kind of code is regarded as highly dangerous. It's an open invitation to making mistakes and messing the code up. – gnasher729 Sep 10 '14 at 20:30
  • Unfortunately, you can't always factor out cleanup into function. For example, if your code did `malloc()` in the beginning of your function and must `free()` upon return, you can't really move this into cleanup function call easily. Also, it becomes really ugly if your cleanup function needs parameters to clean up. – mvp Sep 11 '14 at 01:40
  • @gnasher729 I think you would have to be a very confused person to mess up this code. When maintaining code, you can't simply toss in some random lines in the middle without understanding the whole function. And if you understand the whole function in this case, you will understand the need for a clean up upon exit. There are lots of religious dogmas against multiple return statements, particularly in standards for safety-critical programming, but there are very few rationales explaining why. "This is highly dangerous". Why? – Lundin Sep 12 '14 at 06:20
5

I think the question is very interesting, but cannot be answered without being influenced by subjectivity because elegance is subjective. My ideas on it are as follows: In general, what you want to do in the scenario you describe, is to prevent control from passing through a series of statements along the execution path. Other languages would do this by raising an exception, which you would have to catch.

I had already written down neat hacks to do what you want to do with pretty much every control statement there is in C, sometimes in combination, but I think they are all just very obscure ways of expressing the idea of skipping to a special point. Instead I'll just make my point on how we arrive at a point where goto can be preferable : Once again, what you want to express using is that something has occurred that prevents following the regular execution path. Something that is not just a regular condition that can be handled by taking a different branch down the path, but something makes it impossible to use the path to the regular return point in a safe way in the current state. I think there are three options to proceed at that point:

  1. return through a conditional clause
  2. goto an error-label
  3. every statement that could fail is inside a conditional statement, and regular execution is considered a series of conditional operations.

If your cleanup is similar enough on every possible emergency exit I would prefer the goto, because writing the code redundantly just clutters the function. I think you should trade the number of return points and replicated clean-up code that you create against the awkwardness of using a goto. Both solutions should be accepted as a personal choice of the programmer, unless there are severe reasons for not doing so, e.g. you agreed that all functions must have a single exit. However, the use of either should be consequent and consistent across the code. The third alternative is - imo - the less readable cousin of the goto, because, in the end you will skip to a set of cleanup routines - possibly enclosed by else-statements too, but it makes it much harder for humans to follow the regular flow of you program, due to the deep nesting of conditional statements.

tl;dr: I think choosing between conditional return and goto based on consequent style-decisions is the most elegant way, because it is the most expressive way to represent your ideas behind the code and clarity is elegance.

midor
  • 5,487
  • 2
  • 23
  • 52
4

I guess that elegant may mean for you weird and that you simply want to avoid the goto keyword, so....

You might consider using setjmp(3) and longjmp :

void foo() {
jmp_buf jb;
if (setjmp(jb) == 0) {
   some_stuff();
   //// etc...
   if (bad_thing() {
       longjmp(jb, 1);
   }
 };
};

I have no idea if it fits your elegance criteria. (I believe it is not very elegant, but this is only an opinion; however, there is no explicit goto).

However, the interesting thing is that longjmp is a non-local jump : You could have passed (indirectly) jb to some_stuff and have some other routine (e.g. called by some_stuff) do the longjmp. This may become unreadable code (so comment it wisely).

Even uglier than longjmp : use (on Linux) setcontext(3)

Read about continuations and exceptions (and the call/cc operation in Scheme).

And of course, the standard exit(3) is an elegant (and useful) way to go out of some function. You could sometimes play neat trick by also using atexit(3)

BTW, Linux kernel code uses quite often goto including in some code which is considered as elegant.

My point is : IMHO don't be fanatic against goto-s since there are cases where using (with care) it is in fact elegant.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 6
    setjmp isn't particularly elegant, and when it goes wrong, it goes very wrong. I wouldn't recommend anyone to use it for this purpose or others. – Lundin Sep 10 '14 at 14:13
  • I don't claim `setjmp` is elegant in my view. I believe elegance is an opinion. – Basile Starynkevitch Sep 10 '14 at 14:14
  • 2
    Elegant or not, these functions have a bad reputation as unsafe and bug prone, since they can cause UB if not used properly. Serious C coding standards ban these functions or put restrictions on how to use them. – Lundin Sep 10 '14 at 14:19
  • Some language features more than others. I agree with your goto remark though, there's not really anything wrong with the original code. There is no point in avoiding goto, just for the sake of avoiding goto. – Lundin Sep 10 '14 at 14:26
  • 1
    Sadly, almost all implementations of `setjmp` and `getjmp` suffer from serious flaws. – Alice Sep 10 '14 at 17:51
  • But `exit` is elegant! – Basile Starynkevitch Sep 10 '14 at 18:03
  • 1
    It is possible to develop macros implementing *poor man's exceptions* in C, based on the `setjmp` and `longjmp` functions. Maybe it can be an acceptable encapsulation, able to keep the breed rate of Ubs low in code using `setjmp` and `longjmp`. – Michaël Le Barbier Sep 10 '14 at 20:49
  • It is amazing how far people would go to avoid using `goto`. I think `setjmp` is much more dangerous than simple `goto` here – mvp Sep 11 '14 at 01:38
  • 1
    @Alice Interesting. Can you give some references? – parvus Sep 11 '14 at 05:09
3

I'm a fan of:

void foo(exp) 
{
    if(    ate_breakfast(exp)
        && tied_shoes(exp)
        && finished_homework(exp)
      )
    {
        good_to_go(exp);
    }
    else
    {
        fix_the_problems(exp);
    }
}

Where ate_breakfast, tied_shoes, and finished_homework take a pointer to exp that they work on, and return bools indicating a failure of that particular test.

It helps to remember that short circuit evaluation is at work here - Which may qualify as a code smell to some people, but like everybody else has been saying, elegance is somewhat subjective.

int Pi
  • 61
  • 6
  • Sometimes it is a code smell IMO, but only when it gets relatively common. – Claudia Sep 11 '14 at 04:10
  • This is too blunt. Because most often you have some code, then an error check, then some other code, then another error check. – Lundin Sep 12 '14 at 06:15
  • It kind of goes without saying that nothing works everywhere - Although when you can group the tests by how the errors are handled (Open files, and handle failures. Check validity, then handle validity problems, etc. ), IMO it provides a little more clarity than calling the same handling routine repeatedly at various semirandom points through the code - and makes it easier for maintenance coders to know where to place new tests. Obviously, if each and every test requires unique handling, you're going to be thrown back on testing individually. TL;DR: Sure. Blunt objects are useful too. – int Pi Sep 12 '14 at 17:35
-1

goto statement is never necessary, also it is easier to write code without using it.

Instead you can use a cleaner function and return.

if(exit_cond) {
    clean_the_mess();
    return;
}

Or you can break as Vlad mentioned above. But one drawback of that, if your loop has deeply nested structure break will only exit from innermost loop. For example:

While ( exp ) {
    for (exp; exp; exp) {
        for (exp; exp; exp) {
            if(exit_cond) {
                clean_the_mess();
                break;
            }
        }
    }
}

will only exit from inner for loop and doesn't abandon process.

  • 2
    Your `clean_the_mess()` function would need to be visible to the invoking function, so it would at least have file scope. Also: since the goto-version is mostly needed for cleanup, it would also need at least have access to the resources that need cleaning up. So it would need arguments, or worse: these resources would need to be globally visible. – wildplasser Sep 10 '14 at 14:36
  • function just written as a prototype. Of course it could take argument, or there could be some memory cleaning lines instead of function. – Kürşat Kobya Sep 10 '14 at 15:17
  • 1
    What makes you think this code you wrote is easier to write (once you fill out the details) ? – gnasher729 Sep 10 '14 at 20:32
  • 1
    That bottom doesn't look pretty. Goto is never necessary in theory, but I couldn't tell you how much a `switch (false)` in JavaScript has helped me simplify logic. Sometimes, the lack of a `goto` requires a whole new, often single-use, function that is otherwise unnecessary. – Claudia Sep 11 '14 at 04:17
  • 1
    The code snippet at bottom shows when the things can go wrongly with break statement. Not a recommended way to code, just an example of simple bug – Kürşat Kobya Sep 11 '14 at 06:37
  • For, repeat, iterate, recursive function calls, and if are never needed; just use while! Use goto when it's the best choice; don't avoid it if it requires you to dance hoops to avoid it. – Alice Sep 12 '14 at 18:24