2010-11-12 70 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。你有功能,使用它们:)

相关问题