Reputation: 22820
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
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
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
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
Reputation: 67546
Labels as values can be used only if compiled using gcc
as it is the gcc extension.
So it works 100% fine.
Answering the second question - yes you can use it different functions: https://godbolt.org/z/aoA3XQ
Upvotes: -1