Как я могу создать таблицу поиска на основе типов для реализации множественной диспетчеризации в C ++? - PullRequest
3 голосов
/ 01 декабря 2009

Я пытаюсь создать систему обмена сообщениями, в которой любой класс, производный от «Messageable», может получать сообщения в зависимости от того, как перегружена функция handleMessage (). Например:

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
        }
};

В двух словах, я хочу, чтобы корректная версия handleMessage вызывалась на основе значения RTTI любого данного сообщения.

Как я могу реализовать это предпочтительно без какого-либо монолитного оператора switch / case.

Ответы [ 4 ]

4 голосов
/ 01 декабря 2009

Вы должны посмотреть на шаблон Double Dispatch. Смотри информацию здесь .

Вы должны быть в состоянии реализовать VisualMessage как класс:

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

и затем назовите это так:

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

Затем он вызовет TestMessageable :: handleMessage (VisualMessage & visualMessage)

Это потому, что Message :: dispatch будет основан на типе VisualMessage. Затем, когда VisualMessage :: dispatch вызывает inMessageable.handleMessage (* this), он вызывает правильный handleMessage, потому что тип указателя * this - VisualMessage, а не Message.

1 голос
/ 01 декабря 2009

Чтобы исправить ваш код:

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
        }
    }
};

Эти статические трансляции могут быть динамическими трансляциями для «дополнительной безопасности» (при условии, что существуют виртуальные функции). Но дизайн означает, что вы знаете, что self должно быть указателем на S, потому что иначе у него не было бы зарегистрировано этой функции, и вы знаете, что m должно ссылаться на T, потому что его typeid уже был проверен в dispatchMessage. Поэтому неудачное приведение не может произойти, если класс используется правильно, и все, что вы можете сделать, если это произойдет, это отладка.

На самом деле, я думаю, что вы могли бы немного сократить словоблудие, сделав шаблон bindFunction также:

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

Затем назовите его с:

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

Но, тем не менее, вы можете понять, почему код двойной отправки Стива Роу обычно предпочтительнее ...

0 голосов
/ 21 ноября 2013

Это старый вопрос, но библиотека NUClear разработана для обеспечения быстрой и безопасной передачи сообщений в том же ключе, что и первоначальная цель этого вопроса.

Полное раскрытие: я один из со-разработчиков NUClear

В этом случае класс TestMessageable реализован как NUClear::Reactor примерно так:

#include <NUClear.h>

// TestMessageable.h
class TestMessageable : NUClear::Reactor {
    public:
        TestMessageable(NUClear::PowerPlant* powerPlant);
    private:
};

// TestMessageable.cpp
#include "TestMessageable.h"

TestMessageable::TestMessageable(NUClear::PowerPlant* powerPlant)
: NUClear::Reactor(powerPlant) {
    on<Trigger<VisualMessage>>([this](const VisualMessage& message) {
        // Do something with VisualMessage here
        // On can also take anything that is callable with a const& VisualMessage.

        // Messages are sent using emit.
        // If you don't have C++14 NUClear provides std::make_unique
        auto classifiedData = std::make_unique<ClassifiedVision>(/* stuff */);
        emit(std::move(classifieData));
    });

    on<Trigger<DanceMessage>>([this](const DanceMessage& message) {
         // Do something with DanceMessage here.
    });
}
0 голосов
/ 01 декабря 2009

Вы найдете такую ​​реализацию в Более эффективном Скотте Мейерсе C ++ и вещь - 31 - это то, что вы хотите и приятно объяснили.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...