¿Cómo puedo crear una tabla de búsqueda basada en tipos para implementar el despacho múltiple en C ++?

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

Pregunta

Estoy intentando crear un sistema de mensajería en el que cualquier clase derivada de "Mensajeable" puede recibir mensajes en función de cómo se sobrecarga la función handleMessage (). Por ejemplo:

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

En pocas palabras, quiero que se llame a la versión correcta de handleMessage en función del valor RTTI de cualquier mensaje dado.

¿Cómo puedo implementar esto preferiblemente sin algún tipo de cambio monolítico / declaración de caso?

¿Fue útil?

Solución

Debería buscar en el patrón de Despacho doble. Ver información aquí .

Debería poder implementar VisualMessage como una clase como esta:

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

y luego llámalo así:

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

Luego llamará a TestMessageable :: handleMessage (VisualMessage & amp; visualMessage)

Esto se debe a que Message :: dispatch se basará en el tipo de VisualMessage. Luego, cuando VisualMessage :: dispatch llama a inMessageable.handleMessage (* this), llamará al handleMessage correcto porque el tipo de * this pointer es VisualMessage, no Message.

Otros consejos

Para arreglar su código:

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

Esas transmisiones estáticas podrían ser transmisiones dinámicas para " seguridad adicional " (suponiendo que haya funciones virtuales dando vueltas). Pero el diseño significa que sabes que self debe ser un puntero a S, porque de lo contrario no tendría esta función registrada, y sabes que m debe referirse a una T, porque su typeid ya se ha verificado en dispatchMessage. Por lo tanto, un lanzamiento fallido no puede suceder si la clase se usa correctamente, y todo lo que puede hacer si sucede es depurar.

En realidad, creo que podría reducir un poco más la palabrería haciendo de bindFunction una plantilla también:

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

Luego llámalo con:

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

Pero aún así, puede ver por qué generalmente se prefiere el código de envío doble de Steve Rowe ...

Esta es una pregunta antigua, pero la NUClear está diseñada para proporcionar un envío de mensajes rápido y seguro. en una línea similar a la intención original de esta pregunta.

Divulgación completa: soy uno de los desarrolladores de NUClear

En este caso, la clase TestMessageable se implementa como NUClear :: Reactor de esta manera:

#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.
    });
}

Encontrará este tipo de implementación en C ++ más eficaz de Scott Meyers y   elemento - 31 es lo que quieres & amp; bien explicado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top