質問
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スタイルのコード」のシンプルさを好みます。主に、やや複雑な「状態スタイル」が理由です。メモリを浪費し、絶対に何も購入しません(以下を参照)。スタイルは数ミリ秒以内にスタックをオーバーフローさせます。
&quot; State Style&quot; :あなたの&quot; typesafe yay&quot; 「状態スタイル」でコードは少し不当です。他のメンバーの switch
に基づいて、どのメンバーにアクセスするかをまだ決定しているので、コードには「SDLスタイル」と同じ弱点がすべてあります。コードには、メモリを間違った型として解釈することにつながるSDLスタイルのコードで犯す可能性のあるミスについて、Stateスタイルのコードで初期化されていないメンバーにアクセスするという同様に悪いミスがあります。
&quot;機能的な擬似C ++スタイル&quot; :ベースイベントタイプからさまざまなイベントタイプを継承して、どこかに到達しています。明らかに、愚かな再帰はループになる必要があり、整理するいくつかの小さなことがあります( UserWidget
の transform()
という名前の3つのメソッドを呼び出したいと思います) handle()
; Boost.Functionなどを使用して、テンプレート仮想メソッドがないという問題を解決できると思います。このアプローチには可能性があると思いますが、SDLスタイルのシンプルさを好みます。
しかし、より基本的には、ポーリングインターフェースの必要性について疑問を持っています。 pollEvent()
がブロックできない理由はありますか?現状では、3つのコードセグメントすべてがCPU時間を燃やしており、99.99%の時間は何もしていません。