How can I use C++11 variadic templates to define a vector-of-tuples backed by a tuple-of-vectors?

c++, c++11, templates, variadic-templates, vector

Solution

One idea is to keep the storage in the "struct of array" style in form of vectors for good performance if only a subset of the fields are used for a particular task. Then, for each kind of task requiring a different set of fields, you can write a lightweight wrapper around some of those vectors, giving you a nice random access iterator interface similar to what `std::vector` supports.

Concerning the syntax of variadic templates, this is how a wrapper class (without any iterators yet) could look like:

template<class ...Ts> // Element types
class WrapMultiVector
{
    // references to vectors in a TUPLE
    std::tuple<std::vector<Ts>&...> m_vectors;

public:
    // references to vectors in multiple arguments
    WrapMultiVector(std::vector<Ts> & ...vectors)
        : m_vectors(vectors...)    // construct tuple from multiple args.
    {}
};

To construct such a templated class, it's often preferred to have a template type deducting helper function available (similar to those `make_{pair|tuple|...}` functions in `std`):

template<class ...Ts> // Element types
WrapMultiVector<Ts...> makeWrapper(std::vector<Ts> & ...vectors) {
    return WrapMultiVector<Ts...>(vectors...);
}

You already see different types of "unpacking" the type list.

Adding iterators suitable to your application (you requested in particular random access iterators) is not so easy. A start could be forward only iterators, which you might extend to random access iterators.

The following iterator class is capable of being constructed using a tuple of element iterators, being incremented and being dereferenced to obtain a tuple of element references (important for read-write access).

class iterator {
    std::tuple<typename std::vector<Ts>::iterator...> m_elemIterators;

public:
    iterator(std::tuple<typename std::vector<Ts>::iterator...> elemIterators) 
        : m_elemIterators(elemIterators)
    {}

    bool operator==(const iterator &o) const {
        return std::get<0>(m_elemIterators) == std::get<0>(o.m_elemIterators);
    }
    bool operator!=(const iterator &o) const {
        return std::get<0>(m_elemIterators) != std::get<0>(o.m_elemIterators);
    }

    iterator& operator ++() {
        tupleIncrement(m_elemIterators);
        return *this;
    }
    iterator operator ++(int) {
        iterator old = *this;
        tupleIncrement(m_elemIterators);
        return old;
    }

    std::tuple<Ts&...> operator*() {
        return getElements(IndexList());
    }

private:
    template<size_t ...Is>
    std::tuple<Ts&...> getElements(index_list<Is...>) {
        return std::tie(*std::get<Is>(m_elemIterators)...);
    }
};

For demonstration purposes, two different patterns are in this code which "iterate" over a tuple in order to apply some operation or construct a new tuple with some epxression to be called per element. I used both in order to demonstrate alternatives; you can also use the second method only.

`tupleIncrement`: You can use a helper function which uses meta programming to index a single entry and advance the index by one, then calling a recursive function, until the index is at the end of the tuple (then there is a special case implementation which is triggered using SFINAE). The function is defined outside of the class and not above; here is its code:

template<std::size_t I = 0, typename ...Ts>
inline typename std::enable_if<I == sizeof...(Ts), void>::type
tupleIncrement(std::tuple<Ts...> &tup)
{ }
template<std::size_t I = 0, typename ...Ts>
inline typename std::enable_if<I < sizeof...(Ts), void>::type
tupleIncrement(std::tuple<Ts...> &tup)
{
    ++std::get<I>(tup); 
    tupleIncrement<I + 1, Ts...>(tup);
}

This method can't be used to assign a tuple of references in the case of `operator*` because such a tuple has to be initialized with references immediately, which is not possible with this method. So we need something else for `operator*`:

`getElements`: This version uses an index list (https://stackoverflow.com/a/15036110/592323) which gets expanded too and then you can use `std::get` with the index list to expand full expressions. The `IndexList` when calling the function instantiates an appropriate index list which is only required for template type deduction in order to get those `Is...`. The type can be defined in the wrapper class:

// list of indices
typedef decltype(index_range<0, sizeof...(Ts)>()) IndexList;

More complete code with a little example can be found here: http://ideone.com/O3CPTq

Open problems are:

If the vectors have different sizes, the code fails. Better would be to check all "end" iterators for equality; if one iterator is "at end", we're also "at end"; but this would require some logic more than `operator==` and `operator!=` unless it's ok to "fake" it in; meaning that `operator!=` could return false as soon as any operator is unequal.

The solution is not const-correct, e.g. there is no `const_iterator`.

Appending, inserting etc. is not possible. The wrapper class could add some `insert` or and / or `push_back` function in order to make it work similar to `std::vector`. If your goal is that it's syntactically compatible to a vector of tuples, reimplement all those relevant functions from `std::vector`.

Not enough tests ;)

Problem

Suppose I have a bunch of vectors: ``` vector<int> v1; vector<double> v2; vector<int> v3; ``` all of the same length. Now, for every index i, I would like to be able to treat (v1[i], v2[i], v3[i]) as a tuple, and maybe pass it around. In fact, I want to have a a vector-of-tuples rather than a tuple-of-vectors, using which I can do the above. (In C terms, I might say an array-of-structs rather than a struct-of-arrays). I do not want to effect any data reordering (think: really long vectors), i.e. the new vector is backed by the individual vectors I pass in. Let's . Now, I want the class I write (call it `ToVBackedVoT` for lack of a better name) to support any arbitrary choice of vectors to back it (not just 3, not int, double and int, not every just scalars). I want the vector-of-tuples to be mutable, and for no copies to be made on construction/assignments. If I understand correctly, variadic templates and the new `std::tuple` type in C++11 are the means for doing this (assuming I don't want untyped `void*` arrays and such). However, I only barely know them and have never worked with them. Can you help me sketch out how such a class will look like? Or how, given ``` template <typename ... Ts> ``` I can express something like "the list of template arguments being the replacement of each typename in the original template arguments with a vector of elements of this type"? Note: I think I might also want to later be able to adjoin additional vectors to the backing vectors, making an instance of `ToVBackedVoT<int, double, int>` into, say, an instance of `ToVBackedVoT<int, double, int, unsigned int>`. So, bear that in mind when answering. This is not critically important though.

Original source

Related problems