Boost logo

Boost Users :

From: Ian McCulloch (ianmcc_at_[hidden])
Date: 2006-10-06 21:22:52


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


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net