Question

Supposons que vous conceviez une bibliothèque de fenêtrage C ++. Elle peut ou non fournir une API de rappel, mais elle doit également fournir une API d'interrogation pour faciliter un style de programmation fonctionnel.

À quoi ressemblerait l'API d'interrogation?

Quelques options

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

Style d'état

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

Style pseudo-C ++ fonctionnel et amusant

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

Les réponses pour d'autres langages que C ++ sont acceptables si elles fournissent des informations intéressantes.

Pas de commentaires sur l'encapsulation s'il vous plaît - oui les champs publics devraient vraiment être des accesseurs, je les ai laissés de côté pour plus de clarté.

Était-ce utile?

La solution

Pour répondre rapidement à votre question, je préfère la simplicité du "code de style SDL". Principalement parce que votre "State Style" légèrement plus compliqué gaspille de la mémoire et ne vous rapporte absolument rien (voir ci-dessous), ainsi que la récursion dans votre "pseudo-C ++ fonctionnel fonctionnel". style débordera de la pile en quelques millisecondes.

"Style d'état" : votre " typesafe yay " dans le " State Style " le code est un peu injustifié. Vous décidez toujours à quel membre accéder en fonction d'un commutateur sur un autre membre. Le code présente donc les mêmes faiblesses que le & SD; Style SDL " code has - si vous commettez une erreur avec le code de style SDL qui conduit à interpréter la mémoire comme un type incorrect, vous commettez l'erreur tout aussi grave d'accéder à un membre non initialisé avec le code de style State.

"Style pseudo-C ++ fonctionnel" : vous obtenez maintenant quelque chose, héritant de différents types d'événement d'un type d'événement de base. Évidemment, la récursion idiote doit devenir une boucle et il y a quelques petites choses à ranger (je pense que vos 3 méthodes nommées transform () dans UserWidget veulent être appelées handle () ; je suppose que vous pouvez résoudre le problème de l'absence de méthodes de modèles virtuels à l'aide de Boost.Function ou similaire). Je pense que cette approche a du potentiel, même si je préfère la simplicité du style SDL.

Mais plus fondamentalement: je remets en question la nécessité d’une interface de sondage. Existe-t-il une raison pour laquelle pollEvent () ne peut pas bloquer? En l'état actuel des choses, les trois segments de code utilisent du temps processeur sans rien faire à 99,99% du temps.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top