如何生成免费函数
How to generate free functions
问题
我有一个类似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
s:
// 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);
}
}
这不好。看起来我正试图智胜编译器,让它生成看似多余的代码,我认为我注定会失败:)
问题(最后)
- 有没有一种干净的方法(没有singletons和all)来解决我最初的问题
- 有没有一种方法可以像模板一样做到这一点,确保我为
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
中删除。子类仍然需要实现它,但它可以是公共的,或者APIClient
可以是子类的朋友。
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- 优点和缺点 在类内为大型项目定义的内联朋友助手免费函数
- 为什么比较和交换操作同时存在免费函数和成员函数?
- 为什么 std::list::splice 不是一个免费函数?
- Bison试图在解析器上使用免费的yylex()函数C++
- 如何生成免费函数
- 实现运算符的正确方法!=用于类外的模板作为一个免费函数
- 为什么C++标准使正则表达式算法成为免费函数
- 这是否可以减少免费模板函数的编译时间
- std::unique_ptr 用于需要免费的 C 函数
- 带有参数的免费函数,获取返回类型
- 最Vexing的朋友?朋友化一个专门的免费函数模板会引发编译错误(当重载一个方法时)
- 使用boost.asio的免费async_*函数时出现奇怪的错误
- 免费/开源的C/ c++矢量数学函数库