boost的signal和solt機制使用入門

來源:互聯網
上載者:User

本文根據boost的教程文檔整理。

signal-slot是一個非常方便的介面機制,在Qt和Gtk中廣泛使用。boost也實現了一個signal-slot機制。

編譯包含signal-slot的代碼

使用signal-slot,必須包含標頭檔

#include <boost/signal.hpp>

signal-slot在boost中不是純標頭檔,需要一個libboost_signals.so檔案,在編譯時間,需要

g++ -o signal2 signal2.cpp -l boost_signals

初見signal-slot

從HelloWorld開始吧

首先定義hellword函數

void helloworld() {    std::cout << "Hello, World!(func)" << std::endl;}

然後,定義signal對象

boost::signal<void ()>sig;

在main函數中使用

int main(){    sig.connect(&helloworld);    sig();}

sig()相當與emit。

除了直接的對象外,還可以使用函數對象

struct HelloWorld {    void operator() () const    {        std::cout << "Hello, World!" << std::endl;    }};

在main函數中,這樣使用

    HelloWorld hello;    sig.connect(hello);

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

void printMore(const std::string& user){    std::cout << user << " say: Hello World!\n";}

在main函數中,這樣使用

    sig.connect(boost::bind(printMore, "Tom"));    sig.connect(boost::bind(printMore, "Jerry"));

列印的結果是

Tom say: Hello World!Jerry say: Hello World!

singal-slot的順序

預設情況下,signal-slot是按照添加順序進行的,例如

struct Hello {    void operator() () const    {        std::cout << "Hello ";    }};struct World {    void operator() () const    {        std::cout << ", World" << std::endl;    }};

如果這樣寫

    sig.connect(Hello());    sig.connect(World());

輸入的結果是

Hello , World

先調用了Hello,後調用了World

但是,如果這樣寫

    sig.connect(1, World());    sig.connect(0, Hello());

結果仍然同上面的一樣。

signal connection的管理disconnection

signal disconnect方法

sig.connect(&helloworld);....sig.connect(&helloworld);

目前發現的只有函數可以這樣做,函數對象,bind對象都不可以。

connection對象的disconnect方法

    HelloWorld hello;    boost::signals::connection c = sig.connect(hello);....    c.disconnect();

block slot

slot可以被暫時阻止,然後在恢複,如

    HelloWorld hello;    boost::signals::connection c = sig.connect(hello);.....    c.block();    sig();....    c.unblock();    sig();

block和unblock都是boost::signals::connection對象的方法,需要首先得到這個connection。

在範圍範圍內的slot

{  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的自動跟蹤

考慮下面的代碼

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的聲明

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可以添加任意多參數的,比如這個例子

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

int main(){    boost::signal<void (float, float) > sig;    sig.connect(&print_sum);    sig.connect(&print_product);    sig.connect(&print_difference);    sig(5, 3);}

我們得到的結果,將是

The sum is 8The product is 15The difference is 15

帶傳回值的slot

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的值。

增加傳回值處理器

如果這不是你想要的值,你可以增加新的傳回值處理器來實現

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類型對象。這個例子中,它擷取傳回值中的最大值。

它是這樣使用的

    boost::signal<float (float x, float y), maximum<float> > sig;...........

在 signal聲明時,作為模板參數給出。

我們還可以收集slot的傳回值,這通過定義一個收集器實現

template<typename Container>struct aggregate_values{    typedef Container result_type;    template<typename InputIterator>    Container operator()(InputIterator first, InputIterator last) const    {        return Container(first, last);    }};

這樣使用

    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就不會被觸發。例如

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來完成的

如,下面的例子:

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的類型,且可下面的使用

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類

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的聯絡

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

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;    }};

使用方法:

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");}

該代碼運行後,可以看到如下的結果

================= document view test ===============TextView:Hello world!HexView:  48 65 6c 6c 6f 20 77 6f 72 6c 64 21 aTextView:Hello world!Good!HexView:  48 65 6c 6c 6f 20 77 6f 72 6c 64 21 a 47 6f 6f 64 21 aTextView: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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.