Wie kann ich eine Art Basis-Lookup-Tabelle, um Multiple-Versand in C ++ zu implementieren schaffen?

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

Frage

Ich bin versucht, ein Messaging-System zu machen, in dem jede Klasse von „Messageable“ abgeleiteten Nachrichten empfangen kann die Funktion handelte auf, wie () überlastet ist. Zum Beispiel:

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

In aller Kürze mag ich die richtige Version von handelte forderte den RTTI-Wert von einer bestimmten Nachricht zu stützen.

Wie kann ich das umsetzen vorzugsweise ohne irgendeine Art von monolithischer Schalter / Case-Anweisung.

War es hilfreich?

Lösung

Sie sollten das Double-Dispatch-Muster suchen. Siehe Informationen hier .

Es soll möglich sein VisualMessage als eine Klasse wie solche zu implementieren:

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

und rufen Sie dann wie folgt aus:

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

Es wird dann rufen TestMessageable :: handle (VisualMessage & visualMessage)

Das ist, weil die Nachricht :: dispatch wird auf dem VisualMessage Typ basieren. Dann, wenn VisualMessage :: dispatch inMessageable.handleMessage (* this) anruft, wird es das Recht handle nennen, weil die Art des * dieser Zeiger VisualMessage ist, nicht mehr Nachricht.

Andere Tipps

Ihr Code zu beheben:

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

Diese static_casts könnte dynamic_casts für „zusätzliche Sicherheit“ (vorausgesetzt, es sind virtuelle Funktionen rund um Tritte) sein. Aber auch das Design bedeutet, dass Sie wissen selbst ein Zeiger auf S sein muss, denn sonst wäre es nicht diese Funktion, um es registriert haben, und Sie wissen m zu einem T beziehen müssen, weil seine typeid bereits in Dispatch geprüft. So eine gescheiterte Besetzung kann nicht passieren, wenn die Klasse richtig verwendet wird, und alles, was Sie tun können, wenn es geschieht ist debug.

Eigentlich glaube ich Ihnen die Wortschwall ein bisschen mehr abgeholzt könnte, indem sie bindFunction eine Vorlage zu:

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

Dann rufen Sie es mit:

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

Aber noch können Sie sehen, warum Double-Dispatch Code Steve Rowe ist in der Regel bevorzugt ...

Dies ist eine alte Frage, aber die Nukleare Bibliothek ausgelegt ist schnell und bietet typsichere Message Passing in ähnlicher Weise auf die ursprüngliche Absicht dieser Frage.

Full Disclosure: Ich bin einer der Co-Entwickler von Kern

In diesem Fall wird die TestMessageable Klasse wird als NUClear::Reactor wie so umgesetzt:

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

In Kürze erhalten Sie eine solche Art der Implementierung finden in Scott Meyers' More Effective C ++ und   Artikel -. 31 ist, was Sie wollen und schön erklärt

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top