2011-07-06 73 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