繁体   English   中英

如何创建基于类型的查找表以便在C ++中实现多分派?

[英]How can I create a type based lookup table in order to implement multiple-dispatch in C++?

我正在尝试创建一个消息传递系统,其中任何派生自“Messageable”的类都可以根据函数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值调用handleMessage的正确版本。

如果没有某种单片开关/ case语句,我怎样才能实现这一点。

您应该查看Double Dispatch模式。 请参阅此处的信息

您应该能够将VisualMessage实现为类似这样的类:

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

然后像这样调用它:

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

然后它将调用TestMessageable :: handleMessage(VisualMessage&visualMessage)

这是因为Message :: dispatch将基于VisualMessage类型。 然后,当VisualMessage :: dispatch调用inMessageable.handleMessage(* this)时,它将调用正确的handleMessage,因为* this指针的类型是VisualMessage,而不是Message。

要修复您的代码:

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_casts可以是dynamic_casts,用于“额外的安全性”(假设有虚拟功能)。 但是设计意味着你知道self必须是一个指向S的指针,因为否则它不会向它注册这个函数,你知道m必须引用一个T,因为它的typeid已经在dispatchMessage中被检查过了。 因此,如果正确使用了类,则无法执行失败的转换,如果确实发生了,则所有可以执行的操作都是调试。

实际上我认为你也可以通过使bindFunction成为模板来减少措辞:

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

然后用:

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

但是,你仍然可以看到为什么Steve Rowe的双重调度代码通常是首选......

这是一个古老的问题,但NUClear库旨在提供快速和类型安全的消息传递,其方式与此问题的原始意图类似。

完全披露:我是NUClear的联合开发者之一

在这种情况下, TestMessageable类实现为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 ++”中找到这样的实现,而项目 - 31就是您想要的并且很好地解释。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM