c++templatesforward-declarationdouble-dispatch

Forward declaring class with templated code


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!


Solution

  • 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.