C ++ Как реализовать базовый класс шаблона шаблона наблюдателя с использованием TR1 - PullRequest
1 голос
/ 22 декабря 2011

Я хотел бы реализовать класс "MyClass", который действует как база для классов Observerable. Например:

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();.....

Ниже приведена реализация, которую я имею до сих пор. Я верю, что это будет хорошо работать, если не считать проблем с компиляцией.

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

Буду признателен за любую помощь по вопросам I и II (в комментариях).

I: Я определяю struct TypeHelper в попытке получить требуемый тип указателя на член. Не смог придумать другой способ сделать это, когда класс набрал шаблонную M.

II: Как я могу принудительно установить тип шаблона в MyClass, чтобы он соответствовал наследуемому типу, как в class Observed : public MyClass<Observed>. Какой лучший подход здесь?

Ответы [ 2 ]

2 голосов
/ 22 декабря 2011

Вот возможное решение с некоторыми изменениями оригинала, который работал (с использованием VC2010 с отключенными расширениями):

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

Возможно, вам потребуется заново добавить пространство имен tr1, если это необходимо. Основным изменением было удаление шаблона класса TypeHelper.

Что касается использования static_cast, то хорошо, если вы уверены, что знаете тип объекта. См. this для подробного ответа на static_cast и dynamic_cast.

EDIT:

Как заставить тип шаблона в MyClass соответствовать типу наследования, как в классе> Observed: public MyClass. Какой лучший подход здесь?

Возможным решением этой проблемы во время выполнения будет использование dynamic_cast, так как он вернет 0, если приведение неверно:

    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 вернет 0, если будет определен следующий класс:

class XObserved : public MyClass<Observed>
{
public:
    void DoWork()
    {
        MyClass<Observed>::NotifyObservers();
    }
};
0 голосов
/ 22 декабря 2011

Как заставить тип шаблона в MyClass соответствовать типу наследование как в классе Observed: публичный MyClass. Что такое лучший подход здесь?

Это можно сделать и во время компиляции. Вероятно, для обработчика Observer наиболее целесообразно выполнить проверку. Добавьте тип, скажем DerivedType, в MyClass, который обработчик может проверить:

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>();
    }
};

Если VV не является производным от MyClass, то HandleObserved () не сможет скомпилироваться, поскольку SameType будет успешным, только если VV имеет тип DerivedType в качестве члена, и если VV и VV :: DerivedType имеют одинаковый тип, который может случается только если VV наследуется от MyClass. Некоторые примеры наблюдаемых:

class Derived: public MyClass<Derived>
{
};

class NotDerived
{
};

class Foo 
{
};

class BadDerived1: public MyClass<Foo>
{
};

class BadDerived2: public MyClass<Derived>
{
};

И тест, который показывает код для этих наблюдаемых, вызывающий ошибку во время компиляции:

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
}

Я не пробовал с VS2010, только 2005.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...