std::bind and std::function usage in callbacks
I was trying to understand c++0x std::bind and std::function usage in event handling / callbacks. So I was examining some code snippets and encountered some interesting places which I can't fully understand.
Let's say we have:
class EventHandler
{
public:
template<typename Event, typename Listener>
bool bindEvent (const Listener& function)
{
if (std::is_array<Event>::value)
{
std::cout << "You cannot register an array as an event type" << std::endl;
开发者_运维问答 return false;
}
if (std::is_convertible<Listener, std::function<void (Event&)>>::value)
{
std::cout << "Invalid callback for this event type" << std::endl;
return false;
}
listeners.insert(std::make_pair(&typeid(typename std::decay<Event>::type),
[=](void* ev) { function(*static_cast<Event*>(ev)); }));
}
private:
// for each event type, we have a list of callbacks
// these callbacks are of the form std::function<void (void*)>
// because we will convert between the &event and void*
typedef std::function <void (void*)> fun;
std::unordered_multimap <const std::type_info*, fun> listeners;
};
// example of an event type
struct KeyboardEvent
{
int keyCode;
};
// example of an event callback
void do_key (const KeyboardEvent &event)
{
std::cout << "I am working" << std::endl;
}
int main (int argc, char** argv)
{
EventHandler handler;
handler.bindEvent<KeyboardEvent> (&do_key); // fixed typo
return 0;
}
Question: What type does Listener holds in this part ?:
template<typename Event, typename Listener>
bool bindEvent(const Listener& function)
Since in main method we call this function only with .
PS: Also, this code fails in std::is_convertible part. (as I understand, because of mismatch type from habindEvent<KeyboardEvent> (&do_key);
Listener
will be inferred by the compiler to be the type of function pointer that you pass it, in this case void(const KeyboardEvent&)
.
And your test fails because it's the wrong way around: you want
if (!std::is_convertible<Listener, std::function<void (Event&)>>::value)
instead (note the negation).
By the way, both std::is_array
and std::is_convertable
are decided at compile time, which means that you are using a run-time check for something that is statically determined. Instead, you can make the template fail to bind for invalid types using SFINAE:
template<typename Event, typename Listener>
typename std::enable_if<!std::is_array<Event>::value && std::is_convertible<Listener, std::function<void(Event&)>>::value, bool>::type bindEvent (const Listener& function)
{
}
This will cause a compiler error if you try to instantiate the template with types that don't match your conditions.
Well first I assume that habindEvent<KeyboardEvent> (&do_key);
is a typo and should be handler.bindEvent<KeyboardEvent>(&do_key)
.
So the type of the listener is a template deduction from the parameter. So in your special case it would be this.
typedef void(*Fn_t)(const KeyboardEvent &);
EventHandler handler;
handler.bindEvent<KeyboardEvent, Fn_t> (&do_key);
But you don't need the Fn_t since the compiler can do this work for you.
And after fixing your type the code compiles on my machine.
精彩评论