mercredi 30 novembre 2016

How to implement a subset iterator for a class with N number of vectors in it

If I had a class that holds N number of equally sized vectors. How would I go about implementing a standard iterator template that would iterate between 1 and N number of the vectors together. I wrote a small example demonstrating the problem.

#include <bitset>
#include <tuple>
#include <type_traits>
#include <vector>

//Since std::get<>() for types isn't in c++11, I use this meta-function to determine the index
//of a Type in a list of Types, starting from 0. ex: IndexOf<C,  A, B, C>::value = 2
template <typename T, typename... Ts>
struct IndexOf;

template <typename T, typename... Ts>
struct IndexOf<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};

template <typename T, typename U, typename... Ts>
struct IndexOf<T, U, Ts...> : std::integral_constant<std::size_t, 1 + IndexOf<T, Ts...>::value> {};

//Used to determine the slot we're interesting in.
using Handle = const std::size_t;

template<typename... Types>
class DataManager
{
    static constexpr std::size_t TypeCount = sizeof... (Types);
    using Flags = std::bitset<TypeCount>; //BitMask to determine if the handle has a certain piece of data initialized

    std::size_t count, capacity;
    std::tuple<std::vector<Types>..., std::vector<Flags>> vectors; //Tuple of vectors, holding the types and flags.
public:
    DataManager(std::size_t n) : count(0), capacity(n),
    vectors(std::make_tuple(std::vector<Types>(n)..., std::vector<Flags>(n)))
    {}

    template <typename Type, typename... Args>
    void add(Handle handle, Args&&... args) { //Initializes the type in the handle slot of the vector
        Flags& flags = std::get<TypeCount>(vectors)[handle];    //Flag the bit, notify that handle
        flags.set(IndexOf<Type, Types...>::value);              //has that piece of data initialized

        std::get<IndexOf<Type, Types...>::value>(vectors)[handle] = Type{ args... };
    }

    template <typename Type>
    Type& get(Handle handle) { //Returns the Type in handle slot of the vector
        return std::get<IndexOf<Type, Types...>::value>(vectors)[handle];
    }

    template <typename Type>
    bool has(Handle handle) { //Returns true if the Type is initialized, by checking the bitset
        Flags& flags = std::get<TypeCount>(vectors)[handle];
        return flags.test(IndexOf<Type, Types...>::value);
    }

    Handle push_back() {
        return count++;
    }
};

Which I currently use like this to access data:

//Simple Data
struct D0 { int x, y;    };
struct D1 { float  n, m; };
struct D2 { int x, y, z; };

int main()
{
    DataManager<D0, D1, D2> manager(100);
    Handle h0 = manager.push_back();

    std::cout << manager.has<D0>(h0) << std::endl;   //prints false, h0 doesn't have D0 initialized
    manager.add<D0>(h0, 75, 20);                     //initialize D0 for h0

    std::cout << manager.has<D0>(h0) << std::endl;   //prints ture, h0 is now initialzed
    std::cout << manager.get<D0>(h0).x << std::endl; //prints 75
}

How could I add iterator functionality to the DataManager class, that would only iterate over selected data like this?

 int main() 
 {
     ...
     for (D0 d1, D3 d3 : manager) {
         ... //Iterate over all D0s and D3s between 0 and count
     }
     //or 
     for(DataManager<D0>::iterator it = v.begin(); it != v.end(); ++it {
         ... //Iterate over just D0s between 0 and count - 10
     }
 }

Aucun commentaire:

Enregistrer un commentaire