使用其中包含 goto 和标签的宏展开循环
Unroll a loop using a macro that has a goto and a label in it
这个问题与C或C++语言能力密切相关。我不推荐下面的代码作为设计模式。我不使用它,也不鼓励它。但我只是好奇,想提高我的知识!
我有一个包含标签和转到条件的定义。
#define BROP(num, sum) \
num = rand_lcg(generated); \
if (num % 2) \
{ \
rng1: \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
goto rng1; \
}
稍后在代码中我这样使用它:
for (i = 0; i < iterations; i++)
{
BROP(num, sum);
BROP(num, sum);
BROP(num, sum);
// ...
}
我最终遇到的情况是循环展开并且 标签被重新定义。
是否可以使用智能构造让编译器在每次定义为 "instantiated" 时重命名标签?
我知道避免这种说法的所有选择,但我仍然不知道问题的答案。
你真的应该让它成为一个 do
...while
循环:
#define BROP(num, sum) do{ \
bool again = false; \
num = rand_lcg(generated); \
if (num % 2) { \
do { again = false; \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
again = true; \
} while(again); } while(0)
参见 this 了解旧的外部do{
...}while(0)
技巧。
如果您(错误地)坚持使用标签并使用 GCC compiler (or Clang/LLVM which should be compatible on that), you could use local labels 扩展名(即使用 __label__
...)
您也可以在预处理器中使用 concatenation 从 __LINE__
数字生成标签。从
中汲取灵感
#define STUPID_LOOP_BIS(Test,Lin) do { \
lab##Lin: if (Test) goto lab##Lin; } while(0)
#define STUPID_LOOP_AT(Test,Lin) STUPID_LOOP_BIS(Test,Lin)
#define STUPID_LOOP(Test) STUPID_LOOP_AT(Test,__LINE__)
出于不明原因,您需要所有三个宏!
并使用
STUPID_LOOP(x++ < 100);
STUPID_LOOP(y-- > 0);
在不同的行上。当然可以根据您的需要进行调整和改进。
您绝对应该使用并信任更多编译器优化能力并具有static inline
功能。并非每个测试都被编译到机器分支(例如,因为 CMOV instructions); not every loop is compiled to a machine loop (e.g. because of loop unrolling)。您可能正在浪费开发人员的时间,更重要的是,您正在通过您的技巧禁用 优化(因此您的代码可能会变慢,而不是变快)。
如果使用 GCC 或 Clang 启用优化和警告:因此使用
gcc -Wall -Wextra -O3 -mtune=native
进行编译
忽略它的原因,以下版本的 BROP
可以干净地编译为 C 和 C++
#define BROP(num, sum, lbl) \
num = rand_lcg(generated); \
if (num % 2) \
{ \
lbl : \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
goto lbl; \
}
我将其调用为
for (i = 0; i < 1000; i++)
{
BROP(num,sum, lbl1);
BROP(num,sum, lbl2);
}
这不依赖于任何编译器扩展,因此您应该能够在大量编译器中使用它。
这个问题与C或C++语言能力密切相关。我不推荐下面的代码作为设计模式。我不使用它,也不鼓励它。但我只是好奇,想提高我的知识!
我有一个包含标签和转到条件的定义。
#define BROP(num, sum) \
num = rand_lcg(generated); \
if (num % 2) \
{ \
rng1: \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
goto rng1; \
}
稍后在代码中我这样使用它:
for (i = 0; i < iterations; i++)
{
BROP(num, sum);
BROP(num, sum);
BROP(num, sum);
// ...
}
我最终遇到的情况是循环展开并且 标签被重新定义。
是否可以使用智能构造让编译器在每次定义为 "instantiated" 时重命名标签?
我知道避免这种说法的所有选择,但我仍然不知道问题的答案。
你真的应该让它成为一个 do
...while
循环:
#define BROP(num, sum) do{ \
bool again = false; \
num = rand_lcg(generated); \
if (num % 2) { \
do { again = false; \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
again = true; \
} while(again); } while(0)
参见 this 了解旧的外部do{
...}while(0)
技巧。
如果您(错误地)坚持使用标签并使用 GCC compiler (or Clang/LLVM which should be compatible on that), you could use local labels 扩展名(即使用 __label__
...)
您也可以在预处理器中使用 concatenation 从 __LINE__
数字生成标签。从
#define STUPID_LOOP_BIS(Test,Lin) do { \
lab##Lin: if (Test) goto lab##Lin; } while(0)
#define STUPID_LOOP_AT(Test,Lin) STUPID_LOOP_BIS(Test,Lin)
#define STUPID_LOOP(Test) STUPID_LOOP_AT(Test,__LINE__)
出于不明原因,您需要所有三个宏!
并使用
STUPID_LOOP(x++ < 100);
STUPID_LOOP(y-- > 0);
在不同的行上。当然可以根据您的需要进行调整和改进。
您绝对应该使用并信任更多编译器优化能力并具有static inline
功能。并非每个测试都被编译到机器分支(例如,因为 CMOV instructions); not every loop is compiled to a machine loop (e.g. because of loop unrolling)。您可能正在浪费开发人员的时间,更重要的是,您正在通过您的技巧禁用 优化(因此您的代码可能会变慢,而不是变快)。
如果使用 GCC 或 Clang 启用优化和警告:因此使用
gcc -Wall -Wextra -O3 -mtune=native
忽略它的原因,以下版本的 BROP
可以干净地编译为 C 和 C++
#define BROP(num, sum, lbl) \
num = rand_lcg(generated); \
if (num % 2) \
{ \
lbl : \
generated = rand_lcg(generated); \
if (generated < 512) \
sum -= generated; \
else \
goto lbl; \
}
我将其调用为
for (i = 0; i < 1000; i++)
{
BROP(num,sum, lbl1);
BROP(num,sum, lbl2);
}
这不依赖于任何编译器扩展,因此您应该能够在大量编译器中使用它。