Я пытаюсь сделать разумно пригодной для использования реализацию моей системы событий. Чтобы определить типы событий,
У меня есть то, что я называю «типовой путь», который определяет путь к типу события через иерархию.
Таким образом, я могу обрабатывать, например, все InputEvents в одном месте, будь то нажатия клавиш, ввод с помощью мыши,
или что-то еще. Тем не менее, проблема заключается в том, что типы событий должны идентифицировать себя. Что у меня больше всего
недавно сделали это, заставив каждый экземпляр извлекать листовую идентичность из статической функции-члена
класса Event, который служит просто интерфейсом, отличным от выполнения этой функции.
Однако самый простой способ гарантировать, что каждый тип имеет только одну идентичность в этой структуре
казалось, чтобы использовать карты, основанные на путях типа (до, но исключая листовой идентификатор / идентификатор)
и typeid (). hash_code ().
В частности, я хочу иметь систему, в которую можно легко добавлять события без
необходимость искать кучу информации или выполнять много глупого дерьма. Учитывая это
(и, возможно, вещи, которые я не понимаю, я должен хотеть?),
Довольно простой пример того, что я сейчас имею:
#include <iostream>
#include <vector>
#include <typeinfo>
#include <map>
void spew(std::vector<unsigned int> vect) { for (unsigned int i=0;i<vect.size();++i) std::cout << vect.at(i) << ","; std::cout << std::endl; }
class Foo
{
public:
Foo() {}
virtual ~Foo() {}
static unsigned int getSubtype(std::vector<unsigned int> typePath, Foo *evt)
{
static std::map<std::vector<unsigned int>, std::map<std::size_t, unsigned int> > typeMap;
std::size_t typehash = typeid(*evt).hash_code();
if (typeMap.find(typePath) == typeMap.end())
{
unsigned int val = typeMap[typePath].size();
typeMap[typePath][typehash] = val;
return val;
}
else
{
if (typeMap[typePath].find(typehash) == typeMap[typePath].end())
{
unsigned int val = typeMap[typePath].size();
typeMap[typePath][typehash] = val;
return val;
}
return typeMap[typePath][typehash];
}
}
virtual void test() { std::cout << "Foo" << std::endl; }
protected:
std::vector<unsigned int> m_typePath;
};
class Bar : public Foo
{
public:
Bar()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Bar() {}
virtual void test() { std::cout << "Bar: "; spew(m_typePath);}
};
class Baz : public Foo
{
public:
Baz()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Baz() {}
virtual void test() { std::cout << "Baz: "; spew(m_typePath);}
};
class Qux : public Baz
{
public:
Qux()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Qux() {}
virtual void test() { std::cout << "Qux: "; spew(m_typePath);}
};
int main()
{
Foo foo0;
std::cout << "----" << std::endl;
Bar bar0;
std::cout << "----" << std::endl;
Baz baz0;
std::cout << "----" << std::endl;
Qux qux0;
}
Выход:
----
Bar: 0,
----
Baz: 1,
----
Baz: 1,
Qux: 1,0,
Этот и другие тесты показывают желаемое поведение, чтобы быть ясным.
Изменить: предыдущий заголовок не совсем соответствует тому, что я хотел спросить.
Возможно соответствующие примечания: это предназначено для части библиотеки, и очень параллельной в этом. Я упустил код, относящийся к параллелизму, для простоты представления дизайна, но, возможно, такая информация будет полезна и для целей проектирования. Также обратите внимание, что я все еще только прошу помощи в создании / назначении идентификаторов типов; Я упоминаю об этом, потому что некоторые проекты могут быть неприменимы, учитывая их подразумеваемые ограничения.
Win edit:
Ну, у меня есть нелепо быстрая реализация, которая делает именно то, что мне нужно. Имея несколько производных классов, я могу создать десять миллионов для каждого потока (я добавил в TBB для некоторых других тестов; он может использовать или не использовать ровно восемь потоков, как ему угодно), распределенных по производным классам, каждый из которых имеет два или более элементов в его путь, как правило, хорошо под 0,02 с. Первоначальная реализация обходилась в четыре или пять секунд в зависимости от контейнеров и тому подобного и была просто глупой. Результат (достаточно, чтобы понять идею, так или иначе):
template<typename T> class EventID
{
public:
static const std::size_t typeID;
};
template<typename T> const std::size_t EventID<T>::typeID = typeid(T).hash_code();
class Foo
{
public:
Foo()
{
m_typePath.push_back(EventID<Foo>::typeID);
}
protected:
neolib::vecarray<std::size_t, 100, neolib::nocheck> m_typePath;
};
class Bar : public Foo
{
public:
Bar()
{
m_typePath.push_back(EventID<Bar>::typeID);
}
};
Я бы полагался на иерархию классов, которую поддерживает компилятор, плюс список кодов типов:
typedef enum { EVENT, INPUT, MOUSE, MOUSEDOWN, MOUSEUP, MOUSEMOVE, KEYBOARD, KEYDOWN, KEYUP } EventType;
typedef std::vector<EventType> ETVector;
class Event
{
public:
virtual void appendType(ETVector& v) { v.push_back(EVENT); }
};
class InputEvent : public Event
{
public:
virtual void appendType(ETVector& v) { v.push_back(INPUT); Event::appendType(v); }
};
class MouseEvent : public InputEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSE); InputEvent::appendType(v); }
};
class MouseDownEvent : public MouseEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSEDOWN); MouseEvent::appendType(v); }
};
class MouseUpEvent : public MouseEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSEUP); MouseEvent::appendType(v); }
};
class MouseMoveEvent : public MouseEvent
// . . .
class KeyboardEvent : public InputEvent
// . . .
class KeyDownEvent : public KeyboardEvent
// . . .
class KeyUpEvent : public KeyboardEvent
// . . .
Затем, чтобы сделать свой тест, у вас будет что-то вроде этого:
KeyUpEvent kue;
EventTypeVector type_path;
kue.appendType(type_path);
for (EventTypeVector::const_iterator i = type_path.begin(); i != type_path.end(); i++)
{
cout << *i << endl;
}
Вектор хранит ваш тип пути. Там нет затрат времени хранения, и нет static
переменные. Это может быть возможно использовать typeid
вместо перечисления, поддерживаемого вручную, но с помощью перечисления все находится под вашим контролем, и вы можете легко избежать конфликтов с другими типами в вашей программе. В качестве альтернативы, можно было бы наполнить класс appendType()
метод с помощью шаблона, чтобы уменьшить код еще дальше.
Это немного утомительно, чтобы явно назвать родительский класс в appendType()
, но я не знаю другого пути в C ++, благодаря множественному наследованию. В Java я мог бы использовать super
Хотя рефлексия, вероятно, будет лучшим подходом.
Это проще, чем у тебя, или я что-то упустил? Удачи.
См окончательное редактирование в исходное сообщение для решения.