【问题标题】:Is this macro argument counting tactic legit?这个宏参数计数策略合法吗?
【发布时间】:2014-10-21 23:06:16
【问题描述】:

我知道VA_NARGS 宏,如C Preprocessor, Macro "Overloading" 所述 有一段时间了,但我总是被让它工作所需的大量样板所推迟。

我最近需要这个功能,所以我咬紧牙关写了所有必要的宏代码,尽其“荣耀”。

在我的特定情况下,我可以始终依赖所有 varargs 参数为特定类型。这让我想到也许有更好的方法,使用sizeof 和数组类型。我在我的本地系统上试过这个,它似乎工作。但是,我担心这个解决方案可能很脆弱(超出类型限制)。

我的问题是:这实际上是解决问题的安全且合理的解决方案吗?或者也许:如果我使用这个,我会问什么麻烦?最后:如果尝试存在问题(如下),是否可以应用一些调整来挽救一般方法?

这是代码,以及一个演示 main() 函数。在这种情况下,可变参数参数都必须是整数:

#include <stdio.h>

#define ARG_ARRAY(...) ((int[]) { __VA_ARGS__ })
#define ARG_COUNT(...) (sizeof (ARG_ARRAY(__VA_ARGS__)) / sizeof (int))

#define STUFF(label, ...) \
    stuff(label, ARG_COUNT(__VA_ARGS__), ARG_ARRAY(__VA_ARGS__))

void stuff(char *label, int count, int *values) {
    printf("[%s] count %d", label, count);

    for (int i = 0; i < count; i++) {
        printf("%s %d", (i == 0) ? ":" : ",", values[i]);
    }

    printf("\n");
}

int return1(void) {
    printf("Called `return1()`.\n");
    return 1;
}

int main(int argc, char **argv) {
    STUFF("blort");
    STUFF("frotz", return1());
    STUFF("fizmo", 2 + 3, 6 + 1);
    STUFF("glorf", 99, 999, 999);
    STUFF("igram", 9, 8, 7, 6, 5, 4, 3, 2, 1);
}

这是一个成绩单:

[blort] count 0
Called `return1()`.
[frotz] count 1: 1
[fizmo] count 2: 5, 7
[glorf] count 3: 99, 999, 999
[igram] count 9: 9, 8, 7, 6, 5, 4, 3, 2, 1

return1() 打印输出是为了验证函数没有被调用两次。


更新:

在 cmets 中指出 (int[]) { args } 是 C99 而不是 C++。就我而言,我可以指望对相关代码使用 C 编译器,但了解这个特殊限制仍然很好。

在一个现已删除的答案中指出,C99 要求一个可变参数宏参数至少由一个实际参数填充(尽管我认为规范在这方面充其量是模棱两可的)。我手头的编译器(OS X 上的 Clang)接受带有 -Wall 的代码,但实际上确实抱怨 -pedantic,正如 @2501 在他们的评论中恰当地展示的那样。

-pedantic 还抱怨大小为零的数组(无参数扩展 (int[]) { }),尽管这可以通过始终包含一个虚拟元素来解决。

【问题讨论】:

  • 如果你可以使用Boost.PP,那就去做吧。所有样板文件都为您完成了这么多方便的东西。它适用于 C 和 C++。
  • (int[]) { __VA_ARGS__ }:这是一个 C99 复合字面量(如果它完全有效的话),不存在 C++ 等价物。
  • 谢谢!就我而言,我可以指望它是 C 代码。我会更新问题。
  • @2501 是的,在 OS X 上使用 Clang。
  • 你的代码不是 C.ideone.com/XyINb0

标签: c macros variadic-macros


【解决方案1】:

如果宏参数都是有效且非空的表达式,您可以尝试使用decltype,如下所示:

#include <tuple>
#define NARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value

不评估宏参数。例如:

#include <iostream>
#include <type_traits>

int main(int argc, char *argv[])
{
    std::cout << "Have "
              << NARGS("bar", 1, argc, 3, std::declval<int>())
              << " things\n";
}

【讨论】:

  • 谢谢,虽然我的问题不是关于替代品,其中有几个可用的(我注意到其中一个)。相反,我想具体了解这个特定建议解决方案的问题。同样 FWIW,在我目前的情况下,我必须使用 C 编译器而不是 C++ 进行编译。
  • @danfuzz:你为什么要标记C++这个问题?!
  • 我最初认为建议的解决方案适用于 C 和 C++。我将取消标记。
  • @chris:你能以不评估论点的方式做到这一点吗?
  • 公平点。如果不手动将decltype 附加到每个人,我想不出任何事情。
猜你喜欢
  • 2011-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-18
  • 1970-01-01
  • 1970-01-01
  • 2019-05-29
  • 1970-01-01
相关资源
最近更新 更多