[英]If you pass args from a variadic function to another function with va_list, how do you call va_start (without error)
[英]Is it possible to pass va_list to variadic template?
我知道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;
};
不,可變參數函數參數是運行時特性,並且必須在編譯時知道傳遞給可變參數模板的參數數量(盡管是變量)。
正如在RFC1925中所觀察到的那樣,“只要有足夠的推力,豬就會飛得很好。但是,這不一定是個好主意。”
正如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
語法要求您傳遞具有相同sizeof...(Ts)
值的變量( num
sizeof...(Ts)
。
但是,我再說一遍,只是為了好玩。
為什么,為了善良,我們應該編寫一個像foo()
這樣的函數,當我們可以直接編寫像bar()
這樣的函數時?
對於C ++模板,編譯器必須在編譯時生成每個實例。 因此,對於每個參數組合(int,double,float)
,相應的實例應該出現在目標文件中。
你的foo
不可能知道每個參數組合,因為有無限量 - 所以除非你以某種方式限制參數空間,你的問題的答案是“不”。
但是,通過一些模板魔術,它是可能的 ,但實際上並不實用 。 我將一個具體的例子作為概念證明,但請不要在實際代碼中使用它 。
讓我們說
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!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.