This meets all my requirements, now make it non dependent on exceptions

Started by
5 comments, last by Zahlman 14 years, 2 months ago
For a while Ive been trying to come up with the "perfect" implementation of polymorphic event handling. This example demonstrates all the behaviour I want, however, it uses exceptions in what can only be described as an abusive way. Those who are easily upset need not continue.

class IMessageHandler;

class Message
{
    public:
        virtual void evil()=0;
};

class IMessageHandler
{
    public:
       virtual bool handleMessage( boost::shared_ptr<Message> msg )=0;

};

class BadNews : public Message
{
    public:
        BadNews(){}
        virtual void evil(){ throw (BadNews()); }
};

class TabloidNews : public Message
{
    public:
        TabloidNews(){}
        virtual void evil(){ throw (TabloidNews()); }
};

class GoodNews: public Message
{
    public:
        GoodNews(){}
        virtual void evil(){ throw (GoodNews()); }
};
    
class NewsMessageHandler : public IMessageHandler
{
    public:
    
        virtual bool handleMessage( boost::shared_ptr<Message> msg )
        {
            
            try
            {
                msg->evil();
            }
			catch(GoodNews& goodNews)
			{
			    cout<<"Got some good news"<<endl;
			}
			catch(BadNews& badNews)
			{
			    cout<<"Got some bad news"<<endl;
			}
            catch(Message& message)
            {
                cout<<"This isnt news"<<endl;
            }
            catch(...)
            {
                return false;
            }
            return true;
        }
};

int main()
{
    boost::shared_ptr<NewsMessageHandler> h( new NewsMessageHandler() );
    
    boost::shared_ptr<Message> b ( new BadNews() );
    boost::shared_ptr<Message> g ( new GoodNews() );    
    boost::shared_ptr<Message> t ( new TabloidNews() );   
    
    h->handleMessage(b);
    h->handleMessage(g);
    h->handleMessage(t);
}



Any implementation of this system MUST satisfy these requirements: 1) Everything is a shared ptr. I dont want to be passing around by value or reference and I dont want the risk of leaks. 2) I must be able to have an inheritance tree of messages. For example, as demonstrated, I have class Message, and I subclass this into GoodNews, BadNews, and TabloidNews. Flat lists of message type, each corresponding to an enum, are undesireable. 3) My handlers must not be dependent on any list of all message types. Each handler selectively implements only the types it wants. 4) The messages themselves are not dependent on any kind of message handler; they have no such concept. 5) I can have an inheritance tree of message handlers. Some message handlers might check for types lower down the heirarchy, and delegate events to more specialised event handlers. So, I might subclass BadNews into various types of bad news, but have a handler which only checks for a BadNews and delegates. I also want to have lists of message handlers, deriving from IMessageHandler, and iterate it and send events to them all. Is there a perfect implementation which demonstrates the same behaviour as above, but without using exceptions abusively? I've looked at using visitors, but the use of shared_ptr causes problems here. When being visited, a message doesnt know its shared_ptr. I've looked at RTTI but then I cant tell if a message is derived from some base or not, without checking for all known derived classes of that base.
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!
Advertisement
Quote:Original post by speciesUnknown
I've looked at using visitors, but the use of shared_ptr causes problems here. When being visited, a message doesnt know its shared_ptr.


class Message : boost::enable_shared_from_this<Message>{    ...};class BadMessage : Message{public:    boost::shared_ptr<BadMessage> shared_from_this() const { return boost::static_pointer_cast<BadMessage>(Message::shared_from_this()); }    ...};

Alternatively, with CRTP, and closer to const correct:
class Message : boost::enable_shared_from_this<Message>{    ...};template < typename SelfT, typename BaseT > class inherit_shared_from_this : public BaseT {public:    boost::shared_ptr<SelfT> shared_from_this() { return boost::static_pointer_cast<SelfT>( BaseT::shared_from_this() ); }    boost::shared_ptr<const SelfT> shared_from_this() const { return boost::static_pointer_cast<const SelfT>( BaseT::shared_from_this() ); }};class BadMessage : inherit_shared_from_this< BadMessage, Message >{public:    ...};



Quote:I've looked at RTTI but then I cant tell if a message is derived from some base or not, without checking for all known derived classes of that base.

This you can do with dynamic_cast -- or dynamic_pointer_cast for shared_ptr.

boost::shared_ptr<Message> foo = ...;boost::shared_ptr<BadNews> bad = boost::dynamic_pointer_cast<BadNews>(foo);if ( bad ) {    // foo points to a BadNews or something derived from BadNews} else {    // foo was null or not a BadNews}
I decided to go with the dynamic_ptr_cast method. Tested as working nicely

#include "boost/pointer_cast.hpp"using boost::shared_ptr;using boost::dynamic_pointer_cast;class IMessageHandler;class Message{    public:        virtual ~Message(){}};class IMessageHandler{    public:       virtual bool handleMessage( shared_ptr<Message> msg )=0;};class BadNews : public Message{};    class YouWereAdopted : public BadNews{};class GoodNews: public Message{};    class YouWonTheLottery : public GoodNews{};class TabloidNews : public Message{    public:};    class InaneDrivel : public TabloidNews{};    class NewsMessageHandler : public IMessageHandler{    public:            virtual bool handleMessage( shared_ptr<Message> msg )        {            shared_ptr<BadNews> bad;            shared_ptr<GoodNews> good;            shared_ptr<TabloidNews> tabloid;                        if( bad = dynamic_pointer_cast<BadNews>(msg) )            {                cout<<"Sorry, you got some bad news"<<endl;                return true;            }            else if( tabloid = dynamic_pointer_cast<TabloidNews>(msg) )            {                cout<<"Sorry, you got some tabloid news"<<endl;                return true;            }            else if( good = dynamic_pointer_cast<GoodNews>(msg) )            {                cout<<"You got some good news"<<endl;                return true;            }            return false;        }};    int main(){    shared_ptr<NewsMessageHandler> h( new NewsMessageHandler() );        shared_ptr<Message> b ( new BadNews() );    shared_ptr<Message> g ( new GoodNews() );        shared_ptr<Message> t ( new TabloidNews() );       shared_ptr<Message> lottery ( new YouWonTheLottery() );        shared_ptr<Message> adopted ( new YouWereAdopted() );    shared_ptr<Message> drivel ( new InaneDrivel() );           h->handleMessage(b);    h->handleMessage(g);    h->handleMessage(t);    h->handleMessage(adopted);    h->handleMessage(lottery);    h->handleMessage(drivel);}


As always MaulingMonkey is the go to guy for awkward solutions to problems with an awkward langauge :)
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!
I don't understand your restriction on *having* to pass around shared pointers. Why are references undesirable? Are you dispatching the messages asynchronously or something?

If your message manager is just adding messages to a queue for later processing, but then you actually dispatch them to the handlers synchronously, then sure store shared_ptr<>s in the queue, but right before you're about to dispatch it, just do

foreach (MessageHandler* handler in handlers)   handler->handle(*message);


by reference.
Quote:Original post by cache_hit
I don't understand your restriction on *having* to pass around shared pointers. Why are references undesirable? Are you dispatching the messages asynchronously or something?

If your message manager is just adding messages to a queue for later processing, but then you actually dispatch them to the handlers synchronously, then sure store shared_ptr<>s in the queue, but right before you're about to dispatch it, just do

*** Source Snippet Removed ***

by reference.


THe reason I want to do this is because I want to route events high on the inheritance tree by their base types first. Note how YouWereAdopted is a derived class of BadNews, and yet I can route all my BadNews by testing for derivation from BadNews. I could have one handler for all my BadNews, one for all my GoodNews, and one for all my TabloidNews. I then use a main handler which routes these out to their respective handlers. If i pass things by reference I will get type slicing and thus lose all ability to have an inheritance tree of events.
Don't thank me, thank the moon's gravitation pull! Post in My Journal and help me to not procrastinate!
Quote:Original post by speciesUnknown
If i pass things by reference I will get type slicing

Err... you mean object slicing? It only occurs when passing things by value. Shared_ptr isn't enabling anything other than lifetime extension.

And on a barely sidetangent, FWIW, I'm looking forward to C#4's dynamic kewyord because it will end up helping simplify what you're doing:
class Message { ... }interface IMessageHandler {    bool Handle( Message message );}class BadNews : Message {}class GoodNews : Message {}class TerribleNews : BadNews {}class SomeMessageHandler : IMessageHandler {    void DoHandle( GoodNews ) { ... }    void DoHandle( BadNews ) { ... }    void DoHandle( Message ) { ... }    public void Handle( Message message ) { DoHandle((dynamic)message); }}
It looks to me like the fundamental problem is that you want the message handling to depend on both the message type and the handler type. This is called "double dispatch", and [google] knows many things thereabout.

This topic is closed to new replies.

Advertisement