質問

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%の時間は何もしていません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top