2014-01-21 3 views
1

Я пытаюсь создать систему, способную выделять любой тип и группировать одинаковые типы вместе в массивах.Как создать пул нескольких типизированных объектов в C++

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

Что-то вроде этого:

ObjectDatabase 
{ 
    template<typename T> 
    T* Allocate(); 

    template<typename T> 
    Array<T>& GetObjects(); 
} 

Мой тип массива фактически пул так распределение/удаление быстро.

Я думал о сопоставлении каждого массива в std :: map, используя int, представляющий идентификатор типа для каждого T, но тогда каждый тип T должен был бы наследовать от базового класса, поэтому его можно сохранить на карте, и, таким образом, приводит к кастингу, когда я перебираю массив.

Я думаю, что этот шаблон был сделан раньше, но я не уверен, как.

Может кто-нибудь помочь?

Update:

Так что я пытаюсь в основном создать структуру, как это:

struct ObjectDatabase 
{ 
    Array<Entities> mEntities; 
    Array<Transforms> mTransforms; 
    Array<Physics> mPhysics; 
    Array<Graphics> mGraphics; 
} 

Но я хотел бы каким-то образом создать набор массивов во время компиляции .. используя шаблоны?

Затем обеспечивают функции шаблона, чтобы получить доступ к каждому массиву, и выделить из каждого массива

+0

Разве вы не изобретаете вариант? Вы можете использовать boost :: variant или QVariant как T и избегать шаблона или посмотреть, как они реализованы, если вы хотите, чтобы ваш собственный объект –

+0

. Вы можете обернуть массив (например, 'vector ') в качестве члена данных в который основывается на базовом классе. Например. 'struct base {/ * чистые виртуальные функции * /}; template struct wrapper: base {vector arr;/* virtual functions * /}; 'А затем сохраните' map 'через некоторое type->' int' mapping. – dyp

+0

Я не хочу никаких виртуальных функций, так как T - это всего лишь структура во всех случаях без методов. Im просто хранит наборы данных. Вы можете думать об этом как о наборе массивов, где каждый массив хранит один тип. Если я использую boost :: variant, я считаю, что мне нужно получить соответствующий тип для элемента в массиве, поскольку я повторяю его, что замедлит работу. – Mash

ответ

1

Вы, вероятно, хотите использовать шаблоны, чтобы сделать тип Пропуска. Вот пример, который может быть похож на то, что вы ищете. Класс ObjectDatabase использует шаблоны и полиморфизм внутри себя для создания типа elision, поэтому используемые классы не имеют никаких ограничений на них (кроме обычных ограничений для размещения в контейнере стандартной библиотеки).

#include <iostream> 
#include <typeinfo> 
#include <deque> 
#include <map> 
#include <cassert> 
using namespace std; 

struct ObjectDatabase { 
    ObjectDatabase() { } 

    template<typename T> 
    T &allocate() { 
     deque<T> &a = getObjects<T>(); 
     a.push_back(T()); 
     return a.back(); 
    } 

    template<typename T> 
    deque<T> &getObjects() { 
     CollectionBase *&el = m_obdb[typeid(T).name()]; 
     if (not el) 
      el = new Collection<T>(); 
     Collection<T> *elc = dynamic_cast<Collection<T>*>(el); 
     assert(elc); 
     deque<T> &a = elc->elements; 
     return a; 
    } 

    ~ObjectDatabase() { 
     for (ObDB::iterator i=m_obdb.begin(); i!=m_obdb.end(); ++i) 
      delete i->second; 
    } 
private: 
    ObjectDatabase(ObjectDatabase const &); 
    ObjectDatabase &operator=(ObjectDatabase const &); 

    struct CollectionBase { 
     virtual ~CollectionBase() { } 
    }; 
    template<typename T> 
    struct Collection : CollectionBase { 
     deque<T> elements; 
    }; 
    typedef map<string, CollectionBase *> ObDB; 
    ObDB m_obdb; 
}; 

struct Foo { 
    Foo() : name("Generic Foo") { } 
    char const *name; 
}; 

struct Bar { 
    string name; 
}; 

int main() { 
    ObjectDatabase obdb; 
    obdb.allocate<Foo>().name = "My First Foo"; 
    obdb.allocate<Bar>().name = "My First Bar"; 
    { 
     Foo &f = obdb.allocate<Foo>(); 
     f.name = "My Second Foo"; 
     Bar &b = obdb.allocate<Bar>(); 
     b.name = "My Second Bar"; 
    } 
    obdb.allocate<Foo>(); 
    obdb.allocate<Bar>(); 
    { 
     cout << "Printing Foo Names\n"; 
     deque<Foo> &foos = obdb.getObjects<Foo>(); 
     for (deque<Foo>::iterator i = foos.begin(); i!=foos.end(); ++i) 
      cout << " -> " << i->name << "\n"; 
    } 
    { 
     cout << "Printing Bar Names\n"; 
     deque<Bar> &bars = obdb.getObjects<Bar>(); 
     for (deque<Bar>::iterator i = bars.begin(); i!=bars.end(); ++i) 
      cout << " -> " << i->name << "\n"; 
    } 
} 

Когда я запускаю эту программу, я получаю этот выход:

Printing Foo Names 
    -> My First Foo 
    -> My Second Foo 
    -> Generic Foo 
Printing Bar Names 
    -> My First Bar 
    -> My Second Bar 
    -> 

Это показывает, что отдельные объекты хранятся в контейнерах, характерных для их собственного типа. Вы заметите, что Foo и Bar ничего особенного, просто регулярные агрегаты. (Foo будет даже POD, если бы не его конструктор по умолчанию.)

======== ======== EDIT

Если вы не хотите использовать RTTI, вам нужно избавиться от typeid и dynamic_cast.

Избавление от dynamic_cast довольно просто --- вам это действительно не нужно. Вместо этого вы можете использовать static_cast; вы просто не можете проверить, что производный тип верен с assert(). (Но если тип был неправильным, это было бы ошибкой в ​​любом случае.)

typeid немного сложнее, так как это используется для построения идентификатора для различения различных типов бетона.Но вы можете использовать некоторый шаблон магию и статические объекты, чтобы заменить string (от type_info::name()) с помощью простого void const * указателя:

template<typename T> 
struct TypeTag { 
    static char const tag; 
}; 
template<typename T> 
char const TypeTag<T>::tag = '\0'; 

template<typename T> 
void const *get_typemarker() { 
    return &TypeTag<T>::tag; 
} 

Теперь мы можем использовать get_typemarker<T>() вернуть void const * ключ в карту. Мы меняем тип ключа ObDB от string до void const * и заменяем typeid(T).name() на get_typemarker<T>(). Я тестировал его, и он дает тот же результат в моей тестовой программе, что и версия с поддержкой RTTI.

+0

Да, это именно то, что я хочу! Спасибо, я могу работать с этим. Хотя у меня нет поддержки RTTI .. Как бы это работать без typeid() и dynamic_cast? – Mash

+0

@Mash, я добавил новый материал в нижней части ответа, который должен помочь вам в том, что вы хотите. –

+0

@Mash Я уверен, 'typeid (T)' is * not * RTTI. Это не функция времени выполнения, которая проверяет тип какого-либо объекта, но предоставляет объект информации типа * типа *. 'Dynamic_cast' просто не требуется, предполагая уникальные имена типов. – dyp

Смежные вопросы