views:

234

answers:

4

I'm attempting to make a messaging system in which any class derived from "Messageable" can receive messages based on how the function handleMessage() is overloaded. For example:

class Messageable
    {
    public:
        void takeMessage(Message& message)
            {
            this->dispatchMessage(message);
            }
    protected:
        void bindFunction(std::type_info type, /* Need help here */ func)
            {
            m_handlers[type] = func;
            }

        void dispatchMessage(Message& message)
            {
            m_handlers[typeid(message)](message);
            }
    private:
        std::map<std::type_info, /*Need help here*/ > m_handlers;
    };

class TestMessageable : public Messageable
    {
    public:
        TestMessageable()
            {
            this->bindFunction(
                typeid(VisualMessage), 
                void (TestMessageable::*handleMessage)(VisualMessage));

            this->bindFunction(
                typeid(DanceMessage),
                void (TestMessageable::*handleMessage)(DanceMessage));
            }
    protected:
        void handleMessage(VisualMessage visualMessage)
            {
            //Do something here with visualMessage
            }

        void handleMessage(DanceMessage danceMessage)
            {
            //Do something here with danceMessage
            }
    };

In a nutshell I want the correct version of handleMessage to be called based on the RTTI value of any given message.

How can I implement this preferably without some sort of monolithic switch/case statement.

+4  A: 

You should look into the Double Dispatch pattern. See information here.

You should be able to implement VisualMessage as a class like such:

class VisualMessage : public Message
{
    public:
        virtual void dispatch(Messageable & inMessageable)
        {
            inMessageable.handleMessage(*this);
        }
};

and then call it like this:

Message & vMessage = VisualMessage();
Messageable & tMessageable = TestMessageable();
vMessage.dispatch(tMessageable);

It will then call TestMessageable::handleMessage(VisualMessage & visualMessage)

This is because Message::dispatch will be based on the VisualMessage type. Then when VisualMessage::dispatch calls inMessageable.handleMessage(*this) it will call the right handleMessage because the type of the *this pointer is VisualMessage, not Message.

Steve Rowe
VoDurden
Steve Rowe
This approach almost works, the only problem I am having is that it is necessary to declare every possible message that can be received in the base message class. As in if I want VisualMessages to be recieveable then the Message class must have virtual void handleMessage(VisualMessage
VoDurden
A: 

From the comments which u have provided next to ur code i.e.

std::map<std::type_info, /*Need help here*/ m_handlers> i understand you are trying to prepare a map which looks something like:

map<typeid, member function pointer>

If this is the case this is how you can implement it:

Add following line to your Messegeable class: typedef void (Messageable::*FPTR)(Message&) and then your function looks like

void bindFunction(std::type_info type, FPTR func) { m_handlers[type] = func; }

My apologies for any syntactic errors, if any

Steve Jessop
A: 

You will find such kind of implementation in Scott Meyers' More Effective C++ and item - 31 is what you want & nicely explained.

Ashish
Hoping to get this book (and the original) for Christmas. Until then I'll have to wait to check this out. Thanks anyway
VoDurden
if you want i can send that article to you dude give me your email id.
Ashish
I appreciate the offer but it's alright, I'll wait till I can get the actual book.
VoDurden
+1  A: 

To fix your code:

struct CompareTypeInfo 
  : std::binary_function<const std::type_info*, const std::type_info*, bool> 
{
    bool operator()(const std::type_info* a, const std::type_info* b) {
        return a->before(*b);
    }
};

class Messageable 
{
protected:
    typedef void (*handlefn)(Messageable *, Message &);
    void bindFunction(const std::type_info& type, handlefn func) {
        m_handlers[&type] = func;
    }

    void dispatchMessage(Message& message) {
        m_handlers[&typeid(message)](this, message);
    }
    template <typename S, typename T>
    static void handle(Messageable *self, Message &m) {
        static_cast<S*>(self)->handleMessage(static_cast<T&>(m));
    }
private:
    std::map<const std::type_info*, handlefn, CompareTypeInfo> m_handlers;
};

class TestMessageable : public Messageable
{
public:
    TestMessageable()
        {
        this->bindFunction(
            typeid(VisualMessage), &Messageable::handle<TestMessageable,VisualMessage>);

        this->bindFunction(
            typeid(DanceMessage), &Messageable::handle<TestMessageable,DanceMessage>);
        }
public:
    void handleMessage(VisualMessage visualMessage)
        {
        //Do something here with visualMessage
        }

    void handleMessage(DanceMessage danceMessage)
        {
        //Do something here with danceMessage
        }
    }
};

Those static_casts could be dynamic_casts for "extra safety" (assuming there are virtual functions kicking around). But the design means you know self must be a pointer to S, because otherwise it wouldn't have this function registered to it, and you know m must refer to a T, because its typeid has already been checked in dispatchMessage. So a failed cast can't happen if the class is used correctly, and all you can do if it does happen is debug.

Actually I think you could cut down the verbiage a bit more by making bindFunction a template too:

template <typename S, typename T>
void bindFunction(void)
    {
    m_handlers[&typeid(T)] = handle<S,T>;
    }

Then call it with:

this->bindFunction<TestMessageable,VisualMessage>();

But still, you can see why Steve Rowe's double dispatch code is usually preferred...

Steve Jessop
Thanks for solving the original problem, I ended up going with a Double Dispatch method but it is interesting to see how the original design could have been accomplished.
VoDurden
I decided to give this design a try so that my base Messageable class didn't need to know about every type of message it can receive. Some of the problems I ran into included std::type_info not being compatible with map (changed to using std::type_info.name() ). The shortened version did up up working however I have a question. How exatly does " m_handlers[typeid(T)] = handle<S,T>;" work, doesn't it need to return something? What exactly am I missing?
VoDurden
handle<S,T> in the template version of bindFunction is the same as handle<TestMessageable,VisualMessage> and handle<TestMessageable,DanceMessage> in the longer version: functions generated from templates, and we use pointers to them. So that line assigns a pointer to a function (different function according to the template parameters) to an element of the map (different element according to T). To do this, all we need to know is the types, hence we don't need any parameters at runtime. If everything is inlined, then it will result in pretty much the same code as before.
Steve Jessop
Note that `type_info` is not copyable and doesn't have `operator<`, which makes it unusable with `std::map`.
Georg Fritzsche
@Georg: good point. I don't really remember writing this answer in the first place, not sure whether the questioner's code can be salvaged after all. Maybe `type_info::name()` would serve as a key?
Steve Jessop
I haven't actually ever used it myself but as long as no shared-libraries are involved etc. both pointers (5.2.8 says that the lifetime of the `typeid` result *"extends to the end of the program"*) and `name()` seem to work in practice.
Georg Fritzsche
@Georg: yeah, having checked the spec it is just an "in practice" thing. If there was a lifetime issue, you could avoid it by converting the result of `name()` to a string, but I think the portability problem is that the standard doesn't explicitly say that `name()` returns different strings, or even that it returns different pointers, for different types. You'd hope that each type has a unique name, but I don't know if that's entirely reliable...
Steve Jessop
Using `type_info*` and its `before()` / `operator==` should be fully compliant.
Georg Fritzsche
@Georg: is it? You'd need there to only be one `type_info` object for each type. Even if we rule out dlls, and assume the program is a monolithic executable, is that guaranteed? To be honest, I'm starting to regret I tried to fix this code just by filling in the function type ;-).
Steve Jessop
I meant `type_info::operator==` which *"Returns: true if the two values describe the same type"*. This one seems to be fine. The wording of `before()` got me (again...) though. Sorry for excavating this, but i couldn't help stopping on that `map` line ;)
Georg Fritzsche
@Georg: Ah, I think I see what you mean now. Does it seem odd that the standard provides `type_info::before`, but doesn't also offer a `std::less` overload for `type_info`? The `map<type_info*,V>` needs a custom comparator either way, so I guess it doesn't make that much difference, but it seems a bit out of character to me. Anyway, feel free to edit my answer if you'd like to fix it properly. No apology needed for excavating: thanks for making the effort. It's my own haste that I'm regretting :-)
Steve Jessop