Domanda

Supponi che stavi progettando una libreria di finestre C ++. Può o meno fornire un'API di callback, ma deve fornire un'API di polling per facilitare uno stile funzionale di programmazione.

Che aspetto avrebbe l'API di polling?

Alcune opzioni

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

Stile stato

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

Divertente stile pseudo-C ++ funzionale

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

Le risposte per lingue diverse da C ++ sono OK, se forniscono informazioni interessanti.

Non ci sono commenti sull'incapsulamento per favore - sì, i campi pubblici dovrebbero davvero essere accessori, l'ho lasciato fuori per chiarezza.

È stato utile?

Soluzione

Per rispondere rapidamente alla tua domanda, preferisco la semplicità del "codice in stile SDL". Principalmente perché il tuo stile "stato" leggermente più complicato spreca memoria e non ti compra assolutamente nulla (vedi sotto), e la ricorsione nel tuo pseudo-C ++ funzionale torturato " lo stile traboccerà dello stack in pochi millisecondi.

" Stile di stato " : il tuo " typesafe yay " nello "Stile di stato" il codice è un po 'ingiustificato. Stai ancora decidendo a quale membro accedere in base a un switch su un altro membro, quindi il codice presenta tutti gli stessi punti deboli dello stile "SDL" codice ha - per qualsiasi errore che potresti commettere con il codice in stile SDL che porta a interpretare la memoria come il tipo sbagliato, commetterai l'errore altrettanto grave di accedere a un membro non inizializzato con il codice in stile Stato.

" Stile pseudo-C ++ funzionale " : ora stai arrivando da qualche parte, ereditando diversi tipi di eventi da un tipo di evento di base. Ovviamente la stupida ricorsione deve diventare un ciclo, e ci sono alcune piccole cose da mettere in ordine (penso che i tuoi 3 metodi chiamati transform () in UserWidget vogliano essere chiamati handle () ; Immagino che tu possa risolvere il problema senza metodi virtuali di template usando Boost.Function o simili). Penso che questo approccio abbia un potenziale, anche se preferisco la semplicità dello stile SDL.

Ma soprattutto: metto in dubbio la necessità di un'interfaccia di polling. C'è un motivo per cui pollEvent () non può bloccare? Allo stato attuale, tutti e 3 i segmenti di codice stanno bruciando il tempo della CPU senza fare il 99,99% delle volte.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top