2008-09-18 49 views
5

Tôi đã cố gắng triển khai hệ thống sự kiện C# giống như trong C++ với các mẫu chức năng tr1 được sử dụng để lưu trữ một hàm xử lý sự kiện.So sánh tiêu chuẩn :: tr1 :: function <> objects

Tôi tạo ra một vector để nhiều người nghe có thể được gắn liền với sự kiện này, tức là .:

vector< function<void (int)> > listenerList; 

Tôi muốn để có thể loại bỏ một handler từ danh sách để ngăn chặn một người biết lắng nghe nhận sự kiện.

Vì vậy, làm cách nào tôi có thể tìm thấy mục nhập trong danh sách này tương ứng với một người nghe nhất định? Tôi có thể kiểm tra nếu một đối tượng 'chức năng' trong danh sách đề cập đến một chức năng cụ thể không?

Cảm ơn!

EDIT: Đã xem xét phương pháp tăng cường :: tín hiệu, có vẻ như nó có thể được triển khai bằng cách sử dụng hệ thống mã thông báo như một số bạn đã đề xuất. Here's some info on this. Người quan sát giữ lại đối tượng "Kết nối" khi họ đính kèm vào một sự kiện và đối tượng kết nối này được sử dụng để ngắt kết nối nếu cần. Vì vậy, có vẻ như cho dù bạn sử dụng Boost hoặc cuộn của riêng bạn với tr1, nguyên tắc cơ bản là như nhau. tức là nó sẽ hơi vụng về :)

Trả lời

2

FAQ #1 trong tài liệu chức năng tăng dường như giải quyết câu hỏi của bạn - và câu trả lời dễ dàng là "không".

1

proposal (phần IIIb.) Tuyên bố rằng chúng sẽ không thể so sánh được theo bất kỳ cách nào. Nếu bạn đính kèm một số thông tin bổ sung cho họ, bạn có thể dễ dàng xác định từng cuộc gọi lại. Ví dụ, nếu bạn chỉ định nghĩa một struct bao gồm con trỏ hàm, bạn có thể loại bỏ chúng (giả sử bạn có cùng cấu trúc mà bạn đã chèn). Bạn cũng có thể thêm một số trường vào cấu trúc (giống như một guid được tạo tự động mà máy khách có thể giữ) và so sánh với trường đó.

+0

Nếu bạn định sử dụng cấu trúc có thông tin bổ sung, bạn cũng có thể lưu trữ functors dưới dạng giá trị băm và GUID/bất cứ điều gì dưới dạng khóa băm. :-) –

+0

Bạn không thể lưu trữ functors dưới dạng giá trị băm. Bạn không thể làm bất cứ điều gì với họ. Bao bọc chúng trong một cấu trúc (thậm chí là một cấu trúc một phần tử của chỉ là functor) sẽ cho phép bạn làm bất cứ điều gì với chúng. Đó là cố ý mà họ không thể được so sánh/băm/etc mà không có một số công việc. – hazzen

0

Nếu bạn chỉ lưu trữ các con trỏ hàm (và không phải các trình tìm kiếm khác khớp với chữ ký bắt buộc), điều này rất dễ dàng (xem mã bên dưới). Nhưng nói chung, câu trả lời, giống như các áp phích khác đã nói, là không. Trong trường hợp đó, bạn có thể muốn lưu trữ các hàm của bạn trong một băm, dưới dạng các giá trị, với các khóa là thứ mà người dùng cung cấp khi thêm và loại bỏ.

Đoạn mã dưới đây minh họa cách nhận đối tượng functor/pointer được gọi. Để sử dụng nó, bạn phải biết chính xác loại đối tượng cần trích xuất (tức là, typeid của loại bạn chỉ định phải khớp với typeid của functor/con trỏ chứa).

#include <cstdio> 
#include <functional> 

using std::printf; 
using std::tr1::function; 

int main(int, char**); 
static function<int (int, char**)> main_func(&main); 

int 
main(int argc, char** argv) 
{ 
    printf("%p == %p\n", *main_func.target<int (*)(int, char**)>(), &main); 
    return 0; 
} 
0

gì về

map<key-type, function<void (int)> > listeners; 
+0

Tôi nghĩ rằng một băm (std :: tr1 :: unordered_map) là một lựa chọn tốt hơn cho loại điều này. Trừ khi điều quan trọng là gọi người nghe theo thứ tự đăng ký, trong trường hợp này, loại khóa phải bao gồm/là số sê-ri. –

4

Được rồi, bạn đã cho tôi làm việc. Phần khó khăn là cố gắng để phù hợp với mô hình sử dụng chính xác của các sự kiện C#. Nếu bạn bỏ qua điều đó, có nhiều cách dễ dàng hơn để làm những gì bạn đang yêu cầu. (Đồng nghiệp của tôi Jason sử dụng một đối tượng Notifier trên khắp nơi.) Dù sao, đây là mã cực kỳ nhàm chán mà làm những gì bạn muốn. Thật không may, nó không cho phép bạn chuyển các tham số từ Subject đến Observer. Để làm điều đó, bạn cần phải thêm nhiều thông minh hơn nữa.

#include "stdafx.h" 
#include <iostream> 
#include <string> 
#include <list> 
#include <algorithm> 
#include <boost/tr1/functional.hpp> 
#include <boost/tr1/memory.hpp> 

using namespace std; 
using namespace std::tr1; 

template <typename T> 
class ObserverHandle 
{ 
public: 
    typedef boost::function<void (T*)> const UnderlyingFunction; 

    ObserverHandle(UnderlyingFunction underlying) 
     : _underlying(new UnderlyingFunction(underlying)) 
    { 
    } 

    void operator()(T* data) const 
    { 
     (*_underlying)(data); 
    } 

    bool operator==(ObserverHandle<T> const& other) const 
    { 
     return (other._underlying == _underlying); 
    } 

private: 
    shared_ptr<UnderlyingFunction> const _underlying; 
}; 

class BaseDelegate 
{ 
public: 
    virtual bool operator==(BaseDelegate const& other) 
    { 
     return false; 
    } 

    virtual void operator()() const = 0; 
}; 

template <typename T> 
class Delegate : public BaseDelegate 
{ 
public: 
    Delegate(T* observer, ObserverHandle<T> handle) 
     : _observer(observer), 
     _handle(handle) 
    { 
    } 

    virtual bool operator==(BaseDelegate const& other) 
    { 
     BaseDelegate const * otherPtr = &other; 
     Delegate<T> const * otherDT = dynamic_cast<Delegate<T> const *>(otherPtr); 
     return ((otherDT) && 
      (otherDT->_observer == _observer) && 
      (otherDT->_handle == _handle)); 
    } 

    virtual void operator()() const 
    { 
     _handle(_observer); 
    } 

private: 
    T* _observer; 
    ObserverHandle<T> _handle; 
}; 

class Event 
{ 
public: 
    template <typename T> 
    void add(T* observer, ObserverHandle<T> handle) 
    { 
     _observers.push_back(shared_ptr<BaseDelegate>(new Delegate<T>(observer, handle))); 
    } 

    template <typename T> 
    void remove(T* observer, ObserverHandle<T> handle) 
    { 
     // I should be able to come up with a bind2nd(equals(dereference(_1))) kind of thing, but I can't figure it out now 
     Observers::iterator it = find_if(_observers.begin(), _observers.end(), Compare(Delegate<T>(observer, handle))); 
     if (it != _observers.end()) 
     { 
      _observers.erase(it); 
     } 
    } 

    void operator()() const 
    { 
     for (Observers::const_iterator it = _observers.begin(); 
      it != _observers.end(); 
      ++it) 
     { 
      (*(*it))(); 
     } 
    } 

private: 
    typedef list<shared_ptr<BaseDelegate>> Observers; 
    Observers _observers; 

    class Compare 
    { 
    public: 
     Compare(BaseDelegate const& other) 
      : _other(other) 
     { 
     } 

     bool operator() (shared_ptr<BaseDelegate> const& other) const 
     { 
      return (*other) == _other; 
     } 

    private: 
     BaseDelegate const& _other; 
    }; 
}; 

// Example usage: 

class SubjectA 
{ 
public: 
    Event event; 

    void do_event() 
    { 
     cout << "doing event" << endl; 
     event(); 
     cout << "done" << endl; 
    } 
}; 

class ObserverA 
{ 
public: 
    void test(SubjectA& subject) 
    { 
     subject.do_event(); 
     cout << endl; 

     subject.event.add(this, _observe); 
     subject.do_event(); 
     subject.event.remove(this, _observe); 
     cout << endl; 

     subject.do_event(); 
     cout << endl; 

     subject.event.add(this, _observe); 
     subject.event.add(this, _observe); 
     subject.do_event(); 
     subject.event.remove(this, _observe); 
     subject.do_event(); 
     subject.event.remove(this, _observe); 
     cout << endl; 

    } 

    void observe() 
    { 
     cout << "..observed!" << endl; 
    } 

private: 
    static ObserverHandle<ObserverA> _observe; 
}; 

// Here's the trick: make a static object for each method you might want to turn into a Delegate 
ObserverHandle<ObserverA> ObserverA::_observe(boost::bind(&ObserverA::observe, _1)); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    SubjectA sa; 
    ObserverA oa; 
    oa.test(sa); 

    return 0; 
} 

Và đây là kết quả:

làm sự kiện
làm

làm sự kiện
..observed!
làm

làm sự kiện
làm

làm sự kiện
..observed!
..observed!
thực hiện
đang thực hiện sự kiện
..observed!
làm

8

Tôi không biết nếu bạn đang bị khóa vào std C++ và TR1, nhưng nếu bạn không phải là, nó có vẻ như vấn đề của bạn có thể tránh được hoàn toàn nếu bạn chỉ sử dụng một cái gì đó giống như boost :: tín hiệu và tăng cường :: ràng buộc để giải quyết vấn đề ban đầu của bạn - tạo ra một hệ thống sự kiện - thay vì cố gắng cuộn của riêng bạn.

+1

Có, điều quan trọng là phải nhìn thấy khu rừng bất chấp cây cối, để nói chuyện. Cảm ơn bạn đã viết câu trả lời mà tôi ước tôi đã lùi lại để nghĩ đến! –

0

Tôi gặp sự cố tương tự và đã tìm ra giải pháp cho nó. Tôi đã sử dụng một số tính năng C++ 0x, nhưng chỉ để thuận tiện, chúng không phải là một phần thiết yếu. Hãy xem tại đây:
>Messaging system: Callbacks can be anything

Các vấn đề liên quan