Pergunta

Say você estivesse projetando um C ++ de janelas biblioteca. Pode ou não pode fornecer uma API de retorno de chamada, mas as necessidades para fornecer uma API de consulta para facilitar um estilo funcional de programação.

Qual seria a aparência de votação API como?

Algumas opções

SDL estilo

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

Fun funcional pseudo-C ++ estilo

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

Respostas para outros idiomas além do C ++ são OK, se eles fornecem uma visão interessante.

Não há comentários sobre o encapsulamento por favor - sim campos públicos deve realmente ser acessores, eu deixei isso para clareza

.
Foi útil?

Solução

Para responder à sua pergunta rapidamente, eu prefiro a simplicidade do "code-style SDL". Principalmente porque o seu um pouco mais complicado memória resíduos "Estilo Estado" e compra-lhe absolutamente nada (veja abaixo), ea recursão em sua torturado "Funcional pseudo-C ++" estilo vai estourar a pilha dentro de alguns milissegundos.

"State Style" : Seu "yay typesafe" no código "Estilo Estado" é um pouco injustificada. Você ainda está decidindo qual membro de acesso baseado em um switch em outro membro, de modo que o código tem as mesmas deficiências que o código "SDL Style" tem - para qualquer erro que você poderia fazer com o código de estilo SDL que leva a interpretar memória como o tipo errado, você faria o igualmente mau erro de acessar um membro uninitialised com o código de estilo Estado.

"++ estilo funcional pseudo-C" : Agora você está chegando a algum lugar, herdando diferentes tipos de eventos a partir de um tipo de evento base. Obviamente, as necessidades de recursão tolas para se tornar um loop, e há algumas pequenas coisas para arrumar (I acha que seus 3 métodos chamados transform() em UserWidget quer ser chamado handle(), eu estou supondo que você pode resolver o problema de nenhum modelo virtual métodos que utilizam Boost.Function ou semelhante). Penso que esta abordagem tem potencial, embora eu prefiro a simplicidade de estilo SDL.

Mas, mais fundamentalmente: Eu questiono a necessidade de uma interface de voto. Existe uma razão pela qual pollEvent() não pode bloquear? Tal como está, todos os 3 segmentos de código estão queimando tempo de CPU não fazer nada 99,99% do tempo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top