c++genericstypesmessagingerasure

Generic messaging


I am working on a messaging system on C++. I have;

class MessageData
{
public:
    typedef std::vector<std::shared_ptr<MessageData>> MessageList;

    virtual int getValue(std::shared_ptr<int>) { throw "Not implemented!"; };
    virtual float getValue(std::shared_ptr<float>) { throw "Not implemented!"; };
    virtual std::string getValue(std::shared_ptr<std::string>) { throw "Not implemented!"; };
    ...
    ...

    virtual ~MessageData() {};
};

template <typename T>
class Message : public MessageData
{
    T val;
public:
    static std::shared_ptr<Message<T>> Make(T val) { return std::make_shared<Message<T>>(val); };
    static T Get(std::shared_ptr<MessageData> in) { return in->getValue(std::make_shared<T>()); };
    Message(T i) { val = i; };
    T getValue(std::shared_ptr<T> out) override { return *out = val; }
    ~Message() {};
};

Using these, I can send/receive generic messages of different length conveniently using e.g;

sendMessage(MessageData::MessageList{
                Message<std::string>::Make("paint"),
                Message<int>::Make(14),
                Message<float>::Make(129.3f),
                ...
            });

Then I get the values;

sendMessage(MessageData::MessageList data) {
    auto a = Message<std::string>::Get(data[0]);
    auto b = Message<int>::Get(data[1]);
    auto c = Message<float>::Get(data[2]);
    ...
}

The downside is that I have to list all the types I need to use in MessageData class. This isn't a big deal as I can limit the types I want to support but I'm really curious about how to templatize the type list without using a 3rd party library. Or is there a completely different and better method that I can use with similar clean syntax and type safety to pass messages around?


Solution

  • I think I've developed a decent solution to my problem.

    class MessageData {
    public:
        typedef std::vector<std::shared_ptr<MessageData>> MessageList;
        virtual ~MessageData() {};
    };
    
    template<typename T>
    class Message : public MessageData {
        T val;
    public:
        template<typename U>
        friend U GetMessage(std::shared_ptr<MessageData> in);
    
        Message(T i) { val = i; };
    };
    
    template<typename T>
    T GetMessage(std::shared_ptr<MessageData> in) {
        std::shared_ptr<Message<T>> tmp = std::dynamic_pointer_cast<Message<T>>(in);
        if (tmp) {
            return tmp->val;
        } 
        throw "Incorrect type!";
    };
    
    template<typename T>
    std::shared_ptr<Message<T>> MakeMessage(T val)
    {
        return std::make_shared<Message<T>>(val);
    };
    

    Then send & receive values using;

    sendMessage(MessageData::MessageList{
                    MakeMessage(std::string("paint")),
                    MakeMessage(14),
                    MakeMessage(129.3f),
                    ...
                });
    
    sendMessage(MessageData::MessageList data) {
        auto a = GetMessage<std::string>(data[0]);
        auto b = GetMessage<int>(data[1]);
        auto c = GetMessage<float>(data[2]);
        ...
    }