1

Is it possible to use the C preprocessor to nest goto labels in C11 or C99? My case is probably best illustrated by looking at the following code. Compiles cleanly with gcc -std=c99 -pedantic -Wall -Wextra.

#include <stdio.h>

// Macro for mangling the identifier to avoid collisions
#define CTX_ID_(NAME) context_label_ ## NAME ## _
#define CTX_ID(NAME) CTX_ID_(NAME)

// The context keyword starts a block that can be exited with break (ID);
// Just syntactic sugar to keep it structured.
#define context(ID) \
    if (0) { CTX_ID(ID): ; } else

// Overloaded break keyword. Doesn't prevent using the plain break;    
#define break(ID) \
    do { goto CTX_ID(ID); } while (0)

// Example run
int main(void) {
    context (c) {
        while (1) {
            puts("Outer loop, visible.");
            while (1) {
                puts("Inner loop, visible.");
                break (c);
                puts("You won't see me.");
            }
        }
        puts("Nor me.");
    }
}

I'm trying to do away with the identifier (c in this case). However, unlike variables, goto labels cannot be nested/scoped as they have to be unique within a function. Is it possible to implement unique scoped identifiers in the C preprocessor that can be used as goto labels?

GCC supports taking the address of a label but it is not a part of the ISO standard. Also, I am specifically trying to avoid setjmp due to the overhead and volatileness issues. Finally, if you don't see the usefulness of the above construct, please think of further uses such as try-catch clauses or Python-style with-expressions to enable RAII-like functionality.

andyn
  • 577
  • 1
  • 6
  • 18
  • 2
    I don't see why you don't want to use the identifier. How would you identify the scope to "break", then? Also, I think overloading keywords isn't really a good idea. Using uppercase letters for macros (unless they are really just generic function interfaces) is really a good convention. – Jens Gustedt Jun 24 '12 at 11:37
  • 1
    I'm going to go a bit further and say that trying to develop a new language on top of an existing one with macros like this is an undertaking you should not make lightly. The cost -- incomprehensibility to somebody not familiar with your pet language -- is almost always a lot greater than the utility. And the cost of writing the functionally equivalent code with standard gotos is zero. – R.. GitHub STOP HELPING ICE Jun 24 '12 at 12:53
  • possible duplicate of [How can I generate unique values in the C preprocessor?](http://stackoverflow.com/questions/1132751/how-can-i-generate-unique-values-in-the-c-preprocessor) – Ciro Santilli OurBigBook.com Sep 20 '15 at 13:03

2 Answers2

2

I'm sure It's possible that the __LINE__ macro will come in handy. There will be no scoping, but at least you'll be able to generate unique label names this way.

However, it's not immediately obvious that this will solve your problem either. I'm going to be bold and state that it's not solvable, although I'm sure it's possible someone will come along and prove me wrong!

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • No, unfortunately `__LINE__` doesn't work for such cases. The standard is not clear what is meant by a physical source line, problem which shows up when you have macro definitions with multiple lines. `gcc` and `clang` for example give you different expansions with that. – Jens Gustedt Jun 24 '12 at 11:32
  • I considered using `__LINE__`. However, I cannot figure out how to reuse the label later in the code so that I can call it with goto. In my example above, the first `__LINE__` at `context (c)` would evaluate to some integer constant n and `break (c)` would evaluate to n+5. – andyn Jun 24 '12 at 11:32
1

Your idea looks nice, the only thing I would miss is that your break(c) could be issued anywhere in the function. I'd add something like that to the two macros:

#define CONTEXT(ID)                                     \
    if (0) { CTX_ID(ID): ; }                            \
    else for (register bool CTX_ID(ID ## ID) = true;    \
              CTX_ID(ID ## ID);                         \
              CTX_ID(ID ## ID) = false)

#define BREAK(ID)                \
    do {                         \
       CTX_ID(ID ## ID) = false; \
       goto CTX_ID(ID);          \
   } while (0)

This would lead to a syntax error if BREAK(c) would be used outside the depending block. To my experience such for variables as used here are easily optimized away by modern compilers.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • Thanks. I cleaned up my code before submitting and before that I actually used something like `(void) sizeof(CTX_ID(ID ## ID));` to check that the control variable exists. – andyn Jun 24 '12 at 19:13
  • @andyn, in other words something like `_Static_assert(sizeof(CTX_ID(ID ## ID));`. Yes probably a better idea than to modify the value. – Jens Gustedt Jun 24 '12 at 20:55