在C++;没有C++;11? < C++中对类进行回调最干净的方法是什么?
我通常创建一个函数,如:在C++;没有C++;11? < C++中对类进行回调最干净的方法是什么?,c++,callback,c++03,C++,Callback,C++03,我通常创建一个函数,如: void registerCallback(void(*callback)(void* param), void* param); 然后这样称呼它: foo->registerCallback(callbackStatic, this); static void callbackStatic(void* param) { ((Type*)(param))->callback(); } 然后我在类中添加一个静态函数,如下所示: foo->r
void registerCallback(void(*callback)(void* param), void* param);
然后这样称呼它:
foo->registerCallback(callbackStatic, this);
static void callbackStatic(void* param)
{
((Type*)(param))->callback();
}
然后我在类中添加一个静态函数,如下所示:
foo->registerCallback(callbackStatic, this);
static void callbackStatic(void* param)
{
((Type*)(param))->callback();
}
它起作用了,但很痛苦。我必须为每个回调创建两个函数。如果我能使用C++11,我会使用lambda。Qt有一个非常好的信号和插槽机制,但需要一个特殊的预编译器
我正在写一个库,所以我想尽量降低要求。没有C++11,没有boost,等等。有没有办法只使用C++03构建一个lambda或信号/插槽类回调系统
有没有办法只使用C++03构建lambda或signal/slot“like”回调系统
另一种方法是只使用普通的旧的纯抽象接口。这不需要通过回调函数对类成员函数进行两阶段增强,只需要使用虚拟多态性
您甚至可以将静态多态性aka用作CRTP,它也适用于c++11之前的标准
这一切都是从早期的标准化开始的
< /p>> p>代码示例。如果您能够从头开始,我建议不要在C++中使用回调函数。而是使用对象
struct CallbackHandler { virtual void doit() = 0; };
void registerCallback(Callbackhandler* handler);
然后
struct MyCallbackHandler : public CallbackHandler { ... };
registerCallback(new MyCallbackHandler());
这可能有点复杂,但对我来说很有效:
#include <functional>
#include <iostream>
#include <vector>
class C {
public:
void foo() {
std::cout << "foo" << std::endl;
}
};
class D {
public:
void bar() {
std::cout << "bar" << std::endl;
}
};
class abstract_bind {
public:
virtual void operator()() = 0;
virtual ~abstract_bind() {}
};
template <typename F, typename T>
class bind : public abstract_bind {
public:
bind(F f_, T *t_) : f(f_), t(t_) {}
virtual void operator()() {
f(t);
}
private:
F f;
T *t;
};
int main() {
C c1, c2;
D d;
std::vector<abstract_bind*> v;
v.push_back(new bind<std::mem_fun_t<void, C>, C>(std::mem_fun(&C::foo), &c1));
v.push_back(new bind<std::mem_fun_t<void, C>, C>(std::mem_fun(&C::foo), &c2));
v.push_back(new bind<std::mem_fun_t<void, D>, D>(std::mem_fun(&D::bar), &d));
for (size_t i=0; i<v.size(); ++i)
(*v[i])();
for (size_t i=0; i<v.size(); ++i)
delete v[i];
return 0;
}
#包括
#包括
#包括
C类{
公众:
void foo(){
std::cout您可以保留C风格的回调,并使用一种结构来调用成员函数:
// C-style callback
// ================
void(*registered_callback)(void* param) = 0;
void* registered_param = 0;
void registerCallback(void(*callback)(void* param), void* param) {
registered_callback = callback;
registered_param = param;
}
void unregisterCallback(void(*callback)(void* param), void* param) {
registered_callback = 0;
registered_param = 0;
}
// Invoker
// =======
struct InvokerBase {
static void callInvoker(void* invoker) {
reinterpret_cast<InvokerBase*>(invoker)->apply();
}
virtual void apply() const = 0;
};
template <typename T>
class Invoker : public InvokerBase
{
public:
Invoker(T& object, void(T::*function)())
: object(&object), function(function)
{
registerCallback(callInvoker, this);
}
~Invoker() {
unregisterCallback(callInvoker, this);
}
void apply() const {
(object->*function)();
}
private:
Invoker(const Invoker&); // no copy
Invoker& operator = (const Invoker&); // no copy
T* object;
void (T::*function)();
};
// Test
// ====
#include<cassert>
#include<iostream>
struct Type {
void print() { std::cout << "Hello\n"; }
};
int main()
{
Type x;
{
Invoker<Type> print_invoker(x, &Type::print);
registered_callback(registered_param);
}
assert(registered_callback == 0);
}
//C风格回调
// ================
无效(*已注册的回调)(无效*参数)=0;
void*registered_参数=0;
作废注册表回调(作废(*回调)(作废*参数),作废*参数){
注册的回调=回调;
注册参数=参数;
}
void unregisterCallback(void(*callback)(void*param),void*param){
注册的回调=0;
注册参数=0;
}
//调用者
// =======
结构调用库{
静态void调用器(void*调用器){
重新解释强制转换(调用程序)->apply();
}
虚空应用()常量=0;
};
模板
类调用者:公共调用者数据库
{
公众:
调用程序(T&对象,void(T::*函数)()
:对象(&O)、函数(函数)
{
注册回调(callInvoker,本);
}
~Invoker(){
取消注册回调(callInvoker,this);
}
void apply()常量{
(对象->*函数)();
}
私人:
调用程序(const Invoker&);//无副本
调用器和运算符=(const Invoker&);//无副本
T*对象;
void(T::*函数)();
};
//试验
// ====
#包括
#包括
结构类型{
作废打印(){std::cout你可以试试Synapse,它是一个无需预编译的信号槽库。在Synapse中,任何对象都可以用作发射器:C++11真的有那么大的障碍吗?它已经标准化5年了,甚至在标准化之前就支持编译器。而且,它可以使代码更高效,因为移动语义。@NathanOliver不幸的是,对于某些公司来说,它可能是。你会惊讶于任务关键型操作的代码完全依赖于VS 2008。@RawN我明白,但在大多数情况下,他们无论如何都不会使用这个库,因为它需要彻底测试,就像他们不使用的新功能一样升级。@NathanOliver:作为上个月才更新公司所有工具链以获得C++11支持的人,是的!除非您有一个可变的构建环境,并且除非您在项目中共享的库可以使用这两种标准进行构建,否则您将停留在C++03上,直到有充分的理由花时间迁移e所有的一切。在企业中,你不需要在不需要的时候进行更改;我相信你知道每一次更改都有成本,包括直接的成本和回归风险。但我很高兴我现在得到了auto
,可变模板和lambdas omgIt也意味着放弃对CentOS 6的支持(当然,其他RHEL6派生DistROS;这些方法被广泛使用,并正式支持到2020),除非您愿意重新分配所有C++共享库(包括LIbSTDC++)。.I/我们决定这样做,但这仅仅是因为在GCC 4.8和C++11中标准化几乎所有内容的机会大于在我们的一个目标平台上乱搞的成本。SCL有一点帮助(因为我们至少不需要从源代码构建GCC)但是,是的,总的来说,还不清楚每个人是否都能使用C++11。这样做的缺点是,如果你订阅两个回调,它们都会调用doit(),因此您必须传递源参数或其他内容。@FigBug,我的答案指出了核心思想。它可以通过多种方式进行优化,以满足您的特殊需要。@FigBug我没有收到您的评论?@FigBug:反正是这样。另外,如果不使用C++11中的智能指针,您可能会得到一个悬空的指针。