1

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++ 以外の言語の回答は、興味深い洞察が得られる場合は問題ありません。

カプセル化についてのコメントはありません。はい、パブリック フィールドは実際にはアクセサーである必要があります。明確にするために省略しました。

4

1 に答える 1

1

あなたの質問に素早く答えるために、私は「SDL スタイルのコード」の単純さを好みます。主な理由は、少し複雑な「State Style」がメモリを浪費し、まったく何も購入しないためです (以下を参照)。また、拷問された「Functional pseudo-C++」スタイルの再帰により、数ミリ秒以内にスタックがオーバーフローします。

「State Style」 : 「State Style」コードの「typesafe yay」は少し不当です。別のメンバーに基づいてアクセスするメンバーをまだ決定しているswitchため、コードには「SDL スタイル」コードと同じ弱点がすべてあります。間違った型としてメモリを使用すると、状態スタイルのコードで初期化されていないメンバーにアクセスするという同様に悪い間違いを犯すことになります。

「Functional pseudo-C++ style」 : これで、基本イベント タイプからさまざまなイベント タイプを継承して、どこかに到達しました。明らかに、ばかげた再帰はループになる必要があり、整理することがいくつかあります (名前が付けられた 3 つのメソッドを呼び出す必要があると思いますtransform()。BoostUserWidgethandle()使用してテンプレート仮想メソッドがないという問題を解決できると思います)。 .関数または同様のもの)。このアプローチには可能性があると思いますが、私は SDL スタイルのシンプルさを好みます。

しかし、もっと根本的なこととして、私はポーリング インターフェイスの必要性に疑問を持っています。pollEvent()ブロックできない理由はありますか?現状では、3 つのコード セグメントすべてが 99.99% の時間、何もせずに CPU 時間を浪費しています。

于 2009-01-09T12:47:09.477 に答える