问题
我有一个无法控制的类C API,具有注册/注销事件回调的功能:
enum Event { Evt1, Evt2, Evt3 }; // events generated by API library
typedef void(__cdecl *Callback)(Event e, void* context);
void API_add_callback(Event e, Callback cb, void* context);
void API_remove_callback(Event e, Callback cb, void* context);
我去创建一个包装器基类
APIClient
来封装此API,如下所示:class APIClient
{
public:
APIClient(){}
protected:
// this is to be used by subclasses
void subscribe(const set<Event>& events)
{
_events = events;
set<Event>::const_iterator it;
for (it = _events.begin(); it != _events.end(); ++it)
{
API_add_callback(e, &callback, this);
}
}
// this is to be used by subclasses
void unsubscribe()
{
set<Event>::const_iterator it;
for (it = _events.begin(); it != _events.end(); ++it)
{
API_remove_callback(e, &callback, this);
}
}
// this is to be implemented by subclasses
virtual void on_event(Event e) = 0;
private:
// this is the proxy callback that we register for all events
static void __cdecl callback(Event e, void* context)
{
APIClient* instance = (APIClient*)context;
// forward the event to the subclass
instance->on_event(e);
}
set<Event> _events;
};
到目前为止,一切都很好,我想。然后,我创建了两个子类
Foo
和Bar
,它们是APIClient
:// This one is interested in Evt1 and Evt2 of the API...
class Foo : public APIClient
{
public:
Foo() : APIClient()
{
set<Event>s;
s.insert(Evt1);
s.insert(Evt2);
subscribe(s);
}
~Foo()
{
unsubscribe();
}
protected:
virtual void on_event(Event e)
{
// here e will be Evt1 or Evt2, whenever they are fired
// by the API
}
};
// And this one is interested in Evt2 and Evt3 of the API...
class Bar : public APIClient
{
public:
Bar() : APIClient()
{
set<Event>s;
s.insert(Evt2);
s.insert(Evt3);
subscribe(s);
}
~Bar()
{
unsubscribe();
}
protected:
virtual void on_event(Event e)
{
// here e will be Evt2 or Evt3, whenever they are fired
// by the API
}
};
麻烦的是,它不起作用... ,因为API背后的库基于事件和回调而不是上下文(上下文只是附加的,可选的用户数据)来确定唯一的订阅。因此,一般来说,
API_add_callback(Evt2, &callback, instance_of_Foo);
API_add_callback(Evt2, &callback, instance_of_Bar);
只有第二个订阅获胜,因此
Foo
从未听过Evt2
。我失败的解决方案尝试
由于似乎该API对于同一事件的每个新订阅都需要特定的回调(即不同的地址),所以我认为:代码生成...模板!
在对APIClient进行模板化之后,像
&APIClient<Foo>::callback
和&APIClient<Bar>::callback
这样的东西应该给我不同的地址,对吗?错误!如果它们足够不同,它只会生成不同的地址(即不同的功能)。所以
template<typename T>
class APIClient
{
// ... other code ...
static void __cdecl callback(Event e, void* context)
{
APIClient* instance = (APIClient*)context;
// forward the event to the subclass
instance->on_event(e);
}
}
不好但是下面的代码将强制为
T=Foo
和T=Bar
模板实例化,从而给我&APIClient<Foo>::callback != &APIClient<Bar>::callback
:template<typename T>
class APIClient
{
// ... other code ...
static void __cdecl callback(Event e, void* context)
{
APIClient* instance = (APIClient*)context;
// Use T here explicitely to force different template
// instantiations of APIClient<T>::callback
T::call_something();
// forward the event to the subclass
instance->on_event(e);
}
}
不好看来我正试图使编译器不明智,以使其生成似乎是多余的代码,而且我注定要失败:)
问题(最后)
APIClient<T>::callback
的每个实例化获得不同的功能而又不做任何丑陋的事情? 注意:不幸的是,排除了C++ 11。
最佳答案
这称为奇怪的重复模板模式(http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
template<typename T>
class APIClient
{
public:
APIClient(){}
protected:
// this is to be used by subclasses
void subscribe(const set<Event>& events)
{
_events = events;
set<Event>::const_iterator it;
for (it = _events.begin(); it != _events.end(); ++it)
{
API_add_callback(it, &(T::callback), this);
}
}
// this is to be used by subclasses
void unsubscribe()
{
set<Event>::const_iterator it;
for (it = _events.begin(); it != _events.end(); ++it)
{
API_remove_callback(it, &(T::callback), this);
}
}
private:
// this is the proxy callback that we register for all events
static void __cdecl callback(Event e, void* context)
{
T * instance = (T*)context;
// forward the event to the subclass
instance->on_event(e);
}
set<Event> _events;
};
重要的变化是回调现在使用
T * instance
。这使访问成为问题。您可以选择几种解决方法。您可以像我一样将on_event
保留为虚拟,或将其从APIClient
中完全删除。子类仍然需要实现它,但是它可以是公共(public)的,也可以是APIClient
可以是子类的 friend 。