cmacrosgotodispatchjump-table

"Local" labels in C and jump table implementation


I'm trying to make a macro-based jump table in C.

Here's some example code:

#include "stdio.h"

#define GOTO(X) static void* caseArg[] = {&&I0, &&R0, &&S0, &&F0, &&G0, &&H0}; \
    goto *caseArg[X];

#define FINISH() goto caseEnd;

int main(int argc, char** argv) {

    GOTO(1);

    I0: printf("in I0\n"); FINISH();
    R0: printf("in R0\n"); FINISH();
    S0: printf("in R0\n"); FINISH();
    F0: printf("in R0\n"); FINISH();
    G0: printf("in R0\n"); FINISH();
    H0: printf("in R0\n"); FINISH();

    caseEnd:;

}

The possible labels (I0, R0, etc) have to be the same.

The problem is: I want to be able to use the same macro in different scoped parts of the same source file. However, the compiler complains that the labels are defined.

What I want to achieve:

int main(int argc, char** argv) {

     { // scope 1 

        GOTO(1);

        I0: printf("in I0\n"); FINISH();
        R0: printf("in R0\n"); FINISH();
        S0: printf("in R0\n"); FINISH();
        F0: printf("in R0\n"); FINISH();
        G0: printf("in R0\n"); FINISH();
        H0: printf("in R0\n"); FINISH();

        caseEnd:;

    }

    { // scope 2

        GOTO(4);

        I0: printf("in I0\n"); FINISH();
        R0: printf("in R0\n"); FINISH();
        S0: printf("in R0\n"); FINISH();
        F0: printf("in R0\n"); FINISH();
        G0: printf("in R0\n"); FINISH();
        H0: printf("in R0\n"); FINISH();

        caseEnd:;

    }

}

Any ideas? Any possible workaround?


Solution

  • You need the __label__ extension (at least in gcc, clang, and tinycc), which allows you to scope labels to a block.

    The labels need to be declared at the very start of a block with

    __label__ I0, R0, S0, F0, G0, H0;
    

    (Contiguous __label__ I0; __label__ R0; ... or a mix of the two forms works as well.).

    Unless declared scope-local with __label__, C labels are scoped to their enclosing function.

    Your example with __label__:

    #include "stdio.h"
    
    #define GOTO(X) static void* const caseArg[] = {&&I0, &&R0, &&S0, &&F0, &&G0, &&H0}; \
        goto *caseArg[X];
    
    #define FINISH() goto caseEnd;
    
    #define DECL_LBLS() __label__ I0, R0, S0, F0, G0, H0, caseEnd
    
    int main(int argc, char** argv) {
    
        {   DECL_LBLS();
    
            GOTO(2);
    
            I0: printf("in I0\n"); FINISH();
            R0: printf("in R0\n"); FINISH();
            S0: printf("in S0\n"); FINISH();
            F0: printf("in F0\n"); FINISH();
            G0: printf("in G0\n"); FINISH();
            H0: printf("in H0\n"); FINISH();
            caseEnd:;
        }
    
        {   DECL_LBLS();
    
            GOTO(1);
    
            I0: printf("in I0\n"); FINISH();
            R0: printf("in R0\n"); FINISH();
            S0: printf("in S0\n"); FINISH();
            F0: printf("in F0\n"); FINISH();
            G0: printf("in G0\n"); FINISH();
            H0: printf("in H0\n"); FINISH();
            caseEnd:;
        } 
    }
    

    https://gcc.godbolt.org/z/63YSkG

    In this particular case, such a local-label based jumptable seems to buy little over a plain old switch.