add two variadic template list of integers
Given this type
template<std::size_t N, int ...content> struct list { inline int reduce() { int result = 0; constexpr int arr[N] = { content... }; for(std::size_t k = 0; k < N; ++k) { result += arr[k]; } return result; } };
I'd like to implement a function add
, which returns a new list containing the element-by-element addition of the two input lists.
In other words (pseudo-code):
add([a0, a1, a2], [b0, b1]) -> [a0 + b0, a1 + b2, a2]
Problem:
- I don't even know how to declare the return type of such a function
- and i don't know if it's possible
Here is how I would do it:
#include <iostream> #include <utility> template<std::size_t N, int ...content> struct list { inline int reduce() { int result = 0; constexpr int arr[N] = { content... }; for(std::size_t k = 0; k < N; ++k) { result += arr[k]; } return result; } }; template <std::size_t I, int ...A> constexpr int list_at(list<sizeof...(A),A...>) { if constexpr (I < sizeof...(A)) { constexpr int arr[] {A...}; return arr[I]; } else { return 0; } } template <int ...A, int ...B, std::size_t ...I> constexpr auto list_sum_low(list<sizeof...(A),A...>, list<sizeof...(B),B...>, std::index_sequence<I...>) { return list<sizeof...(I), (list_at<I>(list<sizeof...(A),A...>{}) + list_at<I>(list<sizeof...(B),B...>{}))...>{}; } template <int ...A, int ...B> constexpr auto list_sum(list<sizeof...(A),A...>, list<sizeof...(B),B...>) { constexpr int a = sizeof...(A), b = sizeof...(B); return list_sum_low(list<a,A...>{}, list<b,B...>{}, std::make_index_sequence<(a > b ? a : b)>{}); } template <int ...A> void print_list(list<sizeof...(A),A...>) { (void(std::cout << ' ' << A) , ...); } int main() { constexpr auto x = list_sum(list<4, 1,2,3,4>{}, list<2, 10,20>{}); print_list(x); }
Also, note that there is no need have size_t N
template parameter for class list
. Parameter packs know their own size.
Parameter pack(since C++11), A variadic class template can be instantiated with any number of int Tuple<int, float> t2; // Types contains two arguments: int and float� A variadic template is a class or function template that supports an arbitrary number of arguments. This mechanism is especially useful to C++ library developers because you can apply it to both class templates and function templates, and thereby provide a wide range of type-safe and non-trivial functionality and flexibility.
Just another solution, based on good old partial specialization:
template <size_t N, int... E> struct list { }; template <typename, typename> struct list_cat; template <size_t N1, int... E1, size_t N2, int... E2> struct list_cat<list<N1, E1...>, list<N2, E2...>> { using type = list<N1 + N2, E1..., E2...>; }; template <typename, typename> struct list_add; template <size_t N1, int E1H, int... E1T, size_t N2, int E2H, int... E2T> struct list_add<list<N1, E1H, E1T...>, list<N2, E2H, E2T...>> { using type = typename list_cat< list<1, E1H + E2H>, typename list_add<list<N1 - 1, E1T...>, list<N2 - 1, E2T...>>::type >::type; }; template <size_t N2, int... E2> struct list_add<list<0>, list<N2, E2...>> { using type = list<N2, E2...>; }; template <size_t N1, int... E1> struct list_add<list<N1, E1...>, list<0>> { using type = list<N1, E1...>; }; template <> struct list_add<list<0>, list<0>> { using type = list<0>; }
Which can be used as:
using L1 = list<3, -1, -2, -3>; using L2 = list <2, 10, 20>; using L = typename list_add<L1, L2>::type;
Live demo: https://wandbox.org/permlink/x8LYcoC3lWu51Gqo
[PDF] Variadic Templates, 1. 2 A Variadic Templates Primer. 2. 2.1 Variadic Class Templates . typedef tuple<char, short, int, long, long long> integral types; to count, then pack the rest of the arguments into a template parameter pack to be counted in the final sum:. Variadic Template is introduced in C++11. Variadic Template allows a function to take variable number of arguments of any type. Let’s understand by an example. Suppose we want to create a function log() that accepts variable number of arguments of any type and prints them on console i.e.
Another solution std::integer_sequence
based.
When the dimension of the two lists is the same, an add()
function is trivially simple
template <std::size_t N, int ... Is1, int ... Is2> constexpr auto add (list<N, Is1...>, list<N, Is2...>) { return list<N, Is1+Is2...>{}; }
The problem is when we have lists of different length.
A possible solution is extend the shorter list with zeros and apply the preceding function to the length-uniformed lists.
Given a extender as follows
template <std::size_t N1, std::size_t N0, int ... Is, std::size_t ... Js> constexpr auto listExtend (list<N0, Is...>, std::index_sequence<Js...>) { return list<N1, Is..., ((void)Js, 0)...>{}; } template <std::size_t N1, std::size_t N0, int ... Is, std::enable_if_t<(N1 > N0), bool> = true> constexpr auto listExtend (list<N0, Is...> l) { return listExtend<N1>(l, std::make_index_sequence<N1-N0>{}); }
we need only to add the following add()
functions
template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2, std::enable_if_t<(N1 > N2), bool> = true> constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2) { return add(l1, listExtend<N1>(l2)); } template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2, std::enable_if_t<(N1 < N2), bool> = true> constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2) { return add(listExtend<N2>(l1), l2); }
The following is a full compiling C++14 (unfortunately std::make_index_sequence
/std::index_sequence
require C++14) example
#include <utility> #include <type_traits> template <std::size_t, int ...> struct list { }; template <std::size_t N1, std::size_t N0, int ... Is, std::size_t ... Js> constexpr auto listExtend (list<N0, Is...>, std::index_sequence<Js...>) { return list<N1, Is..., ((void)Js, 0)...>{}; } template <std::size_t N1, std::size_t N0, int ... Is, std::enable_if_t<(N1 > N0), bool> = true> constexpr auto listExtend (list<N0, Is...> l) { return listExtend<N1>(l, std::make_index_sequence<N1-N0>{}); } template <std::size_t N, int ... Is1, int ... Is2> constexpr auto add (list<N, Is1...>, list<N, Is2...>) { return list<N, Is1+Is2...>{}; } template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2, std::enable_if_t<(N1 > N2), bool> = true> constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2) { return add(l1, listExtend<N1>(l2)); } template <std::size_t N1, int ... Is1, std::size_t N2, int ... Is2, std::enable_if_t<(N1 < N2), bool> = true> constexpr auto add (list<N1, Is1...> l1, list<N2, Is2...> l2) { return add(listExtend<N2>(l1), l2); } int main () { list<3u, 1, 2, 3> l1; list<2u, 10, 20> l2; auto l3 = add(l1, l2); static_assert( std::is_same<decltype(l3), list<3u, 11, 22, 3>>::value, "!" ); }
Variadic template, In computer programming, variadic templates are templates that take a variable number of tuple<int, std::vector<int>, std::map<std::string, std::vector<int>>> type-safe add-on to variadic functions (such as printf), but also allowing a function (If both had the same list of initial parameters, the call would be ambiguous — a� Variadic templates are a C++ feature which looks quite magic the first time you see it. Thanks to C++ Insights, most of the magic disappears. Variadic templates are one of the powerful new constructs we have since C++11. Variadic Templates. They are great because we can have a function that takes multiple arguments and still is strongly typed.
I will use std::integer_sequence
:
template <int ... Is> using int_sequence = std::integer_sequence<int, Is...>;
When size match, it would be easy, so create method to increase size and fill with zero:
template <int... Is, int... Zeros> int_sequence<Is..., (0 * Zeros)...> fill_with_zero(int_sequence<Is...>, int_sequence<Zeros...>) { return {}; } template <std::size_t N, int... Is> auto fill_with_zero_to_reach(int_sequence<Is...> seq) -> decltype(fill_with_zero(seq, std::make_integer_sequence<int, (sizeof...(Is) < N ? N - sizeof...(Is) : 0)>{})) { return {}; }
Then the addition:
// simple case, sizes match: template <int... Is1, int... Is2, std::enable_if_t<sizeof...(Is1) == sizeof...(Is2), int> = 0> int_sequence<(Is1 + Is2)...> add(int_sequence<Is1...>, int_sequence<Is2...>) { return {}; } // sizes mismatch: template <int... Is1, int... Is2, std::enable_if_t<sizeof...(Is1) != sizeof...(Is2), int> = 0> auto add(int_sequence<Is1...> seq1, int_sequence<Is2...> seq2) -> decltype(add(fill_with_zero_to_reach<std::max(sizeof...(Is1), sizeof...(Is2))>(seq1), fill_with_zero_to_reach<std::max(sizeof...(Is1), sizeof...(Is2))>(seq2))) { return {}; }
Using Variadic Templates cleanly, When one comes across examples for variadic templates, almost always recursion is the tail of the arguments to a list of parameters for the function (see t above). The next problem we will look at, is to calculate the sum of the sums of the const Ts& args) { (void) std::initializer_list<int>{print_to_stream(stream, args). In C programming, variadic function will contribute to the flexibility of the program that you are developing. To understand this flexibility, let us start with a basic example. If we like to add two numbers, we might write a code like this: int addNumbers (int nNumberOne, int nNumberTwo) { return nNumberOne + nNumberTwo; }.
[PDF] Variadic templates, void fi(initializer_list<int>); fi(1);. // error fi({1});. // ok fi({1,2});. // ok fi({{1},{2}});. // ok ? Variadic templates and initializer lists. • List min(): template< typename T, class Compare> const T& add allocators to constructors and assignments. Variadic templates. One of the new features of C++11 is variadic templates.Finally, there's a way to write functions that take an arbitrary number of arguments in a type-safe way and have all the argument handling logic resolved at compile-time, rather than run-time.
#include <tr1/tuple> /** * Object Function Tuple Argument Unpacking * * This recursive template unpacks the tuple parameters into * variadic template arguments until we reach the count of 0 where the function * is called with the correct parameters * * @tparam N Number of tuple arguments to unroll * * @ingroup g_util_tuple */ template < uint N
A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A function parameter pack is a function parameter that accepts zero or more function arguments. A template with at least one parameter pack is called a variadic template.
Comments
- I don't get why it returns an int and not a list<N, int...>
- Sorry, had a brain fart. Will write an answer in a moment.
- ot: you dont need the
inline
. actually I am a bit surprised that it is even allowed in that place - How is the
reduce
function related to your problem? reduce
is a red herring; it's a completely different operation. Do you want your result to be a type or a value? You said that it should produce "a new list", but then it's not really a function, if it's supposed to put everything in a type. A value of typelist<...>
is as good as the type itself.- Or we could use the array initialization rules to do the padding. I don't think it gets much simpler than this :-).
- This is much nicer than the
integer_sequence
IMHO. I started writing one that was very similar, but had to go out for a while and obviously someone snatched the idea. Good job! :) - @BartekBanachewicz That happens to me all the time. People tend to be freakishly quick here on SO ;-)