2017-02-18 112 views
2

我知道va_list通常是这样,因为它不是很安全,你应该避免的,但有可能从一个函数传递参数,如:是否可以将va_list传递给variadic模板?

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; 
}; 
+5

不,这是不可能的。 –

+0

这将如何使它成为任何救星?一旦变量处于'...'中,每个类型信息都会丢失,这正是使其不可用的原因。这种损失是无法收回的。请注意,当然,您可以明确地将'va_list'的内容转换为某些类型,然后由variadic模板将其拾取,但是没有任何内容会自动为您恢复丢失的类型信息。 –

回答

2

没有,可变参数函数的参数是一个运行特征,并传递给一个可变参数模板参数的个数,虽然可变,必须在编译时是已知的。

0

如RFC1925指出,“只要有足够的推力,猪飞就好了。但是,这并不一定是个好主意。”

正如指出由彼得·奥三,老C风格的可变参数函数的参数是意在运行时的工作特征;在编译时新的可变参数模板C++风格的工作。

所以...只是为了好玩......我想这可能是可能的,如果你知道,编译时间,该类型foo()的说法。

例如,如果foo()就像在下面的例子中foo()一个可变参数模板函数...这编译和铿锵++工作,但给人以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语法要求您传递变量(num)具有相同的值sizeof...(Ts)

但是,我再说一遍,只是为了好玩。

为什么,看在上帝的份上,我们应该写这样foo()功能时,我们可以直接写像bar()的功能?

+0

我写foo是因为foo可以是虚拟的,但是这不会起作用我现在正在查看存储参数包,越来越近谢谢你的帮助 –

+0

@AndreasLoanjoe--不确定理解;我想你应该编辑你的问题来展示你想要的东西的例子;无论如何:这个解决方案真的很愚蠢,这只是为了好玩。 – max66

+0

那么我想做的是传递一个可变参数包到派生类,但我想出了一种方法来做到这一点,现在没有... va_list所以现在的问题是一种无关紧要,因为我认为va_list是相当无用的我现在读的东西。 –

0

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

您的foo不可能知道每个参数组合,因为有无限量 - 所以除非您以某种方式限制参数空间,否则您的问题的答案是“否”。

然而,一些模板魔术也可能,但没有实际用途。我举一个具体的例子作为概念验证,但请不要在实际代码中使用它。

比方说

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

期望格式字符串等"ffis",其中每个字符指定(在这种情况下双,双,整数,字符串)参数类型。我们也有一个可变参数模板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!"); 
} 

所得的代码编译约10秒机智h gcc,但生成正确的字符串2.3, 3.4, 1, hello!