题
假设您正在设计一个C ++窗口库。它可能会也可能不会提供回调API,但需要提供一个轮询API来促进编程的功能。
轮询API看起来像什么?
一些选项
SDL风格
struct Event {
enum { MousePress, KeyPress } type;
union {
struct { Point pos; MouseButton b; } mousePress;
struct { Modifiers mods; char key; } keyPress;
};
};
void userCode() {
for(;;) {
Event e; if(pollEvent(&e)) {
switch(e.type) {
case MousePress: cout<<event.mousePress.pos.x; break; // not typesafe
case KeyPress: cout<<event.keyPress.key; break;
}
}
}
}
州风格
struct Input {
enum { Mouse, Keyboard, Nothing } whatChanged;
MouseButtonsBitfield pressedButtons;
bool keysPressed[keyCount];
};
void userCode() {
for(;;) {
Input in = pollInput();
switch(in.whatChanged) {
// typesafe yay
case Mouse: cout << "is LMB pressed? " << bool(in.pressedButtons&LeftButton); break;
case Keyboard: cout << "is A pressed? " << in.keysPressed['A']; break;
}
}
}
有趣的功能伪C ++样式
struct Event {
// transforms listener by notifying it of event,
// returns transormed listener. nondestructive.
template<class Listener> // sadly invalid, templates can't be virtual.
// a solution is to make Listener the base
// of a hierarchy and make Listener::handle virtual
// but then we're forced to use imperative style
virtual Listener transform(Listener const&) =0;
};
struct MousePress : Event { // yay we're extensible via inheritance
template<class Listener>
virtual Listener transform(Listener const& listener) {
return listener.handle(*this); // calls the MousePress overload
}
Point pos; MouseButton b;
};
struct KeyPress : Event {
template<class Listener>
virtual Listener transform(Listener const& listener) {
return listener.handle(*this); // calls the KeyPress overload
}
Modifiers mods; char key;
};
struct NoEvent : Event {
template<class Listener>
virtual Listener transform(Listener const& listener) {
return listener.handle(*this);
}
};
struct UserWidget {
UserWidget handle(NoEvent) {
return UserWidget();
}
UserWidget handle(MousePress p) {
return (UserWidget) { string("pressed at")+lex_cast<string>(p.pos)) };
}
UserWidget handle(KeyPress k) {
return (UserWidget) { string("pressed key=")+lex_cast<string>(k.key)) };
}
string pendingOutput;
};
void userTick(UserWidget const& w) {
cout<<w.pendingOutput;
userTick(pollEvent().transform(w));
}
void userCode() {
userTick(UserWidget());
}
如果他们提供有趣的见解,那么除了C ++之外的其他语言的答案都可以。
请不要对封装进行评论 - 是的公共字段应该是真正的访问者,为了清楚起见,我将其留下了。
解决方案
为了快速回答您的问题,我更喜欢简单的“SDL风格代码”。主要是因为你稍微复杂的“国家风格”浪费记忆并且绝对没有买任何东西(见下文),以及你折磨的“Functional pseudo-C ++”中的递归。样式将在几毫秒内溢出堆栈。
&quot; State Style&quot; :您的“类型安全yay”在“国家风格”中代码有点无根据。您仍在根据另一个成员上的 switch
决定访问哪个成员,因此该代码具有与“SDL Style”相同的所有弱点。代码有 - 对于您可以使用SDL样式代码导致将内存解释为错误类型的任何错误,您将犯同样错误的错误,即使用状态代码访问未初始化的成员。
&quot; Functional pseudo-C ++ style&quot; :现在你正在某个地方,从基本事件类型继承不同的事件类型。显然,愚蠢的递归需要成为一个循环,并且有一些小东西可以整理(我认为 UserWidget
中的3个名为 transform()
的方法需要调用 handle()
;我猜你可以使用Boost.Function或类似方法来解决没有模板虚拟方法的问题。我认为这种方法有潜力,但我更喜欢SDL风格的简单性。
但更基本的是:我质疑是否需要轮询界面。有没有理由为 pollEvent()
阻止?就目前而言,所有3个代码段都在99.99%的时间内不再占用CPU时间。