zoukankan      html  css  js  c++  java
  • boost signal2

    boost------signals2的使用1(Boost程序库完全开发指南)读书笔记

    原创 2013年08月01日 08:13:36

    signals2基于Boost的另一个库signals,实现了线程安全的观察者模式。在signals2库中,观察者模式被称为信号/插槽(signals and slots),他是一种函数回调机制,一个信号关联了多个插槽,当信号发出时,所有关联它的插槽都会被调用。

    许多成熟的软件系统都用到了这种信号/插槽机制(另一个常用的名称是事件处理机制:event/event handler),它可以很好地解耦一组互相协作的类,有的语言设置直接内建了对它的支持(如c#),signals2以库的形式为c++增加了这个重要的功能。

    1、操作函数

    signal最重要的操作函数是插槽管理connect()函数,它吧插槽连接到信号上,相当于为信号(事件)增加了一个处理的handler。

    插槽可以是任意的可调用对象,包括函数指针、函数对象、以及它们的bind表达式和function对象,signal内部使用function作为容器来保存这些可调用对象。连接时可以指定组号也可以不指定组号,当信号发生时将依据组号的排序准则依次调用插槽函数。

    如果连接成功connect()将返回一个connection,表示了信号与插槽之间的连接关系,它是一个轻量级的对象,可以处理两者间的连接,如断开、重连接、或者测试连接状态。

    成员函数disconnect()可以断开插槽与信号的连接,它有两种形式:传递组号将断开该组的所有插槽,传递一个插槽对象将仅断开该插槽。函数disconnect_all_slots()可以一次性断开信号的所有插槽连接。

    2、插槽的连接于使用

    signal就像一个增强的function对象,它可以容纳(使用connect())多个符合模板参数中函数签名类型的函数(插槽),形成一个插槽链表,然后在信号发生时一起调用:

    [cpp] view plain copy
     
    1. #include "stdafx.h"  
    2. #include "boost/utility/result_of.hpp"  
    3. #include "boost/typeof/typeof.hpp"  
    4. #include "boost/assign.hpp"  
    5. #include "boost/ref.hpp"  
    6. #include "boost/bind.hpp"  
    7. #include "boost/function.hpp"  
    8. #include "boost/signals2.hpp"  
    9. #include "iostream"  
    10. using namespace std;  
    11.   
    12.   
    13. void slots1()  
    14. {  
    15.     cout << "slots1 call" << endl;  
    16. }  
    17.   
    18. void slots2()  
    19. {  
    20.     cout << "slots2 call" << endl;  
    21. }  
    22.   
    23.   
    24. struct Hello  
    25. {  
    26.     void operator()() const   
    27.     {  
    28.         std::cout << "Hello";  
    29.     }  
    30. };  
    31.   
    32. int _tmain(int argc, _TCHAR* argv[])  
    33. {  
    34.     boost::signals2::signal<void()> sig;  
    35.     sig.connect(&slots1);  
    36.     sig.connect(&slots2);  
    37.   
    38.     sig();  
    39.   
    40.     boost::signals2::signal<void ()> sig1;  
    41.   
    42.     sig1.connect(Hello());  
    43.     sig1();  
    44.   
    45.   
    46.     return 0;  
    47. }  

    注:编译这个程序的时候,确保已经在stdafx.h中加入#define _SCL_SECURE_NO_WARNINGS或者,在C/C++----预处理器----预处理器定义中加上了_SCL_SECURE_NO_WARNINGS,否则会引发错误(或不能正确输出),下同。

    在连接插槽时省了了connect()的第二个参数connect_position,它的缺省值是at_back,表示插槽将插入到信号插槽链表的尾部,因此slot2将在slot1之后被调用。

    下面是使用和组号的情况:

    [cpp] view plain copy
     
    1. #include "stdafx.h"  
    2. #include "boost/utility/result_of.hpp"  
    3. #include "boost/typeof/typeof.hpp"  
    4. #include "boost/assign.hpp"  
    5. #include "boost/ref.hpp"  
    6. #include "boost/bind.hpp"  
    7. #include "boost/function.hpp"  
    8. #include "boost/signals2.hpp"  
    9. #include "iostream"  
    10. using namespace std;  
    11.   
    12.   
    13. template<int N>  
    14. struct Slot  
    15. {  
    16.     void operator()()  
    17.     {  
    18.         cout << "Slot current N value is : " << N << endl;  
    19.     }  
    20. };  
    21.   
    22.   
    23. int _tmain(int argc, _TCHAR* argv[])  
    24. {  
    25.     boost::signals2::signal<void()> sig;  
    26.   
    27.     sig.connect(Slot<1>(), boost::signals2::at_back);  // 最后一个被调用  
    28.     sig.connect(Slot<99>(), boost::signals2::at_front);  // 第一个被调用  
    29.   
    30.     sig.connect(5, Slot<55>(), boost::signals2::at_back); // 组号5的最后一个  
    31.     sig.connect(5, Slot<57>(), boost::signals2::at_front);// 组号5的第一个  
    32.   
    33.     sig.connect(10, Slot<100>());// 组号10该组只有一个  
    34.   
    35.     sig();  
    36.   
    37.     return 0;  
    38. }  

    3、信号的返回值

    signal如function一样,不仅可以把输入参数转发给所以插槽,也可以传回插槽的返回值。默认情况下signal使用合并器optional_last_value<R>,它将使用optional对象返回最后被调用的插槽的返回值。

    [cpp] view plain copy
     
    1. #include "stdafx.h"  
    2. #include "boost/utility/result_of.hpp"  
    3. #include "boost/typeof/typeof.hpp"  
    4. #include "boost/assign.hpp"  
    5. #include "boost/ref.hpp"  
    6. #include "boost/bind.hpp"  
    7. #include "boost/function.hpp"  
    8. #include "boost/signals2.hpp"  
    9. #include "iostream"  
    10. using namespace std;  
    11.   
    12.   
    13. template<int N>  
    14. struct Slot  
    15. {  
    16.     int operator()(int x)  
    17.     {  
    18.         cout << "Slot current N * x value is : " << endl;  
    19.   
    20.         return (N * x);  
    21.     }  
    22. };  
    23.   
    24.   
    25. int _tmain(int argc, _TCHAR* argv[])  
    26. {  
    27.     boost::signals2::signal<int(int)> sig;  
    28.   
    29.     sig.connect(Slot<10>());  
    30.     sig.connect(Slot<100>());  
    31.     cout << *sig(2) << endl;;  
    32.   
    33.     return 0;  
    34. }  

    signal的operator()调用这时需要传入一个整数参数,这个参数会被signal存储一个拷贝,然后转发给各个插槽。最后signal将返回插槽链表末尾slots<100>()的计算结果,它是一个optional对象,必须用接引用操作符*来获得值(但你可以发现Slotcurrent N * x value is是输出了两次的)。


    4、合并器

    大多数时候,插槽的返回值都是有意义的,需要以某种方式处理多个插槽的返回值。

    signal允许用户自定义合并器来处理插槽的返回值,把多个插槽的返回值合并为一个结果返回给用户。

    [cpp] view plain copy
     
    1. #include "stdafx.h"  
    2. #include "boost/utility/result_of.hpp"  
    3. #include "boost/typeof/typeof.hpp"  
    4. #include "boost/assign.hpp"  
    5. #include "boost/ref.hpp"  
    6. #include "boost/bind.hpp"  
    7. #include "boost/function.hpp"  
    8. #include "boost/signals2.hpp"  
    9. #include "numeric"  
    10. #include "iostream"  
    11. using namespace std;  
    12.   
    13.   
    14. template<int N>  
    15. struct Slot  
    16. {  
    17.     int operator()(int x)  
    18.     {  
    19.         cout << "Slot current N * x value is : " << endl;  
    20.   
    21.         return (N * x);  
    22.     }  
    23. };  
    24.   
    25.   
    26. template<typename T>  
    27. class combiner  
    28. {  
    29. public:  
    30.     typedef pair<T, T> result_type;  
    31.     combiner(T t = T()) : v(t)  
    32.     {  
    33.   
    34.     }  
    35.   
    36.     template<typename InputIterator>  
    37.     result_type operator()(InputIterator begin, InputIterator end) const  
    38.     {  
    39.         if (begin == end)  
    40.         {  
    41.             return result_type();  
    42.         }  
    43.   
    44.         vector<T> vec(begin, end);  
    45.         T sum = accumulate(vec.begin(), vec.end(), v);  
    46.         T max = *max_element(vec.begin(), vec.end());  
    47.   
    48.         return result_type(sum, max);  
    49.     }  
    50.   
    51. private:  
    52.     T v;  
    53. };  
    54.   
    55.   
    56. int _tmain(int argc, _TCHAR* argv[])  
    57. {  
    58.     boost::signals2::signal<int(int), combiner<int> > sig;  
    59.   
    60.     sig.connect(Slot<10>());  
    61.     sig.connect(Slot<20>());  
    62.     sig.connect(Slot<3>());  
    63.   
    64.     BOOST_AUTO(x, sig(2));  
    65.     cout << x.first << ", " << x.second << endl;  
    66.   
    67.     return 0;  
    68. }  

    combiner类的调用操作符operator()的返回值类型可以是任意类型,完全由用户指定,不一定必须是optional或者是插槽的返回值类型。operator()的模板参数InputIterator是插槽链表的返回值迭代器,可以使用它来遍历所有插槽的返回值,进行所需的处理。

    当信号被调用时,signal会自动把引用操作转换为插槽调用,将调用给定的合并器的operator()逐个处理插槽的返回值,并最终返回合并器operator()的结果。

    如果我们不适用signal的缺省构造函数,而是在构造signal时传入一个合并器的实例,那么signal将使用逐个合并器(的拷贝)处理返回值。例如,下面的代码使用了一个有初值的合并器对象,累加值从100开始:

    signal<int(int),combiner<int> > sig(combiner<int>(100));


    5、管理信号的连接

    信号与插槽的链接并不要求是永久的,当信号调用玩插槽后,有可能需要把插槽从信号中断开,再连接到其他的信号上去。

    signal可以用成员函数disconnect()断开一个或一组插槽,或者使用disconnect_all_slots()断开所有插槽连接,函数empty()和num_slots()用来检测信号当前插槽的连接状态。

    要断开一个插槽,插槽必须能够进行登记等价比较,对于函数对象来说就是重载一个等价语义的operator==。下面对slots<N>增加一个等价比较:

    [cpp] view plain copy
     
    1. #include "stdafx.h"  
    2. #include "boost/utility/result_of.hpp"  
    3. #include "boost/typeof/typeof.hpp"  
    4. #include "boost/assign.hpp"  
    5. #include "boost/ref.hpp"  
    6. #include "boost/bind.hpp"  
    7. #include "boost/function.hpp"  
    8. #include "boost/signals2.hpp"  
    9. #include "numeric"  
    10. #include "iostream"  
    11. using namespace std;  
    12.   
    13.   
    14. template<int N>  
    15. struct Slot  
    16. {  
    17.     int operator()(int x)  
    18.     {  
    19.         cout << "Slot current N * x value is : " << endl;  
    20.   
    21.         return (N * x);  
    22.     }  
    23. };  
    24.   
    25. template<int N>  
    26. bool operator== (const Slot<N>& a, const Slot<N>& b)  
    27. {  
    28.     return true;  
    29. }  
    30.   
    31. template<typename T>  
    32. class combiner  
    33. {  
    34. public:  
    35.     typedef pair<T, T> result_type;  
    36.     combiner(T t = T()) : v(t)  
    37.     {  
    38.   
    39.     }  
    40.   
    41.     template<typename InputIterator>  
    42.     result_type operator()(InputIterator begin, InputIterator end) const  
    43.     {  
    44.         if (begin == end)  
    45.         {  
    46.             return result_type();  
    47.         }  
    48.   
    49.         vector<T> vec(begin, end);  
    50.         T sum = accumulate(vec.begin(), vec.end(), v);  
    51.         T max = *max_element(vec.begin(), vec.end());  
    52.   
    53.         return result_type(sum, max);  
    54.     }  
    55.   
    56. private:  
    57.     T v;  
    58. };  
    59.   
    60.   
    61. int _tmain(int argc, _TCHAR* argv[])  
    62. {  
    63.     boost::signals2::signal<int(int)> sig;  
    64.  // assert(sig.empty());  
    65.    
    66.     sig.connect(0, Slot<10>());  
    67.     sig.connect(Slot<20>());  
    68.     sig.connect(1, Slot<30>());  
    69.   
    70.     assert(sig.num_slots() == 3);  
    71.     sig.disconnect(0);  
    72.   
    73. //  assert(sig.num_slots() == 1);  
    74.     sig.disconnect(Slot<30>());  
    75.   
    76. //  assert(sig.empty());  
    77.   
    78.     sig(2);  
    79.   
    80.     return 0;  
    81. }  

    6、更灵活的管理信号连接

    signals2库提供另外一种较为灵活的连接管理方式:使用connection对象。

    每当signal使用connect()连接插槽时,他就会返回一个connection对象。connection对象像是信号与插槽连接关系的一个句柄(handle),可以管理链接:

    [cpp] view plain copy
     
    1. #include "stdafx.h"  
    2. #include "boost/utility/result_of.hpp"  
    3. #include "boost/typeof/typeof.hpp"  
    4. #include "boost/assign.hpp"  
    5. #include "boost/ref.hpp"  
    6. #include "boost/bind.hpp"  
    7. #include "boost/function.hpp"  
    8. #include "boost/signals2.hpp"  
    9. #include "numeric"  
    10. #include "iostream"  
    11. using namespace std;  
    12.   
    13.   
    14. template<int N>  
    15. struct Slot  
    16. {  
    17.     int operator()(int x)  
    18.     {  
    19.         cout << "Slot current N * x value is : " << endl;  
    20.   
    21.         return (N * x);  
    22.     }  
    23. };  
    24.   
    25. template<int N>  
    26. bool operator== (const Slot<N>& a, const Slot<N>& b)  
    27. {  
    28.     return true;  
    29. }  
    30.   
    31. template<typename T>  
    32. class combiner  
    33. {  
    34. public:  
    35.     typedef pair<T, T> result_type;  
    36.     combiner(T t = T()) : v(t)  
    37.     {  
    38.   
    39.     }  
    40.   
    41.     template<typename InputIterator>  
    42.     result_type operator()(InputIterator begin, InputIterator end) const  
    43.     {  
    44.         if (begin == end)  
    45.         {  
    46.             return result_type();  
    47.         }  
    48.   
    49.         vector<T> vec(begin, end);  
    50.         T sum = accumulate(vec.begin(), vec.end(), v);  
    51.         T max = *max_element(vec.begin(), vec.end());  
    52.   
    53.         return result_type(sum, max);  
    54.     }  
    55.   
    56. private:  
    57.     T v;  
    58. };  
    59.   
    60.   
    61. int _tmain(int argc, _TCHAR* argv[])  
    62. {  
    63.     boost::signals2::signal<int(int)> sig;  
    64.     boost::signals2::connection c1 = sig.connect(0, Slot<10>());  
    65.     boost::signals2::connection c2 = sig.connect(0, Slot<10>());  
    66.     boost::signals2::connection c3 = sig.connect(0, Slot<10>());  
    67.   
    68.     c1.disconnect();  
    69.     assert(sig.num_slots() == 2);  
    70.     assert(!c1.connected());  
    71.     assert(c2.connected());  
    72.   
    73.     return 0;  
    74. }  
    [cpp] view plain copy
     
    1.   

    另外一种连接管理对象是scoped_connection,它是connection的种类,提供类似scoped_ptr的RAII功能:插槽与信号的连接仅在作用域内生效,当离开作用域时连接就会自动断开。当需要临时连接信号时scoped_connection会非常有用:

    [cpp] view plain copy
     
    1. #include "stdafx.h"  
    2. #include "boost/utility/result_of.hpp"  
    3. #include "boost/typeof/typeof.hpp"  
    4. #include "boost/assign.hpp"  
    5. #include "boost/ref.hpp"  
    6. #include "boost/bind.hpp"  
    7. #include "boost/function.hpp"  
    8. #include "boost/signals2.hpp"  
    9. #include "numeric"  
    10. #include "iostream"  
    11. using namespace std;  
    12.   
    13.   
    14. template<int N>  
    15. struct Slot  
    16. {  
    17.     int operator()(int x)  
    18.     {  
    19.         cout << "Slot current N * x value is : " << endl;  
    20.   
    21.         return (N * x);  
    22.     }  
    23. };  
    24.   
    25. template<int N>  
    26. bool operator== (const Slot<N>& a, const Slot<N>& b)  
    27. {  
    28.     return true;  
    29. }  
    30.   
    31. template<typename T>  
    32. class combiner  
    33. {  
    34. public:  
    35.     typedef pair<T, T> result_type;  
    36.     combiner(T t = T()) : v(t)  
    37.     {  
    38.   
    39.     }  
    40.   
    41.     template<typename InputIterator>  
    42.     result_type operator()(InputIterator begin, InputIterator end) const  
    43.     {  
    44.         if (begin == end)  
    45.         {  
    46.             return result_type();  
    47.         }  
    48.   
    49.         vector<T> vec(begin, end);  
    50.         T sum = accumulate(vec.begin(), vec.end(), v);  
    51.         T max = *max_element(vec.begin(), vec.end());  
    52.   
    53.         return result_type(sum, max);  
    54.     }  
    55.   
    56. private:  
    57.     T v;  
    58. };  
    59.   
    60.   
    61. int _tmain(int argc, _TCHAR* argv[])  
    62. {  
    63.     boost::signals2::signal<int(int)> sig;  
    64.   
    65.     sig.connect(0, Slot<10>());  
    66.     assert(sig.num_slots() == 1);  
    67.     {  
    68.         boost::signals2::scoped_connection sc = sig.connect(0, Slot<20>());  
    69.         assert(sig.num_slots() == 2);  
    70.     }  
    71.     assert(sig.num_slots() == 1);  
    72.   
    73.     return 0;  
    74. }  

    插槽与信号的连接一旦断开就不能再连接起来,connection不提供reconnet()这样的函数。但可以暂时地阻塞插槽与信号的连接,当信号发生时被阻塞的插槽将不会被调用,connection对象的blocked()函数可以检查插槽是否被阻塞。但被阻塞的插槽并没有断开与信号的链接,在需要的时候可以随时解除阻塞。

    connection对象自身没有阻塞的功能,他需要一个辅助类shared_connection_block,它将阻塞connection对象,知道它被析构或者显式调用unblock()函数:

    [cpp] view plain copy
     
    1. #include "stdafx.h"  
    2. #include "boost/utility/result_of.hpp"  
    3. #include "boost/typeof/typeof.hpp"  
    4. #include "boost/assign.hpp"  
    5. #include "boost/ref.hpp"  
    6. #include "boost/bind.hpp"  
    7. #include "boost/function.hpp"  
    8. #include "boost/signals2.hpp"  
    9. #include "numeric"  
    10. #include "iostream"  
    11. using namespace std;  
    12.   
    13.   
    14. template<int N>  
    15. struct Slot  
    16. {  
    17.     void operator()(int x)  
    18.     {  
    19.         cout << "Slot current N is : " << N << endl;  
    20.     }  
    21. };  
    22.   
    23. template<int N>  
    24. bool operator== (const Slot<N>& a, const Slot<N>& b)  
    25. {  
    26.     return true;  
    27. }  
    28.   
    29. template<typename T>  
    30. class combiner  
    31. {  
    32. public:  
    33.     typedef pair<T, T> result_type;  
    34.     combiner(T t = T()) : v(t)  
    35.     {  
    36.   
    37.     }  
    38.   
    39.     template<typename InputIterator>  
    40.     result_type operator()(InputIterator begin, InputIterator end) const  
    41.     {  
    42.         if (begin == end)  
    43.         {  
    44.             return result_type();  
    45.         }  
    46.   
    47.         vector<T> vec(begin, end);  
    48.         T sum = accumulate(vec.begin(), vec.end(), v);  
    49.         T max = *max_element(vec.begin(), vec.end());  
    50.   
    51.         return result_type(sum, max);  
    52.     }  
    53.   
    54. private:  
    55.     T v;  
    56. };  
    57.   
    58.   
    59. int _tmain(int argc, _TCHAR* argv[])  
    60. {  
    61.     boost::signals2::signal<void(int)> sig;  
    62.   
    63.     boost::signals2::connection c1 = sig.connect(0, Slot<10>());  
    64.     boost::signals2::connection c2 = sig.connect(0, Slot<20>());  
    65.   
    66.     assert(sig.num_slots() == 2);  
    67.     sig(2);  
    68.   
    69.     cout << "begin blocking..." << endl;  
    70.     {  
    71.         boost::signals2::shared_connection_block block(c1);  
    72.         assert(sig.num_slots() == 2);  
    73.         assert(c1.blocked());  
    74.         sig(2);  
    75.     }  
    76.       
    77.     cout << "end blocking.." << endl;  
    78.     assert(!c1.blocked());  
    79.     sig(2);  
    80.   
    81.     return 0;  
    82. }  
  • 相关阅读:
    MVC 中创建自己的异常处理
    ajax 跨域解决方法
    值传递,引用传递
    值类型,引用类型 结合 栈,堆的理解
    SQL中 ALL 和 ANY 区别的
    SQL中的逻辑运算符
    使用函数输出两个数的最大值
    九句话,送给正在奋斗的我们,每天读一遍
    C语言函数调用简简介
    优秀项目经理的七个习惯
  • 原文地址:https://www.cnblogs.com/bingzzzZZZ/p/8404110.html
Copyright © 2011-2022 走看看