C++ Circular Class Dependency with tinyfsm - forward declarations don't work

340 Views Asked by At

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.

1

There are 1 best solutions below

0
On

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)