
Stefan Schild wrote:
Ian McCulloch wrote:
Stefan, can you give more details, i.e. a *real* example where you want it to fail to compile, but it does?
I can give you the original design I thought may be possible to improve. It's out of memory so don't hit me if it contains bugs.
We have a message factory class that stores and executes message functors. The "DoDispatch" and "Execute" methods needs a pointer to T, because we want to access members of the derived class in the message functor (we want do to something useful...).
template<class T> class MessageFunctor { public: void Execute( T* target ) = 0; }
template<class T> class MessageFactory { // Contains the logic to store, find and execute // message functors. Something like: public:
I guess there is something like typedef std::map<int, MessageFunctor<T> > MessageFunctorMap; supposed to be here?
virtual void Dispatch( T* d, const int key ) { MessageFunctorMap::iterator mf = m_message_map.find(key); if ( mf != m_message_functor_map.end() ) mf->Execute( d ); } private: MessageFunctorMap m_message_functor_map; }
If we want to add message dispatching to any class, we apply the CRTP and add a dispatch method that calls DoDispatch with the this pointer:
class ControlledClass : public MessageFactory<ControlledClass> { void DoDispatch( const int key ) { DoDispatch( this, key ); } }
Now, if we were SURE that the T in the MessageFactory class is equal to ControlledClass, i.e., equal to a derived class, couldn't we get our hands on the this pointer in the MessageFactory class already? We would not need the DispatchMessage method any more...
template<class T> class MessageFactory { // Contains the logic to store, find and execute // message functors. Something like: public: virtual void Dispatch( const int key ) { // Make sure that T is equal to ControlledClass, // i.e., that the CRTP was used, so we can use this MessageFunctorMap::iterator mf = m_message_map.find(key); if ( mf != m_message_functor_map.end() ) mf->Execute( this ); } private: MessageFunctorMap m_message_functor_map; }
In CRTP, the functions are usually not virtual. The ideom is template<class T> class MessageFactory { public: typedef T self_type; self_type& self() { return static_cast<T&>(*this); } self_type const& self() { return static_cast<T const&>(*this); } void Dispatch(int const key) { MessageFunctorMap::iterator mf = m_message_map.find(key); if ( mf != m_message_functor_map.end() ) mf->Execute( this->self() ); // pass the derived class here } // ... } [ aside: a common usage is to simply forward to the derived class, as in void MessageFactory<T>::foo() { return this->self().foo(); } ] If T does not inherit from MessageFactory<T>, the static_cast inside the self() function will not compile. If you wanted a more informative error message, that would be the place to put the STATIC_ASSERT; but a comment would also suffice; you will get a compile failure anyway. Although I guess there are some weird cases where it would compile, eg if MessageFactory itself inherited from some class U, and someone tried to instantiate MessageFactory<U> .... So on second thoughts maybe the STATIC_ASSERT is a good idea!. Cheers, Ian