2010-11-12 85 views
2

我正在爲遊戲製作一個Gui Api。用戶可以始終在窗口小部件上使用繼承並覆蓋,但我想要回調。我想用一個模板回調系統:模板化多態回調是一個好主意嗎?

所以如果他們想有一個他們從一個版本的模板回調基地mouseargs的繼承鼠標:

所以基本是這樣的:

template <typename T> 

class AguiEventCallback { 

public: 
virtual void callback(AguiWidget* sender, T arg) = 0; 

}; 

將模板與多態這樣混合是一個好主意嗎?我會更好地爲每個需要的類型(鼠標,鍵盤,遊戲手柄等)創建回調函數嗎?

謝謝

+1

我認爲你最好爲每個相關事件創建回調接口。很難說什麼是一個好的解決方案(有各種各樣的活動方案,每個發明者大概認爲這個特定的方案是最好的)。但是對於參數類型的模板可能不會給你帶來任何東西(如果參數的數量不是1呢?)。乾杯, – 2010-11-12 05:07:56

回答

3

看看boost :: function和boost :: bind。接受一個帶有特定事件的定義參數列表的函數對象,並且調用者可以做他們想做的事情。

這使回調實現具有很大的靈活性,生成事件的對象需要更少的回調實現知識。

例如:

typedef boost::function<void (AguiWidget* sender)> CallbackFunc; 
void register_callback(CallbackFunc const& f); 

而且客戶端:

class Caller { 
    void do_register() { register_callback(bind(&Caller::event, this, 123, _1)); } 

    void event(int arg, AguiWidget* sender) { ... } 
}; 

僅僅只是功能/綁定,忽略了很多其他問題;例如。內存管理,對象生命週期。

0

暫時接受你的虛擬調度解決方案,你的模板化方法保證了回調函數名稱和參數的一致性。令人遺憾的是,這將迫使很多其他代碼來消除哪個回調被調用/覆蓋,可能會導致更多的麻煩而不是好的。

這就是說,正如詹姆說的其他選項存在。函子更強大(你可以在運行時改變它們在現有的對象上,你可以有觀察者列表),但也必須在正確的時間初始化(純虛函數有效地提醒程序員在編譯時提供它們) ,並引入更多的運行時狀態來推理和理解。

您可能也可以使用模板策略或Curiously Recurring模板模式在編譯時提供行爲,允許內聯,死代碼消除,類型特定的行爲和其他優化。

1

有時使用模板的方式很好。還有由於問題,你必須給你的模板虛析構函數和

  • 如果內嵌虛擬析構函數(如你與大多數模板函數做的)一些編譯器很難堅持一個定義規則,特別是如果圖書館跨庫使用。

  • 如果你不內聯你的虛擬析構函數,你必須實例化你將要使用的每個類型。這是我自己的首選方法。

對於回調,您可以選擇使用boost :: function。這樣可以避免必須從模板中派生類,並用新的方法創建它們,並可能將它們粘貼到某處的shared_ptr中。我發現,boost :: function作爲回調函數的缺點是,如果出現錯誤,它很難調試。小心這個問題。

+0

你不需要使析構函數爲虛擬的,你也可以讓它具有'protected'和non-virtual:回調接口不能用於'刪除'對象,只是爲了執行調用。即使你決定使用它(你想通過其中一個接口存儲對象並通過它刪除對象,將虛擬析構函數添加到已經具有虛擬方法的類的代價可以忽略不計,但常見模式是使用虛擬析構函數定義在類定義的內部,我從來沒有遇到過你在第一個項目符號中提到的問題,你可以擴展?什麼編譯器?代碼? – 2010-11-12 09:02:25

0

除了這裏的其他答案,您可能會看看boost :: signal庫。它實現了一個信號/插槽機制,這對於GUI來說確實很有用。性能不如您所期望的那麼好(成本不僅僅是對虛擬方法的調用),但對於GUI來說,它就好。

boost :: signal庫也可以和boost :: bind一起使用,這個組合非常強大。

我不喜歡對回調使用非常多的繼承。它大部分時間只用1種方法產生很多類。它是C++而不是Java。你有功能,使用它們:)

相關問題