2011-07-06 43 views
3

我想了解事件处理/回调中的C++ 0x std :: bind和std :: function用法。所以我正在检查一些代码片段,并遇到了一些我不能完全理解的有趣的地方。std ::绑定和std ::函数在回调中的用法

比方说,我们有:

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

问题:不监听在这部分持有什么类型的?

template<typename Event, typename Listener> 
bool bindEvent(const Listener& function) 

由于主要方法,我们调用这个函数只。

PS:另外,这段代码在std :: is_convertible部分失败。 (据我所知,因为不匹配的类型,从habindEvent<KeyboardEvent> (&do_key);

回答

6

Listener将由编译器推断为您传递它的函数指针的类型,在此例中为void(const KeyboardEvent&)

而且你的测试失败,因为它周围的错误的方式:你想

if (!std::is_convertible<Listener, std::function<void (Event&)>>::value) 

代替(注意否定)。

顺便说一下,std::is_arraystd::is_convertable都是在编译时决定的,这意味着您正在使用运行时检查来查找静态确定的内容。相反,你可以使模板不能结合使用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) 
{ 
} 

这将导致一个编译器错误,如果你尝试实例与不符合您的条件类型的模板。

+0

的确很不错。之前从未想过这件事。 – legion

+4

或者你可以使用static_assert来代替,并且在编译时优雅地失败并且不那么神秘的消息 – David

1

那么首先我认为habindEvent<KeyboardEvent> (&do_key);是一个错字,应该是handler.bindEvent<KeyboardEvent>(&do_key)

所以听者的类型与参数模板扣除。因此,在您特殊情况下,它会是这样。

typedef void(*Fn_t)(const KeyboardEvent &); 
EventHandler handler; 
handler.bindEvent<KeyboardEvent, Fn_t> (&do_key); 

但你并不需要Fn_t因为编译器能为你做这项工作。
和修复您的类型后的代码编译我的机器上。

+0

谢谢。这很好。是的,这是一个错字(从IDE粘贴)。但仍然有一个问题,由于类型错误,我检查了std :: is_convertible上的错误。 – legion

+2

@legion:看看Svens的答案。你希望Listener类型是可转换的。因为这是你要做的。所以否定测试,它应该没问题。 – mkaes

+0

啊我明白了。那是我的错误。 – legion