带 if 语句的 C 预处理器
我有以下宏:
#define IF_TRACE_ENABLED(level) if (IsTraceEnabled(level))
用户代码应如下所示:
IF_TRACE_ENABLED(LEVEL1)
{
... some very smart code
}
这里强调大括号 - 我想防止宏中的“if”“吃”其他代码:
if (...)
IF_TRACE_ENABLED(LEVEL1)
printf(....);
else
bla bla bla
在此示例中 IF_TRACE_ENABLED
“吃”否则阻止。
有没有办法强制用户代码在没有大写刹车的情况下不编译,或者有其他定义宏来实现安全?
I have the following macro:
#define IF_TRACE_ENABLED(level) if (IsTraceEnabled(level))
The user code should look following:
IF_TRACE_ENABLED(LEVEL1)
{
... some very smart code
}
The emphasis here on curly brackets - I want to prevent "if" from macro to "eat" other code:
if (...)
IF_TRACE_ENABLED(LEVEL1)
printf(....);
else
bla bla bla
In this example IF_TRACE_ENABLED
"eats" else block.
Is there way to enforce user code not compile without curly brakes or there are other to define the macro to achieve the safety?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这不会强制宏的用户使用大括号,但会防止
else
子句被无意中吃掉:附注:
printf()
周围的大括号在问题的第二个示例中不会解决问题 - 与bla bla bla
关联的else
仍将绑定到if
宏中的语句。This doesn't force the user of the macro to use braces, but it will prevent an
else
clause from being unintentionally eaten:A side note: braces around the
printf()
in the second example of the question wouldn't have fixed the problem - theelse
associated withbla bla bla
would still be bound to theif
statement in the macro.你可以尝试这个:
我认为没有任何方法可以仅从宏的开头行“强制”良好的语法。您将需要使用两个。
编辑
我在宏内添加了一对额外的大括号以避免所有歧义。
作为对评论的回应,该宏的用途如下:
不作为声明。根据记录,我认为这是对预处理器的滥用,任何人都不应该这样做。如果需要的话,直接写出来并用
#ifdef DEBUG
括起来有什么问题。You could try this:
I don't think there's any way to "enforce" good syntax from only the opening line of the macro. You will need to use two.
EDIT
I've added an extra pair of braces inside the macro to avoid all ambiguity.
In response to the comment, this macro is meant to be used like this:
Not as a statement. For the record, I think this is an abuse of the preprocessor and nobody should do this at all. What's wrong with just writing it out, bracketed with
#ifdef DEBUG
if necessary.如果您只想根据宏的评估来预测语句/块的执行,那么悬空的 else 是一个危险的潜在错误,我已经看到很多团队都在忍受这种错误。
提出的其他解决方案改变了代码的结构,或者以某种方式变得笨拙。下面提出的解决方案并不完美——它有自己的尴尬,但希望它是更宜居的尴尬。
通常可以将预处理器宏的输出从简单但有风险的形式转换
为
for
循环,该循环将根据表达式
执行 0 或 1 次。作为一个循环,不存在其他悬挂的风险。它的工作原理如下:请注意,
表达式
很可能是一个被文本替换的复杂表达式,因此我们需要循环条件为_internal_once && (表达式)
既避免运算符优先级问题,又保证表达式
只计算一次(由于 C 布尔短路逻辑)。这个解决方案的尴尬之处在于,它改变了谓词代码中的
break
和continue
行为,这使得情况变得更糟,因为循环是隐藏的并且很容易忘记。对我来说,这在现实世界的使用中还不是问题。我们使用这些谓词宏来保护指标、日志记录、遥测和其他不经常与外循环交互的支持代码。在这个循环的前一个版本中,我遇到了一个现实世界的问题。永远不要永远为循环控制变量使用简单的名称,我已经看到它隐藏了另一个变量并导致语句/块看到错误的值。
此解决方案不会强制在客户端代码上使用块/大括号,但可以说,由于 C 和 C++ 不这样做,您也不应该这样做。这是客户的选择/政策。
有人可能会说,您应该为循环变量生成一个更唯一的名称,将标识符与后缀
__LINE__ 或
__COUNTER__
连接起来(对于格努)。这对我来说感觉太过分了,循环控制变量都是本地/自动的并且应该正确地进行阴影处理。您甚至可以实现像
for (static bool _internal_once = true; _internal_once; _internal_once = false)
If you just want to predicate the execution of a statement/block based on the evaluation of a macro, that dangling else is a risky latent bug that I've seen a lot of teams just live with.
The other solutions presented alter the structure of the code or are otherwise awkward in some fashion. The solution presented below isn't perfect - it has its own awkwardness, but hopefully it is more livable flavor of awkward.
One can generally transform a preprocessor macro's output from the simple but risky form of
to a
for
loop that will execute 0 or 1 times depending uponexpression
. As a loop, there's no risk of dangling else. This works like so:Note that
expression
could very well be a complex expression being text substituted, so we need the loop condition to be_internal_once && (expression)
to both avoid operator precedence issues, and also guaranteeexpression
is only evaluated once (due to C boolean short circuit logic).The awkwardness of this solution is that is changes the
break
andcontinue
behavior within predicated code, made worse because the loop is hidden and it is easy to forget. This has not yet been a problem in real world usage for me. We use these predicate macros to guard metrics, logging, telemetry, and other support code that doesn't often interact with an outer loop.I have been bit by a real world problem in a previous incarnation of this loop. Never NEVER NEVER use a simple name for the loop control variable, I've seen it shadow another variable and cause the statement/block to see the wrong value.
This solution doesn't force the use of a block/braces on the client's code, but arguably since C and C++ don't, neither should you. It's the client's choice/policy.
One could argue you should generate a more unique name for the loop variable, concatenating
##
the identifier with a suffix of__LINE__
or__COUNTER__
(for gnu). That feels excessive to me, the loop control variables are all local/auto
and should shadow correctly.You can even implement predicates like
for (static bool _internal_once = true; _internal_once; _internal_once = false)
这应该可行,但您也可以将 if 块的内容作为参数传递给宏:
This should work, but you'll have the pass the contents of the if block as an argument to the macro as well: