Reputation: 1223
I would like to implement a class "MyClass" that acts as a base for Observerable classes. For example:
class Observed : public MyClass<Observed>{
public: void DoWork() {.... MyClass<Observed>::NotifyObservers();}
};
class AnyObserver {public: HandleObserved(Observed& ob){...}};
AnyObserver any;
Observed observed;
observed.AddObserver(&any, &AnyObserver::HandleObserved);
observed.DoWork();.....
The following is the implementation I have so far. I believe this will work just fine, except I went into compilation problems.
template <typename T>
class MyClass
{
typedef std::tr1::function<void( T& )> CallbackFunction;
std::list< CallbackFunction > m_observers;
template<class S>
struct TypeHelper
{
//I. Compilation error is here: Unexpected "...
typedef void (typename S::*Signature)(T&);
};
public:
template<class M>
void AddObserver(M& observer, TypeHelper<M>::Signature callback)
{
CallbackFunction bound = std::tr1::bind(callback, observer, tr1::placeholders::_1);
m_observers.push_back(bound);
}
protected:
void NotifyObservers()
{
std::list< CallbackFunction >::iterator iter = m_observers.begin();
for (; iter != m_observers.end(); ++iter )
{
// II. is static_cast the right way to go?
(*iter)(static_cast<T&>(*this));
}
}
};
Any help with issues I and II (in comments) will be appreciated.
I: I define struct TypeHelper in an attempt to obtain the desired member pointer type. Couldn't figure out another way to do this when the class type it the templated M.
II: How can I enforce the template type in MyClass to match the type inheriting as in class Observed : public MyClass<Observed>
. What's the best approach here?
Upvotes: 1
Views: 1114
Reputation: 29571
How can I enforce the template type in MyClass to match the type inheriting as in class Observed : public MyClass. What's the best approach here?
This can be done at compile time too. It probably makes most sense for the Observer handler to do the check. Add a type, say DerivedType, in MyClass that the handler can check for:
template <typename TT>
class MyClass
{
public:
typedef TT DerivedType;
};
template <typename AA, typename BB>
struct SameType;
template <typename AA>
struct SameType<AA, AA> {};
class AnyObserver
{
public:
template <typename VV>
void HandleObserved(VV&)
{
std::cout << "Observed\n";
SameType<VV, VV::DerivedType>();
}
};
If VV does not derive from MyClass, then HandleObserved() will fail to compile because the SameType will succeed only if VV has a type DerivedType as a member, and if VV and VV::DerivedType are the same type, which can only happen if VV derives from MyClass. Some examples of observables:
class Derived: public MyClass<Derived>
{
};
class NotDerived
{
};
class Foo
{
};
class BadDerived1: public MyClass<Foo>
{
};
class BadDerived2: public MyClass<Derived>
{
};
And a test that shows code for those observables, causing compile-time error:
void test()
{
Derived foo; // ok
NotDerived bad1; // ok so far
BadDerived1 bar1; // ok so far
BadDerived2 bar2; // ok so far
AnyObserver obs;
obs.HandleObserved(foo); // ok
//obs.HandleObserved(bad1); // BANG! at compile time
//obs.HandleObserved(bar1); // BANG! at compile time
//obs.HandleObserved(bar2); // BANG! at compile time
}
I haven't tried with VS2010, only 2005.
Upvotes: 0
Reputation: 122001
Here is a possible solution with some changes to the original that worked (using VC2010 with extensions disabled):
template <typename T>
class MyClass
{
private:
typedef std::function<void( T& )> CallbackFunction;
std::list< CallbackFunction > m_observers;
public:
virtual ~MyClass() {}
template<class M>
void AddObserver(M& observer, void typename (M::*callback)(T&))
{
CallbackFunction bound =
std::bind(callback, observer, std::placeholders::_1);
m_observers.push_back(bound);
}
protected:
void NotifyObservers()
{
std::list< CallbackFunction >::iterator iter = m_observers.begin();
for (; iter != m_observers.end(); ++iter )
{
(*iter)(static_cast<T&>(*this));
}
}
};
class Observed : public MyClass<Observed>
{
public:
void DoWork()
{
MyClass<Observed>::NotifyObservers();
}
};
class AnyObserver
{
public:
void HandleObserved(Observed&)
{
std::cout << "Observed\n";
}
};
int main(int /*a_argc*/, char** /*a_argv*/)
{
AnyObserver any;
Observed observed;
observed.AddObserver(any, &AnyObserver::HandleObserved);
observed.DoWork();
return 0;
}
You may need to re-add tr1
namespace where appropriate. The main change was to remove the TypeHelper
template class.
As for use of static_cast
it is fine if you are certain you know the type of the object. See this for a detailed answer on static_cast
and dynamic_cast
.
EDIT:
How can I enforce the template type in MyClass to match the type inheriting as in class > Observed : public MyClass. What's the best approach here?
A possible runtime solution to this would be to use dynamic_cast
, as it will return 0 if the cast is invalid:
void NotifyObservers()
{
std::list< CallbackFunction >::iterator iter = m_observers.begin();
for (; iter != m_observers.end(); ++iter )
{
T* t = dynamic_cast<T*>(this);
if (0 != t)
{
(*iter)(*t);
}
else
{
std::cerr << "Failed to cast\n";
}
}
}
dynamic_cast
would return 0 if the following class was defined:
class XObserved : public MyClass<Observed>
{
public:
void DoWork()
{
MyClass<Observed>::NotifyObservers();
}
};
Upvotes: 2