Search code examples
c++c++11visual-c++-2012stdtuple

How do I recurse tuples?


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.


Solution

  • 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