Есть ли лучший способ обработки присвоения идентификаторов классам в иерархии для использования во время выполнения?

Я пытаюсь сделать разумно пригодной для использования реализацию моей системы событий. Чтобы определить типы событий,
У меня есть то, что я называю «типовой путь», который определяет путь к типу события через иерархию.
Таким образом, я могу обрабатывать, например, все InputEvents в одном месте, будь то нажатия клавиш, ввод с помощью мыши,
или что-то еще. Тем не менее, проблема заключается в том, что типы событий должны идентифицировать себя. Что у меня больше всего
недавно сделали это, заставив каждый экземпляр извлекать листовую идентичность из статической функции-члена
класса Event, который служит просто интерфейсом, отличным от выполнения этой функции.
Однако самый простой способ гарантировать, что каждый тип имеет только одну идентичность в этой структуре
казалось, чтобы использовать карты, основанные на путях типа (до, но исключая листовой идентификатор / идентификатор)
и typeid (). hash_code ().

В частности, я хочу иметь систему, в которую можно легко добавлять события без
необходимость искать кучу информации или выполнять много глупого дерьма. Учитывая это
(и, возможно, вещи, которые я не понимаю, я должен хотеть?),

  1. Этот дизайн имеет недостатки в каких-либо очевидных способов?
  2. Есть ли способ лучше, чем использовать typeid ()? Я немного читал об этом, и кажется, что это считается чем-то, что, в зависимости от человека, чье мнение спрашивают, никогда не должно использоваться или использоваться почти никогда. Поскольку возможно, что я просто ржавый или глупый, я хотел бы знать, знает ли кто-нибудь о решении, которое, если не что-то иное, менее неопределенное (очевидно, некоторые реализации typeid () довольно плохие, хотя я не не знаю, что или если это достаточно плохо, чтобы серьезно иметь значение).

Довольно простой пример того, что я сейчас имею:

#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);
}
};

3

Решение

Я бы полагался на иерархию классов, которую поддерживает компилятор, плюс список кодов типов:

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Хотя рефлексия, вероятно, будет лучшим подходом.

Это проще, чем у тебя, или я что-то упустил? Удачи.

0

Другие решения

См окончательное редактирование в исходное сообщение для решения.

0

По вопросам рекламы [email protected]