1

I'm having trouble with the following code:

#include "tinyfsm.hpp"

struct Event : tinyfsm::Event { };

struct State1;
struct State2;

struct Fsm : tinyfsm::Fsm<Fsm> {
    virtual void react(Event const &) { }
    virtual void entry() { }
    void exit() { };
    virtual ~Fsm() { }
};

struct State1 : Fsm{
    void react(Event const & event) override { transit<State2>(); }
    void entry() override { }

};

struct State2 : Fsm{
    void react(Event const & event) override { transit<State1>(); }
    void entry() override { }
};

FSM_INITIAL_STATE(Fsm, State1)

The compiler gives the message:

"..\src\tinyfsm.hpp", line 134: cc0513:  error: a value of type "State2 *" cannot be assigned to an entity of type "Fsm *"
current_state_ptr = &_state_instance<S>::value;
                      ^
      detected during instantiation of "void tinyfsm::Fsm<F>::transit<S>() [with F=Fsm, S=State2]" at line 31 of "..\src\testbed.cpp"

I'm pretty sure this is because the compiler does not understand that State2 inherits from Fsm.

Is there any way to break the circular dependency, or give the compiler the relevant information so that it will compile correctly?

I'm using ccblkfn.exe version 8.12.0.0 (working on a blackfin processor)

I think it might be a compiler bug as this code compiles just fine on g++ 6.3.0.

knelson
  • 93
  • 1
  • 6
  • Since the compiler reports an error in the header file, when it is included, this gets reported before the compiler even has a chance to read what's shown here. Unless there are other error messages that are not shown here, and the compiler is actually issueing a diagnostic that traces back to something defined in the header file. In either case, without seeing what's in the header file nothing more can be said. – Sam Varshavchik Dec 09 '18 at 03:21
  • Hi Sam, I updated the compiler warning. I'm not great at templates, but as far as I can understand, the compiler is in the header because the expression under question is templated, so that is where the compiler happens to notice that it can't perform the appropriate cast from State2 to Fsm. I'm pretty sure there isn't an error in tinyfsm.hpp - it's a publicly available library that compiles correctly on a modern g++ (6.3.0). – knelson Dec 09 '18 at 04:00

1 Answers1

2

The compilation error appears to be because:

struct Fsm : tinyfsm::Fsm<Fsm> 

This declares a struct named Fsm in the global namespace.

The header defines a type with the same name in the tinyfsm namespace. The macro call

FSM_INITIAL_STATE(Fsm, State1)

Expands to this macro declaration:

#define FSM_INITIAL_STATE(_FSM, _STATE)                               \
namespace tinyfsm {                                                   \
  template<> void Fsm< _FSM >::set_initial_state(void) {              \
    current_state_ptr = &_state_instance< _STATE >::value;            \
  }                                                                   \
}

This ends up expanding to:

namespace tinyfsm {
  template<> void Fsm<Fsm>::set_initial_state(void) {
    current_state_ptr = &_state_instance< _STATE >::value;
  }
}

What do you think the <Fsm> part ends up refering to? Not your class, but the Fsm template in the tinyfsm namespace. Hillarity ensues.

There are several simple ways to resolve this ambiguity.

FSM_INITIAL_STATE(::Fsm, State1)

The macro now inserts a reference to your Fsm struct in the global namespace.

Another way is to simply rename your Fsm class to something else.

A third way is to put all of your classes into their own namespace, then (outside of your namespace).

namespace fsmimpl {

// Your existing class declarations

}

FSM_INITIAL_STATE(fsmimpl::Fsm, State1)
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148