【问题标题】:Macro for run-once conditioning用于运行一次调节的宏
【发布时间】:2015-07-07 21:53:25
【问题描述】:

我正在尝试构建一个只运行一次代码的宏。 例如,如果您循环代码并希望其中的某些内容只发生一次,则非常有用。简单易用的方法:

static int checksum;

for( ; ; )
{
    if(checksum == 0) { checksum == 1; // ... }
}

但这有点浪费和混乱。所以我有这个宏使用检查位而不是检查变量的真/假状态:

#define CHECKSUM(d) static d checksum_boolean
#define CHECKSUM_IF(x) if( ~(checksum_boolean >> x) & 1) \
                    {                                               \
                        checksum_boolean |= 1 << x;
#define CHECKSUM_END }1

末尾的1是强制用户在末尾加上分号。在我的编译器中这是允许的。


问题是在不让用户指定x 的情况下弄清楚如何执行此操作(n 位要检查)。 所以他可以使用这个:

CHECKSUM(char); // 7 run-once codes can be used

for( ; ; )
{
    CHECKSUM_IF
        // code..
    CHECKSUM_END;
}

想法我该如何实现?

【问题讨论】:

  • 末尾的 1 是强制用户在末尾加上分号。在我的编译器中这是允许的。,看来你不知道do { } while (0)这个把戏。
  • 没有,但是 C89 的 clang 编译器.. 放置一个 1 做同样的事情。
  • @Malina 不,它没有。例如,它将接受CHECKSUM_END + 3;。这就是do ... while (0) 技巧的最大优势:) 和分号之间不能有任何内容。 (而且它适用于所有版本的 C,甚至 C89。)
  • 我不明白你的意思是它会接受CHECKSUM_END +3; 在常规环境中我使用这个技巧.. 但在当前的工作细节中.. 它只适用于1
  • 看看WARN_ONCE在Linux内核中的实现:lxr.free-electrons.com/source/include/asm-generic/bug.h#L119

标签: c if-statement macros conditional


【解决方案1】:

我猜您是说您希望宏以某种方式自动跟踪您的位掩码的哪一位包含当前测试的标志。你可以这样做:

#define CHECKSUM(d) static d checksum_boolean; \
    d checksum_mask
#define CHECKSUM_START do { checksum_mask = 1; } while (0)
#define CHECKSUM_IF do {                       \
    if (!(checksum_boolean & checksum_mask)) { \
        checksum_boolean |= checksum_mask;
#define CHECKSUM_END                           \
    }                                          \
    checksum_mask <<= 1;                       \
} while (0)
#define CHECKSUM_RESET(i) do { checksum_boolean &= ~((uintmax_t) 1 << (i)); } while (0)

你可以这样使用:

CHECKSUM(char); // 7 run-once codes can be used

for( ; ; )
{
    CHECKSUM_START;
    CHECKSUM_IF
        // code..
    CHECKSUM_END;
    CHECKSUM_IF
        // other code..
    CHECKSUM_END;
}

但请注意,这有严重限制:

  • CHECKSUM_START 宏和所有对应的CHECKSUM_IF 宏必须都出现在同一范围内
  • 控制必须始终在任何 CHECKSUM_IF 块之前通过 CHECKSUM_START
  • 控制必须始终以相同的顺序到达CHECKSUM_IF 块。如果它还跳过所有使用相同校验和位掩码的后续块,它可能只会跳过 CHECKSUM_IF 块。

出现这些限制是因为预处理器无法计数。

换句话说,除非宏重新定义,没有任何参数的宏总是扩展为完全相同的文本。因此,如果您不使用宏参数来指示在每种情况下应用哪个标志位,则需要在 运行 时间进行跟踪。

【讨论】:

  • 足够接近了!谢谢。您是否可以另外添加一个用于重置特定位的宏以允许再运行一次?
  • 带或不带参数?如果是“with”,那么它的放置位置将受到极大限制,原因与我的回答中已经讨论过的原因相同。
  • 如果没有参数,这将如何实现?用户必须指定必须再次解析哪些受限代码。所以我猜是争论。
  • 添加了 CHECKSUM_RESET() 宏。预期的参数是要重置的标志的从零开始的索引。请注意,只有在校验和变量的类型是无符号的情况下,它的行为才是明确定义的。要解决该限制,您需要有第二个参数,将 d 参数复制到 CHECKSUM() 宏。
  • 所以这会重置所有位?难道不能只重置一个特定的吗? (我只是不太明白这是如何工作的,希望我不需要)。
猜你喜欢
  • 1970-01-01
  • 2017-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多