C ++에서 다중 디스패치를 구현하기 위해 유형 기반 조회 테이블을 어떻게 만들 수 있습니까?
-
22-07-2019 - |
문제
"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 당신이 원하는 것과 멋지게 설명했습니다.