Автоматическое добавление типов через шаблоны для уникальных идентификаторов всего проекта - PullRequest
1 голос
/ 15 июля 2011

Так как вы любите знать, для чего это, здесь немного предыстории.

Мы интенсивно используем нашу собственную систему событий. Типичная реализация:

  • Listener зарегистрируйтесь в каком-то Event
  • EventManager события отправки в главном цикле
  • Listener обрабатывает данные Event и делает некоторые ...

Теперь Listener нужно идентифицировать Event типов, чтобы потенциально превратить его во что-то полезное. Для этой цели в классе Event есть член int с именем id. Чтобы гарантировать уникальность идентификаторов, которые они дают enum, ведьма должна находиться в центральном и доступном месте, чтобы любой мог добавить id перед созданием нового типа Event ... Вы получаете это.

Однажды я попытался уменьшить относительную сложность этой системы, чтобы позволить пользователям (программистам) создавать новые Event более простым способом, не теряя при этом уникальность .

Поэтому я решил пойти с чем-то вроде этого:

EventBase.h

class EventBase
{
public:
    virtual int getId() const = 0;
    static int registerEvent() { return ++numberOfEvent_;}
private:
    static int numberOfEvent_;
};

EventBase.cpp

int EventBase::numberOfChild_ = 0;

Event.h

template<class T>
class Event : public EventBase
{
public:
    virtual int getId() const { return id_;}
    static const int id_;
};

template<class T> const int Event<T>::id_ = EventBase::registerEvent();
* * Event_1.h тысяча сорок-девять
class Event_1 : public Event<Event_1> {};

Event_2.h

class Event_2 : public Event<Event_2> {};

Так что в принципе любой новый тип Event имеет свой собственный статический идентификатор const в зависимости от количества определенного типа Event, так что пока все хорошо, нам не нужно связываться с enum ... Моя единственная проблема является то, что нам нужно правильно определить параметр шаблона, в противном случае многие Event могут использовать один и тот же идентификатор.

Итак, мой вопрос, есть ли способ «скрыть» параметр шаблона?

В заключение примерно так:

class Event_1 : public Event {};

Ответы [ 2 ]

3 голосов
/ 15 июля 2011

C ++ уже имеет систему типов.

Создайте каждый Event разный, полиморфный, производный тип.

Ваши Listener s должны зарегистрироваться на событие, подобное этомуsingleton EventManager ради аргумента):

EventManager::registerCallback<cattle_prod_event>(bind(&Me::onCattleProd, this, _1));

И реализовать функцию-член:

void Me::onCattleProd(cattle_prod_event const* msg) {}

Теперь вы точно знаете, какой это тип сообщения в точке получения.

Единственным недостатком здесь является то, что хранение обратных вызовов становится немного более сложным.(Но, соберите вашу систему из API!)

0 голосов
/ 16 июля 2011

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

Чтобы ответить на ваш фактический вопрос, если вы хотите остаться с идентификаторами типов и вам не нужна эффективность, вы можете сделать это:

class Event {
 public:
  int getId() {
    std::map<std::string, int>::iterator it = types.find(typeid(*this).name());
    if (it == types.end()) {
      int newId = types.size();
      return types[typeid(*this).name()] = newId;
    } else
      return it->second;
  }
 private:
  static std::map<std::string, int> types;
};

Это, вероятно, не компилируется, так как я не пробовал, но вы поняли идею. Вы также можете использовать метод type_info.before, чтобы избежать использования строк, но это все равно оставляет поиск карты. Если вы используете хеш-таблицу, то это может быть не так уж и медленно, но все равно не так быстро, как решение, которое вы уже получили.

Также может работать быстрее использование адреса const char *, возвращаемого type_info :: name напрямую в качестве идентификатора, но я не уверен, что есть гарантия, что он всегда возвращает один и тот же адрес для одного и того же типа, хотя разумно надеяться, что так и будет. Это было бы довольно быстро в этом случае. Я не очень рекомендую это решение.

Я думаю, что вы беспокоитесь о своем собственном заявленном решении, что два разных класса событий могут происходить из одного и того же события, например из-за ошибки вырезать и вставить. Чтобы поймать это, вы можете проверить, что Event :: getId никогда не вызывается для объектов с разными динамическими типами, такими как:

#ifdef DEBUG
static type_info myType = typeid(*this);
assert(myType == typeid(*this));
#endif

Обратите внимание, что это будет подтверждено, если вы подклассифицируете событие с идентификатором, но не хотите давать новому дочернему классу отдельный идентификатор типа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...