簡體   English   中英

是否可以將va_list傳遞給可變參數模板?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM