设计轮询事件API

时间:2009-01-09 10:53:22

标签: c++ api events polling

假设您正在设计一个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 个答案:

答案 0 :(得分:1)

为了快速回答您的问题,我更喜欢“SDL风格代码”的简单性。主要是因为你的稍微复杂的“状态风格”浪费了内存并且完全没有购买任何东西(见下文),并且你在折磨的“Functional pseudo-C ++”风格中的递归将在几毫秒内溢出堆栈。

“州风格”:“州风格”代码中的“typesafe yay”有点无根据。您仍在根据另一个成员的switch决定访问哪个成员,因此代码具有与“SDL Style”代码相同的所有弱点 - 对于您可以使用SDL样式所犯的任何错误导致将内存解释为错误类型的代码,使用状态代码访问未初始化成员时会犯同样错误的错误。

“功能性伪C ++样式”:现在您正在某处,从基本事件类型继承不同的事件类型。显然,愚蠢的递归需要成为一个循环,并且有一些小东西要整理(我认为transform()中名为UserWidget的3个方法想要被称为handle();我'我猜你可以使用Boost.Function或类似方法解决没有模板虚拟方法的问题)。我认为这种方法有潜力,但我更喜欢SDL风格的简单性。

但更基本的是:我质疑是否需要轮询界面。有pollEvent()无法阻止的原因吗?就目前而言,所有3个代码段都在99.99%的时间内不再占用CPU时间。