Okay, you got me working. The hard part is trying to match the exact usage pattern of C# events. If you skip that, there are MUCH easier ways to do what you're asking. (My co-worker Jason uses a Notifier object all over the place.) Anyway, here's the incredibly boring code which does what you want. Unfortunately, it doesn't allow you to pass parameters from the Subject to the Observer. To do that, you'd need to add even more smarts.
#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;
}
And here's the output:
doing event
done
doing event
..observed!
done
doing event
done
doing event
..observed!
..observed!
done
doing event
..observed!
done