Как вытащить конкретный аргумент из variadic template
От: Аноним  
Дата: 27.08.13 13:35
Оценка:
Я правильно понимаю, что единственный способ обратиться к конкретному аргументу из variadic template (учитывая, что все они могут быть разных типов) — это сделать что-то наподобие такого

#include <iostream>
#include <tuple>

template <typename... Args>
void foo(Args... args)
{
    std::tuple<Args...> temp = std::make_tuple(args...);
    std::cout << std::get<0>(temp) << '\n';
}

int main()
{
    foo(0, 1.0f, "str");
}


или такого

#include <iostream>

template <typename A1, typename... Args>
A1 get_first(const A1& first_arg, Args... other_args)
{
    return first_arg;
}

template <typename A1, typename A2, typename... Args>
A2 get_second(const A1& first_arg, const A2& second_arg, Args... other_args)
{
    return second_arg;
}

template <typename A1, typename A2, typename A3, typename... Args>
A3 get_third(const A1& first_arg, const A2& second_arg, const A3& third_arg, Args... other_args)
{
    return third_arg;
}

// etc

template <typename... Args>
void some_function_receiving_var_args(Args... args)
{
    std::cout << get_first(args...) << '\n'
              << get_second(args...) << '\n'
              << get_third(args...) << '\n';
}

int main()
{
    some_function_receiving_var_args(0, "str", 1.5);
}


Или есть более интересные способы?
Re: Как вытащить конкретный аргумент из variadic template
От: Кодт Россия  
Дата: 27.08.13 13:57
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Или есть более интересные способы?


Не буду рыться в бусте, хотя наверняка там есть готовые решения.
Просто покажу
template<unsigned I, class A0, class... Args> struct getter : getter<I-1, Args...>
{
    typedef getter<I-1,Args...> Base;
    static typename Base::type get(A0 a0, Args... args) { return Base::get(args...); }
};
template<class A0, class... Args> struct getter<0,A0,Args...>
{
    typedef A0 type;
    static type get(A0 a0, Args... args) { return a0; }
};
 
template<unsigned I, class... Args>
typename getter<I,Args...>::type
get(Args... args)
{
  return getter<I,Args...>::get(args...);
}
 
int main()
{
    int         x = get<0>(1,"hello",'x');
    const char* y = get<1>(1,"hello",'x');
    char        z = get<2>(1,"hello",'x');
}
Перекуём баги на фичи!
Re[2]: Как вытащить конкретный аргумент из variadic template
От: FrozenHeart  
Дата: 27.08.13 14:01
Оценка:
Так ведь это то же самое, что и я написал, разве нет? Синтаксис тот же самый в итоге. Например, требуется в compile-time знать индекс аргумента, к которому обращаешься.
Re[3]: Как вытащить конкретный аргумент из variadic template
От: _NN_ www.nemerleweb.com
Дата: 27.08.13 14:14
Оценка:
Здравствуйте, FrozenHeart, Вы писали:

FH>Так ведь это то же самое, что и я написал, разве нет? Синтаксис тот же самый в итоге. Например, требуется в compile-time знать индекс аргумента, к которому обращаешься.


Так шаблоны они во время компиляции и работают.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Как вытащить конкретный аргумент из variadic template
От: Кодт Россия  
Дата: 27.08.13 14:32
Оценка:
Здравствуйте, FrozenHeart, Вы писали:

FH>Так ведь это то же самое, что и я написал, разве нет? Синтаксис тот же самый в итоге. Например, требуется в compile-time знать индекс аргумента, к которому обращаешься.


С той разницей, что
— нет нужды писать get_first, get_second, ..., get_nineth, ... или делать соответствующие специализации get<0>, get<1>, ..., get<9>, ... — достаточно единственной специализации getter<0> и арифметики
— нет нужды переупаковывать россыпь аргументов в кортеж, с тем, чтобы потом извлечь один

Разумеется, рекурсивная функция плоха тем, что если у N-го аргумента нетривиальное и тяжеловесное копирование, то может случиться беда N раз, тогда как со специализациями она не случится, а с кортежом — случится единожды.
Перекуём баги на фичи!
Re[4]: Как вытащить конкретный аргумент из variadic template
От: _NN_ www.nemerleweb.com
Дата: 27.08.13 14:36
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Разумеется, рекурсивная функция плоха тем, что если у N-го аргумента нетривиальное и тяжеловесное копирование,

А зачем копировать , perfect-forwarding на что ?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Как вытащить конкретный аргумент из variadic template
От: _smit Россия  
Дата: 27.08.13 14:58
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Я правильно понимаю, что единственный способ обратиться к конкретному аргументу из variadic template (учитывая, что все они могут быть разных типов) — это сделать что-то наподобие такого


А>
А>#include <iostream>
А>#include <tuple>

А>template <typename... Args>
А>void foo(Args... args)
А>{
А>    std::tuple<Args...> temp = std::make_tuple(args...);
А>    std::cout << std::get<0>(temp) << '\n';
А>}

А>int main()
А>{
А>    foo(0, 1.0f, "str");
А>}
А>


Да, правильно! тип возвращаемого значения через класс свойств, прмер в: tuple_element

Второй, приведенный тобой способ мне меньше нравится, т.к. "ручная работа".

А>Или есть более интересные способы?


Можно пробегаться автоматически по всем элементам кортежей, применять функции к элементам и т.п.: Fun with tuples.
Re: Как вытащить конкретный аргумент из variadic template
От: Кодт Россия  
Дата: 27.08.13 15:02
Оценка: 2 (1)
Здравствуйте, Аноним, Вы писали:

А>Или есть более интересные способы?


Существенно более интересный способ — это реализовать вариадики вручную, с помощью expression templates.
Очень грубо говоря,
// на вариадиках
foo(1, 2, 3, 4, 5);
// на префиксных ET
foo(cons(1, cons(2, cons(3, cons(4, cons(5, nil)))))); // правый список
foo(snoc(snoc(snoс(snoс(snoc(lin, 1), 2), 3), 4), 5)); // левый список
// на инфиксных ET
foo(start() << 1 << 2 << 3 << 4 << 5); // snoc-выражение, где в роли lin - start(), snoc - operator<<

Особенность ET в том, что
— аргументы можно хранить и доставать по ссылке
— список может быть перебалансирован в дерево
— доступны всякие плюшки вроде обхода списка каким-либо визитёром
template<class... Args> struct arglist; // на самом деле, можно даже без вариадиков обойтись - это я просто не вдаюсь в реализацию arglist
//...MAGIC...MAGIC...MAGIC...

struct F
{
  template<class A> void operator()(A const& a) {.....}
};
struct G
{
  template<class A> void operator()(int i, A const& a) {.....}
};

template<class... Args>
void foo(arglist<Args...> const& args)
{
  int constexpr n = args.size();
  for_each_arg(args, F());
  for_each_indexed_arg(args, G());
}

int main()
{
  foo(start());
  foo(start() << 1 << "hello" << 'x');
}
Перекуём баги на фичи!
Re[2]: Как вытащить конкретный аргумент из variadic template
От: _smit Россия  
Дата: 28.08.13 04:58
Оценка:
Здравствуйте, _smit, Вы писали:

_>Здравствуйте, Аноним, Вы писали:

А>>Я правильно понимаю, что единственный способ обратиться к конкретному аргументу из variadic template (учитывая, что все они могут быть разных типов) — это сделать что-то наподобие такого

...

_>Да, правильно! тип возвращаемого значения через класс свойств, прмер в: tuple_element

_>Второй, приведенный тобой способ мне меньше нравится, т.к. "ручная работа".
А>>Или есть более интересные способы?

Так,.. почитав внимательнее ответы от Кодт, понял, что речь о функциональном программировании, а я опять о классах [facepalm]... мой предыдущий ответ... создавать кортеж для доступа к аргументам функции -- тяжеловесно...
Смотрим код от Кодт...
Re[5]: Как вытащить конкретный аргумент из variadic template
От: Кодт Россия  
Дата: 28.08.13 08:59
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Здравствуйте, Кодт, Вы писали:


К>>Разумеется, рекурсивная функция плоха тем, что если у N-го аргумента нетривиальное и тяжеловесное копирование,

_NN>А зачем копировать , perfect-forwarding на что ?

Это как?

http://ideone.com/NNSX5W
#include <cstdio>
 
struct Heavy
{
    Heavy() { printf("h "); }
    Heavy(const Heavy&) { printf("H "); }
};
 
template<int N, class... Args> struct getter;
template<class A, class... Args> struct getter<0,A,Args...>
{
    typedef A type;
    static type get(A a, Args...) { printf("0 "); return a; }
};
template<int N, class A, class... Args> struct getter<N,A,Args...>
{
    typedef getter<N-1,Args...> base;
    typedef typename base::type type;
    static type get(A a, Args... args) { printf("%d ", N); return base::get(args...); }
};
 
template<int N, class... Args>
typename getter<N,Args...>::type
get(Args... args) { printf("go "); return getter<N,Args...>::get(args...); }
 
int main()
{
    get<4>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
    get<5>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
    get<9>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
}

вывод:
h go H 4 H 3 H 2 H 1 H 0 
h go H 5 H 4 H 3 H 2 H 1 H 0 H 
h go H 9 H 8 H 7 H 6 H 5 H 4 3 2 1 0

Вот какое бешеное количество копирований.

Что сюда нужно прикрутить, чтобы perfect-forwarding заработало из коробки? Обернуть в cref(Heavy()) не получается, там заблокировано создание reference_wrapper из rvalue reference.
Своё писать, рукодельное?
Перекуём баги на фичи!
Re[6]: Как вытащить конкретный аргумент из variadic template
От: _NN_ www.nemerleweb.com
Дата: 28.08.13 09:28
Оценка: 69 (2)
Здравствуйте, Кодт, Вы писали:

К>Это как?

Для начала добавляем && и std::forward:

http://ideone.com/Onx2la

#include <cstdio>
#include <utility>
 
using namespace std;
 
struct Heavy
{
    Heavy() { printf("h "); }
    Heavy(const Heavy&) { printf("H "); }
};
 
template<int N, class... Args> struct getter;
template<class A, class... Args> struct getter<0,A,Args...>
{
    typedef A type;
    static type get(A a, Args&&...) { printf("0 "); return a; }
};
template<int N, class A, class... Args> struct getter<N,A,Args...>
{
    typedef getter<N-1,Args...> base;
    typedef typename base::type type;
    static type get(A a, Args&&... args)
    {
        printf("%d ", N);
        return base::get(forward<Args>(args)...);
    }
};
 
template<int N, class... Args>
typename getter<N,Args...>::type
get(Args&&... args)
{
    printf("go ");
    return getter<N,Args...>::get(forward<Args>(args)...);
}
 
int main()
{
    get<4>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
    get<5>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
    get<9>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
}


Получаем:
h go 4 3 2 1 0 
h go 5 4 3 2 1 H 0 H 
h go 9 8 7 6 5 H 4 3 2 1 0


Вместо прежнего

К>вывод:

К>
К>h go H 4 H 3 H 2 H 1 H 0 
К>h go H 5 H 4 H 3 H 2 H 1 H 0 H 
К>h go H 9 H 8 H 7 H 6 H 5 H 4 3 2 1 0 
К>

К>Вот какое бешеное количество копирований.

Копирования остаются из-за этого:
typedef A type;
static type get(A a, Args&&...) { printf("0 "); return a; }

static type get(A a, Args&&... args)

typename getter<N,Args...>::type
get(Args&&... args)


Тут принимаем по значению и возвращаем.

Если заменить значения на ссылки, то получим 0 копий !
type -> type const&

http://ideone.com/QWKJgG

h go 4 3 2 1 0 
h go 5 4 3 2 1 0 
h go 9 8 7 6 5 4 3 2 1 0


Тут конечно нужно приделать нормальную поддержку не константных значений, но это так мелочи
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[7]: Как вытащить конкретный аргумент из variadic template
От: _smit Россия  
Дата: 28.08.13 10:25
Оценка:
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Кодт, Вы писали:
К>>Это как?
_NN>Для начала добавляем && и std::forward:
_NN>http://ideone.com/Onx2la
_NN>Вместо прежнего
...
К>>вывод:
К>>
К>>h go H 4 H 3 H 2 H 1 H 0 
К>>h go H 5 H 4 H 3 H 2 H 1 H 0 H 
К>>h go H 9 H 8 H 7 H 6 H 5 H 4 3 2 1 0 
К>>

К>>Вот какое бешеное количество копирований.
...
_NN>Тут принимаем по значению и возвращаем.
_NN>Если заменить значения на ссылки, то получим 0 копий !
_NN>type -> type const&

_NN>http://ideone.com/QWKJgG


_NN>
_NN>h go 4 3 2 1 0 
_NN>h go 5 4 3 2 1 0 
_NN>h go 9 8 7 6 5 4 3 2 1 0 
_NN>


С применением функции std::forward полностью согласен, но с возвратом и передачей параметров по ссылке не всё так однозначно, как кажется на первый взгляд. Предлагаю взглянуть на статью Dave Abrahams <b>Want Speed? Pass by Value.</b>
Re[8]: Как вытащить конкретный аргумент из variadic template
От: _NN_ www.nemerleweb.com
Дата: 28.08.13 10:57
Оценка: +1
Здравствуйте, _smit, Вы писали:


_>С применением функции std::forward полностью согласен, но с возвратом и передачей параметров по ссылке не всё так однозначно, как кажется на первый взгляд. Предлагаю взглянуть на статью Dave Abrahams <b>Want Speed? Pass by Value.</b>


Я в курсе про эту статью.
Нужно немного поиграться с кодом, чтобы добиться отсутствия копий, с ссылками это было проще всего
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[8]: Как вытащить конкретный аргумент из variadic template
От: Roman Odaisky Украина  
Дата: 28.08.13 18:42
Оценка:
Здравствуйте, _smit, Вы писали:

_><b>Want Speed? Pass by Value.</b>


А причем тут скорость, когда речь о подстановках в compile time? Ведь get<N>(args...) просто раскроется в выбор одного из аргументов с нулевым оверхедом в RT.
До последнего не верил в пирамиду Лебедева.
Re[9]: Как вытащить конкретный аргумент из variadic template
От: _smit Россия  
Дата: 28.08.13 20:24
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:
RO>Здравствуйте, _smit, Вы писали:
_>><b>Want Speed? Pass by Value.</b>
RO>А причем тут скорость, когда речь о подстановках в compile time? Ведь get<N>(args...) просто раскроется в выбор одного из аргументов с нулевым оверхедом в RT.

Откуда тогда у _NN_ в логах копирование? Судя по тому, как представлено определение get<N>(args...), там, вроде, не аргумент подставляются, а из рекурсии конечные функции подставляются, которые возвращают нужный аргумент. Т.е. рекурсия в compiled-time, а в RT производится возврат значения из рассчитанной функции. Поэтому я и отметил, что обозначение типа возвращаемого значения в виде ссылки может поставить крест на RVO (return value optimization). (но я бы тоже ссылки поставил... )
Re[10]: Как вытащить конкретный аргумент из variadic template
От: Roman Odaisky Украина  
Дата: 28.08.13 20:45
Оценка: +1
Здравствуйте, _smit, Вы писали:

RO>>Здравствуйте, _smit, Вы писали:

_>>><b>Want Speed? Pass by Value.</b>
RO>>А причем тут скорость, когда речь о подстановках в compile time? Ведь get<N>(args...) просто раскроется в выбор одного из аргументов с нулевым оверхедом в RT.

_>Откуда тогда у _NN_ в логах копирование? Судя по тому, как представлено определение get<N>(args...), там, вроде, не аргумент подставляются, а из рекурсии конечные функции подставляются, которые возвращают нужный аргумент. Т.е. рекурсия в compiled-time, а в RT производится возврат значения из рассчитанной функции. Поэтому я и отметил, что обозначение типа возвращаемого значения в виде ссылки может поставить крест на RVO (return value optimization). (но я бы тоже ссылки поставил... :) )


Причем тут RVO? Здесь же что-нибудь вроде

template<class... Args>
void do_something(Args... args) // или &, или &&, это уже другой вопрос
{
    . . .
    do_something_else(get<1>(args...));
    . . .
}

do_something(a, b, 42);

и воплотиться это должно в
void do_something(A a, B b, int c)
{
    . . .
    do_something_else(b); // perfect forwarding же
    . . .
}

Функция get<> инлайнится полностью, нет и речи о том, чтобы копировать что-либо у нее на входе или на выходе.
До последнего не верил в пирамиду Лебедева.
Re[7]: Как вытащить конкретный аргумент из variadic template
От: MT-Wizard Украина  
Дата: 28.08.13 21:09
Оценка: 1 (1)
Здравствуйте, _NN_, Вы писали:

_NN>Здравствуйте, Кодт, Вы писали:


К>>Это как?

_NN>Для начала добавляем && и std::forward:

А почему не добавить их везде:

http://ideone.com/YYh6w9
#include <cstdio>
#include <utility>

using namespace std;

struct Heavy
{
    Heavy() { printf("h "); }
    Heavy(const Heavy&) { printf("H "); }
};

template<int N, class... Args> struct getter;
template<class A, class... Args> struct getter<0,A,Args...>
{
    typedef A type;
    static type&& get(A&& a, Args&&...) { printf("0 "); return std::forward<A>(a); }
};
template<int N, class A, class... Args> struct getter<N,A,Args...>
{
    typedef getter<N-1,Args...> base;
    typedef typename base::type type;
    static type&& get(A&& a, Args&&... args)
    {
        printf("%d ", N);
        return std::forward<type>(base::get(forward<Args>(args)...));
    }
};

template<int N, class... Args>
typename getter<N,Args...>::type&&
get(Args&&... args)
{
    printf("go ");
    return std::forward<typename getter<N,Args...>::type>(getter<N,Args...>::get(forward<Args>(args)...));
}

int main()
{
    get<4>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
    get<5>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
    get<9>(0,1,2,3,4, Heavy(), 6,7,8,9);
    printf("\n");
}


Получается идеально:

output:
h go 4 3 2 1 0 
h go 5 4 3 2 1 0 
h go 9 8 7 6 5 4 3 2 1 0
А ти, москалику, вже приїхав (с)
Re[8]: Как вытащить конкретный аргумент из variadic template
От: _NN_ www.nemerleweb.com
Дата: 29.08.13 07:39
Оценка:
Здравствуйте, MT-Wizard, Вы писали:

MW>А почему не добавить их везде:


MW>http://ideone.com/YYh6w9


Да как-то с первого раза не вышло, так я и забил
http://rsdn.nemerleweb.com
http://nemerleweb.com
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.