gpt4 book ai didi

c++ - 使用引用捕获替代函数指针

转载 作者:搜寻专家 更新时间:2023-10-31 02:23:00 25 4
gpt4 key购买 nike

我正在编写一个事件处理程序来监听按键,然后在任何按下的键上调用一个处理程序。我的目标是允许这样的事情:

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 只是一个可移动的点状对象。

我已经准备好了,然后我意识到不能将具有引用捕获的 lambda 变成函数指针(回想起来,原因是有道理的)。

我能找到的唯一替代方法是使用 std::/boost::function,但语法相当丑陋,而且显然它们的数量相当可观开销。

什么是这个系统的好替代品?我希望能够将某种“处理程序”传递给接受字符的 EventHandler,并且能够对某些外部范围产生副作用。

在下面的源代码中,LockedQueue 是一个使用 mutexes 实现线程安全的 FIFO 队列。

事件处理器.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

事件处理器.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 是“损坏的”,因为我在函数内部加入,所以控制永远不会离开它。我需要想出一个解决方法。虽然不会改变问题,但代码在其当前状态下不可测试。

最佳答案

The only alternative I could find was to use std::/boost::function, but the syntax is rather ugly, and apparently they come with a decent amount of overhead.

与内联函数相比,开销是不错的,但是 it's measured in nanoseconds .如果您每秒仅调用该函数 60 次,则开销是无法估量的。

也就是说,如果您需要能够随时更改事件处理程序,您唯一的选择是虚拟方法调用,具有类似的开销。本文对这些选择的性能影响进行了彻底探讨:Member Function Pointers and the Fastest Possible C++ Delegates .

如果您乐于将 EventHandler 对象限制为执行在编译时定义的单个代码块,请使用模板来存储编译器为 lambda 生成的类型的实例;这应该允许编译器执行更多优化,因为它可以确定正在调用什么代码。在这种情况下,KeyHandler 成为模板类型,并且可以使用 decltype 关键字找到 lambda 的类型:

template <class KeyHandler>
class EventHandler {
// elided
}

void EventLoopDecltype() {
Entity player(0, 0);
auto myEventHandler = [&](char ch) { /* elided */ };
EventHandler<decltype(myEventHandler)> eh(myEventHandler);
}

或者(对于调用者来说更方便)推断为模板函数的参数:

template <class KeyHandler>
EventHandler<KeyHandler> MakeEventHandler(KeyHandler handler) {
return EventHandler<KeyHandler>(handler);
}

void EventLoopInferred() {
Entity player(0, 0);

auto eh = MakeEventHandler([&](char c) {
// elided
});
}

关于c++ - 使用引用捕获替代函数指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29721667/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com