2

I am building a Boost state machine. My state has a pointer to its own backend (fsm) to process events. All events of my state machine are children of an MySatetMachineEvent class (with an example child like EventChild). State transitions are only defined for children like EventChild of MySTateMachineEvent.
To clean up my code I want to create a function processEvent(MySatetMachineEvent event) taking all possible events. This class should then call the process_event() function with the passed event. For example:

processEvent(MyStateMachineEvent event)
{
    fsm.process_event(event);
}
processEvent(EventCild());

should case a call of

fsm.process_event(EventChild());

Creating such a function causes the error that fsm.process_event() is called with an instance of MyStateMachineEvent. As written above there are no state transitions defined for this case.

This hinders my state machine from working in a proper manner, obviously.


So my question is if there is a way to pass any EventChild or other child of MyStateMachineEvent to my processEvent(MySTateMachineEvent event) function without casting the passed Object to MyStateMachineEvent.


I am aware of the solution to overlode my function like

processEvent(EventChild event) {
    fsm.process_event(event);
}

This would cause may functions (with the exact same line of code inside) in my case, thus i am looking for a cleaner and more fancy solution.

  • @VittorioRomeo has a good solution, but if there is no additional logic in `processEvent()`, why not just call `fsm.process_event()` directly? Is there more logic in `processEvent()`, or is the static type of the argument passed to `processEvent()` a `MyStateMachineEvent` (i.e. the type is not known at compile time)? If the latter, Vittorio's solution won't work and you need to look at `dynamic_cast<>` or refactor the design so that the events themselves know how to advance the state machine with their own `process_event()` member functions. – TypeIA Dec 20 '18 at 15:04
  • Actually the `fsm` thing needs to be accessed with `msm::back::state_machine &fsm = static_cast &>(*this);`. Thats the reason why I would like to create the function. – CoffeeKangaroo Jan 02 '19 at 10:25
  • @CoffeeKangaroo Is there an `explicit` cast operator for the type of `this` to state machine? Otherwise, simply `/*...*/ &fsm = *this;` should do the trick as well (non-explicit cast operator or `this` inheriting from the class in question). – Aconcagua Jan 02 '19 at 10:41
  • I stick to the boost guide for [msm](https://www.boost.org/doc/libs/1_60_0/libs/msm/doc/HTML/ch03s02.html) with this line of code. Your suggested line does not work. I guess there is a cast operator defined within the boost library. – CoffeeKangaroo Jan 02 '19 at 10:55

2 Answers2

3

You can use a function template:

template <typename Event>
void processEvent(Event event)
{
    fsm.process_event(event);
}

Every instantiation will preserve the exact type of the event argument that was passed in.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Thank you! I like this solution more than the one pressented by [Aconcagua](https://stackoverflow.com/users/1312382/aconcagua). To be honest I run also with this solution into a problem: the complier tells me that a reference to the specific usage of the template is missing. For example when I use the code given above with `something.porcessEvent(EventChild)` compliter gives mit the following error: `undefined reference to void processEvent(EventChild)`. – CoffeeKangaroo Jan 02 '19 at 10:09
  • @CoffeeKangaroo Have a look at [here](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file)... – Aconcagua Jan 02 '19 at 10:33
  • NOTE: the undefined reference is easy to solve, by moving everything into the header. It seams to be a common problem with templates. There are a lot of stackoverflow questions covering this problem: see [here](https://stackoverflow.com/questions/8752837/undefined-reference-to-template-class-constructor). – CoffeeKangaroo Jan 02 '19 at 10:51
0

Albeit I like Vittorio Romeo's template approach, it might not be suitable if you e. g. have some kind of event queue holding arbitrary events. A polymorphic approach could be more suitable then:

class MyStateMachineEvent
{
public:
    virtual ~MyStateMachineEvent() { }
    virtual void doOrGetSomething() = 0;
};

class EventChild : public MyStateMachineEvent
{
public:
    void doOrGetSomething() override;
};

Now your processEvent function might accept a reference, just as would the fsm's variant as well:

void processEvent(MyStateMachineEvent& event)
{
    fsm.processEvent(event);
}

void FSM::processEvent(MyStateMachineEvent& event)
{
    // ...
    event.doOrGetSomething();
    // ...
}

And usage might look like this:

std::queue<std::unique_ptr<MyStateMachineEvent>> events;

events.emplace(new EventChild());

processEvent(**events.front());
events.pop();
Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • Thank you for you suggestion, but as far as I got it your solution does not solve the problem since the actual class of the Object passed to the function `processEvent` is not guarded. There is still a cast to `MyStateMachineEvent`, or am I wrong? – CoffeeKangaroo Jan 02 '19 at 10:16
  • Have a close look: I changed both functions to accept references. The important part is: References preserve the original type, although hidden away (they behave like pointers in this respect), so no, [object slicing](https://en.wikipedia.org/wiki/Object_slicing) (as with the code in your question) will *not* occur. – Aconcagua Jan 02 '19 at 10:28
  • @CoffeeKangaroo The trick is to use reference *or* pointer *instead of* passing by value: `processEvent(MyStateMachineEvent` **&** `event)` as well as `processEvent(MyStateMachineEvent` **\*** `event)` both do the trick wheras `processEvent(MyStateMachineEvent event)` does not. – Aconcagua Jan 02 '19 at 12:20