9

假设我定义了一个宏,并且在 if else 语句中使用该宏

#include <iostream>

#define LOG(x) {if (x) std::cout << "What is up" << std::endl;}

int main(void) {
  if (true)
    LOG(true);
  else
    false;
  return 0;
}

现在这是一个棘手的案例,我意识到根据缩进的不同,“如果”和“其他”应该搭配哪个“如果”可能会有一些歧义。

我想出了这个灵魂

(some_condition) ? dosomething() : true;

这解决了问题,但我不确定拥有真实陈述的后果是什么。这是一个好的解决方案,还是有更好的方法?

编辑:这是我使用的代码,它不起作用。看看你能不能解决这个问题?

4

6 回答 6

15

你应该这样定义你的宏:

#define LOG(X) do { if (some_condition) dosomething(); } while (0)
于 2013-08-08T15:21:52.370 回答
5

缩进是为了人类的利益,对编译器没有影响。如有疑问,请添加大括号,我建议您一直这样做。

宏(通常)很糟糕。有什么理由不能使用函数吗?

如果 (..?..:..) 的结果没有在任何地方“使用”或“存储”,则结果将被忽略,但仍会调用该函数。所以你的代码应该可以正常工作,尽管它的风格很糟糕而且令人困惑。

与其他答案不同,我没有注意到宏中缺少大括号会破坏 else。但这对我来说并不重要,因为我永远不会因为这个原因使用那个宏!

于 2013-08-08T15:23:13.507 回答
4

预处理是文本替换。您可以要求您的编译器提供预处理的文本形式(使用GCC使用gcc -C -E

您的代码扩展为

if (somebool)
  if (some_condition) dosomething();
 else
   somethingelse();

所以else 申请考试some_condition,肯定不是你想要的。

诀窍是始终将类似语句的宏扩展为do{...}while(0)例如

#define LOG(X) do{ if (some_condition) dosomething(); }while(0)

注意:while(0)循环将被编译器优化掉!

实际上,如果X两者都出现some_condition并且dosomething()您可以使用 GCC 扩展,例如

#define LOG(X) do {typeof(X) _x=(X); \
                   if (predicate(_x)) handle(_x); }while(0)

如果X总是int替换typeof(X)int. 使用 C++11,您可以auto改用。

这将使LOG(i++)宏调用做一些更明智的事情。(您可能不希望增量进行两次)。

更好的是,尽可能避免使用宏并使用内联函数。

如果处理由 GCC 编译的巨大源代码,您甚至可以使用MELTgcc或其他 GCC 插件自定义编译器 - 例如添加您自己的特定内置函数或 pragmas ,但这种方法需要一些工作,因此仅在大型项目中值得。

顺便说一句,GPP预处理器可以配置为 C/C++ 预处理器,并提供更强大的功能。

于 2013-08-08T15:22:31.677 回答
1

你可以做:

#define LOG(X) ((some_condition) && dosomething())

在这里,我使用了 : 的惰性求值&&:仅在为真时才对正确的部分求some_condition值。

警告:dosomething()可能不会 return void,并且根据它返回的内容,您可能需要强制转换以防止在&&.

另一方面,您使用? :, 的解决方案非常好。具有随机值的表达式完全没有问题(true在您的情况下);编译器会很好地优化它。

于 2013-08-08T15:24:25.170 回答
0

如果您希望您的宏在需要语句的上下文中可用,并且它包含任何类型的控制结构(for、、、if... while),那么您应该将其包装在do { ... } while(0). 然后调用者附加一个分号,从而导致一个只执行一次的循环。这避免了在if/else语句中使用时的歧义。(参见comp.lang.c FAQ的问题 10.4 。)

如果您希望宏在需要表达式的上下文中可用,则不能使用if/else(除非您使用像 gcc 的语句表达式这样的语言扩展,但这会使您的代码不可移植)。因此,您可以改用三元条件运算符?:

在你的情况下,这个:

#define LOG(x) {if (x) std::cout << "What is up" << std::endl;}

可以写成:

#define LOG(x) ( (x) ? (std::cout << "What is up" << std::endl), 0 : 0 )

如您所见,这可能会变得复杂。我必须添加一个任意的0,因为?:需要三个操作数(与if语句不同,其中else是可选的)。我还必须添加一个逗号运算符,以便第二个和第三个操作数是类型兼容的。

在这种情况下可能不值得付出努力,因为LOG(...)可能只会在语句上下文中使用(除非您更新宏以产生有意义的结果),但它通常是一种有用的技术。

再说一次,内联函数可能更好。

于 2013-08-08T17:58:03.863 回答
0

您可以在您的内部进行测试或dosomething()其包裹以便进行测试吗?

我假设这是您想在某些版本中删除的某种日志记录功能,因此需要定义吗?如果是这样,我会考虑尽可能多地编写它,以便它对您实际实现的功能尽可能薄。我会这样做:

void loggingFunction(x)
{
  if (condition)
    dosomething(x);
  else
    dosummatelse(x);
}

#define LOG(x) loggingFunction(x)

这有一个额外的好处,可以很好地处理诸如LOG(x++).

于 2013-08-08T15:56:07.080 回答