boost :: signals2的包装器,用于通用插槽的生存期管理

时间:2013-04-26 12:40:26

标签: c++ templates signals boost-signals2

我想为模块(线程)创建一个包装类,用于向插槽发送信号的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()方法引用了一个槽。这是如何在内部处理的。它是否使用连接插槽的参考或是否制作插槽的副本?这很重要,因为我的插槽处理动态分配的内存。

2 个答案:

答案 0 :(得分:1)

我认为你的意思是警告C4996。这是Microsoft的c ++标准库实现的一个功能,它可以在使用可能不安全的参数编译标准算法时向您发出警告,例如:在此代码段中,调用者负责sourcetarget的足够大小:

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_signalvex/functional/contracts。基于signals2::signal的实施可以在vex/functional/implementation/bs2_signal.h中找到。然而,这仍然是一个游乐场,我正在尝试替代实施......

编辑:抱歉,我不知道如何指向codeplex的hg存储库的提示。我不得不删除链接,因为自昨天以来发生了很多变化......

答案 1 :(得分:0)

此问题已在不同的背景下得到解答here

实际上必须使用std :: ref或boost :: ref,因为boost :: signals2 connect方法复制其参数。但是类Slot是不可复制的,因为它是一个抽象类。因此,使用boost :: ref是推荐的解决方案,因为它使插槽可以复制。