使用其中包含 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);
}

这不依赖于任何编译器扩展,因此您应该能够在大量编译器中使用它。