C ++에서 다중 디스패치를 ​​구현하기 위해 유형 기반 조회 테이블을 어떻게 만들 수 있습니까?

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

문제

"messagable"에서 파생 된 클래스가 함수 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
        }
};

간단히 말해서 나는 특정 메시지의 RTTI 값을 기반으로 올바른 핸드 레지지를 호출하기를 원합니다.

어떤 종류의 모 놀리 식 스위치/사례 명령문 없이이이를 구현할 수있는 방법.

도움이 되었습니까?

해결책

이중 파견 패턴을 살펴 봐야합니다. 정보를 참조하십시오 여기.

그러한 클래스로 VisualMessage를 구현할 수 있어야합니다.

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

그런 다음 다음과 같이 부릅니다.

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

그런 다음 testMessAgable을 호출합니다 :: handleMessage (VisubLessage & VisualMessage)

메시지 :: Dispatch는 VisualMessage 유형을 기반으로하기 때문입니다. 그런 다음 VisualMessage :: Dispatch가 inMessAged.HandleMessage ( *this) 호출하면이 포인터의 유형이 메시지가 아닌 VisualMessage이므로 오른쪽 핸드 레드를 호출합니다.

다른 팁

코드를 수정하려면 :

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

이러한 static_cast는 "추가 안전"을위한 Dynamic_cast가 될 수 있습니다 (가상 기능이 걷어차는 것으로 가정). 그러나 디자인은 자아가 S에 대한 포인터라는 것을 알 수 있음을 의미합니다. 그렇지 않으면이 기능이 등록되지 않기 때문에 M은 이미 DispatchMessage에서 확인 되었기 때문에 M을 참조해야한다는 것을 알고 있습니다. 따라서 클래스가 올바르게 사용되면 실패한 캐스트가 발생할 수 없으며, 발생하면 할 수있는 일은 디버그입니다.

실제로 Bindfunction을 템플릿으로 만들어서 언어를 조금 더 줄일 수 있다고 생각합니다.

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

그런 다음 다음으로 호출하십시오.

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

그러나 여전히 Steve Rowe의 이중 발송 코드가 일반적으로 선호되는 이유를 알 수 있습니다 ...

이것은 오래된 질문이지만 핵무기 라이브러리는이 질문의 원래 의도와 비슷한 정맥으로 전달하는 빠르고 유형-안전 메시지를 제공하도록 설계되었습니다.

전체 공개 : 나는 핵의 공동 개발자 중 하나입니다.

이 경우 TestMessageable 클래스는 a로 구현됩니다 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.
    });
}

그러한 종류의 구현을 찾을 수 있습니다 Scott Meyers의 더 효과적인 C ++ 그리고 항목 -11 당신이 원하는 것과 멋지게 설명했습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top