2015-11-30 29 views
0

首先,讓我提供一下上下文。我爲我的遊戲創建了一個小型遊戲框架,我擁有遊戲系統和事件系統。我嘗試使用模板等語言功能來避免用戶代碼中的樣板文件。我可以自動推導基類模板方法中的子類型嗎?

還有Component類,從中派生出其他遊戲元素,即Camera,Sprite,Button。

using EventType = unsigned int; 
using EventCallback = std::function<void(Event)>; 

class Component { 
public: 
    // This is public version for general functor objects, 
    // particularly used with lambda 
    void addHandler(EventType type, const EventCallback &callback); 

protected: 
    // Method for child classes. Creates event handler from method of the class. 
    // 
    // This is template method, because std::bind is used for functor creation, 
    // which requires class type at compile time. 
    template <class T> 
    void addHandler(EventType type, void (T::*method)(Event event)) { /*...*/ } 
private: 
    // ... 
}; 

每個組件監聽特定的事件集合,所以它並沒有實現處理程序爲每個可能的事件的類型。此外,組件的用戶應該能夠添加自定義事件偵聽器而不必爲每個遊戲要素創建新類,例如:

class Button : public Component { 
public: 
    Button() { 
     addHandler(kOnTouch, &Button::onTouch); 
    } 
    // ... 
}; 

Button ok, cancel; 
ok.addHandler(kOnClick, [](Event) { 
    // ... 
}); 

cancel.addHandler(kOnClick, [](Event) { 
    // ... 
}); 

// Add another handler somewhere else in the code 
cancel.addHandler(kOnClick, someCallback); 

所以,我想要做的是後期綁定,與成員函數編譯時檢查。我想確保傳遞給addHandler()的方法指針屬於調用addHandler()的子類。我可以在template argument deduction的幫助下在addHandler()中獲得類型的方法所有者。但是我沒有找到一種推導孩子班級類型的方法。我在這裏是如何試圖與decltype(*this)和類型的特徵來實現:

template <class T> 
void addHandler(EventType type, void (T::*method)(Event event)) { 
    /***** This check is INCORRECT *****/ 
    // Check that T is same as child class 
    using ChildClass = std::remove_reference<decltype(*this)>::type; 
    static_assert(std::is_same<T, ChildClass>::value, 
        "Event handler method must belong to caller class"); 

    using namespace std::placeholders; 

    EventHandler::Callback callback = 
      std::bind(method, static_cast<T *>(this), _1); 
    addHandler(EventHandler(type, callback)); 
} 

在這裏我們需要子類類型T搭配比較。看起來ChildClass被分配給基類Component而不是child。有沒有辦法自動推斷子類的類型,只更改方法版本addHandler()?僅對此重載的addHandler()而不是整個Component類進行模板化以使生成的代碼最小化並能夠使用多態性非常重要。所以它是更通用的addHandler()的小包裝,取std :: function。

此刻,我只能檢查T是組件:

static_assert(std::is_base_of<Component, T>::value, 
       "Event handler method must belong to caller class"); 
+0

那麼,你需要決定是否要早期或後期綁定使用。如果您想使用早期綁定,請勿使用公共子類。 –

+0

@Let_Me_Be我想遲到(運行時)綁定而不是早期(編譯時),所以用戶代碼可以管理事件處理程序,即在條件語句中。 http://stackoverflow.com/questions/10580/what-is-the-difference-between-early-and-late-binding –

回答

1

似乎ChildClass被分配到基礎組件類,而不是孩子。

這裏沒有什麼被「分配」。濫用術語令人困惑。

基類的成員函數中的this的類型當然是基類。

要知道呼叫者您需要的this指針調用者,而不是基類成員函數內this指針的類型。

template <class T, class U> 
    void addHandler(EventType type, void (T::*method)(Event event), U* that) 

然後,你甚至不真正需要的靜態斷言,你可以綁定thatmethod

:您可以通過要求調用方將它作爲參數傳遞獲取this指針調用者的NB如果確實保留靜態斷言,則可能需要使用std::is_base_of而不是std::is_same

另外,您可以擺脫addHandler超載的,只是需要派生類型做綁定:

class Button : public Component { 
public: 
    Button() { 
    addHandler(kOnTouch, [this](Event e) { onTouch(e); }); 
    } 
    // ... 
}; 
+0

當然,這不是像變量一樣的賦值,我只是用了有意義的單詞。 「this」轉讓有趣的選擇。但避免它會很好。超負荷點實際上是不使用lambdas。 –

+0

如果我使用'std :: is_base_of',那麼用戶可能會不經意地將指針傳遞給其他組件的成員,並且這可能會導致崩潰,因爲綁定到其他類型的對象實例(請注意'static_cast ')。 std :: bind需要'static_cast'來解決重載問題。 –

+1

不,如果正確使用'is_base_of'則不行。這樣做很好:'struct FancyButton:Button {FancyButton(){addHandler(kOnTouch,&Button :: onTouch); }};'因爲'FancyButton'是一個'Button',所以'static_cast'將是正確的。您不需要類型相同,只需要轉換是正確的,並且如果您綁定到方法的指針是方法的類型_or或者公開且明確地從它派生的類型,則這是正確的。這就是'is_base_of'告訴你的。 –

相關問題