通过模板自动增量类型,用于项目范围内的唯一ID

时间:2011-07-15 14:08:19

标签: c++ templates

既然你喜欢知道它的用途,这里有一点背景知识。

我们密集使用我们自己的事件系统。典型实施:

  • Listener注册某些Event
  • EventManager在主循环中调度事件
  • Listener处理给定Event并执行某些操作...

现在,Listener必须识别Event类型才能将其转化为有用的内容。 Event类为此目的有一个名为int的{​​{1}}成员。为了保证ID的唯一性id女巫必须将其置于中心且可访问的位置,这样任何人都可以在创建新enum之前添加id Event 1}}类型......你明白了。

我曾尝试降低此系统的相对复杂度,以允许用户(程序员)以更简单的方式创建新的Event,而不会失去唯一性的安全性< / em>的。

所以我决定采用这样的方式:

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 id,具体取决于定义的Event类型的数量,到目前为止我们不必混淆enum ...我唯一的问题是我们需要正确定义模板参数,否则许多Event可以共享相同的ID。

所以我的问题是,有没有办法'隐藏'模板参数?

以这样的结局结束:

class Event_1 : public Event {};

2 个答案:

答案 0 :(得分:3)

C ++已经有了一个类型系统。

使每个Event具有不同的多态衍生类型。

你的Listener应该注册一个类似的事件(为了论证,假设单身EventManager):

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

实现成员函数:

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

现在您确切知道收到时邮件的类型是什么。

这里唯一的缺点是存储回调变得更加复杂。 (但是,从API构建您的系统!)

答案 1 :(得分:0)

我建议在每个事件上使用虚函数来执行任何事件。如果你真的需要在一个中心位置处理事件(如果是这样,你确定需要它吗?)那么你可以使用访问者模式,这样每个事件都有一个虚拟函数,可以调用特定于事件的方法。中央事件处理器。这样,您可以在没有任何强制转换或类型ID的情况下检测事件的类型。

要回答您的实际问题,如果您希望使用类型ID,并且您不关心效率,那么您可以这样做:

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方法来避免使用字符串,但仍然会离开地图查找。如果你使用哈希表,那么这可能不会那么慢,但它仍然不会像你已经得到的那样快。

将type_info :: name返回的const char *的地址直接用作id也可以更快地工作,但是我不确定是否保证它总是返回相同的地址类型,虽然希望它会这样做是合理的。在那种情况下,那将是非常快的。我真的不推荐这个解决方案。

我认为您对自己声明的解决方案的关注是两个不同的事件类可能来自同一个事件,例如由于剪切和粘贴错误。要捕获它,您可以检查Event :: getId永远不会在具有不同动态类型的对象上调用,如下所示:

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

请注意,如果您使用id对事件进行子类化但不希望为新子类提供单独的类型ID,则会断言。