Dr.Kameleon
Dr.Kameleon

Reputation: 22820

"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?

Upvotes: 4

Views: 961

Answers (4)

Petr Skocik
Petr Skocik

Reputation: 60067

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.

Upvotes: 8

Roberto Caboni
Roberto Caboni

Reputation: 7490

Consider adding a scope parameter to your macro.

It would result in something like this:

#include "stdio.h"

#define GOTO(scope,X) static void* caseArg[] = {&&scope##_I0, &&scope##_R0, &&scope##_S0, &&scope##_F0, &&scope##_G0, &&scope##_H0}; \
    goto *caseArg[X];

#define FINISH(scope) goto scope##_caseEnd;

int main(int argc, char** argv)
{
  {
    GOTO(SCOPE_1, 1);

    SCOPE_1_I0: printf("in I0\n"); FINISH(SCOPE_1);
    SCOPE_1_R0: printf("in R0\n"); FINISH(SCOPE_1);
    SCOPE_1_S0: printf("in R0\n"); FINISH(SCOPE_1);
    SCOPE_1_F0: printf("in R0\n"); FINISH(SCOPE_1);
    SCOPE_1_G0: printf("in R0\n"); FINISH(SCOPE_1);
    SCOPE_1_H0: printf("in R0\n"); FINISH(SCOPE_1);

    SCOPE_1_caseEnd:;
  }
  {
    GOTO(SCOPE_2, 3);

    SCOPE_2_I0: printf("in I0\n"); FINISH(SCOPE_2);
    SCOPE_2_R0: printf("in R0\n"); FINISH(SCOPE_2);
    SCOPE_2_S0: printf("in R0\n"); FINISH(SCOPE_2);
    SCOPE_2_F0: printf("in R0\n"); FINISH(SCOPE_2);
    SCOPE_2_G0: printf("in R0\n"); FINISH(SCOPE_2);
    SCOPE_2_H0: printf("in R0\n"); FINISH(SCOPE_2);

    SCOPE_2_caseEnd:;
  }
}

That's not optimal, but it would work in every compiler.

Please note that in case the blocks to be called have all the same pattern as in the example shown in the question, you could even define a further macro to be called as FULL_MACRO(scope,X), since all GOTO and FINISH calls will be able to be parameterized.

Upvotes: 0

user694733
user694733

Reputation: 16043

This is pretty ugly solution, but if you are willing to add extra prefix definitions to the scopes, you can do this with concatenation

#include "stdio.h"

// Helpers
#define CONCAT(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a ## b

// Label redirection
#define I0 CONCAT(PREFIX, I0)
#define R0 CONCAT(PREFIX, R0)
#define S0 CONCAT(PREFIX, S0)
#define F0 CONCAT(PREFIX, F0)
#define G0 CONCAT(PREFIX, G0)
#define H0 CONCAT(PREFIX, H0)
#define caseEnd CONCAT(PREFIX, caseEnd)

#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) {

     { // scope 1 
        #define PREFIX SCOPE1
        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:;
        #undef PREFIX

    }

    { // scope 2
        #define PREFIX SCOPE2
        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:;
        #undef PREFIX
    }

}

Upvotes: 1

0___________
0___________

Reputation: 67546

Labels as values can be used only if compiled using gcc as it is the gcc extension.

So it works 100% fine.

https://godbolt.org/z/jQvssU

enter image description here

Answering the second question - yes you can use it different functions: https://godbolt.org/z/aoA3XQ

Upvotes: -1

Related Questions