4

I'm attempting to make a variadic template container that stores a tuple of vectors of elements. The point of this container is the elements across all vectors are all related, and I want to maintain that correlation for later, but it isn't necessary for computation. Imagine, if you will, a vector_3 and a ref_id of some type.

The container will only uniformly mutate the vectors together. So the parts I do understand would look like this:

template<typename ...Elems>
class container
{
    std::tuple<std::vector<Elems>...> data_;

public:
    template<typename I>
    const typename std::tuple_element<I, data_type>::type &nth_index() const
    { return std::get<I>(data_); }
};

I'm struggling with an insert method. I was thinking something along the lines of:

void push_back(std::tuple<Elems...> &values)
{
    std::tuple<std::back_insert_iterator<std::vector<Elems>>...> inserters;
}

But I have no idea how to initialize this "inserters" tuple. I've been looking at various recursive template examples here on stackoverflow and I can't keep it all in my head long enough to comprehend it.

I was presuming if I had such a tuple, I could use simple assignment:

inserters = values;

I'd also like to write an accessor across all the arrays that returns a tuple of values:

std::tuple<Elems &...> operator[](const size_t index)
{
     ...
}

But once again, I don't know how to initialize this tuple.

I can't be the only one who ever wanted to do this and I can't find a good resource to learn it. In the meantime, I'm trying to digest the original variadic template proposal for 0x. Insight would be appreciated. I'm limited by the MSVC 2012 implementation.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
Matthew Reddington
  • 1,409
  • 3
  • 13
  • 24

2 Answers2

3
#include <vector>
#include <tuple>
#include <cstddef>
#include <utility>

template <typename... Elems>
class container
{
    using data_type = std::tuple<std::vector<Elems>...>;

    data_type data_;

public:    
    template <std::size_t I>
    const typename std::tuple_element<I, data_type>::type& nth_index() const
    { return std::get<I>(data_); }

    void push_back(const std::tuple<Elems...>& values)
    {
        return push_back(std::make_index_sequence<sizeof...(Elems)>{}, values);
    }    

    std::tuple<Elems&...> operator[](std::size_t index)
    {
        return get_elems(std::make_index_sequence<sizeof...(Elems)>{}, index);
    }

private:
    template <std::size_t... Is>
    void push_back(std::index_sequence<Is...>, const std::tuple<Elems...>& values)
    {
        using expand = int[];
        static_cast<void>(expand{ 0, (std::get<Is>(data_).push_back(std::get<Is>(values)), 0)... });
    }

    template <std::size_t... Is>
    std::tuple<Elems&...> get_elems(std::index_sequence<Is...>, std::size_t index)
    {
        return std::forward_as_tuple(std::get<Is>(data_)[index]...);
    }
};

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • MSVC 2012 doesn't implement std::integer_sequence (or constexpr), so I snatched it from N3658 with little modification. – Matthew Reddington Feb 16 '16 at 22:33
  • Further, your expand trick blew my mind, but it doesn't work on my compiler. Any other ideas? – Matthew Reddington Feb 16 '16 at 22:34
  • @MatthewReddington try `int expand[] = { 0, (std::get(data_).push_back(std::get(values)), 0)... }; static_cast(expand);` instead – Piotr Skotnicki Feb 17 '16 at 08:27
  • Still no good, I blame my compiler version; I know the next addresses a lot of short comings, we just haven't upgraded our projects yet. I'll keep this snippet around and try it when that happens. Thanks, though. – Matthew Reddington Feb 17 '16 at 18:15
1

A C++11 solution with SFINAE and type traits:

template<typename ...Elems>
class container {
  std::tuple<std::vector<Elems>...> data_;

  template<std::size_t N>
  typename std::enable_if<(N <std::tuple_size<decltype(data_)>::value), int>::type
  push_back_impl(std::tuple<Elems...> const &values) {
    std::get<N>(data_).push_back(std::get<N>(values));
    return push_back_impl<N + 1>(values);
  }

  template<std::size_t N>
  typename std::enable_if<(N == std::tuple_size<decltype(data_)>::value), int>::type
  push_back_impl(std::tuple<Elems...> const &values) {
    return 0;
  }

public:

  void push_back(std::tuple<Elems...> const &values) {
     push_back_impl<0>(values);
  }

};

Live Demo

as for the subscript operator you'll need some extra machinery found in this SO answer:

template <size_t ...I>
struct index_sequence {};

template <size_t N, size_t ...I>
struct make_index_sequence : public make_index_sequence<N - 1, N - 1, I...> {};

template <size_t ...I>
struct make_index_sequence<0, I...> : public index_sequence<I...> {};

template<typename ...Elems>
class container {
  std::tuple<std::vector<Elems>...> data_;

  template<size_t ...I>
  std::tuple<Elems&...> access_impl(std::size_t const idx, index_sequence<I...>) {
    return std::tie(std::get<I>(data_)[idx]...);
  }

public:

  std::tuple<Elems&...> operator[](std::size_t const idx) {
    return access_impl(idx, make_index_sequence<sizeof...(Elems)>());
  }
};

Live Demo

Community
  • 1
  • 1
101010
  • 41,839
  • 11
  • 94
  • 168
  • Black magic, my friend. I'll be studying this for a while trying to understand all of what's going on. This stuff is not intuitive to me. Do you have a resource to learn generic and modern template programming? – Matthew Reddington Feb 16 '16 at 23:15
  • @MatthewReddington A good start is Bjarne's [The C++ Programming Language 4th edition](http://www.amazon.com/C-Programming-Language-4th/dp/0321563840/ref=sr_1_1?ie=UTF8&qid=1455664887&sr=8-1&keywords=C%2B%2B). But most stuff you'll learn from SO and [cppreference](http://en.cppreference.com/w/) and by experimenting. – 101010 Feb 16 '16 at 23:21