Search code examples
c++c++17template-meta-programmingstdtuple

sophisticated summation of elements in objects residing in a std:tuple


Consider a tuple t of n objects with similar interface (like all have the same base class); n is known during compilation. I need to make a sum as following

std::get<0>(t).foo(vec_0) + std::get<1>(t).foo(vec_1) + ... + std::get<n>(t).foo(vec_n)

I know to add elements of a tuple of numbers, like here, and even variable\functions of an objects in a tuple, e.g.

std::get<0>(t).bar()+std::get<1>(t).bar()...

The situation that I still interesting to find a (template) solution for is the following one:

  1. all vec_0,...,vec_n are different size slices of a bigger vector Vec.
  2. the information about size of each is known from the tuple as well so actually what I need looks as the following
std::get<0>(t).foo(Vec.head(std::get<0>(t).n)) + 
std::get<1>(t).foo(Vec.segment(std::get<0>(t).n, std::get<1>(t).n))+
std::get<2>(t).foo(Vec.segment(std::get<0>(t).n + std::get<1>(t).n, std::get<2>(t).n))+
... +
std::get<n>(t).foo(Vec.tail(std::get<n>(t).n))

Here segment takes (start, size) and return required vector; the size is known from std::get<?>(t).n.


Solution

  • You just need extra std::index_sequence usage for the inner elements, something like:

    template <std::size_t ... Is, typename Tuple, typename Vec>
    auto helper(std::index_sequence<Is...>, const Tuple& t, const Vec& vec)
    {
        constexpr auto N = sizeof...(Is);
        constexpr auto Size = std::tuple_size<Tuple>::value;
    
        if constexpr (Size == 1) {
            // Unsure what you want here
            return std::get<0>(t).foo(Vec.head(std::get<0>(t).n))
                 + std::get<N>(t).foo(Vec.tail(std::get<0>(t).n));
        } else if constexpr (N == 0) {
            return std::get<0>(t).foo(Vec.head(std::get<0>(t).n)); 
        } else if constexpr (N + 1 == Size) {
            return std::get<N>(t).foo(Vec.tail(std::get<N>(t).n));
        } else {
            return std::get<N>(t).foo(Vec.segment((0 + ... + std::get<Is>(t).n),
                                                  std::get<N>(t).n));
        }
    }
    
    template <std::size_t ... Is, typename Tuple, typename Vec>
    auto summation(std::index_sequence<Is...>, const Tuple& t, const Vec& vec)
    {
        return (0 + ... + helper(std::make_index_sequence<Is>(), t, vec));
    }
    
    template <typename Tuple, typename Vec>
    auto summation(const Tuple& t, const Vec& vec)
    {
        constexpr auto Size = std::tuple_size<Tuple>::value;
        return summation(std::make_index_sequence<Size>(), t, vec));
    }