【发布时间】:2015-04-19 18:53:23
【问题描述】:
我正在编写一个事件处理程序来监听按键,然后在任何按下的键上调用一个处理程序。我的目标是允许这样的事情:
Entity player(0, 0);
EventHandler eh([&](char c) {
switch (c) {
case 'W': {
player.moveBy(0,-1);
break;
}
case 'S': {
player.moveBy(0, 1);
break;
}
case 'A': {
player.moveBy(-1, 0);
break;
}
case 'D': {
player.moveBy(1, 0);
break;
}
}
});
Entity 只是一个可移动的点状对象。
我已经准备好了,然后我意识到不能将带有引用捕获的 lambdas 制作成函数指针(回想起来,原因是有道理的)。
我能找到的唯一替代方法是使用std::/boost::function,但语法相当丑陋,而且显然它们带来了相当大的开销。
这个系统有什么好的替代品?我希望能够将某种“处理程序”传递给EventHandler,它接受一个字符,并且能够对某些外部范围执行副作用。
在下面的源代码中,LockedQueue 是一个 FIFO 队列,使用 mutexes 使其成为线程安全的。
EventHandler.h:
#ifndef EVENT_HANDLER_H
#define EVENT_HANDLER_H
#include <vector>
#include <atomic>
#include "LockedQueue.h"
class EventHandler {
typedef void(*KeyHandler)(char);
std::atomic<bool> listenOnKeys = false;
std::vector<char> keysToCheck;
LockedQueue<char> pressedKeys;
KeyHandler keyHandler = nullptr;
void updatePressedKeys();
void actOnPressedKeys();
public:
EventHandler();
EventHandler(KeyHandler);
~EventHandler();
void setKeyHandler(KeyHandler);
void setKeysToListenOn(std::vector<char>);
void listenForPresses(int loopMSDelay = 100);
void stopListening();
};
#endif
EventHandler.cpp:
#include "EventHandler.h"
#include <windows.h>
#include <WinUser.h>
#include <thread>
#include <stdexcept>
EventHandler::EventHandler() {
}
EventHandler::EventHandler(KeyHandler handler) {
keyHandler = handler;
}
EventHandler::~EventHandler() {
stopListening();
}
void EventHandler::updatePressedKeys() {
for (char key : keysToCheck) {
if (GetAsyncKeyState(key)) {
pressedKeys.push(key);
}
}
}
void EventHandler::actOnPressedKeys() {
while (!pressedKeys.empty()) {
//Blocking if the queue is empty
//We're making sure ahead of time though that it's not
keyHandler(pressedKeys.waitThenPop());
}
}
void EventHandler::setKeyHandler(KeyHandler handler) {
keyHandler = handler;
}
void EventHandler::setKeysToListenOn(std::vector<char> newListenKeys) {
if (listenOnKeys) {
throw std::runtime_error::runtime_error(
"Cannot change the listened-on keys while listening"
);
//This could be changed to killing the thread by setting
// listenOnKeys to false, changing the keys, then restarting
// the listening thread. I can't see that being necessary though.
}
//To-Do:
//Make sure all the keys are in upper-case so they're
// compatible with GetAsyncKeyState
keysToCheck = newListenKeys;
}
void EventHandler::listenForPresses(int loopMSDelay) {
listenOnKeys = true;
std::thread t([&]{
do {
updatePressedKeys();
actOnPressedKeys();
std::this_thread::sleep_for(std::chrono::milliseconds(loopMSDelay));
} while (listenOnKeys);
});
t.join();
}
void EventHandler::stopListening() {
listenOnKeys = false;
}
编辑:
哎呀。请注意,listenForPresses 已“损坏”,因为我正在加入函数内部,因此控制权永远不会离开它。我需要想出一个解决方法。虽然不会改变问题,但代码在当前状态下不可测试。
【问题讨论】: