Comment créer une table de correspondance basée sur le type afin d'implémenter l'envoi multiple en C ++?

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

Question

J'essaie de créer un système de messagerie dans lequel toute classe dérivée de " Messageable " peut recevoir des messages en fonction de la façon dont la fonction handleMessage () est surchargée. Par exemple:

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 résumé, je souhaite que la version correcte de handleMessage soit appelée en fonction de la valeur RTTI de tout message donné.

Comment puis-je implémenter cela de préférence sans une sorte d'instruction monolithique switch / case.

Était-ce utile?

La solution

Vous devriez regarder dans le modèle Double Dispatch. Voir les informations ici .

Vous devriez pouvoir implémenter VisualMessage en tant que classe telle que:

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

puis appelez-le comme ceci:

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

Il appellera ensuite TestMessageable :: handleMessage (VisualMessage & visualMessage)

Cela est dû au fait que Message :: dispatch sera basé sur le type VisualMessage. Ensuite, lorsque VisualMessage :: dispatch appelle inMessageable.handleMessage (* this), il appellera le handleMessage correct, car le type du pointeur * this est VisualMessage, pas Message.

Autres conseils

Pour corriger votre code:

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

Ces static_casts peuvent être des dynamic_casts pour "une sécurité supplémentaire". (en supposant qu'il y ait des fonctions virtuelles qui bougent). Mais la conception signifie que vous savez que vous devez être un pointeur sur S, sinon cette fonction ne serait pas enregistrée, et vous savez que m doit faire référence à un T, car son typeid a déjà été vérifié dans dispatchMessage. Donc, un cast ayant échoué ne peut pas se produire si la classe est utilisée correctement, et tout ce que vous pouvez faire si cela se produit est un débogage.

En fait, je pense que vous pourriez réduire un peu plus le verbiage en faisant de bindFunction un modèle également:

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

Ensuite, appelez-le avec:

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

Mais vous pouvez quand même comprendre pourquoi le code de double dépêche de Steve Rowe est généralement préféré ...

Il s'agit d'une vieille question, mais la bibliothèque NUClear est conçue pour permettre la transmission rapide et sécurisée des messages. dans le même esprit que l'intention initiale de cette question.

Full Disclosure: Je suis l'un des co-développeurs de NUClear

Dans ce cas, la classe TestMessageable est implémentée en tant que NUClear :: Reactor comme suit:

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

Vous trouverez ce type d'implémentation dans C ++ plus efficace de Scott Meyers .   élément - 31 est ce que vous souhaitez & amp; bien expliqué.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top