如何生成免费函数

How to generate free functions

本文关键字:函数 免费 何生成      更新时间:2023-10-16

问题

我有一个类似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;
};

到目前为止还不错,我想。然后我做了两个子类,FooBar,它们是APIClients:

// 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=FooT=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可以是子类的朋友。