Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > variadic templates и "разворот" в типы посредники


Автор: boostcoder 10.1.2012, 22:23
всем привет.

к примеру, имеется у нас следующая функция:
Код

template<typename... Args>
void func(const Args&... args) {}

используем мы ее как обычно:
Код

func(1);
func(1,2);

тут проблем нет.

теперь давайте представим, что эта функция на самом деле декларирована так:
Код

void func(ArgWrapper a0 = ArgWrapper(0,0), ArgWrapper a1 = ArgWrapper(0,0), ArgWrapper a2 = ArgWrapper(0,0), и т.д....) {}

а мы хотим ее использовать так:
Код

struct type {
   template<typename... Args>
   static void method(const Args&... args) {
      func(args...); // тут как записать?
   }
};


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

в общем, последний пример, для ясности должен компилятором генерироваться так:
Код

struct type {
   template<typename... Args>
   static void method(const Args&... args) {
      func(
         ArgWrapper(typeid(args[0]).name(), args[0]),
         ArgWrapper(typeid(args[1]).name(), args[1]),
         ArgWrapper(typeid(args[2]).name(), args[2])
      );
   }
};


не знаю как еще описать..

Автор: mes 11.1.2012, 15:35
проблема пройтись по списку ? рекурсия в помощь..
как обернуть отдельный тип полагаю вопросов не возникает.. 

Автор: boostcoder 11.1.2012, 18:41
оказалось все сильно проще. просто нужно больше времени уделять чтению доки.
Код

#include <typeinfo>
#include <iostream>

struct ArgWrapper {
   ArgWrapper(const char* name, const void* data)
      :name(name)
      ,data(data)
   {}
   
   const char* name;
   const void* data;
};

struct invoker {
   static void func(ArgWrapper a0 = ArgWrapper("",""),
                    ArgWrapper a1 = ArgWrapper("",""),
                    ArgWrapper a2 = ArgWrapper("",""))
   {
      std::cout
      << "a0: name = " << a0.name << ", data = " << a0.data << std::endl
      << "a1: name = " << a1.name << ", data = " << a1.data << std::endl
      << "a2: name = " << a2.name << ", data = " << a2.data << std::endl;
   }
};

struct type {
   template<typename... Args>
   void method(const Args&... args) {
      invoker::func(ArgWrapper(typeid(Args).name(), &args)...); // <<<<<<<<<<<<<<<<<<<<<<<<<<
   }
};

int main() {
   type t;
   t.method(1);
   std::cout << "ping 1" << std::endl;
   t.method(1, 2.9);
   std::cout << "ping 2" << std::endl;
   t.method(.1, 2, .14159);
   std::cout << "ping 3" << std::endl;
}


http://liveworkspace.org/code/bcd126457d6b99620dddac6e8f4fe874
я не знал что при "развороте" вариадика, может так же развернуться все выражение в котором вариадик используется smile 
это многое упрощает и в моих прошлых задачах!

всем спасибо.

зы

mes, не могли бы Вы пояснить такой момент.
почему злополучную строку я не могу записать так?: invoker::func({typeid(Args).name(), &args}...);
вроде как это валидная запись.. но компилятор говорит:
Цитата

source.cpp:29:52: error: no matching function for call to 'invoker::func(<brace-enclosed initializer list>&)'
source.cpp:29:52: note: candidate is:
source.cpp:15:16: note: static void invoker::func(ArgWrapper, ArgWrapper, ArgWrapper)
source.cpp:15:16: note:   no known conversion for argument 1 from '<brace-enclosed initializer list>' to 'ArgWrapper'

Автор: mes 11.1.2012, 19:34
Цитата(boostcoder @  11.1.2012,  17:41 Найти цитируемый пост)
я не знал что при "развороте" вариадика, может так же развернуться все выражение в котором вариадик используется 

 smile 

Цитата(boostcoder @  11.1.2012,  17:41 Найти цитируемый пост)
 просто нужно больше времени уделять чтению доки.

поделитесь  плиз докой, где такое нашли 
 smile 

Цитата(boostcoder @  11.1.2012,  17:41 Найти цитируемый пост)
у я не могу записать так?:

не знаю, мож в той доке написано ?

Автор: boostcoder 11.1.2012, 21:19
mes, по правде говоря, мне подсказали это решение.
но сейчас, полазав по гуглу, я действительно нашел упоминание (вскользь) о такой возможности:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2080.pdf
глава "2.5 Is the Meta-Operator '...' Necessary?"

http://www.generic-programming.org/~dgregor/cpp/variadic-templates.html
в самом начале, в примере:
Код

template<typename... Mixins>
struct myclass : public Mixins... /*наследование от вариадик списка предков!*/ {
  myclass(const Mixins&... mixins)
    : Mixins(mixins)... { } // <<<<<<<<<<<<<<<
};


все же мало пока что доки по возможностям нового стандарта, и еще меньше людей, которые уже достаточно плотно используют С++11...

Добавлено через 9 минут и 42 секунды
еще тут большой кусок доки с примерами:
http://publib.boulder.ibm.com/infocenter/lnxpcomp/v111v131/index.jsp?topic=%2Fcom.ibm.xlcpp111.linux.doc%2Flanguage_ref%2Fvariadic_templates.html

Добавлено через 13 минут и 8 секунд
и здесь, в первом ответе используется такой же трюк:
http://stackoverflow.com/questions/7381805/c-c11-switch-statement-for-variadic-templates
Код

template<int... Indices>
struct indices {
    typedef indices<Indices..., sizeof...(Indices)> next;
};

template<int N>
struct build_indices {
    typedef typename build_indices<N - 1>::type::next type;
};

template<>
struct build_indices<0> {
    typedef indices<> type;
};

// No need to be variadic on the tuple elements as we don't care about them
// So I'm using perfect forwarding for the tuple
template<typename Tuple, int... Indices>
void
do_something_by_index(Tuple&& tuple, int index, indices<Indices...>)
{
    using std::get;

    typedef void (*element_type)(Tuple&&);
    static constexpr element_type table[] = {
        [](Tuple&& tuple)
        { get<Indices>(std::forward<Tuple>(tuple)).DoSomething(); }
        ... // <<<<<<<<<<<<<<<<<<<<<< а выглядит как незаконченная мысль :)
    };

    table[index](std::forward<Tuple>(tuple));
}

// Proverbial layer of indirection to get the indices
template<typename Tuple>
void
do_something_by_index(Tuple&& tuple, int index)
{
    typedef typename std::decay<Tuple>::type decay_type;
    constexpr auto tuple_size = std::tuple_size<decay_type>::value;
    typedef typename build_indices<tuple_size>::type indices_type;

    do_something_by_index(std::forward<Tuple>(tuple), index, indices_type{});
}

Автор: mes 12.1.2012, 14:48
boostcoder, спасибо, никогда раньше не приходилось натыкаться, классная возможность!

Автор: boostcoder 12.1.2012, 19:23
та же самая проблема. уже третий день. думал только у меня. ну....или...модеры наказали ;)

Автор: spyswamp 12.1.2012, 20:40
Оффтоп: +1. Репутацию даже смотреть сейчас нельзя.

Автор: boostcoder 13.1.2012, 08:04
карму починили.

Автор: boostcoder 13.1.2012, 09:52
гы. и на википедии появился этот пример smile
заговор какой-то, чесслово..
http://en.wikipedia.org/wiki/Variadic_templates

Добавлено @ 09:54
Цитата

The ... operator has two roles. When it occurs to the left of the name of a parameter, it declares a parameter pack. By using the parameter pack, user can bind zero or more arguments to the variadic template parameters. Parameter packs can also be used for non-type parameters. By contrast, when the ... operator occurs to the right of a template or function call argument, it unpacks the parameter packs into separate arguments, like the args... in the body of printf below. In practice, the use of ... operator in the code causes that the whole expression that precedes the ... operator, will be repeated for every next argument unpacked from the argument pack, and all these expressions will be separated by a comma.


да и вообще, всю страницу по С++11 сильно обновили: http://en.wikipedia.org/wiki/C%2B%2B11

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)