2008-09-18 10 views
5

Ich habe versucht, ein C# -artiges Ereignissystem in C++ mit den tr1-Funktionsvorlagen zu implementieren, die zum Speichern einer Funktion verwendet werden, die das Ereignis behandelt.Vergleichen std :: tr1 :: function <> Objekte

Ich habe einen Vektor, so dass mehrere Zuhörer an dieser Veranstaltung angebracht werden kann, d.h .:

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

ich einen Handler aus der Liste in der Lage sein möchten, entfernen Sie einen Listener Ereignisse empfangen zu stoppen.

Also, wie kann ich den Eintrag in dieser Liste finden, der einem bestimmten Listener entspricht? Kann ich testen, ob sich ein "Funktions" -Objekt in der Liste auf eine bestimmte Funktion bezieht?

Danke!

EDIT: Nach dem boost :: signal-Ansatz scheint es, dass es wahrscheinlich mit einem Token-System implementiert wurde, wie einige von Ihnen vorgeschlagen haben. Here's some info on this. Ein Beobachter behält ein "Connection" -Objekt bei, wenn er an ein Ereignis anfügt, und dieses Verbindungsobjekt wird verwendet, um bei Bedarf die Verbindung zu trennen. So sieht es aus, ob Sie Boost verwenden oder Ihre eigenen mit Tr1 rollen, das Grundprinzip ist das gleiche. d. h. es wird ein wenig tollpatschig :)

Antwort

2

FAQ #1 in der Boost-Funktion Dokumentation scheint Ihre Frage zu adressieren - und die einfache Antwort ist "Nein".

1

Die proposal (Abschnitt IIIb.) Besagt, dass sie in keiner Weise vergleichbar sein werden. Wenn Sie ihnen zusätzliche Informationen hinzufügen, können Sie jeden Rückruf leicht identifizieren. Wenn Sie beispielsweise einfach eine Struktur definieren, die den Funktionszeiger umschließt, können Sie sie entfernen (vorausgesetzt, Sie haben dieselbe Struktur, die Sie eingefügt haben). Sie können der Struktur auch einige Felder hinzufügen (wie eine automatisch generierte GUID, an der sich der Client halten kann) und dagegen vergleichen.

+0

Wenn Sie eine Struktur mit zusätzlichen Informationen verwenden, können Sie auch die Funktoren als Hash-Werte und die GUID/was auch immer als Hash-Schlüssel speichern. :-) –

+0

Sie können Funktoren nicht als Hashwerte speichern. Du kannst nichts mit ihnen machen. Wenn man sie in eine Struktur (sogar eine Ein-Element-Struktur nur des Funktors) einpackt, kann man etwas mit ihnen machen. Es ist beabsichtigt, dass sie nicht ohne etwas Arbeit verglichen werden können. – hazzen

0

Wenn Sie nur Funktionszeiger speichern (und keine anderen Funktoren, die der Signatur entsprechen), ist dies einfach (siehe Code unten). Aber im Allgemeinen ist die Antwort, wie andere Plakate gesagt haben, nein. In diesem Fall möchten Sie Ihre Funktoren wahrscheinlich als Werte in einem Hash speichern, wobei die Schlüssel dem Benutzer beim Hinzufügen und Entfernen zur Verfügung stehen.

Der folgende Code veranschaulicht, wie Sie das Funktor/Zeiger-Objekt abrufen, das aufgerufen werden soll. Um es zu verwenden, müssen Sie den exakten Typ des zu extrahierenden Objekts kennen (d. H. Der typeid des angegebenen Typs muss mit dem typeid des enthaltenen Funktors/Zeigers übereinstimmen).

#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

Was

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

Ich denke, ein Hash (std :: tr1 :: unordered_map) ist eine bessere Wahl für diese Art von Sache. Es sei denn, es ist wichtig, Hörer in der Reihenfolge der Registrierung anzurufen. In diesem Fall sollte der Schlüsseltyp eine Seriennummer enthalten. –

4

Okay, du hast mich arbeiten. Der schwierige Teil versucht, das genaue Verwendungsmuster von C# -Ereignissen zu finden. Wenn Sie das überspringen, gibt es VIEL einfachere Möglichkeiten, um das zu tun, was Sie fragen. (Mein Kollege Jason benutzt überall ein Notifier-Objekt.) Wie auch immer, hier ist der unglaublich langweilige Code, der das tut, was Sie wollen. Leider können Sie keine Parameter vom Subject an den Observer übergeben. Um dies zu tun, müssten Sie noch mehr intelligente hinzufügen.

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

Und hier ist der Ausgang:

Ereignis

tun Veranstaltung
..observed getan zu tun!

getan

Veranstaltung tun

getan

Ereignis tun
..observed!
..beobachtet!
erledigt
doing event
..beobachtet!

getan
8

Ich weiß nicht, ob Sie in std C sind gesperrt ++ und TR1, aber wenn Sie es nicht sind, scheint es, wie Ihr Problem vollständig, wenn Sie nur so etwas wie Auftrieb verwendet vermieden werden könnten :: signal und boost :: bind, um dein ursprüngliches Problem zu lösen - erstelle ein Event-System - anstatt zu versuchen, dein eigenes zu rollen.

+1

Ja, es ist wichtig, den Wald trotz der Bäume sozusagen zu sehen. Danke, dass du mir die Antwort geschrieben hast, von der ich wünschte, ich wäre zurückgetreten! –

0

Ich hatte ein ähnliches Problem und habe eine Lösung gefunden. Ich habe einige C++ 0x-Funktionen verwendet, aber nur aus praktischen Gründen sind sie kein wesentlicher Bestandteil. Sehen Sie sich hier an:
>Messaging system: Callbacks can be anything