开发者

Double dispatching fails for an InputMap in C++ - codes reduced to simplicity

开发者 https://www.devze.com 2023-04-05 08:01 出处:网络
Hello in one of my current projects I want to implement an InputMap. So I have an abstract input //Input.h

Hello in one of my current projects I want to implement an InputMap. So I have an abstract input

//Input.h
namespace INPUT {
class InputMap;
class Input {
public:
    Input();
    virtual ~Input();
    virtual void Dispatch( InputMap * pMap ) = 0;
};
}

and an InputMap

//InputMap.h
namespace INPUT {
class InputMap {
public开发者_如何学Python:
    InputMap();
    virtual void HandleInput( INPUT::Input & anInput ) {
    }
    virtual ~InputMap();
};
}

so far so good - no functionality here. Now I derive my first real Input from my abstract input class:

//StringInput.h
#include "Input.h"
#include "InputMap.h"
#include <string>

class StringInput : INPUT::Input {
public:
    StringInput();
    virtual ~StringInput();
    void Dispatch(INPUT::InputMap * pMap)
    {
        pMap->HandleInput( *this );
    }
    void SetMessage(std::string message);
    std::string GetMessage() const;
private:
     std::string m_message;
};

and here is my derived InputMap

//MyInputMap.h
#include "InputMap.h"
#include "StringInput.h"

class MyInputMap: public INPUT::InputMap {
public:
    MyInputMap();
    void HandleInput( StringInput & anInput );
    void HandleInput( INPUT::Input & anInput );
    virtual ~MyInputMap();
};

and here is the test:

//main.cpp
MyInputMap map;
StringInput input;
input.SetMessage("Test");
input.Dispatch(&map);

of course I expect that input.Dispatch(&map) invokes map.HandleInput(StringInput input), but unfortunately the default handler is always invoked. Did I program this pattern wrong? Thanks guys, I have been staring my code forever, but I don't see it.


You should read about the Visitor pattern.

Basically, the issue is that virtual function are statically bound (ironic), so the solution is to declare all HandleInput (for every single type of Input) in InputMap.

class InputMap {
public:
    InputMap();
    virtual void HandleInput(StringInput&) = 0;
    virtual void HandleInput(IntInput&) = 0;
    virtual ~InputMap();
};

Note: the convention is to use pure virtual methods, so that no derived class forgets from overriding one.

Of course, this causes an issue of dependencies. Fortunately, you can forward declare the "real" input types in the header containing InputMap.

There are more complicated variations (search for Acyclic Visitor), but you should not need it right now :)


The function lookup and overload resolution is performed on the static type. So when you say pMap->HandleInput(*this) in StringInput::Dispatch(), this always finds the InputMap::HandleInput(Input &) overload, because pMap is of static type InputMap. This is then dispatched dynamically to the override MyInputMap::HandleInput(Input &).

One way to overcome this is to add dynamic dispatch inside the (unique) HandleInput() function which determines the dynamic type of argument at runtime.

0

精彩评论

暂无评论...
验证码 换一张
取 消