Thread: desiging generic message class

  1. #1
    Registered User
    Join Date
    Jan 2007
    Posts
    330

    desiging generic message class

    I'm having a problem designing a generic message class to be used in a mailbox like system.
    What I want is have a baseclass 'Message' with a certain interface and subclasses define their own data which can be retreived through that interface. But I dont know how...

    Something like:

    Code:
    class Message 
    { 
    //   ???? 
    };
    
    class ConcreteMessage : public Message
    {
    public:
    // override function "????"
    private:
      int x;
      short y;
    };
    Then there is one function

    Code:
    void HandleMessage(Message &message)
    {
      message.Parse(); // how to get the int and short here?
    }
    I hope I'm being clear. Is there something that can be done in a generic way without reverting to something like:

    Code:
    void HandleMessage(Message &message)
    {
      if (message.type() == INT_SHORT)
      {
        try {
          ConcreteMessage &mymessage = dynamic_cast<ConcreteMessage &>(message);
    
          int x     = mymessage.get_int();
          short s = mymessage.get_short();
        }
        catch (std::bad_cast &e) {}
      }
    }

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    You really shouldn't tell us how you'd like to do something.

    Instead, you should tell us what you're trying to do.

    For example, what does `HandleMessage' do?

    Soma

  3. #3
    Registered User
    Join Date
    Jan 2007
    Posts
    330
    ok

    I'm trying to create a messagebus.

    Components subscribe themselves to certain message types they are interested in and when another component sends a message with that type all the components that registered to that type
    will receive that message with a ConcrteMessage passed in.

    Code:
    class UpdateMessage { /* data */ };
    class ShutDownMessage { /* data */ };
    
    class Model : public Component
    {
    public:
      Model(const std::string &name, MessageBroker &broker)
    	: Component(name, broker)
      {
    // register to receive UPDATE_MESSAGE and SHUTDOWN types
        broker.Subscribe(*this, UPDATE_MESSAGE); 
        broker.Subscribe(*this, SHUTDOWN); 
      }
    
      virtual void OnMessage(Message &message)
      {
         // how can I get the internal data of the ShutDownMessage and UpdateMessage
         // without dynamic_cast?
      }
    };

  4. #4
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    >_<

    THE FORUM ATE MY POST!

    *sigh*

    You'll have to do without all the source and explanation, but I recommend adding a little template magic in the `Subscribe' method which forwards to a virtual `ChildSubscribe' in order to produce a "bound functor" operating as the message type exporting the "message slot" (the enumeration value) and the "callback" mechanism (the visitor pattern) so that you can "virtually request" that the `MessageBroker' object eventually call a specific method typed to a specific "concrete" class of the `Message' variety without having to do any work in any of the `Message' or `Component' classes.

    Code:
    broker.Subscribe(*this, SHUTDOWN_MESSAGE, OnShutDownMessage);
    Code:
    void OnShutDownMessage(ShutDownMessage&message)
    {
    // use "concrete" class normally
    }
    Soma ;_;

  5. #5
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by KIBO View Post
    ok

    I'm trying to create a messagebus.

    Components subscribe themselves to certain message types they are interested in and when another component sends a message with that type all the components that registered to that type
    will receive that message with a ConcrteMessage passed in.

    Code:
    class UpdateMessage { /* data */ };
    class ShutDownMessage { /* data */ };
    
    class Model : public Component
    {
    public:
      Model(const std::string &name, MessageBroker &broker)
        : Component(name, broker)
      {
    // register to receive UPDATE_MESSAGE and SHUTDOWN types
        broker.Subscribe(*this, UPDATE_MESSAGE); 
        broker.Subscribe(*this, SHUTDOWN); 
      }
    
      virtual void OnMessage(Message &message)
      {
         // how can I get the internal data of the ShutDownMessage and UpdateMessage
         // without dynamic_cast?
      }
    };
    AFAIK, there isn't really a way to avoid the dynamic_cast, but it can be somewhat automated using a technique along these lines:

    Code:
    #include <list>
    
    /*
        Just a placeholder for a *real* smart-pointer 
        (recommend std::tr1::shared_ptr, or similar).
    */
    template < typename Type >
    struct not_so_smart_pointer
    {
        not_so_smart_pointer( Type* ptr )
        : ptr( ptr )
        {    }
        
        not_so_smart_pointer( not_so_smart_pointer const& rhs )
        : ptr( rhs.ptr )
        {    
            const_cast< not_so_smart_pointer& >( rhs ).ptr = 0;
        }
        
        Type* operator -> ( void )
        {
            return ptr;
        }
    
        virtual ~not_so_smart_pointer( void )
        {
            delete ptr;
        }
        
        Type* ptr;
    };
    
    struct message
    {    
    /*
        Needs to have at least one virtual 
        function for dynamic_cast to work.
    */    
        virtual ~message( void )
        {    }
    };
    
    struct message_handler
    {
        protected:
    
    /*
        These proxy classes will forward the 
        message to the appropriate handler.
    */    
        struct dispatcher_base
        {
            virtual void handle( message& msg ) = 0;
        };
        
        template < typename Message, typename Handler >
        struct dispatcher : dispatcher_base
        {
            dispatcher( Handler& obj, void ( Handler::*fun )( Message& ) )
            : obj( obj ), fun( fun )
            {    }
            
            virtual void handle( message& msg )
            {
                Message* ptr = dynamic_cast< Message* >( &msg );
                if( ptr )
                    ( obj.*fun )( *ptr );
            }
            
            Handler& obj;
            void ( Handler::*fun )( Message& );
        };
    
        typedef not_so_smart_pointer< dispatcher_base > mgr_t;
        typedef std::list< mgr_t > lst_t;
            
        public:
        
    /*
        An "opaque" handle to a newly-subscribed handler, 
        in the event that we want to unsubscribe later.
    */        
        typedef lst_t::iterator subscription;        
        
        message_handler( void )
        {    }
            
    /*
        Special case: attach to an external handler.
        Note: inserts before subscription "pos".
    */            
        template < typename Message, typename Handler >
        subscription subscribe( Handler& obj, void ( Handler::*fun )( Message& ), subscription pos )
        {
            lst.insert( pos, new dispatcher< Message, Handler >( obj, fun ) );
            return --pos;
        }
        
        template < typename Message, typename Handler >
        subscription subscribe( Handler& obj, void ( Handler::*fun )( Message& ) )
        {
            return subscribe( obj, fun, lst.end( ) );
        }    
    
    /*
        General case: attach to *this.
    */    
        template < typename Message, typename Handler >
        inline subscription subscribe( void ( Handler::*fun )( Message& ), subscription pos )
        {
            return subscribe( static_cast< Handler& >( *this ), fun, pos );
        }
        
        template < typename Message, typename Handler >
        inline subscription subscribe( void ( Handler::*fun )( Message& ) )
        {
            return subscribe( static_cast< Handler& >( *this ), fun, lst.end( ) );
        }
        
        void unsubscribe( subscription sub )
        {
            lst.erase( sub );
        }
        
        virtual void handle( message const& msg )
        {
            for( subscription seq = lst.begin( ), fin = lst.end( ); seq != fin; ++seq )
                ( *seq )->handle( const_cast< message& >( msg ) );
        }
        
        protected:
        
        lst_t lst;
        
        private:
        
    /*
        We have to disable the copy-constructor for now 
        because we are using not_so_smart_pointer's
    */    
        message_handler( message_handler const& );
    };
    
    // Example:
    
    #include <string>
    #include <iostream>
    
    struct shutdown_message : message
    {    };
    
    struct update_message : message
    {    };
    
    struct advanced_update_message : update_message
    {    };
    
    struct my_message_handler : message_handler
    {
        my_message_handler( void )
        {
            subscribe< shutdown_message >( &my_message_handler::handle_shutdown );
            subscribe< update_message >( &my_message_handler::handle_update );
            subscribe< advanced_update_message >( &my_message_handler::handle_advanced_update );
        }
    
        void handle_shutdown( shutdown_message& msg )
        {
            print( "handle_shutdown" );
        }
        
        void handle_update( update_message& msg )
        {
            print( "handle_update" );
        }    
        
        void handle_advanced_update( advanced_update_message& msg )
        {
            print( "handle_advanced_update" );
        }    
        
        void print( std::string const& text )
        {
            std::cout << text << std::endl;
        }
    };
    
    int main( void )
    {
        my_message_handler handler;
        handler.handle( advanced_update_message( ) );
        handler.handle( shutdown_message( ) );
    }
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  6. #6
    Registered User
    Join Date
    Jan 2007
    Posts
    330
    wow thanks for the answers.
    Looking into this code and revisiting the visitor pattern That seems like the way to go

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Default class template problem
    By Elysia in forum C++ Programming
    Replies: 5
    Last Post: 07-11-2008, 08:44 AM
  2. Making a script language?
    By Blackroot in forum Game Programming
    Replies: 10
    Last Post: 02-16-2006, 02:22 AM
  3. Architecture Question
    By Orborde in forum C++ Programming
    Replies: 1
    Last Post: 06-01-2005, 08:05 AM
  4. Replies: 7
    Last Post: 05-26-2005, 10:48 AM
  5. Warnings, warnings, warnings?
    By spentdome in forum C Programming
    Replies: 25
    Last Post: 05-27-2002, 06:49 PM