【问题标题】:Unroll a loop using a macro that has a goto and a label in it使用包含 goto 和标签的宏展开循环
【发布时间】:2015-03-16 14:40:01
【问题描述】:

这个问题与 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);
    // ...
}

我最终陷入循环展开并且标签被重新定义的情况。

每次定义被“实例化”时,我可以使用一个智能构造来让编译器重命名标签吗?

我知道所有避免这种说法的替代方法,但我仍然不知道问题的答案。

【问题讨论】:

  • 您是否有理由不能在宏中只使用do-while 循环结构而不是goto
  • 其实……为什么这不是一个函数?
  • C 还是 C++?它们是不同的语言!
  • 如何避免分支?每个if 语句都会生成一个分支,这会让编译器更难为您优化。
  • @VAndrei,这真是一个糟糕的策略。标签可以引导人们找到他们认为合适的问题。

标签: c++ c compiler-errors


【解决方案1】:

你真的应该把它变成一个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 编译器(或应该兼容的Clang/LLVM),您可以使用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);并非每个循环都编译为机器循环(例如,因为loop unrolling)。您可能正在浪费开发人员的时间,更重要的是,您正在禁用您的技巧优化(因此您的代码可能会变得更慢,而不是更快)。

如果使用 GCC 或 Clang 启用优化和警告:请使用
gcc -Wall -Wextra -O3 -mtune=native

进行编译

【讨论】:

  • 您是否有理由不将循环条件更改为 while(generated &lt; 512) 而不是使用单独的 bool 变量?
  • 同时执行(再次);添加一个分支?除了我明确编写的分支之外,我的代码中不需要任何其他分支。
【解决方案2】:

忽略它的原因,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);
}

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

【讨论】:

  • 有了预处理器连接和__LINE__,它仍然是可移植的,你不必手动发明lbl2标签!
  • 同意,但我要的是最简单的解决方案。我怀疑这是用于基准测试或测试套件,因此明确通常是更好的选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 2015-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多