2

So I would like to be able to call a function like this:

void func(1, 2, 3, (void*)label) // can return normal or to the labels
//some code
label:
//different code

Is it possible, and is it bad practice?

  • 2
    I think the closest you can get is a longjmp but it's not exactly what you're asking for. And yes, I'd consider it bad practice. Just use the return code to decide in the caller what to do next. – 5gon12eder Jun 27 '16 at 18:26
  • 3
    What is the concrete use case you are thinking of? What would your actual func do ? What is your application doing? Please edit your question to improve and motivate it much more... – Basile Starynkevitch Jun 28 '16 at 5:15
  • @JavaProphet: it would be really nice to motivate your question, even if you have accepted my answer. Why did you ask your question? – Basile Starynkevitch Jun 29 '16 at 16:45
  • @BasileStarynkevitch I can use it to improve code flow in some applications where it is not easy to follow what is going on. – JavaProphet Jun 29 '16 at 21:34
  • That is not an explanation (and you certainly do not "improve" the control flow) What concrete application do you have in mind? Please be specific. – Basile Starynkevitch Jun 30 '16 at 4:10
7

In standard C99 or C11 you cannot.

But GCC has a language extension, label as values, which might help (and Clang/LLVM also accepts it).

Even with the computed gotos that extension provides, jumping into another routine is (nearly) undefined behavior. You can jump indirectly to a label inside the same routine. You could return a label to jump into; that is, goto *fun(&&label1, &&label2, x, y); with fun(void*l1, void*l2, int x, int y) returning either one (l1 or l2) of its two first arguments. You can have threaded code.

Notice that on many processors, an indirect jump (or call) is slower than a direct one (because it hurts the branch predictor). So I don't think that returning a label for an indirect goto is good practice (so John Bode's answer suggesting a switch is better), but you might have weird corner cases.

Be also aware that recent GCC (i.e. GCC 6 in june 2016) can sometimes optimize to make a tail-call.

Read also about continuation-passing style (and also about continuations & delimited continuations, call/cc in Scheme, callbacks, closures, and perhaps event loops; Prolog & its backtracking abilities; coroutines; iterators; threads; exceptions; operational semantics & denotational semantics). It could inspire you; look inside the implementation of Chicken Scheme and read the Cheney on the MTA paper and the SICP book. If you are generating some C code (e.g. from some Lisp-like language) or want to understand how Lisp or Scheme works, read also Queinnec's Lisp In Small Pieces book.

For low-level exception handling in C, longjmp (with setjmp) is the preferred way (see also the obsolete, Unix specific, setcontext(3)...; look into backtrace(3)...). But be careful. See this & that.

  • 1
    Do you mean that “labels as values” could be used by passing two labels into the function as parameters, have it return one of them and then write something like goto *func(&&success, &&failure, …); where success and failure are labels in the local scope? Because if func itself would goto them, I think you'll be in “(nearly) undefined behavior land” or – as the manual puts it – “totally unpredictable things happen”. – 5gon12eder Jun 27 '16 at 20:48
  • 1
    +1 for continuation passing style. while it's not clear what OP is trying to achieve, it may well be that CPS is the closest they can easily get without ending up with totally unmaintainable code. And note that setjmp/longjmp are effectively a method for managing (limited) continuations without needing to restructure functions to multiple functions with shared variables on the heap (which could rapidly descend into a nightmare). – Jules Jun 28 '16 at 8:55
4

You can (see the Labels as Values gcc extension). It's rather doubtful that you should.

Dijkstra taught us that goto was harmful. You are proposing not only using goto, and a jump table, but also a function that returns to the passed label.

Understand that a function is essentially a goto (jump) with some pushing onto the stack (the stack frame). That frame is pop'ed on a return to put the stack into an expected state. If you mean to subvert the function's return to its call location, to instead go to the passed label, then wherever you go had better know how to clean up the stack and either have it's own way to return to the main block of code or some idea how the program should continue from here.

Tricks like this are how functions work in the first place. If you want, you can design your own function protocol this way. The question is why? Functions do this already. You can solve many problems that might need this with functions, and if you need to get fancy, function pointers.

  • You should probably point at the labels-as-values is a gcc extension, not part of standard C. – Jules Jun 27 '16 at 19:34
  • 1
    Also, according to the description of the extension: "You may not use this mechanism to jump to code in a different function. If you do that, totally unpredictable things happen", so it can't actually do what the OP asks... – Jules Jun 27 '16 at 19:42
  • @Jules, may not and can not are not the same thing. Give me a Turing complete language and I can give you any other Turing complete language. I've no doubt this can be done. Which is exactly why it's important to point out why it's a bad idea. – candied_orange Jun 27 '16 at 19:46
3

It is not possible, and if it were, it would probably be considered bad practice1. Gotos within a function make code impossible to trace by inspection; gotos across functions would make the situation much worse.

Here's an example:

        i = 4;
label:  printf( "%d\n", i );

What value actually gets printed? Is it 4? Is it ever 4? I can't know until I chase down every instance of goto label in that function. Suppose I discover something like this:

        i = 2;
        goto label;
        ...
        i = 4;
label:  printf( "%d\n", i );

In that scenario, i is never 4; that assignment is unconditionally skipped over. Now, imagine allowing that sort of thing to happen across functions, and the work just gets uglier.

You can kind-of-sort-of fake exception handling using setjmp and longjmp, but that's not exactly what you're looking for. Honestly, I'd have func return an error code and branch based on that:

switch ( func( 1, 2, 3 ) )
{
  case NORMAL:  
    // normal processing
    break;

  case THIS_ERROR: 
    // error processing
    break;

  case THAT_ERROR: 
    // error processing
    break;

  ...
}

or

if ( func( 1, 2, 3 ) )
{
  // normal processing
}
else
{
  // error processing
}


  1. This is giving me flashbacks to a particularly nasty piece of code I encountered early in my career, where we literally spent weeks trying to puzzle out the flow of control.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.