Come posso creare una tabella di ricerca basata sul tipo per implementare il dispacciamento multiplo in C ++?

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

Domanda

Sto tentando di creare un sistema di messaggistica in cui qualsiasi classe derivata da " Messageable " può ricevere messaggi in base al sovraccarico della funzione handleMessage (). Ad esempio:

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 poche parole voglio che venga chiamata la versione corretta di handleMessage in base al valore RTTI di ogni dato messaggio.

Come posso implementarlo preferibilmente senza una sorta di istruzione monolitica switch / case.

È stato utile?

Soluzione

Dovresti esaminare il modello Double Dispatch. Consulta le informazioni qui .

Dovresti essere in grado di implementare VisualMessage come una classe come questa:

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

e quindi chiamalo così:

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

Chiamerà quindi TestMessageable :: handleMessage (VisualMessage & amp; visualMessage)

Questo perché Message :: dispatch si baserà sul tipo VisualMessage. Quindi quando VisualMessage :: dispatch chiama inMessageable.handleMessage (* this) chiamerà il handleMessage giusto perché il tipo di * questo puntatore è VisualMessage, non Message.

Altri suggerimenti

Per correggere il codice:

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

Questi static_casts potrebbero essere dynamic_casts per " extra safety " (supponendo che ci siano funzioni virtuali in movimento). Ma il design significa che sai che il sé deve essere un puntatore a S, perché altrimenti non avrebbe questa funzione registrata su di esso, e sai che m deve riferirsi a una T, perché il suo carattere è già stato verificato in dispatchMessage. Quindi un cast fallito non può accadere se la classe viene usata correttamente e tutto ciò che puoi fare se succede è il debug.

In realtà penso che potresti ridurre un po 'di più la verbosità rendendo anche bindFunction un modello:

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

Quindi chiamalo con:

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

Ma puoi ancora capire perché il doppio codice di invio di Steve Rowe è solitamente preferito ...

Questa è una vecchia domanda ma la libreria NUClear è progettata per fornire un passaggio dei messaggi veloce e sicuro in modo simile all'intento originale di questa domanda.

Divulgazione completa: sono uno dei co-sviluppatori di NUClear

In questo caso la classe TestMessageable è implementata come NUClear :: Reactor in questo modo:

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

Troverai questo tipo di implementazione in C ++ più efficace di Scott Meyers e   item - 31 è quello che vuoi & amp; ben spiegato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top