1

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:
    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.

chris.schuette
  • 307
  • 2
  • 11

2 Answers2

2

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 :)

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Whichever way you skin this cat, you somehow always have to know the concrete types from the start... The acyclic visitor may be a bit more maintainable, at the expense of going through a list of dynamic casts until one succeeds. – Kerrek SB Sep 14 '11 at 14:31
  • @Kerrek: yes, that's why I usually bite the bullet and just use the typical `Visitor`. – Matthieu M. Sep 14 '11 at 14:32
  • actually I think i just got around this: I just added a Dispatch(MyInputMap * pMap) to my class StringInput : INPUT::Input and now I seems to work. This means my BaseClasses - both InputMap and Input are completely free of the derived types. Does this make sense in your experience? – chris.schuette Sep 14 '11 at 14:41
  • @chris: unfortunately, it won't work if you have a function as such: `void dispatch(InputMap& map, Input& i) { map.Dispatch(i); }`. – Matthieu M. Sep 14 '11 at 14:53
  • ah I see - well, at least I am happy, that I can limit my mentioning of the concrete InputMap (i.e. `MyInputMap` ) to the concrete inputs (i.e. `StringInput`) and I do not have to taint my abstract base class `Input` with it. Thanks. – chris.schuette Sep 14 '11 at 19:58
0

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.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084