2017-02-18 29 views
2

Я знаю, что va_list обычно что-то вам следует избегать, так как его не очень безопасно, но можно передать аргументы функции, как:Можно ли передать va_list в вариационный шаблон?

void foo(...); 

к функции как

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

?

edit: Первоначально я хотел попытаться использовать это, чтобы вызвать виртуальную функцию с переменным количеством аргументов/типов, но это не был способ сделать этот вопрос неактуальным. В конце концов, я в конечном итоге делаю что-то вроде этого:

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' для некоторых типов, которые затем будут подбираться вариативными шаблонами, но ничто не будет автоматически восстанавливать информацию о потерянном типе. –

ответ

2

Нет, VARIADIC аргументов функции являются функцией времени выполнения, а количество передаваемых аргументов в VARIADIC шаблона, хотя переменный, должны быть известны во время компиляции.

0

Как отмечено в RFC1925, «с достаточной тягой свиньи летают очень хорошо. Однако это не обязательно хорошая идея».

Как указал Петр Ольшевский, старые аргументы вариационной функции C-стиля - это функция, предназначенная для работы во время выполнения; новый переменный шаблон C++ - работает во время компиляции.

Итак ... просто для удовольствия ... Полагаю, это возможно, если вы знаете, время компиляции, типы аргументов для foo().

К примеру, если foo() является VARIADIC шаблон функции как foo() в следующем примере ... что компилировать и работать с лязгом ++, но дает ошибку компиляции с г ++ ... и я не знаю, кто прав (когда у меня есть время, я буду открывать вопрос об этом) ...

#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, 
} 

Заметим, что вы должны передать reduntant информацию в foo(): число аргументов переменным числом: синтаксис va_start требует, чтобы вы передаете переменная (num) с тем же значением sizeof...(Ts).

Но, повторяю, просто для удовольствия.

Почему, ради блага, мы должны написать функцию, такую ​​как foo(), когда мы можем непосредственно написать функцию, такую ​​как bar()?

+0

Я пишу foo, потому что foo может быть виртуальным, но это не будет работать. Я смотрю на хранение пакетов параметров сейчас, все ближе спасибо за помощь, хотя –

+0

@AndreasLoanjoe - не уверенный в понимании; Полагаю, вы должны отредактировать свой вопрос, чтобы показать пример того, что вам нужно; во всяком случае: это решение действительно глупо; это просто для удовольствия. – max66

+0

Ну, что я хотел сделать, это передать пакет вариационных параметров в производный класс, но я выяснил, как сделать это сейчас без ... va_list, поэтому вопрос сейчас неактуальен, так как я думаю, что va_list довольно бесполезен из что я сейчас читаю. –

0

Для шаблона C++ компилятор должен производить каждый экземпляр во время компиляции. Таким образом, для каждой комбинации параметров (int,double,float) соответствующий экземпляр должен отображаться в объектном файле.

Невозможно узнать каждую комбинацию параметров, так как есть бесконечное количество - поэтому, если вы каким-либо образом не ограничиваете пространство параметров, ответ на ваш вопрос «нет».

Однако, с некоторым шаблоном магии это возможно, но практически не полезно. Я показываю один конкретный пример как доказательство концепции, но, пожалуйста, не используйте это в реальном коде.

Позволяет сказать

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

ожидает строку формата, как "ffis", где каждый символ указывает тип параметра (двойной, двойной, целое число, строка в данном случае). Мы также VARIADIC функции шаблона 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!