我试图在C++中设计一个通用的(但有些特定于用例的)事件传递机制,而不会违反"新风格"C++,并且同时不会过度使用模板.
我的用例有些特别之处在于我需要完全控制何时分发事件.事件系统是世界模拟的基础,世界的每次迭代都对前一帧生成的事件起作用.因此,我要求所有事件在调度之前排队,以便应用程序可以按特定时间间隔刷新队列,有点像经典的GUI事件循环.
我的用例在Ruby,Python甚至C中实现都很简单,但是使用C++我的内容有点短.我已经看了的boost ::信号和其它类似的库,但他们似乎过于复杂或不灵活,以适应我特别的使用情况.(特别是,Boost经常被模板化到完全混乱的程度,特别是像boost :: bind或boost :: function这样的东西.)
消费者通过直接向生成事件的对象注册自己来监听事件.
事件只是字符串名称,但每个事件可能附加了附加数据.
听众只是方法.如果这是C++ 11,我会使用lambdas,但我需要广泛的编译器可移植性,所以暂时使用方法.
当生产者触发事件时,事件将进入队列,直到将其发送到侦听器列表为止.
按事件触发的严格顺序调度队列.(因此,如果生产者A触发事件x,生产者B触发y,生产者B再次触发z,则总顺序为x,y,z.)
重要的是,监听器生成的任何事件都不会在下一次迭代之前发送- 因此内部确实有两个队列.
SpaceshipController::create() {
spaceship.listen("crash", &on_crash);
}
SpaceshipController::on_crash(CrashEvent event) {
spaceship.unlisten("crash", &on_crash);
spaceship.remove();
add(new ExplosionDebris);
add(new ExplosionSound);
}
Run Code Online (Sandbox Code Playgroud)
这是一个制片人:
Spaceship::collided_with(CollisionObject object) {
trigger("crash", new CrashEvent(object));
}
Run Code Online (Sandbox Code Playgroud)
所有这一切都很好,但转换成现代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, …Run Code Online (Sandbox Code Playgroud) 我正在设计一个应用程序,它需要一组分布式处理工作程序,这些工作程序需要异步使用并在特定流程中生成数据.例如:
显然,涉及的不仅仅是三个组成部分.
进一步要求:
这是一种由Storm等面向拓扑的系统解决的数据流.虽然风暴看起来很好,但我持怀疑态度; 它是一个Java系统,它基于Thrift,我都不喜欢它.
我目前倾向于使用AMQP作为数据传输的pub/sub-style方法,使用HTTP作为数据共享/存储的协议.这意味着AMQP队列模型成为公共API - 换句话说,消费者需要知道生产者使用哪个AMQP主机和队列 - 我不是特别高兴,但它可能值得妥协.
AMQP方法的另一个问题是每个组件必须具有非常相似的逻辑:
......以及每个组件需要的许多其他细节.
即使消费者在逻辑上非常简单(想想MapReduce作业,比如将文本分成标记),也有很多样板.当然,我可以自己完成所有这些 - 我非常熟悉AMQP和队列以及其他所有内容 - 并将所有这些包装在一个由所有组件共享的公共包中,但后来我已经在发明框架了.
这类东西是否存在良好的框架?
请注意,我特别询问Go.我想避免使用Hadoop和整个Java堆栈.
编辑:为清晰起见添加了一些要点.
考虑一个组件,它需要管理在其自己的 DOM 树中不是子组件但必须添加到顶级文档的子组件。
一个典型的例子是需要在输入字段下方的浮动菜单中显示自动完成匹配的自动完成字段。必须将浮动菜单添加为文档正文元素的子元素,以逃避树中任何会阻止其显示的“溢出:隐藏”约束。浮动菜单不再使用后需要移除。
在这种情况下,合乎逻辑的方法似乎是将组件安装在任意 div 中,然后在不再需要时卸载它。但是,当使用事件触发此类卸载时,这会引入一个有趣的状态流问题。
这是我当前代码的摘录,以说明问题:
componentDidUpdate: function(prevProps, prevState) {
if (prevState.matches !== this.state.matches) {
if (this._floater) {
this._floater.remove();
this._floater = null;
}
if (this.state.matches.length > 0) {
this._floater = Floater.create(
<Floater
parentElement={this.getDOMNode()}
open={true}>
<SelectableList
items={this.state.matches}
limit={10}
onSelectionChange={this.handleSelectionChange}/>
</Floater>
);
}
}
},
handleSelectionChange: function(items) {
this.setState({matches: [], selectedItem: items[0]});
},
Run Code Online (Sandbox Code Playgroud)
这里,Floater是一个可以包含任何其他组件的通用组件;它将自己设置为绝对,定位自己等等。Floater.create()是一种创建浮动组件并将其插入文档的便捷方法。
Floater.remove() 目前看起来像这样:
remove: function() {
var self = this;
if (this.isMounted()) {
window.setTimeout(function() {
React.unmountComponentAtNode(self.getDOMNode().parentNode);
}, 10);
}
},
Run Code Online (Sandbox Code Playgroud)
它使用超时的原因是允许父组件在状态更新后能够远程控制它。在 中选择某些内容SelectableList …