这个宏观论证计算策略合法吗?

时间:2014-10-21 23:06:16

标签: c macros variadic-macros

我已经了解了C Preprocessor, Macro "Overloading"中描述的VA_NARGS宏 有一段时间了,但是我总是被大量的样板所拖延,以使其发挥作用。

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

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

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

这是代码,以及演示main()功能。在这种情况下,varargs参数都必须是int:

#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()打印输出用于验证函数未被调用两次。


更新

评论中指出(int[]) { args }是C99而不是C ++。在我的情况下,我可以指望使用C编译器来处理相关代码,但知道这个特殊限制仍然很好。

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

-pedantic也抱怨零大小数组(无参数扩展(int[]) { }),但这可以通过总是包含一个虚拟元素来解决。

1 个答案:

答案 0 :(得分: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";
}