Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 957
Regis Portalez Avatar asked May 21 '19 09:05

Regis Portalez


3 Answers

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.

like image 109
HolyBlackCat Avatar answered Oct 16 '22 11:10

HolyBlackCat


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

like image 3
Daniel Langr Avatar answered Oct 16 '22 11:10

Daniel Langr


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,
                  "!" );
 }
like image 3
max66 Avatar answered Oct 16 '22 11:10

max66