Pregunta

Digamos que estaba diseñando una biblioteca de ventanas C ++. Puede o no proporcionar una API de devolución de llamada, pero debe proporcionar una API de sondeo para facilitar un estilo funcional de programación.

¿Cómo sería la API de sondeo?

Algunas opciones

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

Estilo de estado

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

Divertido estilo pseudo-C ++ funcional

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

Las respuestas para otros lenguajes que no sean C ++ están bien, si proporcionan información interesante.

No hay comentarios sobre la encapsulación, sí, sí, los campos públicos realmente deberían ser accesores, lo dejé por razones de claridad.

¿Fue útil?

Solución

Para responder a su pregunta rápidamente, prefiero la simplicidad del " código de estilo SDL " ;. Principalmente porque su "Estilo de Estado" un poco más complicado desperdicia memoria y no te compra absolutamente nada (ver más abajo), y la recurrencia en tu pseudo-C ++ funcional torturado '' el estilo desbordará la pila en unos pocos milisegundos.

" Estilo de estado " : Su " typesafe yay " en el " Estilo de estado " El código es un poco injustificado. Todavía está decidiendo a qué miembro acceder basándose en un switch en otro miembro, por lo que el código tiene las mismas debilidades que el "Estilo SDL". tiene el código: por cualquier error que pueda cometer con el código de estilo SDL que conduce a interpretar la memoria como el tipo incorrecto, cometerá el mismo error grave de acceder a un miembro no inicializado con el código de estilo estatal.

" Estilo funcional pseudo-C ++ " : Ahora está llegando a algún lado, heredando diferentes tipos de eventos de un tipo de evento base. Obviamente, la tonta recursión debe convertirse en un bucle, y hay algunas pequeñas cosas para ordenar (creo que sus 3 métodos llamados transform () en UserWidget quieren ser llamados handle () ; supongo que puede resolver el problema de los métodos virtuales sin plantilla usando Boost.Function o similar). Creo que este enfoque tiene potencial, aunque prefiero la simplicidad del estilo SDL.

Pero más fundamentalmente: cuestiono la necesidad de una interfaz de sondeo. ¿Hay alguna razón por la cual pollEvent () no puede bloquear? Tal como está, los 3 segmentos de código están quemando tiempo de CPU sin hacer nada el 99.99% del tiempo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top