1、背景
在webrtc中使用到信号和槽的机制,信号和槽做qt的核心机制已经非常被大家熟知,但是对于非qt的实现还是第一次遇到,了解下原理。
优点:信号和槽,重点解决了观察者继承耦合关系。通过模板替换了继承关系,降低了耦合。可以链接任意函数。
qt 的信号槽,是由元对象系统实现,在编译前进行moc ,生成对应的moc.cpp 的源码。比webrtc中的实现要复杂的多,但是使用也更简单。
2、认识webrtc中信号、槽
1、如何使用非常简单,和qt差不多,信号对象自带connect函数。
sigslot::signal2<AsyncSocket*, int> SignalCloseEvent; //声明一个信号对象
//链接信号和槽,OnClose 函数不需要继承任何接口都可以使用
SignalCloseEvent.connect(this,&PeerConnectionClient::OnClose);
3、实现核心原理
0、核心关键点
- 可变参数
- 类型擦除(利用静态函数指针),调用时候才重新cast 原来的类型
- 类成员调用,保存下函数指针 obj->*(pm)
1、先看下链接过程
1)为了匹配不同的参数,参数(Args…)
2)m_connected_slots 是槽函数对象的列表
3) _opaque_connection 把对象和成员函数指针进行封装存储(利用)
template <class desttype>
void connect(desttype* pclass, void (desttype::*pmemfun)(Args...)) {
lock_block<mt_policy> lock(this);
//使用std::list 存储m_connected_slots
this->m_connected_slots.push_back(_opaque_connection(pclass, pmemfun));
//槽函数对象,存储了信号对象
pclass->signal_connect(static_cast<_signal_base_interface*>(this));
}
connections_list m_connected_slots;
3、_opaque_connection 存储槽函数
1、类型强转,模板静态函数指针强转函数指针,进行保存,调用的是进行case
2、利用老c++的方法,实现成员函数的调用,如果利用c++ 11 function 就非常简单了
class _opaque_connection {
private:
typedef void (*emit_t)(const _opaque_connection*);
template <typename FromT, typename ToT>
union union_caster {
FromT from;
ToT to;
};
emit_t pemit;
has_slots_interface* pdest;
// Pointers to member functions may be up to 16 bytes for virtual classes,
// so make sure we have enough space to store it.
unsigned char pmethod[16];
public:
template <typename DestT, typename... Args>
_opaque_connection(DestT* pd, void (DestT::*pm)(Args...)) : pdest(pd) {
typedef void (DestT::*pm_t)(Args...);
static_assert(sizeof(pm_t) <= sizeof(pmethod),
"Size of slot function pointer too large.");
//存储函数指针
std::memcpy(pmethod, &pm, sizeof(pm_t));
//类型强转,将模板静态函数指针强转成只有一个参数静态函数
typedef void (*em_t)(const _opaque_connection* self, Args...);
union_caster<em_t, emit_t> caster2;
caster2.from = &_opaque_connection::emitter<DestT, Args...>;
pemit = caster2.to;
}
has_slots_interface* getdest() const { return pdest; }
_opaque_connection duplicate(has_slots_interface* newtarget) const {
_opaque_connection res = *this;
res.pdest = newtarget;
return res;
}
// Just calls the stored "emitter" function pointer stored at construction
// time.
template <typename... Args>
void emit(Args... args) const {
//类型强转,将函数指针,强转成原来的函数
typedef void (*em_t)(const _opaque_connection*, Args...);
union_caster<emit_t, em_t> caster;
caster.from = pemit;
(caster.to)(this, args...);
}
private:
template <typename DestT, typename... Args>
static void emitter(const _opaque_connection* self, Args... args) {
typedef void (DestT::*pm_t)(Args...);
pm_t pm;
std::memcpy(&pm, self->pmethod, sizeof(pm_t)); //将存储的函数地址赋值给pm
//成员函数的调用可以这么搞,将对象和函数绑定
(static_cast<DestT*>(self->pdest)->*(pm))(args...);
}
};