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