0

I'm trying to implement the double dispatch pattern for a message interface in C++. However, I find that I have to be too verbose in my handler class due to having to forward declare each message.

I'm looking for a way to structure the file(s) to be able to omit the forward declarations.

I'm aware that I can template the MessageHandler class with a tuple of all available messages. However, I found this not straightforward enough and looking for more simple - explainable - methods.

struct MessageA;
struct MessageB;
// etc...

class MessageHandler {
public:
    virtual void handle(MessageA& m);
    virtual void handle(MessageB& m);
    // etc...

    virtual void handle(Message&) {}

};

struct Message {
    virtual void dispatch(MessageHandler&) = 0;
};

template<typename ActualMessage>
struct MessageHelper : Message {
    void dispatch(MessageHandler& handler)
    {
        handler.handle(static_cast<ActualMessage&>(*this));
    }
};


struct MessageA : MessageHelper<MessageA> {};
struct MessageB : MessageHelper<MessageB> {};
// etc...

In the real code I'm dealing with more than 20 messages. I'm okay with the verbosity of the handler class, the forward declarations are a bit "much".

Is there some way I can restructure this? Of course I'm limited due to the fact that the MessageHelper class is templated. This restricts me in forward declaring the MessageHandler class instead.

Thanks!

thburghout
  • 27
  • 5
  • Of course, the implementation of MessageHandler is found in the respective cpp file. – thburghout Jul 21 '19 at 19:06
  • IMO not enough info. This looks like [XY problem](http://xyproblem.info/). I'm suspecting you need use CRTP to solve it. – Marek R Jul 21 '19 at 19:18
  • Could you explain how this API supposed to be used? How handlers are called? Why dynamic polymorphism is needed? – Marek R Jul 21 '19 at 19:37
  • @MarekR I may have confused you by my typo in the code block. I fixed it now. – thburghout Jul 28 '19 at 14:27
  • @MarekR I need polymorphism because I want to be able to pass various messages around in the application. I want handler to be able to filter the messages they handle, this is done by only overloading the handle() method for the messages a specific handler is interested in. I'm planning on having a list of handlers, so I'm dispatching each message to all of them. My question was already answered. However, I always appreciate any input. If you have any other questions, please let me know! – thburghout Jul 28 '19 at 14:32

1 Answers1

0

You can't get rid of forward declarations entirely, but you can change the order so that you only need to forward declare MessageHandler and none of the messages:

struct MessageHandler;

struct Message {
    virtual void dispatch(MessageHandler&) = 0;
};

template<typename ActualMessage>
struct MessageHelper : Message {
    void dispatch(MessageHandler& handler);
};


struct MessageA : MessageHelper<GetDeviceConfig> {};
struct MessageB : MessageHelper<GetDeviceConfig> {};
// etc...

class MessageHandler {
public:
    virtual void handle(MessageA& m);
    virtual void handle(MessageB& m);
    // etc...

    virtual void handle(Message&) {}
};

template<typename ActualMessage>
void MessageHelper<ActualMessage>::dispatch(MessageHandler& handler)
{
    handler.handle(static_cast<ActualMessage&>(*this));
}

I'm limited due to the fact that the MessageHelper class is templated. This restricts me in forward declaring the MessageHandler class instead.

It's unclear why you think MessageHelper being templated would restrict you from forward declaring MessageHandler.

eerorika
  • 232,697
  • 12
  • 197
  • 326