如何提取__VA_ARGS__?

4
我是一名有用的助手,可以为您进行翻译。以下是需要翻译的内容:

我有一个宏,用于调用每个参数的静态函数。

例如:

#define FOO(X) X::do();
#define FOO_1(X,Y) X::do(); Y::do();

我的问题是,我需要使用可变数量的参数来使用foo函数,是否可以使用__VA_ARGS__

像下面这行代码一样:

#define FOO(...) __VA_ARGS__::do() ? 

Thanks


3
如果您能在未来花费5秒钟查看问题的预览,意识到格式完全混乱,然后尝试清理它,这可能会使回答问题变得更容易。 :) SO确实具有代码格式化功能。您只需选择代码,然后按下一个按钮即可。 - jalf
请使用可变参数模板代替... - Kerrek SB
是的,我使用了C++11的特性,事实上,我需要一个宏来定义这样一个函数: #define FOO(X)
static void fooFunction() {
X::do(); \ }我能否使用可变参数模板代替?谢谢
- Kennir
@Kennir:你可以。请看我的回答。 - Andy Prowl
3个回答

5
宏展开不像可变模板参数包展开那样工作。你所拥有的将会展开为:
X,Y::do();

同时,不要

X::do(); Y::do();

像您希望的那样。但在C ++11中,您可以使用可变模板。例如,您可以按照以下方式实现所需功能:
#include <iostream>

struct X { static void foo() { std::cout << "X::foo()" << std::endl; }; };
struct Y { static void foo() { std::cout << "Y::foo()" << std::endl; }; };
struct Z { static void foo() { std::cout << "Z::foo()" << std::endl; }; };

int main()
{
    do_foo<X, Y, Z>();
}

你只需要这些(相对简单的)设备:
namespace detail
{
    template<typename... Ts>
    struct do_foo;

    template<typename T, typename... Ts>
    struct do_foo<T, Ts...>
    {
        static void call()
        {
            T::foo();
            do_foo<Ts...>::call();
        }
    };

    template<typename T>
    struct do_foo<T>
    {
        static void call()
        {
            T::foo();
        }
    };
}

template<typename... Ts>
void do_foo()
{
    detail::do_foo<Ts...>::call();
}

这里有一个实时示例


顺便问一下:为什么需要有三个不同模板参数的函数? - Kennir
@Kennir:我不确定我理解你的问题。什么参数? - Andy Prowl
抱歉我的英语不太好。为什么需要三种类型的模板? template <typename... Ts> template<typename T, typename... Ts> template<typename T> - Kennir
@Kennir:首先,那些不是“参数”,而是类模板。第一个是主模板,而接下来的两个是该主模板的特化。您可能需要稍微了解一下模板特化,或在SO上进一步提问-这个主题太广泛了,无法在评论中回答。 - Andy Prowl

1
实际上,您可以部分解决此问题。您可以直接自由地提取C++11的可变参数模板中的每个成员,但是您只能获得第一个元素。例如,假设我们有一个名为OUT(...)的宏,并且我们想要生成std::cout << A << B << C ...,其中A、B、C是宏的可变参数。请尝试以下操作:
#include <iostream>

#define SEPERATOR <<
#define GET_1_OF(element1, ...) element1
#define GET_2_OF(element1, ...) element1 SEPERATOR GET_1_OF(__VA_ARGS__) 
#define GET_3_OF(element1, ...) element1 SEPERATOR GET_2_OF(__VA_ARGS__) 
#define BAR(...) GET_3_OF(__VA_ARGS__)
int main()
{
    std::cout << BAR(1,2,3,4,5);
    return 0;
}

这当然不是你想要的解决方案。但你可以增加GET_N_OF的数量来实现你想要的效果。请注意,SEPERATOR是<<,因此我们的宏可以写成1 << 2 << 3等等。
现在,我们在这段代码中遇到了一个问题。请将BAR(1,2,3,4,5)更改为BAR(1)。你会发现它会出现错误。这是因为它期望有3个参数,虽然多传递一些参数并不是问题(因为它是可变参数),但我们却多传递了一个SEPERATOR。因此,为了解决这个问题,请使用GET_N_OF(...)而不是BAR(...)(因为你知道参数的数量):
#include <iostream>

#define SEPERATOR <<
#define GET_1_OF(element1, ...) element1
#define GET_2_OF(element1, ...) element1 SEPERATOR GET_1_OF(__VA_ARGS__) 
#define GET_3_OF(element1, ...) element1 SEPERATOR GET_2_OF(__VA_ARGS__) 
#define GET_4_OF(element1, ...) element1 SEPERATOR GET_3_OF(__VA_ARGS__) 
#define GET_5_OF(element1, ...) element1 SEPERATOR GET_4_OF(__VA_ARGS__) 

int main()
{
    std::cout << GET_5_OF(1,2,3,4,5);
    std::cout << GET_1_OF(1);
    return 0;
}

请注意,如果您不知道自己在做什么,请不要使用宏!我的回复只是为了分享有趣的宏代码,可能对您有益。在宏确实必要之前,我始终不鼓励使用宏。

1
您无法直接操作宏参数,__VA_ARGS__ 始终被视为一个单独的单元,由逗号分隔的所有参数组成。预处理器没有内置的方法来查找参数数量、分离它们或循环遍历它们。
类似问题的这个答案展示了使用预处理器的基本解决方案:找出参数列表中有多少项,并将其传递给一个确切需要此数量参数的宏。
我建议不要这样做,而是使用Andy Prowls C++11解决方案,甚至重新构造您的代码,以完全不需要这个功能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接