Добавление дополнительных векторов в класс

0

Позвольте мне продемонстрировать свою проблему на примере.

Допустим, у меня есть куча очень простых структур, например:

struct A
{
    int x;
};

struct B
{
    float y;
    bool z;
};

И у меня есть класс, который хранит эти структуры в векторах. У него есть метод для добавления новой партии структур (всегда есть одинаковое количество каждой структуры), и у нее есть способ доступа к каждой структуре по ее индексу. Что-то вроде этого:

class Test
{
    public:
        void Create()
        {
            A a = {};
            B b = {};
            _vecA.push_back(a);
            _vecB.push_back(b);
        }

        A* GetA(unsigned int i)
        {
            return &_vecA[i];
        }

        B* GetB(unsigned int i)
        {
            return &_vecB[i];
        }

    private:
        std::vector<A> _vecA;
        std::vector<B> _vecB;
};

Теперь проблема заключается в том, что всякий раз, когда я добавляю еще одну структуру в программу, мне приходится добавлять в этот класс кучу вещей. Например, если я добавлю другую структуру (давайте назовем ее C), я должен добавить другой вектор, другой вызов push_back и создать метод GetC().

Теперь, мой вопрос: есть ли лучший способ сделать это?

Здесь один из способов, которым я думал:

class ContainerBase
{
    public:
        ContainerBase(){};
        virtual ~ContainerBase(){};
        virtual void Add(){};
};

template <typename T>
class Container : public ContainerBase
{
    public:
            // Singleton
            static Container& Instance()
            {
                static Container instance;
                return instance;
            }

            void Add()
            {
                T t = {};
                _bag.push_back(t);
            }

            T* Get(unsigned int i)
            {
                return &_bag[i];
            }

        private:
            Container(){};
            ~Container(){};

            std::vector<T> _bag;
};

class Test
{
    public:
        template <typename T>
        void Register()
        {
            _containers.push_back(&Container<T>::Instance());
        }

        void Create()
        {
            for (unsigned int i = 0; i < _containers.size(); i++)
                _containers[i]->Add();
        }

        template <typename T>
        T* Get(unsigned int i)
        {
            return Container<T>::Instance().Get(i);
        }

    private:
        std::vector<ContainerBase*> _containers;
};

Я попытался решить эту проблему, создав шаблонный контейнер Container для управления вектором каждого типа. Как вы можете видеть, это предполагает использование одноэлементного шаблона, чтобы убедиться, что всегда существует Контейнер для конкретного типа структуры. Типы структур просто должны быть зарегистрированы в классе с использованием метода Register, так что Test знает, какие контейнеры должны добавлять новые структуры, когда создается новая партия. Это решение проблематично, поскольку оно вводит глобальное состояние в контейнеры, и, делая это, невозможно создать два экземпляра класса Test, которые не разделяют их векторы для определенного типа структур. Если я не делаю контейнеры в одиночном режиме, я не могу получить доступ к ним по типу в функции Test Get, я должен знать, какой индекс в векторе _containers соответствует типу контейнера.

Я чувствую, что должен быть лучший способ сделать это.

Теги:
struct
vector

3 ответа

0
Лучший ответ

Вот как я решил решить проблему.

Я создал класс ContainerMapper, который имеет карту различных типов структур (с использованием типаinfo для получения типа), каждый тип сопоставляется с шаблоном Container для этого типа.

std::map<const std::type_info*, ContainerBase*> _map;

Я также сохраняю вектор всех зарегистрированных типов, поэтому я могу перебирать их, если нужно.

std::vector<const std::type_info*> _types;

Для регистрации новых типов я вызываю метод CointainerMapper Register():

template <typename T>
void Register()
{
    _types.push_back(&typeid(T));
    _map[&typeid(T)] = new Container<T>;
}

И для доступа к ним я использую:

template <typename T>
Container<T>* Get()
{
    return (Container<T>*)_map[&typeid(T)];
}

Код контейнера такой же, как и в примере в вопросе.

Когда я хочу создать еще одну группу структур, я просто перебираю зарегистрированные типы, получаю сопоставленные контейнеры и вызываю их метод Add().

for (unsigned int i = 0; i < _types.size(); i++)
    _map[_types[i]]->Add();

Использование всего этого выглядит так:

ContainerMapper m;
m.Register<A>();

m.Create();

// Get container
Container<A>* cA = m.Get<A>();
// Get first element
A* a = cA->Get(0);
// Set x member variable
a->x = 4;

// Or shorter way
m.Get<A>()->Get(0)->x = 4;
0

Ниже вы решите свою проблему (нужно С++ 11)

#if 1 // std::get<T>(tuple) is not in C++11

// Helper to retrieve index to be able to call std::get<N>(tuple)
template <typename T, typename ... Ts> struct get_index;

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

template <typename T, typename Tail,  typename ... Ts>
struct get_index<T, Tail, Ts...> :
    std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};
#endif

template <typename ... Ts>
class Data
{
public:

    void Create() {
        int dummy[] = {0, (getVector<Ts>().emplace_back(), 0)...};
        static_cast<void>(dummy); // silent warning about unused variable
    }

    template <typename T>
    const T* Get(std::size_t i) const { return &getVector<T>()[i]; }
    template <typename T>
    T* Get(std::size_t i) { return &getVector<T>()[i]; }

private:
    template <typename T>
    const std::vector<T>& getVector() const { return std::get<get_index<T, Ts...>::value>(items); }

    template <typename T>
    std::vector<T>& getVector() { return std::get<get_index<T, Ts...>::value>(items); }
private:
    std::tuple<std::vector<Ts>...> items;
};

Попробуй это:

class A{};
class B{};

int main()
{
    Data<A, B> d;

    d.Create();
    const B* b = d.Get<B>(0);
    return 0;
}
  • 0
    Похоже, это может быть лучшим решением. Хотя я не очень понимаю, что происходит с get_index. Я собираюсь изучить это немного ближе.
  • 0
    Один побочный вопрос, почему вы возвращаете T & в методах Get вместо указателя?
Показать ещё 4 комментария
0

используйте базовый класс, создайте A, B и C, полученные из этого базового класса, и сохраните только вектор/векторы базового класса. Его называют полиморфизмом.

  • 0
    Это, или сделайте контейнерный класс шаблоном, как vector .
  • 0
    Мне нужно иметь отдельные векторы для каждого типа структуры. Это очень важно для моей программы. Наличие только одного вектора затруднит перебор только одного типа структур.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню