I have a generic guard condition which I would like to conditionally prevent transitions under certain circumstances. Below is a somewhat C++/Pseudocode of what I would like to do.
bool operator()(Event const& evt, FSM & fsm, SourceState& src, TargetState& tgt )
{
bool transition = (current_state != next_state);
bool transitionAllowed = (x | y | z); //some custom condition
return (transition && transitionAllowed);
}
I would like to know if the target state is different from the source state and based on that determine if we can allow a transitions based on other parameters. I haven't found much success with documentation so far.
It seems that your question contains two parts. One is how to check the current state and the next state. The other is how to check custom conditions.
In order to checking the current state and the next state, you can use std::is_same
meta function.
Here is an example of a guard:
struct Guard1 {
template <class Event, class Fsm, class Source, class Target>
bool operator()(Event const&, Fsm& fsm, Source& src, Target&) const {
bool transition = !std::is_same<Source, Target>::value;
bool transitionAllowed = x() || fsm.y || src.z;
return transition && transitionAllowed;
}
};
Here is the transition table:
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
// source and target is the same
msmf::Row < State1, Event1, State1, msmf::none, Guard1 >,
// source and target is different
msmf::Row < State1, Event2, State2, msmf::none, Guard1 >
> {};
The transition caused by Event1 has the same source and target states. Both are State1. The transition caused by Event2 has the different source and target states. The source state is State1, and the target state is State2.
The former case std::is_same<Source, Target>::value
returns true, and the latter case returns false.
The variable transition
is the nagation of the result.
You can use this as the part of return value.
In order to evaluate custom condition, you need to get values for evaluation from some sources. I wrote three examples of sources.
x()
is global function. Of course you can use global varibles like this.y
is a member variable of the state machine.z
is a member variable of the source state, State1.Here is the complete code:
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/static_assert.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// You can test changing the value
bool const example_value = true;
struct Event1 {};
struct Event2 {};
// example of a condition
bool x() { return example_value; }
struct Sm_:msmf::state_machine_def<Sm_>
{
// States
struct State1:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "State1::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "State1::on_exit()" << std::endl;
}
bool z = example_value; // example of a condition
};
struct State2:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) {
std::cout << "State2::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "State2::on_exit()" << std::endl;
}
int property;
};
// Set initial state
typedef State1 initial_state;
// Guards
struct Guard1 {
template <class Event, class Fsm, class Source, class Target>
bool operator()(Event const&, Fsm& fsm, Source& src, Target&) const {
bool transition = !std::is_same<Source, Target>::value;
bool transitionAllowed = x() || fsm.y || src.z;
return transition && transitionAllowed;
}
};
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
// source and target is the same
msmf::Row < State1, Event1, State1, msmf::none, Guard1 >,
// source and target is different
msmf::Row < State1, Event2, State2, msmf::none, Guard1 >
> {};
bool y = example_value; // example of a condition
};
// Pick a back-end
typedef msm::back::state_machine<Sm_> Sm;
int main() {
Sm sm;
sm.start();
std::cout << "> Send Event1()" << std::endl;
sm.process_event(Event1());
std::cout << "> Send Event2()" << std::endl;
sm.process_event(Event2());
}
You can change the values of the custom conditions.
// You can test changing the value
bool const example_value = true;
If you set example_value as false, the custom condition is not satisfied.
If custom condition is NOT satisfied, both Event1
and Event2
don't make transition.
If custom condition is satisfied, Event1
doesn't make a transition because the source and the target states are the same. Event2
makes a transition.
Here is the running demo.
The case example_value = true
https://wandbox.org/permlink/6qHcW9e6JX4QXAuH
The case example_value = false