4

I'm playing around with a templated implementation of an FSM and am encountering an ambiguity as follows:

/home/permal/code/FSM/Test/../FSM/dist/include/FSM.h: In instantiation of ‘void fsm::FSM<FSMBaseState>::Event(std::unique_ptr<EventType>) [with EventType = AddEvent; FSMBaseState = EventBaseState]’:
/home/permal/code/FSM/Test/test.cpp:83:44:   required from here
/home/permal/code/FSM/Test/../FSM/dist/include/FSM.h:59:4: error: request for member ‘Event’ is ambiguous
    myCurrent->Event( std::move( event ) );
    ^
In file included from /home/permal/code/FSM/Test/../FSM/dist/include/FSM.h:12:0,
                 from /home/permal/code/FSM/Test/test.cpp:8:
/home/permal/code/FSM/Test/../FSM/dist/include/EventReceiver.h:15:15: note: candidates are: void fsm::EventReceiver<EventType>::Event(std::unique_ptr<_Tp>) [with EventType = SubtractEvent]
  virtual void Event( std::unique_ptr<EventType> event ) = 0;
               ^
/home/permal/code/FSM/Test/../FSM/dist/include/EventReceiver.h:15:15: note:                 void fsm::EventReceiver<EventType>::Event(std::unique_ptr<_Tp>) [with EventType = AddEvent]
In file included from /home/permal/code/FSM/Test/test.cpp:8:0:
/home/permal/code/FSM/Test/../FSM/dist/include/FSM.h: In instantiation of ‘void fsm::FSM<FSMBaseState>::Event(std::unique_ptr<EventType>) [with EventType = SubtractEvent; FSMBaseState = EventBaseState]’:
/home/permal/code/FSM/Test/test.cpp:91:50:   required from here
/home/permal/code/FSM/Test/../FSM/dist/include/FSM.h:59:4: error: request for member ‘Event’ is ambiguous
    myCurrent->Event( std::move( event ) );

So my question is why the compiler cannot choose the correct option even though it spells out that there are two possible candidates, one of which is the correct one.

I hope the code below is enough to show the problem, the rest is available on GitHub

This is the class and method where the ambiguity occurs:

template<typename FSMBaseState>
class FSM
{
public:
    ...

    template<typename EventType>
    void Event( std::unique_ptr<EventType> event )
    {
        if( HasState() )
        {
            // This way of calling causes ambiguous method lookup during template instantiation
            myCurrent->Event( std::move( event ) );

            // casting to the correct type works, but is it really needed?
            // auto* s = myCurrent.get();
            // static_cast<EventReceiver<EventType>*>( s )->Event( std::move( event ) );
        }
    }

};

The above method is called either as fsm.Event( std::make_unique<AddEvent>() ); or fsm.Event( std::make_unique<SubractEvent>() );

myCurrent in the above Event()-method is an instance of the following class:

class EventBaseState
: public fsm::BaseState<EventBaseState>,
      public fsm::EventReceiver<AddEvent>,
      public fsm::EventReceiver<SubtractEvent>
{
public:
EventBaseState( const std::string& name, fsm::FSM<EventBaseState>& fsm ) :
        BaseState( name, fsm )
{}

};

where EventReceiver is defined as follows:

template<typename EventType>
class EventReceiver
{
public:
    virtual void Event( std::unique_ptr<EventType> event ) = 0;
};

Update

Looking at things from a new angle, I ended up with a pure virtual functions for the Event<T>(...) methods, which is actually what I really wanted as the EventBaseState class was supposed to be abstract.

class EventBaseState
    : public fsm::BaseState<EventBaseState>,
      public fsm::EventReceiver<AddEvent>,
      public fsm::EventReceiver<SubtractEvent>
{
public:
    EventBaseState( const std::string& name, fsm::FSM<EventBaseState>& fsm ) :
            BaseState( name, fsm )
    {}

    virtual void Event( std::unique_ptr<AddEvent> event ) override = 0;
    virtual void Event( std::unique_ptr<SubtractEvent> event ) override = 0;
};

Of course, the solution provided by Chajnik-U works too.

Community
  • 1
  • 1
Per
  • 1,074
  • 6
  • 19
  • `myCurrent->Event( std::move( event ) );` This is the important line. What's the type of `myCurrent` and of `event`? How exactly are those two variables declared in the function that calls `myCurrent->Event( std::move( event ) );` – Aaron McDaid Oct 04 '16 at 06:17
  • @AaronMcDaid It's in the question: `myCurrent` is `EventBaseState` and `event` is `std::unique_ptr` or `std::unique_ptr`. – Rakete1111 Oct 04 '16 at 06:19

1 Answers1

3

Methods of the base classes do not participate in "cross class" overload when called via derived class instance. You have to place

using EventReceiver<AddEvent>::Event;
using EventReceiver<SubtractEvent>::Event;

inside EventBaseState to force both methods to be visible by overload. For more details from the point of view of the standard (with simplified example) please see here:

Why do multiple-inherited functions with same name but different signatures not get treated as overloaded functions?

Community
  • 1
  • 1
Chajnik-U
  • 501
  • 4
  • 8
  • You're likely correct and I will mark this as the answer when I've tried it out. I must say that the compiler message really leads you down the wrong path - it actually states that there are two candidates available, but then ignores them. – Per Oct 04 '16 at 08:51