std::bind 转为 void* 再转为 std::function

3

我正在尝试制作一个事件系统。该系统的用户需要执行以下操作来添加事件,例如:

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 被传递到它,这些可以被存储和稍后调用?

刚刚在addEventHandler上使用了函数模板,如果有更好的想法,请在下面说明!谢谢。 - Omarito
&std::bind( 不起作用,因为你正在获取一个临时对象的指针,绑定的函数在该语句结束时被销毁。 - kmdreko
std::function 不是指针类型... - selbie
1
reinterpret_cast<std::function<...>*> 不起作用,因为 std::bind 的结果不是 std::function 而是某种未指定的类型。 - kmdreko
同样令许多开发人员感到惊讶的是,函数指针与数据指针不同。将函数指针强制转换为数据指针(例如void*)可能会产生微妙且难以诊断的错误。(两者都不同于成员函数指针。) - Eljay
1个回答

2

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等)。


我知道解决方案与模板有关,谢谢,这正是我在寻找的,而且比使用原始模板更安全。 - Omarito

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接