函数式宏的合法用途是什么?

时间:2012-09-09 18:15:59

标签: c macros c-preprocessor

C / C ++中不缺少类似功能的坏或宏的例子。

#define SQUARE(x) x * x
printf("%d", SQUARE(4));   //16
printf("%d", SQUARE(3+1)); //7
#undef SQUARE

#define SQUARE(x) (x) * (x)
printf("%d", 36/4);                //9
printf("%d", SQUARE(6)/SQUARE(2)); //36
#undef SQUARE

#define SQUARE(x) ((x) * (x))
int x = 3;
++x;
printf("%d", SQUARE(x)); //16

int y = 3;
printf("%d", SQUARE(++y)); //?
#undef SQUARE

考虑到类似函数的宏的问题,它们有什么样的好/谨慎/推荐用途?

是否有任何时候类似函数的宏更适合某个函数?

我认为必须有一些非常好的案例,否则预处理器制造商会让他们的工作变得更容易并将其排除在外。

6 个答案:

答案 0 :(得分:3)

评论已经抓住了大部分内容。

某些类型的调试和检测只能通过使用函数式宏来完成。最好的示例是assert(),但函数式宏也可以用于检测代码以进行性能分析。像__FILE____LINE__这样的魔术宏以及用于引用的#和用于令牌粘贴的##等功能会使类似函数的宏对调试和分析有价值。

在C ++中,使用模板和通常更具侵略性的内联,使用函数式宏的其他原因很少(如果有的话)。例如,出于问题中说明的原因,模板函数std::max是一个比MAX宏更好的解决方案。

在C中,有时需要优化以确保内联一小段代码。在这种情况下,宏仍然偶尔有用。 ALLCAPS命名约定用于警告程序员,由于简单文本替换的所有问题,这实际上是一个宏而不是函数。例如,如果您在一个性能关键的代码段中有几个需要等效std::max的地方,那么宏 - 具有所有危险 - 可能是一个有用的解决方案。

答案 1 :(得分:2)

大多数情况下您不需要使用宏。但是有些情况是合法的(尽管有讨论的余地)。

如果可以使用枚举,则不应使用宏。您不应该有依赖于具有魔术名称的局部变量的宏。您不应该有可以用作l值的宏。您不应该有对扩展宏周围的代码有副作用的宏。使用宏代替内联函数大多数时候都是一个坏主意,无论如何列表都是无穷无尽的。

你可以使用宏来伪造迭代器,但在C语言中,尤其是Linux内核,你会看到以下内容:

#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
     pos = n, n = pos->next)

在整个Linux内核源代码中使用了许多其他类似类型的宏。

offsetof(3)通常也实现为宏,以及assert(3)等。

答案 2 :(得分:1)

我在这里普遍同意dmp。如果您可以使用其他东西,则使用它并忘记宏。

那说我会重复评论中的内容:

  1. 当你需要在其中使用一些其他预处理器符号(__LINE __,__ FILE __,__ func__等)时,你通常需要像宏这样的函数。这通常意味着断言/调试宏。
  2. 在宏中字符串化/粘贴标记(#和##运算符)。
  3. 你可以(如果你觉得足够的冒险)使用类似函数的宏来创建enum->字符串映射,使用一些切割器技巧,如定义/取消定义和重新定义宏。
  4. 像va_list / va_arg这样的东西,你需要通过指针访问,同时改变它所指向的地方。
  5. 我重复一遍 - 如果你能避免预处理器就这样做。

答案 3 :(得分:0)

C标准有(或允许)一大堆它们,我认为任何以相同的精神做宏的事情应该是合法的:

  • 字符处理函数isspace和类似函数通常以宏的形式实现
  • CMPLX被声明为宏
  • UINT64_C和类似的宏
  • 如果您加入tgmath.hsincos并且许多其他功能都会成为宏
  • _Generic关键字的重点是编写类似函数的宏

答案 4 :(得分:0)

在某些情况下,会有编译器特定的指令,这些指令会因每个编译器的重写而变化并且噪声很大。我经常使用的一个属于此类的宏用于通知编译器参数未使用:

virtual size_t heightForItemAtIndex(const size_t& idx) const {
  MONUnusedParameter(idx); // << MONUnusedParameter() is the macro
  return 12; // << this type's items are all the same height
}

此处更好地介绍了问题:cross platform macro for silencing unused variables warning

答案 5 :(得分:0)

Google Style C++ Guide有一个非常有用的宏的例子(我不确定这是否是'函数式宏'的意思 - 它需要一个参数但它不能被函数调用替换):

DISALLOW_COPY_AND_ASSIGN(类名);

这样的宏可以放在类的私有部分中,以禁用由编译器自动生成的复制构造函数和赋值运算符。除非一个类确实需要它们,否则通常不允许这两个自动生成的方法。没有宏禁用需要在每个类中进行大量输入:

ClassName(const ClassName&amp;);
void operator =(const ClassName&amp;);

宏只是自动生成此代码。这很安全。我不知道这种宏导致问题的任何情况。