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

StackOverflow https://stackoverflow.com/questions/1824835

Вопрос

Я пытаюсь создать систему обмена сообщениями, в которой любой класс получен из " 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.

Это было полезно?

Решение

Вы должны посмотреть на шаблон 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.

Другие советы

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

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>();

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

Это старый вопрос, но библиотека 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.
    });
}

Такую реализацию вы найдете в Более эффективном С ++ Скотта Мейерса и   item - 31 - это то, что вам нужно & amp; хорошо объяснил.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top