std::bind to void* для std::function
Я пытаюсь создать систему событий. Пользователь этой системы должен выполнить это, чтобы добавить событие для ввода, например:
addEventHandler(Event::KEYBOARD_INPUT, reinterpret_cast<void*>(&std::bind(&Game::On_Input, this)));
переданная функция будет вызываться при возникновении события, эти функции хранятся в std:: vectors, и до сих пор проблем нет. Но есть разные сигнатуры функций для разных событий.. и так как std:: function - это сам тип, я не могу позволить этой функции принимать все события, которые я пытался привести к std::bind void*, а затем вернуть обратно к std:: function, но это не сработает, поскольку impl не определен (0xCCCCCCCC)
Текущий код:
void EventManager::addEventHandler(Event event, void* p_Fun)
{
if (event == Event::KEYBOARD_INPUT) {
m_InputCallbacks.push_back(*reinterpret_cast<std::function<void(Keycode, unsigned int)>*>(p_Fun));
}
/*else if(...){ // other events other push backs in other vectors
}*/
}
и сохраненная функция называется так:
void EventManager::HandleEvents(SDL_Event& Event)
{
while (PollEvent(&Event) != 0) {
if (Event.type == KEYDOWN || Event.type == KEYUP) {
//On_Input(Event.key.keysym.sym, Event.type);
for (auto inputCallbackFunc : m_InputCallbacks) {
inputCallbackFunc(Event.key.keysym.sym, Event.type);//CRASH HERE!
}
}
}
}
Обратите внимание, что эта версия обрабатывает только ввод, я не знаю, сколько событий будет добавлено в будущем (столкновение, многопользовательские соединения и т. Д.), Поэтому я хотел бы автоматизировать процесс через функцию addEventHandler, которая проверит следующее: 1) тип события и приведение указателя на функцию void к сигнатуре функции обратного вызова события 2) сохранить их в соответствующем std:: vector
В общем, как заставить AddEventHandler принимать все передаваемые ему std::bind, которые можно сохранить и вызвать позже?
1 ответ
std::function
предоставляет все типы стирания, необходимые для хранения std::vector
одного типа, который имеет подпись, с которой вы будете вызывать его. Там нет необходимости кастинга на void*
Проще говоря, что-то вроде этого - то, что вы хотите.
#include <iostream>
#include <vector>
#include <functional>
#include <type_traits>
using namespace std;
enum Event
{
KEYBOARD_INPUT,
MOUSE_INPUT
};
std::vector< std::function<void(int,int)> > keyboard_handlers;
std::vector< std::function<void(int,int,int)> > mouse_handlers;
template <Event EventType,typename Func,
typename std::enable_if<EventType==KEYBOARD_INPUT,bool>::type=true>
void addEventHandler(Func&& func)
{
keyboard_handlers.push_back(
std::function<void(int,int)>(std::forward<Func>(func)));
}
template <Event EventType,typename Func,
typename std::enable_if<EventType==MOUSE_INPUT,bool>::type=true>
void addEventHandler(Func&& func)
{
mouse_handlers.push_back(
std::function<void(int,int,int)>(std::forward<Func>(func)));
}
void call_keyboard_handlers(int a,int b)
{
for (auto inputCallbackFunc : keyboard_handlers) {
inputCallbackFunc(a,b);
}
}
void keyboard_callback_1(std::string name, int a , int b)
{
std::cout << "keyboard_callback_1 " << name << " " << a << " " << b << std::endl;
}
void keyboard_callback_2(int a , int b)
{
std::cout << "keyboard_callback_2 " << a << " " << b << std::endl;
}
struct keyboard_callback_3_type
{
void operator()(int a,int b)
{
std::cout << "keyboard_callback_3 " << a << " " << b << std::endl;
}
};
int main() {
//With an extra bound in string argument.
addEventHandler<KEYBOARD_INPUT>(std::bind(&keyboard_callback_1,"somestring",std::placeholders::_1,std::placeholders::_2));
//From a plain function pointer
addEventHandler<KEYBOARD_INPUT>(&keyboard_callback_2);
//From a memberfunction pointer
keyboard_callback_3_type keyboard_callback_3;
addEventHandler<KEYBOARD_INPUT>(std::bind(&keyboard_callback_3_type::operator(),&keyboard_callback_3,std::placeholders::_1,std::placeholders::_2));
//From a non capturing lambda
addEventHandler<KEYBOARD_INPUT>([](int a,int b){ std::cout << "lambda_callback " << a << " " << b <<std::endl;});
std::string capture = "Captured";
//From a capturing lambda
addEventHandler<KEYBOARD_INPUT>([&](int a,int b){ std::cout << "lambda_callback " << capture << " " << a << " " << b <<std::endl;});
call_keyboard_handlers(10,20);
return 0;
}
Обратите внимание, что если вы хотите сохранить имя функции как addEventHandler, вам нужно будет включить различные варианты функции в зависимости от типа события (как в примере) или назвать их по-разному (например, addKeyboardEventHandler, addMouseEventHandler и т. Д.)