Frage

Sagen Sie bitte ein C ++ entwerfen Windowing-Bibliothek. Es kann oder auch nicht einen Callback-API zur Verfügung stellen, sondern braucht eine Abfrage API, um eine funktionale Art der Programmierung zu erleichtern.

Was das Abfrage-API aussehen würde?

Einige Optionen

SDL Stil

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;
            }
        }
    }
}

Staat Stil

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;
        }
    }
}

Spaß funktionelle pseudo-C ++ Stil

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());
}

Antworten für andere Sprachen als C ++ sind in Ordnung, wenn sie interessante Einblicke bieten.

Keine Kommentare zu Verkapselung bitte - ja öffentliche Felder wirklich Accessoren sein sollten, verließ ich, dass aus Gründen der Klarheit aus

.
War es hilfreich?

Lösung

Ihre Frage schnell zu beantworten, ich die Einfachheit des „SDL-Stil-Code“ bevorzugen. Vor allem, weil Ihr etwas komplizierter „State Style“ Abfälle Speicher und kauft man absolut nichts (siehe unten), und die Rekursion in Ihrer gefoltert „Functional pseudo-C ++“ Stil innerhalb von wenigen Millisekunden den Stapel überläuft.

"State Style" : Ihre "typsicher yay" in dem "State Style" Code ist ein bisschen unberechtigt. Sie sind immer noch entscheiden, welches Mitglied auf einem switch auf ein anderes Mitglied basierend auf Zugang, so dass der Code hat die gleichen Schwächen, dass die „SDL Style“ Code hat - für jeden Fehler, den Sie mit dem SDL-Stil-Code machen könnte, die dazu führt, Speicher als die falsche Art zu interpretieren, würden Sie das gleich schlecht Fehler machen, ein uninitialised Mitglied mit dem Staat-Stil-Code zugreifen.

"Funktions pseudo-C ++ Stil" : Jetzt bist du irgendwo bekommen, verschiedene Ereignistypen von einer Basisereignistyp erben. Offensichtlich ist die dumme Rekursion muss eine Schleife werden, und es gibt ein paar kleine Dinge aufzuräumen (Ich denke, Ihre 3 Methoden transform() in UserWidget nennen wollen handle() genannt werden; ich vermute, dass Sie das Problem ohne Vorlage lösen können virtuelle Methoden Boost.Function oder ähnliches). Ich denke, dass dieser Ansatz Potenzial hat, obwohl ich die Einfachheit der SDL-Stil bevorzugen.

Aber noch grundsätzlicher: Ich frage die Notwendigkeit einer Polling-Schnittstelle. Gibt es einen Grund, warum pollEvent() kann nicht blocken? Wie es aussieht, sind alle drei Codesegmente CPU-Zeit brennen nichts 99,99% der Zeit zu tun.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top