我想为模块(线程)创建一个包装类,用于向插槽发送信号的boost :: signals2。即模块应该通过继承Signal类来获得典型的简单信令功能(例如公共连接(...)方法)。我还想隐藏使用的实际信号槽实现。
具体槽继承自通用Slot基类,该基类具有定义其签名的模板参数。插槽只是一个带有合适签名的仿函数。
此问题与this问题有些相关。插槽存储为shared_ptr,需要进行生命周期管理。即只要信号本身退出,Signal类应该保持对插槽的引用以使其保持活动状态。因此我无法连接std :: functions或类似的东西。我必须连接slot基类的shared_ptrs。
我目前的方法,目前没有线程安全(MSVC 2010):
template<class FunSig>
class Slot;
template<class R>
class Slot<R()>
{
public:
typedef R Ret_type;
public:
virtual ~Slot() {}
virtual Ret_type operator()() = 0;
};
template<class R, class A1>
class Slot<R(A1)>
{
public:
typedef R Ret_type;
typedef A1 Arg1_type;
public:
virtual ~Slot() {}
virtual Ret_type operator()(Arg1_type) = 0;
};
// and so forth for more arguments
/*
Signalling class.
This class is basically a wrapper for the boost::signals2 class with
lifetime management for slots.
Slots are connected by a shared_ptr which gets stored
in a std::vector to ensure that a slot exists at least as long as the signal.
*/
template<class FunSig>
class Signal
{
public:
typedef Slot<FunSig> Slot_type;
typedef boost::signals2::signal<FunSig> BoostSignal;
typedef typename BoostSignal::slot_type BoostSlot;
public:
virtual ~Signal() {}
void connectSlot(std::shared_ptr<Slot_type> slot_ptr);
protected:
//void emitSignal( ... );
//void disconnectAllSlots();
private:
BoostSignal sig_;
/// vector of shared_ptr to slots for lifetime management
std::vector<std::shared_ptr<Slot_type> > slotsVec_;
};
template<class FunSig>
void Signal<FunSig>::connectSlot(std::shared_ptr<Slot_type> slot_ptr)
{
sig_.connect(*slot_ptr); // version A: compiler error
// OR
sig_.connect(boost::ref(*slot_ptr)); // version B: warning, but compiles and runs
// add slot pointer to vector of slots
slotsVec_.push_back(slot_ptr);
}
此代码(版本A)无法编译。它破坏了内部boosts slot_template.hpp和connectSlot方法中标记的行:
error C2679: binary '=' : no operator found which takes a right-hand operand of type 'const Slot<FunSig>' (or there is no acceptable conversion)
1> with
1> [
1> FunSig=void (const float &)
有趣的是,如果使用版本B,则此代码将编译并运行 - 即插槽传递boost :: ref。虽然有一个编译器警告“函数调用带有可能不安全的参数 - 这个调用依赖于调用者来检查传递的值是否正确。”在boost的singals2中,auto_buffer.hpp。
那么这里的实际问题是什么以及如何解决?为什么这与boost :: ref有效,为什么没有它就不能编译?
我甚至不确定整个设计理念是否有用。最初的想法是将整个信令/插槽内容隐藏在超类中,并专注于签名(并包括生命周期管理)。
关于boost信号2的另一个问题:singals2 connect()方法引用了一个槽。这是如何在内部处理的。它是否使用连接插槽的参考或是否制作插槽的副本?这很重要,因为我的插槽处理动态分配的内存。
答案 0 :(得分:1)
我认为你的意思是警告C4996。这是Microsoft的c ++标准库实现的一个功能,它可以在使用可能不安全的参数编译标准算法时向您发出警告,例如:在此代码段中,调用者负责source
和target
的足够大小:
int source[3] = { 1, 2, 3 };
int target[3];
std::copy(source, source + 3, target);
您不应复制Boost.Signals2已提供的功能:该库提供对连接到信号的“插槽”的复杂生命周期管理。 Read through the docs,他们预见到对这方面的细粒度控制。此外,你失去了一个非常有趣的功能 - Boost.Signals2的线程安全性:你需要自己管理线程安全的插入和删除slotVec_
这是一件非平凡的事情......
我正在研究一个名为vex的类似抽象库,请查看abstract_multicast_delegate
中的abstract_signal
和vex/functional/contracts
。基于signals2::signal
的实施可以在vex/functional/implementation/bs2_signal.h
中找到。然而,这仍然是一个游乐场,我正在尝试替代实施......
答案 1 :(得分:0)
此问题已在不同的背景下得到解答here。
实际上必须使用std :: ref或boost :: ref,因为boost :: signals2 connect方法复制其参数。但是类Slot是不可复制的,因为它是一个抽象类。因此,使用boost :: ref是推荐的解决方案,因为它使插槽可以复制。