"Local" C 中的标签和跳转 table 实现
"Local" labels in C and jump table implementation
我正在尝试在 C 中进行基于宏的跳转 table。
下面是一些示例代码:
#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:;
}
可能的标签(I0、R0 等)必须相同。
问题是:我希望能够在同一个源文件的不同作用域部分使用同一个宏。但是,编译器抱怨标签已定义。
我想达到的目标:
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:;
}
}
有什么想法吗?任何可能的解决方法?
标签作为值只能在使用 gcc
编译时使用,因为它是 gcc 扩展。
所以它 100% 正常工作。
回答第二个问题 - 是的,您可以使用它不同的功能:https://godbolt.org/z/aoA3XQ
考虑向您的宏添加一个 scope
参数。
结果会是这样的:
#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:;
}
}
这不是最优的,但它适用于每个编译器。
请注意,如果要调用的块与问题示例中的模式完全相同,您甚至可以定义另一个宏来调用 FULL_MACRO(scope,X)
,因为所有 GOTO
和 FINISH
调用将能够被参数化。
您需要 __label__
扩展(至少在 gcc、clang 和 tinycc 中),它允许您将标签范围限定为块。
需要在块的最开头声明标签
__label__ I0, R0, S0, F0, G0, H0;
(连续 __label__ I0; __label__ R0; ...
或两种形式的混合也适用。)
除非使用 __label__
声明为局部作用域,否则 C 标签的作用域为它们的封闭函数。
你的例子 __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
在这种特殊情况下,这种基于本地标签的跳转表似乎比普通的旧 switch
买得少。
这是一个非常丑陋的解决方案,但如果您愿意向范围添加额外的前缀定义,则可以通过串联来实现
#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
}
}
我正在尝试在 C 中进行基于宏的跳转 table。
下面是一些示例代码:
#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:;
}
可能的标签(I0、R0 等)必须相同。
问题是:我希望能够在同一个源文件的不同作用域部分使用同一个宏。但是,编译器抱怨标签已定义。
我想达到的目标:
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:;
}
}
有什么想法吗?任何可能的解决方法?
标签作为值只能在使用 gcc
编译时使用,因为它是 gcc 扩展。
所以它 100% 正常工作。
回答第二个问题 - 是的,您可以使用它不同的功能:https://godbolt.org/z/aoA3XQ
考虑向您的宏添加一个 scope
参数。
结果会是这样的:
#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:;
}
}
这不是最优的,但它适用于每个编译器。
请注意,如果要调用的块与问题示例中的模式完全相同,您甚至可以定义另一个宏来调用 FULL_MACRO(scope,X)
,因为所有 GOTO
和 FINISH
调用将能够被参数化。
您需要 __label__
扩展(至少在 gcc、clang 和 tinycc 中),它允许您将标签范围限定为块。
需要在块的最开头声明标签
__label__ I0, R0, S0, F0, G0, H0;
(连续 __label__ I0; __label__ R0; ...
或两种形式的混合也适用。)
除非使用 __label__
声明为局部作用域,否则 C 标签的作用域为它们的封闭函数。
你的例子 __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
在这种特殊情况下,这种基于本地标签的跳转表似乎比普通的旧 switch
买得少。
这是一个非常丑陋的解决方案,但如果您愿意向范围添加额外的前缀定义,则可以通过串联来实现
#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
}
}