andyn
andyn

Reputation: 597

Nesting goto labels with the preprocessor in C99/C11?

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.

Upvotes: 1

Views: 745

Answers (2)

Jens Gustedt
Jens Gustedt

Reputation: 78903

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.

Upvotes: 1

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272487

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!

Upvotes: 2

Related Questions