Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 192|回复: 0

[默认分类] boost的signal和solt机制使用入门

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-5-16 13:59:37 | 显示全部楼层 |阅读模式


    boost的signal和solt机制使用入门


    signal-slot是一个非常方便的接口机制,在Qt和Gtk中广泛使用。boost也实现了一个signal-slot机制。


    编译包含signal-slot的代码
    使用signal-slot,必须包含头文件

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       #include <boost/signal.hpp>  
      

    signal-slot在boost中不是纯头文件,需要一个libboost_signals.so文件,在编译时,需要

      
       
        [plain]
        view plain
         copy
         
       
      
      
       g++ -o signal2 signal2.cpp -l boost_signals  
      



    初见signal-slot
    从HelloWorld开始吧
    首先定义hellword函数

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       void helloworld() {  
           std::cout << "Hello, World!(func)" << std::endl;  
       }  
      


    然后,定义signal对象

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       boost::signal<void ()>sig;  
      


    在main函数中使用

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       int main()  
       {  
           sig.connect(&helloworld);  
           sig();  
       }  
      
    sig()相当与emit。


    除了直接的对象外,还可以使用函数对象

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       struct HelloWorld {  
           void operator() () const  
           {  
               std::cout << "Hello, World!" << std::endl;  
           }  
       };  
      


    在main函数中,这样使用

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       HelloWorld hello;  
       sig.connect(hello);  
      


    还可以使用bind,(请#include <boost/bind.hpp>)

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       void printMore(const std::string& user)  
       {  
           std::cout << user << " say: Hello World!\n";  
       }  
      
    在main函数中,这样使用

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       sig.connect(boost::bind(printMore, "Tom"));  
       sig.connect(boost::bind(printMore, "Jerry"));  
      
    打印的结果是

      
       
        [plain]
        view plain
         copy
         
       
      
      
       Tom say: Hello World!  
       Jerry say: Hello World!  
      



    singal-slot的顺序
    默认情况下,signal-slot是按照添加顺序进行的,例如

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       struct Hello {  
           void operator() () const  
           {  
               std::cout << "Hello ";  
           }  
       };  
       struct World {  
           void operator() () const  
           {  
               std::cout << ", World" << std::endl;  
           }  
       };  
      


    如果这样写

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       sig.connect(Hello());  
       sig.connect(World());  
      
    输入的结果是

      
       
        [plain]
        view plain
         copy
         
       
      
      
       Hello , World  
      
    先调用了Hello,后调用了World
    但是,如果这样写

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       sig.connect(1, World());  
       sig.connect(0, Hello());  
      


    结果仍然同上面的一样。
    signal connection的管理
    disconnection
    signal disconnect方法

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       sig.connect(&helloworld);  
       ....  
       sig.connect(&helloworld);  
      


    目前发现的只有函数可以这样做,函数对象,bind对象都不可以。


    connection对象的disconnect方法

      
       
        [cpp]
        view plain
         copy
         
       
      
      
           HelloWorld hello;  
           boost::signals::connection c = sig.connect(hello);  
       ....  
         
           c.disconnect();  
      



    block slot
    slot可以被暂时阻止,然后在恢复,如

      
       
        [cpp]
        view plain
         copy
         
       
      
      
           HelloWorld hello;  
           boost::signals::connection c = sig.connect(hello);  
       .....  
           c.block();  
           sig();  
       ....  
           c.unblock();  
           sig();  
      


    block和unblock都是boost::signals::connection对象的方法,需要首先得到这个connection。
    在作用域范围内的slot

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       {  
         boost::signals::scoped_connection c = sig.connect(ShortLived());  
         sig(); // will call ShortLived function object  
       }  
       sig(); // ShortLived function object no longer connected to sig  
      


    ShortLive只在作用域内起作用,如果离开了作用域,就不能起作用了。
    slot的自动跟踪
    考虑下面的代码

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       boost::signal<void (const std::string&)> deliverMsg;  
       void autoconnect()  
       {  
           MessageArea * msgarea = new MessageArea();  
         
           deliverMsg.connect(boost::bind(&MessageArea::displayMessage, msgarea, _1));  
         
           deliverMsg("hello world!");  
                
           delete msgarea;  
           //Oops, msgarea is deleted!  
           deliverMsg("again!");  
         
       }  
      
    最后一个deliverMsg被调用时,msgarea已经被删除了,通常情况下,这会引起崩溃。为了避免这个问题,boost引入一个trackable对象。
    请看MessageArea的声明

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       struct MessageArea : public boost::signals::trackable  
       {  
       public:  
           void displayMessage(const std::string& msg) {  
               std::cout<<"** the message is: " << msg<<std::endl;  
           }  
       };  
      
    派生自
    boost::signals::trackable,就可以解决这个自动关闭的问题了!
    autoconnect函数只会调用一次displayMessage。在delete msgarea发生后,deliverMsg对应的slot就被删除了。
    带参数和返回值的signal slot


    带参数的signal
    signal可以添加任意多参数的,比如这个例子

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       void print_sum(float x, float y)  
       {  
           std::cout << "The sum is " << x + y << std::endl;  
       }  
         
       void print_product(float x, float y)  
       {  
           std::cout << "The product is " << x * y << std::endl;  
       }  
         
         
       void print_difference(float x, float y)  
       {  
           std::cout << "The difference is " << x * y << std::endl;  
       }  
      


    定义和使用signal

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       int main()  
       {  
           boost::signal<void (float, float) > sig;  
         
           sig.connect(&print_sum);  
           sig.connect(&print_product);  
           sig.connect(&print_difference);  
         
           sig(5, 3);  
       }  
      
    我们得到的结果,将是

      
       
        [plain]
        view plain
         copy
         
       
      
      
       The sum is 8  
       The product is 15  
       The difference is 15  
      



    带返回值的slot

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       float product(float x, float y) { return x*y; }  
       float quotient(float x, float y) { return x/y; }  
       float sum(float x, float y) { return x+y; }  
       float difference(float x, float y) { return x-y; }  
         
         
       int main(void)  
       {  
           boost::signal<float (float x, float y)> sig;  
         
           sig.connect(&product);  
           sig.connect("ient);  
           sig.connect(&sum);  
           sig.connect(&difference);  
         
           std::cout << sig(5, 3) << std::endl;  
       }  
      


    最后的结果是"
    2",这是最后一个slot difference的结果。signal默认返回最后一个slot的值。


    增加返回值处理器
    如果这不是你想要的值,你可以增加新的返回值处理器来实现

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       template<typename T>  
       struct maximum  
       {  
           typedef T result_type;  
           template<typename InputIterator>  
           T operator()(InputIterator first, InputIterator last) const  
           {  
               if(first == last)  
                   return T();  
         
               T max_value = *first ++;  
               while(first != last) {  
                   if(max_value < *first)  
                       max_value = *first;  
                   ++first;  
               }  
               return max_value;  
           }  
       };  
      


    maximum是一个函数对象,它必须接收两个参数 InputIterator first和last,返回T类型对象。这个例子中,它获取返回值中的最大值。


    它是这样使用的

      
       
        [cpp]
        view plain
         copy
         
       
      
      
           boost::signal<float (float x, float y), maximum<float> > sig;  
       ......  
       .....  
      


    在 signal声明时,作为模板参数给出。


    我们还可以收集slot的返回值,这通过定义一个收集器实现

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       template<typename Container>  
       struct aggregate_values  
       {  
           typedef Container result_type;  
         
           template<typename InputIterator>  
           Container operator()(InputIterator first, InputIterator last) const  
           {  
               return Container(first, last);  
           }  
       };  
      


    这样使用

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       boost::signal<float (float x, float y), aggregate_values<std::vector<float> > > sig2;  
         
       sig2.connect("ient);  
       sig2.connect(&product);  
       sig2.connect(&sum);  
       sig2.connect(&difference);  
         
       std::vector<float> results = sig2(5,3);  
       std::copy(results.begin(), results.end(),  
               std::ostream_iterator<float>(std::cout, " "));  
       std::cout<<std::endl;  
      


    slot执行的结果,将被放在vector<float>对象中,并可以被访问。


    这个返回值收集器在工作的时候,如果first和last没有被访问到,那么,slot就不会被触发。例如

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       template<typename T>  
       struct FirstResult  
       {  
          template<class InputIterator>  
          T operator()(InputIterator first, InputIterator last) {  
               return *first;  
          }  
       };  
      


    这个收集器事实上,仅仅让signal触发了第一个slot,其余的slot均没有被触发。因此,这个slot也可以作为我们过滤slot的方法。


    slot_type传递slot
    slot和signal的声明不会在一个地方(如果那样,就没有必要提供signal-slot机制了),这是,我们需要传递 slot对象,具体做法,是通过signal::slot_type来完成的
    如,下面的例子:

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       class Button  
       {  
           typedef boost::signal<void (int x, int y)> OnClick;  
         
       public:  
           void addOnClick(const OnClick::slot_type& slot);  
         
           void press(int x, int y) {  
               onClick(x, y);  
           }  
         
       private:  
           OnClick onClick;  
       };  
         
       void Button::addOnClick(const OnClick::slot_type& slot)  
       {  
          onClick.connect(slot);  
       }  
      
    OnClick::slot_type定义了slot的类型,且可下面的使用

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       void printCoordinates(long x, long y)  
       {  
           std::cout<<"Button Clicked @(" << x << "," << y <<")\n";  
       }  
         
       void button_click_test()  
       {  
           Button button;  
         
           button.addOnClick(&printCoordinates);  
         
           std::cout<<"===== button onclick test\n";  
         
           button.press(200,300);  
           button.press(20,30);  
           button.press(19,3);  
       }  
      
    button.addOnClick可以直接接收任何能够被signal.connect接受的参数。
    来个综合的例子:Document-View
    定义Document类

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       class Document  
       {  
       public:  
           typedef boost::signal<void (bool)> signal_t;  
           typedef boost::signals::connection connect_t;  
         
       public:  
           Document(){ }  
           connect_t connect(signal_t::slot_function_type subscriber)  
           {  
               return m_sig.connect(subscriber);  
           }  
         
           void disconnect(connect_t subscriber)  
           {  
               subscriber.disconnect();  
           }  
         
           void append(const char* s)  
           {  
               m_text += s;  
               m_sig(true);  
           }  
         
           const std::string& getText() const { return m_text; }  
         
       private:  
           signal_t m_sig;  
           std::string m_text;  
       };  
      


    注意到m_sig定义了一个信号对象。
    View对象建立起和Document的联系

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       class View  
       {  
       public:  
           View(Document& m)  
               :m_doc(m)  
           {  
               m_conn = m_doc.connect(boost::bind(&View::refresh, this, _1));  
           }  
         
           virtual ~View()  
           {  
               m_doc.disconnect(m_conn);  
           }  
         
           virtual void refresh(bool bExtended) const = 0;  
         
       protected:  
           Document& m_doc;  
       private:  
           Document::connect_t m_conn;  
       };  
      


    两个派生类TextView和HexView

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       class TextView : public View  
       {  
       public:  
           TextView(Document& doc) : View(doc) { }  
         
           virtual void refresh(bool bExtended) const {  
               std::cout << "TextView:" << m_doc.getText() << std::endl;  
           }  
       };  
         
       class HexView : public View  
       {  
       public:  
           HexView(Document& doc) : View(doc) { }  
         
           virtual void refresh(bool bExtended) const {  
               std::cout << "HexView: ";  
               const std::string& s = m_doc.getText();  
         
               for(std::string::const_iterator it = s.begin();  
                   it != s.end(); ++it)  
                   std::cout << " " << std::hex << static_cast<int>(*it);  
               std::cout << std::endl;  
           }  
       };  
      


    使用方法:

      
       
        [cpp]
        view plain
         copy
         
       
      
      
       void document_view_test()  
       {  
           Document doc;  
           TextView v1(doc);  
           HexView  v2(doc);  
         
           std::cout<<"================= document view test ===============\n";  
         
           doc.append("Hello world!\n");  
           doc.append("Good!\n");  
           doc.append("Happy!\n");  
       }  
      


    该代码运行后,可以看到如下的结果

      
       
        [plain]
        view plain
         copy
         
       
      
      
       ================= document view test ===============  
       TextView:Hello world!  
         
       HexView:  48 65 6c 6c 6f 20 77 6f 72 6c 64 21 a  
       TextView:Hello world!  
       Good!  
         
       HexView:  48 65 6c 6c 6f 20 77 6f 72 6c 64 21 a 47 6f 6f 64 21 a  
       TextView:Hello world!  
       Good!  
       Happy!  
         
       HexView:  48 65 6c 6c 6f 20 77 6f 72 6c 64 21 a 47 6f 6f 64 21 a 48 61 70 70 79 21 a  

      
      
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-30 03:25 , Processed in 0.633169 second(s), 77 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表