是否可以将va_list传递给可变参数模板?

时间:2017-02-18 16:48:47

标签: c++ c++11 templates variadic-templates variadic-functions

我知道va_list通常是你应该避免的东西,因为它不是很安全,但是可以通过以下函数传递参数:

void foo(...);

到类似

的功能
template<typename... Args>
void bar(Args... arguments);

编辑:最初我想尝试使用它来调用具有可变数量的参数/类型的虚函数,但这不是让这个问题变得无关紧要的方法。最终我最终做了这样的事情:

struct ArgsPackBase
{
    virtual ~ArgsPackBase() {}
};

template<typename... Args>
struct ArgsPack : public ArgsPackBase
{
public:
    ArgsPack(Args... args_)
        : argsTuple(args_...)
    {}

    void call(std::function<void(Args...)> function)
    {
        callExpansion(function, std::index_sequence_for<Args...>{});
    }

private:
    template<std::size_t... I>
    void callExpansion(std::function<void(Args...)> function, std::index_sequence<I...>)
    {
        function(std::get<I>(argsTuple)...);
    }

    std::tuple<Args...> argsTuple;
};

3 个答案:

答案 0 :(得分:2)

不,可变参数函数参数是运行时特性,并且必须在编译时知道传递给可变参数模板的参数数量(尽管是变量)。

答案 1 :(得分:1)

如在RFC1925中所观察到的那样,#34;如果有足够的推力,猪就会飞得很好。但是,这不一定是个好主意。&#34;

正如Piotr Olszewski指出的那样,旧的C风格的可变参数函数参数是一个旨在在运行时工作的特性;新的可变参数模板C ++ - 在编译时工作。

所以......只是为了好玩......我想如果你知道,编译时间,foo()的论证类型就可以。

例如,如果foo()是一个可变参数模板函数,例如以下示例中的foo() ...编译并使用clang ++但是使用g ++编译错误...我不知道知道谁是对的(当我有时间时,我会打开一个关于此的问题)......

#include <cstdarg>
#include <iostream>
#include <stdexcept>

template <typename ... Args>
void bar (Args const & ... args)
 { 
   using unused = int[];

   (void)unused { (std::cout << args << ", ", 0)... };

   std::cout << std::endl;
 }

template <typename ... Ts>
void foo (int num, ...)
 {
   if ( num != sizeof...(Ts) )
      throw std::runtime_error("!");

   va_list args;                     

   va_start(args, num);           

   bar( va_arg(args, Ts)... );

   va_end(args);
 }

int main ()
 {
   foo<int, long, long long>(3, 1, 2L, 3LL); // print 1, 2, 3, 
 }

注意您需要在foo()中传递reduntant信息:可变参数的数量:va_start语法要求您传递具有相同值的变量(numsizeof...(Ts)

但是,我重复一遍,只是为了好玩。

为什么我们应该直接编写像foo()这样的函数来编写像bar()这样的函数?

答案 2 :(得分:0)

对于C ++模板,编译器必须在编译时生成每个实例。因此,对于每个参数组合(int,double,float),相应的实例应出现在目标文件中。

您的foo无法知道每个参数组合,因为有无限量 - 所以除非您以某种方式限制参数空间,否则问题的答案是&#34; no&#34;。< / p>

然而,有了一些模板魔术,可能,但实际上并不实用。我将一个具体示例显示为概念证明,但请不要在实际代码中使用它

让我们说

void foo(const char* s, ...);

期望格式字符串如"ffis",其中每个字符指定一个参数类型(在这种情况下为double,double,integer,string)。我们还有一个可变参数模板bar函数,用于打印其参数:

template <typename Arg, typename... Args>
void doPrint(std::ostream& out, Arg&& arg, Args&&... args)
{
    out << std::forward<Arg>(arg);
    using expander = int[];
    (void)expander {
        0, (void(out << ", " << std::forward<Args>(args)), 0)...
    };
    out << '\n';
}

void bar() {
    std::cout << "no arguments\n";
}

template<typename... Args>
void bar(Args... arguments) {
    doPrint(std::cout, arguments...);
}

要使foo起作用,我们将在编译时生成每个可能的参数组合,长度为N(因此,3 ^ N个实例):

//struct required to specialize on N=0 case
template<int N>
struct CallFoo {
    template<typename... Args>
    static void foo1(const char* fmt, va_list args, Args... arguments) {
        if (*fmt) {
            using CallFooNext = CallFoo<N - 1>;
            switch (*fmt) {
            case 'f':
            {
                double t = va_arg(args, double);
                CallFooNext::foo1(fmt + 1, args, arguments..., t);
            }break;
            case 'i':
            {
                int t = va_arg(args, int);
                CallFooNext::foo1(fmt + 1, args, arguments..., t);
            }break;
            case 's':
            {
                const char* t = va_arg(args, const char*);
                CallFooNext::foo1(fmt + 1, args, arguments..., t);
            }break;
            }
        } else {
            bar(arguments...);
        }
    }
};

template<>
struct CallFoo<0> {
    template<typename... Args>
    static void foo1(const char* fmt, va_list args, Args... arguments) {
        bar(arguments...);
    }
};


void foo(const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    //Here we set N = 6
    CallFoo<6>::foo1<>(fmt, args);
    va_end(args);
}

主要功能,为了完整性:

int main() {
  foo("ffis", 2.3, 3.4, 1, "hello!");
}

结果代码在我的机器上使用gcc编译大约10秒,但生成正确的字符串2.3, 3.4, 1, hello!