Leo
Leo

Reputation: 1223

C++ How to implement observer pattern template base class using TR1

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

Answers (2)

Oliver
Oliver

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

hmjd
hmjd

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

Related Questions