2009-04-20 30 views
54

Tôi biết hướng dẫn tại boost.org giải quyết vấn đề này: Boost.org Signals Tutorial, nhưng các ví dụ không hoàn chỉnh và phần nào đơn giản hơn. Các ví dụ không hiển thị tệp bao gồm và một số phần của mã có chút mơ hồ.Ví dụ hoàn chỉnh bằng cách sử dụng Boost :: Tín hiệu cho sự kiện C++

Dưới đây là những gì tôi cần:
ClassA tăng nhiều sự kiện/tín hiệu
ClassB đặt mua những sự kiện (Nhiều lớp học có thể đăng ký)

Trong dự án của tôi, tôi có một lớp xử lý thông điệp cấp thấp hơn tăng các sự kiện lên một lớp nghiệp vụ thực hiện một số xử lý các thông điệp đó và thông báo cho giao diện người dùng (wxFrames). Tôi cần phải biết làm thế nào tất cả những thứ này có thể được nối dây (thứ tự, ai gọi ai, vv).

Trả lời

82

Mã dưới đây là một ví dụ làm việc tối thiểu về những gì bạn yêu cầu. ClassA phát ra hai tín hiệu; SigA gửi (và chấp nhận) không có tham số, SigB gửi int. ClassB có hai chức năng sẽ xuất ra cout khi mỗi chức năng được gọi. Trong ví dụ này có một phiên bản của ClassA (a) và hai trong số ClassB (bb2). main được sử dụng để kết nối và kích hoạt tín hiệu. Cần lưu ý rằng ClassAClassB không biết gì về nhau (nghĩa là chúng không phải là thời gian biên dịch bị ràng buộc).

#include <boost/signal.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 

using namespace boost; 
using namespace std; 

struct ClassA 
{ 
    signal<void()> SigA; 
    signal<void (int)> SigB; 
}; 

struct ClassB 
{ 
    void PrintFoo()  { cout << "Foo" << endl; } 
    void PrintInt(int i) { cout << "Bar: " << i << endl; } 
}; 

int main() 
{ 
    ClassA a; 
    ClassB b, b2; 

    a.SigA.connect(bind(&ClassB::PrintFoo, &b)); 
    a.SigB.connect(bind(&ClassB::PrintInt, &b, _1)); 
    a.SigB.connect(bind(&ClassB::PrintInt, &b2, _1)); 

    a.SigA(); 
    a.SigB(4); 
} 

Sản lượng:

 
Foo 
Bar: 4 
Bar: 4 

Đối với ngắn gọn tôi đã thực hiện một số phím tắt mà bạn thường không sử dụng trong mã sản xuất (trong kiểm soát truy cập đặc biệt là lỏng lẻo và bạn muốn thường 'hide' đăng ký tín hiệu của bạn đằng sau một chức năng như trong ví dụ của KeithB).

Dường như hầu hết các khó khăn trong boost::signal đều được sử dụng để sử dụng boost::bind.Nó một chút tâm uốn đầu tiên! Đối với một ví dụ phức tạp hơn bạn cũng có thể sử dụng bind để treo lên ClassA::SigA với ClassB::PrintInt mặc dù SigA không không phát ra một int:

a.SigA.connect(bind(&ClassB::PrintInt, &b, 10)); 

Hy vọng rằng sẽ giúp!

+0

Có thể quá tải một chức năng không, và nếu có, bạn có thể thêm vào đó không. s.t. bạn có một cái gì đó như PrintNum (int); và PrintNum (phao); – pyInTheSky

+0

@pyInTheSky Sử dụng một loại hàm (không chắc chắn về thuật ngữ chính xác): '(void (*) (int)) & PrintNum' – Qix

6

Bạn có xem boost/libs/signals/example không?

+0

Cảm ơn! những ví dụ này tốt hơn một chút, nhưng sẽ tốt hơn nếu có một ví dụ lớn hơn và thực tế hơn. –

11

Dưới đây là ví dụ từ codebase của chúng tôi. Nó đã được đơn giản hóa, vì vậy tôi không guarentee rằng nó sẽ biên dịch, nhưng nó phải gần gũi. Phân bổ lại là lớp A của bạn, và Slot1 là lớp B. Chúng tôi có một số các khe như thế này, mỗi vị trí đăng ký một tập con khác nhau của các tín hiệu. Ưu điểm của việc sử dụng lược đồ này là Sublocation không biết gì về bất kỳ slot nào và các slot không cần phải là một phần của bất kỳ hệ thống phân cấp thừa kế nào và chỉ cần thực hiện chức năng cho các slot mà chúng quan tâm. Chúng tôi sử dụng tính năng này để thêm chức năng tùy chỉnh vào hệ thống của chúng tôi với giao diện rất đơn giản.

Sublocation.h

class Sublocation 
{ 
public: 
    typedef boost::signal<void (Time, Time)> ContactSignal; 
    typedef boost::signal<void()> EndOfSimSignal; 

    void endOfSim(); 
    void addPerson(Time t, Interactor::Ptr i); 

    Connection addSignalContact(const ContactSignal::slot_type& slot) const; 
    Connection addSignalEndOfSim(const EndOfSimSignal::slot_type& slot) const;  
private: 
    mutable ContactSignal fSigContact; 
    mutable EndOfSimSignal fSigEndOfSim; 
}; 

Sublocation.C

void Sublocation::endOfSim() 
{ 
    fSigEndOfSim(); 
} 

Sublocation::Connection Sublocation::addSignalContact(const ContactSignal::slot_type& slot) const 
{ 
    return fSigContact.connect(slot); 
} 

Sublocation::Connection Sublocation::addSignalEndOfSim(const EndOfSimSignal::slot_type& slot) const 
{ 
    return fSigEndOfSim.connect(slot); 
} 

Sublocation::Sublocation() 
{ 
    Slot1* slot1 = new Slot1(*this); 
    Slot2* slot2 = new Slot2(*this); 
} 

void Sublocation::addPerson(Time t, Interactor::Ptr i) 
{ 
    // compute t1 
    fSigOnContact(t, t1); 
    // ... 
} 

Slot1.h

class Slot1 
{ 
public: 
    Slot1(const Sublocation& subloc); 

    void onContact(Time t1, Time t2); 
    void onEndOfSim(); 
private: 
    const Sublocation& fSubloc; 
}; 

Slot1.C

Slot1::Slot1(const Sublocation& subloc) 
: fSubloc(subloc) 
{ 
    subloc.addSignalContact(boost::bind(&Slot1::onContact, this, _1, _2)); 
    subloc.addSignalEndSim(boost::bind(&Slot1::onEndSim, this)); 
} 


void Slot1::onEndOfSim() 
{ 
    // ... 
} 

void Slot1::onContact(Time lastUpdate, Time t) 
{ 
    // ... 
} 
+0

Ví dụ hay. Mặc dù tôi muốn tranh luận - khá mạnh mẽ - rằng các tín hiệu nên * không * có thể thay đổi và addSignalXXX nên * không * là const. Chúng là một phần của giao diện công cộng và chắc chắn thay đổi hành vi của SubLocation. – MattyT

+0

Tôi nghĩ rằng điểm này là gây tranh cãi. Tôi có thể hiểu bạn đang đến từ đâu. Mặt khác, việc thêm một vị trí không thay đổi bất kỳ trạng thái thay thế trực tiếp nào. Và nếu slot muốn thay đổi trạng thái khi được gọi, nó phải gọi các hàm thành viên nonconst của sublocation. Nếu điều này đã được đưa ra trong một đánh giá mã, tôi muốn nêu trường hợp của tôi, nhưng sẽ không nhớ làm cho sự thay đổi mà bạn đề nghị nếu đó là sự đồng thuận. – KeithB

+1

Tôi cũng có thể thấy đối số của bạn ... có thể đó là một cuộc thảo luận đánh giá mã thú vị. :) – MattyT

1

Tăng tốc như QT cung cấp việc thực hiện riêng các tín hiệu và khe. Sau đây là một số ví dụ về việc thực hiện nó.

tín hiệu và kết nối Khe cắm cho namespace

Xem xét một namespace gọi GStreamer

namespace GStremer 
{ 
    void init() 
    { 
    .... 
    } 
} 

Dưới đây là làm thế nào để tạo và kích hoạt tín hiệu

#include<boost/signal.hpp> 

... 

boost::signal<void()> sigInit; 
sigInit.connect(GStreamer::init); 
sigInit(); //trigger the signal 

tín hiệu và kết nối Khe cắm cho một Class

Hãy xem xét một Lớp được gọi là GSTAdaptor với niềm vui ction gọi Func1 và Func2 với sau chữ ký

void GSTAdaptor::func1() 
{ 
... 
} 

void GSTAdaptor::func2(int x) 
{ 
... 
} 

Dưới đây là làm thế nào để tạo và kích hoạt tín hiệu

#include<boost/signal.hpp> 
#include<boost/bind.hpp> 

... 

GSTAdaptor g; 
boost::signal<void()> sigFunc1; 
boost::signal<void (int)> sigFunc2; 

sigFunc1.connect(boost::bind(&GSTAdaptor::func1, &g); 
sigFunc2.connect(boost::bind(&GSTAdaptor::func2, &g, _1)); 

sigFunc1();//trigger the signal 
sigFunc2(6);//trigger the signal 
0

Khi biên dịch dụ MattyT với tăng mới (f.e. 1,61) sau đó nó đưa ra một cảnh báo

error: #warning "Boost.Signals is no longer being maintained and is now deprecated. Please switch to Boost.Signals2. To disable this warning message, define BOOST_SIGNALS_NO_DEPRECATION_WARNING." 

Vì vậy, hoặc là bạn xác định BOOST_SIGNALS_NO_DEPRECATION_WARNING để ngăn chặn các cảnh báo hoặc bạn có thể dễ dàng chuyển sang boost.signal2 bằng cách thay đổi ví dụ cho phù hợp:

#include <boost/signals2.hpp> 
#include <boost/bind.hpp> 
#include <iostream> 

using namespace boost::signals2; 
using namespace std; 
Các vấn đề liên quan