c++11 boost - 信号插槽 - signals2

2018-07-13 18:05:24

signals2 基于 boost 里的另一个库 signals 实现了线程安全的观察者模式。观察者模式被称为 信号/插槽(signals/slots),是一种函数回调机制,有点类似事件。

例子-1

// Copyright (c) 2015
// Author: Chrono Law
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
using namespace std;

#include <boost/signals2.hpp>
using namespace boost::signals2;

void slots1(){
	cout << "slot1 called" << endl;
}
void slots2(){
	cout << "slot2 called" << endl;
}

//template<int N>
//struct slots
//{
//    void operator()()
//    {   cout << "slot"<< N <<" called" << endl; }
//};

template<int N>
struct slots
{
    int operator()(int x)
    {
        cout << "slot "<< N <<" called" << endl;
        return x *N;
    }

};

template<int N>
bool operator==(const slots<N>&, const slots<N>&)
{   return true;}

//////////////////////////////////////////

void case1()
{
    signal<void()> sig;

	//默认为 at_back,在链为插入
    sig.connect(&slots1);
    sig.connect(&slots2, at_front);  //从链前面插入

	//产生信号,开始调用slots2(),slots1()
    sig();
}

//可以指定组号,从小到大开始调用
void case2()
{
    //signal<void()> sig;
    signal<int(int)> sig;

    sig.connect(slots<1>(),at_back);
    sig.connect(slots<100>(), at_front);

    sig.connect(5,slots<51>(), at_back);
    sig.connect(5,slots<55>(), at_front);

    sig.connect(3,slots<30>(), at_front);
    sig.connect(3,slots<33>(), at_back);

    sig.connect(10,slots<10>());

    sig(2);
}

//返回值使用 optional 对象
//返回最后被调用插槽的返回值
void case3()
{
    signal<int(int)> sig;

    sig.connect(slots<10>());
    sig.connect(slots<20>());
    sig.connect(slots<50>());

    cout << *sig(2) << endl;
}

//合并器,把多个插槽的返回值合并为一个结果返回给用户。
template<typename T>
class combiner
{
    T v;
public:
    typedef std::pair<T, T> result_type;

    combiner(T t = T()):v(t){}

    template<typename InputIterator>
    result_type operator()(InputIterator begin, InputIterator end) const
    {
        if (begin == end)
        {   return result_type();       }

        vector<T> vec(begin, end);

        T sum = std::accumulate(vec.begin(), vec.end(), v);
        T max = *std::max_element(vec.begin(), vec.end());

        return result_type(sum, max);
    }
};

void case4()
{
    //signal<int(int), combiner<int> > sig;
    signal<int(int), combiner<int> > sig(combiner<int>(100)); //从100开始

    sig.connect(slots<10>());
    sig.connect(slots<20>());
    sig.connect(slots<30>(), at_front);

    auto x = sig(2);
    
    //返回100+2*10+2*20+2*30   60
    cout << x.first << "," << x.second << endl << endl; 
}

//管理信号插槽
void case5()
{
    signal<int(int) > sig;
    assert(sig.empty());

    sig.connect(0,slots<10>());
    sig.connect(0,slots<20>());
    sig.connect(1,slots<30>());
    assert(sig.num_slots() == 3);

    sig.disconnect(0);
    assert(sig.num_slots() == 1);

    sig.disconnect(slots<30>());
    assert(sig.empty());
}

//返回的 connection 对象可以用来管理信号插槽
void case6()
{
    signal<int(int) > sig;

    connection c1 = sig.connect(0,slots<10>());
    connection c2 = sig.connect(0,slots<20>());
    connection c3 = sig.connect(1,slots<30>());

    c1.disconnect();
    assert(sig.num_slots() == 2);
    assert(!c1.connected());
    assert(c2.connected());

    sig.disconnect_all_slots();

    sig.connect(0,slots<10>());
    assert(sig.num_slots() == 1);

    {
    	//类似scoped_ptr,离开作用域自动断开
        scoped_connection sc = sig.connect(0,slots<20>());
        assert(sig.num_slots() == 2);
    }

    assert(sig.num_slots() == 1);
}

void case7()
{
	cout << endl << "case7\n";
    signal<int(int) > sig;

    connection c1 = sig.connect(0,slots<10>());
    connection c2 = sig.connect(0,slots<20>());
    assert(sig.num_slots() == 2);

    sig(2);

    cout << "begin blocking..." << endl;
    {
    	//作用域里阻塞c1
        shared_connection_block block(c1);
        assert(sig.num_slots() == 2);
        assert(c1.blocked());
        sig(2); //不会调用c1
    }

	//自动解除阻塞
    cout << "end   blocking..." << endl;
    assert(!c1.blocked());
    sig(2);
    
    cout << endl;
}

#include <boost/smart_ptr.hpp>

//如果插槽和信号建立连接后,插槽被意外的销毁,将发生未定义行为
//可以用如下方式解决
void case8()
{
    typedef signal<int(int) > signal_t;
    signal_t sig;

    sig.connect(slots<10>());
    //boost::shared_ptr<slots<20> > p(new slots<20>);
    auto p = boost::make_shared<slots<20>>();  //使用 shared_ptr管理资源

    sig.connect(signal_t::slot_type(ref(*p)).track(p));
    p.reset(); //销毁插槽
    assert(sig.num_slots() == 1);  //自动断开了
    sig(1);
}

//使用bind语法直接绑定
void case9()
{
    typedef signal<int(int) > signal_t;
    typedef signal_t::slot_type slot_t;
    signal_t sig;

    auto p1 = boost::make_shared<slots<10>>();
    auto p2 = boost::make_shared<slots<20>>();

    function<int(int)> func = ref(*p1);

    sig.connect(slot_t(func).track(p1));

    sig.connect(slot_t(&slots<20>::operator(), p2.get(), _1).track(p2));

	//销毁2个指针,自动断开
    p1.reset();
    p2.reset();
    assert( sig.num_slots() == 0);
    sig(1);
}

int main()
{
    case1();
    case2();
    case3();
    case4();
    case5();
    case6();
    case7();
    case8();
    case9();
}
[root@192 c++]# g++ -std=c++11 main.cpp 
[root@192 c++]# ./a.out 
slot2 called
slot1 called
slot 100 called
slot 30 called
slot 33 called
slot 55 called
slot 51 called
slot 10 called
slot 1 called
slot 10 called
slot 20 called
slot 50 called
100
slot 30 called
slot 10 called
slot 20 called
220,60


case7
slot 10 called
slot 20 called
begin blocking...
slot 20 called
end   blocking...
slot 10 called
slot 20 called

slot 10 called


例子-2

// Copyright (c) 2015
// Author: Chrono Law
#include <cassert>
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
using namespace std;

#include <boost/signals2.hpp>
using namespace boost::signals2;

#include <boost/random.hpp>
using namespace boost;

class ring
{
public:
    typedef signal<void()> signal_t;
    typedef signal_t::slot_type slot_t;

    connection connect(const slot_t& s){
    	return alarm.connect(s);
    }
    
    void press()
    {
        cout << "Ring alarm..." << endl;
        alarm();
    }
private:
    signal_t alarm;
};

typedef variate_generator<rand48, uniform_smallint<> > bool_rand;
bool_rand g_rand(rand48(time(0)), uniform_smallint<>(0,100));

extern char const  nurse1[] = "Mary";
extern char const  nurse2[] = "Kate";

template<char const *name>
class nurse
{
private:
    bool_rand &rand;
public:
    nurse():rand(g_rand){}

    void action()
    {
        cout << name;
        if (rand() > 30)
        {   cout << " wakeup and open door." << endl;}
        else
        {   cout << " is sleeping..." << endl;}
    }
};

extern char const  baby1[] = "Tom";
extern char const  baby2[] = "Jerry";

template<char const *name>
class baby
{
private:
    bool_rand &rand;
public:
    baby():rand(g_rand){}

    void action()
    {
        cout << "Baby " << name;
        if (rand() > 50)
        {   cout << " wakeup and crying loudly..." << endl; }
        else
        {   cout << " is sleeping sweetly..." << endl;  }
    }
};

class guest
{
public:
    void press(ring &r)
    {
        cout << "A guest press the ring." << endl;
        r.press();
    }
};

void case1()
{
    ring r;
    nurse<nurse1> n1;
    nurse<nurse2> n2;
    baby<baby1> b1;
    baby<baby2> b2;
    guest g;


    r.connect(bind(&nurse<nurse1>::action, n1));
    r.connect(bind(&nurse<nurse2>::action, n2));
    r.connect(bind(&baby<baby1>::action, b1));
    r.connect(bind(&baby<baby2>::action, b2));

    g.press(r);
}

class demo_class
{
public:
    typedef signal<void()> signal_t;
    boost::shared_ptr<signal_t> sig;

    int x;
    demo_class():sig(new signal_t), x(10){}
};

void print()
{   cout << "hello sig." << endl;}

void case2()
{
    demo_class obj;
    assert(obj.sig.use_count() == 1);
    demo_class obj2(obj);
    assert(obj.sig.use_count() == 2);

    obj.sig->connect(&print);
    (*obj2.sig)();
}

class combiner
{
public:
    typedef bool result_type;
    template<typename InputIterator> 
    result_type operator()(InputIterator begin, InputIterator end) const
    {
        while(begin != end)
        {
            if(*begin > 100)
                return true;
        }
        return false;
    }
};


template<int N>
struct slots
{
    int operator()(const connection &conn, int x)
    {
        cout << "conn="<< conn.connected()  << endl;
        return x *N;
    }

};

void case3()
{
    typedef signal<int(int) > signal_t;
    typedef signal_t::extended_slot_type slot_t;
    signal_t sig;


    sig.connect_extended(slot_t(&slots<10>::operator(), slots<10>(), _1, _2));
    sig.connect_extended(slot_t(&slots<20>::operator(), slots<20>(), _1, _2));
    sig(5);
}

void f()
{   cout << "func called" << endl;}

void case4()
{
    boost::function<void()> func;
    func = f;
    func();
    signal<void()> sig;
    sig.connect(&f);
    sig();
}

template<typename Signature>
class sig_ex
{
public:
    typedef signal<Signature> signal_type;
    typedef typename signal_type::slot_type slot_type;

    connection connect(const slot_type& s)
    {   return sig.connect(s);  }

    connection operator+=(const slot_type& s)
    {   return connect(s);  }

    template<typename ... Args>
    typename signal_type::result_type
    operator()(Args&& ... args)
    {   return sig(std::forward<Args>(args)...); }

private:
    signal_type sig;
};

template<int N>
struct slots_ex
{
    int operator()(int x)
    {
        cout << "slot"<< N <<" called" << endl;
        return x *N;
    }

};

void case5()
{
    sig_ex<int(int)> sig;

    sig += slots_ex<10>();
    sig += slots_ex<5>();

    sig(2);
}

int main()
{
    case1();
    case2();
    case3();
    case4();
    case5();
}
[root@192 c++]# g++ -std=c++11 main.cpp 
[root@192 c++]# ./a.out 
A guest press the ring.
Ring alarm...
Mary wakeup and open door.
Kate wakeup and open door.
Baby Tom is sleeping sweetly...
Baby Jerry is sleeping sweetly...
hello sig.
conn=1
conn=1
func called
func called
slot10 called
slot5 called


 备注

1.编译器版本gcc4.8.5,运行环境centos7 64位
2.本文只做简单记录用,详细用法请参考 Boost Library,或者是罗剑锋的 boost程序库完全开发指南 书本
3..原文地址http://www.freecls.com/a/2712/b3


©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。