3

I'm trying to make an event system The user of this system have to execute this to add an event for input for e.g:

addEventHandler(Event::KEYBOARD_INPUT, reinterpret_cast<void*>(&std::bind(&Game::On_Input, this)));

the function passed gonna be called when the event is fired, these functions are stored in std::vectors till now there is no problem.But there is different function signatures for different events.. and since std::function is a type itself I can't let this function accept all events I have tried to cast the std::bind to void* then cast it back to std::function but it wont work since impl is undefined (0xCCCCCCCC)

The current code:

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

    }*/
}

and the function stored called this way :

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!
            }
        }
    }
}

Please note that this version only handles input I don't know how many events gonna be added in the future (Collision, multiplayer connections, etc..) that's why i'd like to automate the proccess throught addEventHandler function which will check the following: 1) whats the event type and cast the void function pointer to that event call back function signature 2) store them in the appropriate std::vector

So all in all how to make AddEventHandler accept every std::bind being passed to it that can be stored and called later?

Omarito
  • 577
  • 6
  • 22
  • Just used function templates on addEventHandler and it works fine if anyone have any better idea please state it below! Thanks – Omarito Sep 16 '18 at 22:54
  • `&std::bind(` won't work because you're taking a pointer of a temporary, the bound function is destroyed at the end of that statement. – kmdreko Sep 16 '18 at 22:57
  • `std::function` is not a pointer type... – selbie Sep 16 '18 at 22:58
  • 1
    `reinterpret_cast*>` won't work because the result of `std::bind` is *not* an `std::function` but some unspecified type – kmdreko Sep 16 '18 at 22:58
  • Also something that surprises a lot of developers, a function pointer is different from a data pointer. Casting a function pointer to a data pointer, such as `void*`, may create subtle and hard to diagnose bugs. (Both of which are different from a member function pointer.) – Eljay Sep 16 '18 at 23:40

1 Answers1

2

std::function provides all the type erasure you need to store a std::vector of a single type that has the signature that you will be calling it with. There is no need to be casting to void*

To keep it simple, something like this is what you want.

#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;
}

Demo

Note, if you want to keep the function name as addEventHandler you will have to enable different variants of the function based on the event type (as in the example), or name them differently (ie addKeyboardEventHandler, addMouseEventHandler etc..)

rmawatson
  • 1,909
  • 12
  • 20
  • I knew that the solution is something to do with templates, Thanks thats what im looking for and its more safer than using raw templates. – Omarito Sep 17 '18 at 05:10