在C ++中设计事件机制 [英] Designing an event mechanism in C++
问题描述
我的用例有点特别,因为我需要完全控制事件分发的时间。事件系统是世界模拟的基础,其中世界的每个迭代作用于由前一帧产生的事件。因此,我要求所有事件在分派之前排队,以便应用程序可以按特定的时间间隔刷新队列,有点像经典的GUI事件循环。
我的用例在Ruby,Python或甚至C中实现起来很简单,但是使用C ++我会有点短。我已经查看了Boost :: Signal和其他类似的库,但是它们似乎太复杂或不灵活,无法满足我的特定用例。 (Boost,特别是基于模板的方法,常常会导致混淆,特别是boost :: bind或boost :: function。)
这是系统,以大笔画:
-
消费者通过直接注册自己
-
事件只是字符串名称,但每个事件可能附加了额外的数据。
-
侦听器只是方法。如果这是C ++ 11,我会使用lambdas,但我需要广泛的编译器可移植性,所以现在使用方法。
-
,则事件进入队列,直到将其分派到侦听器列表。
-
队列按事件触发的严格顺序调度。 (因此,如果生成器A触发事件x,生成器B触发y,生成器B再次触发z,则总顺序为x,y,z。)
- p>重要的是,无论侦听器产生什么事件都不会被分派,直到下一个迭代 - 因此内部实际上有两个队列。
这是一个消费者的理想伪代码示例:
$ b b
SpaceshipController :: create(){
spaceship.listen(crash,& on_crash);
}
SpaceshipController :: on_crash(CrashEvent event){
spaceship.unlisten(crash,& on_crash);
spaceship.remove();
add(new ExplosionDebris);
add(new爆炸声);
}
这里是一个制片人:
Spaceship :: collided_with(CollisionObject object){
trigger(crash,new CrashEvent(object));
}
这一切都很好,但是翻译成现代C ++难度。
问题是,无论是使用旧式C ++还是使用多态实例和丑陋,必须使用编译时定义的类型来处理模板级多态。
我已经尝试使用boost :: bind(),我可以生成一个listen方法像这样:
class EventManager
{
template< class ProducerClass,class ListenerClass,class EventClass>
void EventManager :: listen(
shared_ptr< ProducerClass> producer,
string event_name,
shared_ptr< ListenerClass> listener,
void(ListenerClass :: * method) EventClass * event)
)
{
boost :: function1< void,EventClass *> bound_method = boost :: bind(method,listener,_1);
// ...为地图添加处理程序以供以后执行...
}
}
(注意我如何定义中心事件管理器;这是因为我需要在所有生产者之间维护一个队列。为了方便起见,每个类仍然继承一个mixin,它提供了listen()和trigger )
现在可以通过执行:
void SpaceshipController :: create()
{
event_manager.listen(spaceship,crash,shared_from_this(),
& SpaceshipController :: on_crash);
}
void SpaceshipController :: on_crash(CrashEvent * event)
{
// ...
}
这很好,虽然它是冗长的。我讨厌迫使每个类继承enable_shared_from_this,而C ++要求方法引用包括类名,这很糟糕,但是这两个问题可能是不可避免的。
看看如何实现listen()这种方式,因为类只在编译时才知道。我需要将侦听器存储在每个生产者的映射中,而映射又包含每个事件名称的映射,例如:
unordered_map< shared_ptr< ProducerClass>,
unordered_map< string,vector< boost:function1< void,EventClass *> > > >听众;
但是当然C ++不允许我。我可以作弊:
unordered_map< shared_ptr< void *> ;,
unordered_map< string,vector< boost:function1< ; void,void *> > > >听众;
但是那样感觉很脏。
所以现在我要模板化EventManager或者东西,并为每个生产者保留一个,或许?但我不知道如何做,而不分割队列,我不能这样做。
注意我是如何明确地试图避免为每种类型的事件,Java风格定义纯接口类:
class CrashEventListener
{
virtual void on_crash(CrashEvent * event)= 0;
}
根据我记住的事件数量, 。
这也引发了另一个问题:我想对事件处理程序进行细粒度的控制。例如,有许多生产者提供称为改变的事件。我想能够将生产者A的更改事件挂钩到on_a_change,将生产者的B更改事件挂钩到例如on_b_change。
有了这一切,有人可以请我在