Разработка API события опроса - PullRequest
1 голос
/ 09 января 2009

Скажем, вы разрабатывали библиотеку окон C ++. Он может предоставлять или не предоставлять API обратного вызова, но должен предоставлять API опроса для упрощения функционального стиля программирования.

Как будет выглядеть API опроса?

Некоторые опции

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

Государственный стиль

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

Интересный функциональный псевдо-C ++ стиль

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

Ответы для языков, отличных от C ++, в порядке, если они дают интересную информацию.

Пожалуйста, не комментируйте инкапсуляцию - да, публичные поля действительно должны быть средствами доступа, я оставил это для ясности.

1 Ответ

1 голос
/ 09 января 2009

Чтобы быстро ответить на ваш вопрос, я предпочитаю простоту «кода в стиле SDL». Главным образом потому, что ваш немного более сложный «State Style» тратит впустую память и абсолютно ничего не покупает (см. Ниже), а рекурсия в вашем измученном стиле «Functional pseudo-C ++» переполнит стек за несколько миллисекунд.

«State Style» : Ваш «типосохраняющий yay» в коде «State Style» немного неоправдан. Вы по-прежнему решаете, к какому члену получить доступ, основываясь на switch для другого участника, поэтому у кода есть те же недостатки, что и у кода "SDL Style" - за любую ошибку, которую вы можете совершить с кодом в стиле SDL, который приводит к интерпретации памяти как неправильного типа, вы бы сделали такую ​​же грубую ошибку при доступе к неинициализированному члену с помощью кода в стиле State.

"Функциональный стиль псевдо-C ++" : Теперь вы получаете что-то, наследуя различные типы событий от базового типа события. Очевидно, что глупая рекурсия должна стать циклом, и есть несколько мелких вещей, которые нужно привести в порядок (я думаю, что ваши 3 метода с именем transform() в UserWidget хотят быть вызваны handle(); я предполагаю, что вы можете решить проблема отсутствия шаблонных виртуальных методов с использованием Boost.Function или аналогичных). Я думаю, что у этого подхода есть потенциал, хотя я предпочитаю простоту стиля SDL.

Но более существенно: я подвергаю сомнению необходимость интерфейса опроса. Есть ли причина, по которой pollEvent() не может заблокировать? На самом деле, все 3 сегмента кода сжигают процессорное время, ничего не делая 99,99% времени.

...