Open wuyuanyi135 opened 1 year ago
I think u are using wrong DSL for transition table, like in #542:
*state<Stage1>[&SubSM::stage1_guard] / []{cout << "Transiting to stage 2\n"; } = state<Stage2>
(...)
,state<Stage2>[&SubSM::stage2_guard] / []{cout << "Leaving stage 2\n"; } = state<Stage1>
There is no anonymous transition with guard (see Tutorial/3. Create a transition table or Examples/Transitions)
src_state [guard] = dst_state
It seems that guards can only be used with an event:
state + event [guard]
src_state + event [guard] / action = dst_state
Does this code what u expected? As in #542, I added event before guard and emitted event twice.
#include <boost/sml.hpp>
#include <iostream>
#include <deque>
#include <queue>
using namespace boost::sml;
struct Data {
int count{};
};
struct Stop {};
struct Idle {};
struct SubSM {
struct Stage1 {};
struct Stage2 {};
int stage1_cnt{};
int stage2_cnt{};
bool stage1_guard() {
std::cout << "stage 1 check\n";
return stage1_cnt == 0;
}
bool stage2_guard() {
std::cout << "stage 2 check\n";
return stage2_cnt == 0;
}
void init() {
stage1_cnt = 10;
stage2_cnt = 30;
}
void process_stage1(const Data &evt, back::defer<Data> defer_event) {
std::cout << "In stage 1, Received " << evt.count << "\n";
if (stage1_cnt >= evt.count) {
stage1_cnt -= evt.count;
} else {
std::cout << "In stage 1, reprocess " << evt.count - stage1_cnt << "\n";
defer_event(Data{evt.count - stage1_cnt});
defer_event(Data{evt.count - stage1_cnt});
stage1_cnt = 0;
}
}
void process_stage2(const Data &evt, back::defer<Data> defer_event) {
std::cout << "In stage 2, Received " << evt.count << "\n";
if (stage2_cnt >= evt.count) {
stage2_cnt -= evt.count;
} else {
std::cout << "In stage 2, reprocess " << evt.count - stage2_cnt << "\n";
defer_event(Data{evt.count - stage2_cnt});
defer_event(Data{evt.count - stage2_cnt});
stage2_cnt = 0;
}
}
auto operator()() {
return make_transition_table(
// clang-format off
state<Stage1> + event<Data>[&SubSM::stage1_guard] / []{std::cout << "Transiting to stage 2\n"; } = state<Stage2>
,state<Stage1> + on_entry<_> / &SubSM::init,
*state<Stage1> + event<Data> / &SubSM::process_stage1
,state<Stage2> + event<Data>[&SubSM::stage2_guard] / []{std::cout << "Leaving stage 2\n"; } = state<Stage1>
,state<Stage2> + event<Data> / &SubSM::process_stage2
// clang-format on
);
}
};
struct CompositeSM {
auto operator()() {
return make_transition_table(
// clang-format off
*state<Idle> + event<Data> / defer = state<SubSM>
,state<Data> + event<Stop> = state<Idle>
// clang-format on
);
}
};
int main() {
SubSM sub_sm;
CompositeSM composite_sm;
sm<CompositeSM, defer_queue<std::deque>, process_queue<std::queue>> sm{sub_sm, composite_sm};
sm.process_event(Data{199});
return 0;
}
@Schoppenglas Many thanks for your explanation! I was expecting that the misuse could be caught by the compiler. I will check if this is the solution these days.
@Schoppenglas Thanks. I read through the examples and tutorials and it seems indeed no anonymous transition with a guard (also the doc.)
My question is now how to optimize the guard function? For example, if the guard is heavy (say, decoding the event data), negating it results in another invocation and it seems inefficient.
What is the recommended practice is this case? Should I use eval
to first perform some action to cache the result and reuse them in the following guards?
@wuyuanyi135 Personally I would not do heavy calculations in guards. It is not wrong, but not my personal preference. Maybe u can rethink ur whole structure. Or maybe state machine is not the best method to solve ur problem. And of course, perform heavy calculations only once and cache the result.
@wuyuanyi135 I have rewritten ur example. I think it is much more straight forward now and expresses more what it is supposed to do. Tell me ur opionion on this.
#include <boost/sml.hpp>
#include <iostream>
#include <cassert>
#include <deque>
using namespace boost::sml;
struct Data {
int count{};
};
struct Stop {};
struct Idle {};
struct SubSM {
struct Stage1 {};
struct Stage2 {};
const int stage1_cnt{10};
const int stage2_cnt{30};
bool stage1_guard(const Data& e) {
std::cout << "Stage 1 check\n";
return e.count >= stage1_cnt;
}
bool stage2_guard(const Data& e) {
std::cout << "Stage 2 check\n";
return e.count >= stage2_cnt;
}
void process_stage1(const Data &e, back::defer<Data> defer_event) {
std::cout << "In stage 1, received " << e.count << "\n";
defer_event(Data{e.count - stage1_cnt});
std::cout << "In stage 1, reprocess " << e.count - stage1_cnt << "\n";
std::cout << "Transiting to stage 2\n";
}
void process_stage1_2(const Data &e) {
std::cout << "In stage 1, received " << e.count << "\n";
std::cout << "Leaving SubSM\n";
}
void process_stage2(const Data &e, back::defer<Data> defer_event) {
std::cout << "In stage 2, received " << e.count << "\n";
defer_event(Data{e.count - stage2_cnt});
std::cout << "In stage 2, reprocess " << e.count - stage2_cnt << "\n";
std::cout << "Leaving stage 2\n";
}
void process_stage2_2(const Data &e) {
std::cout << "In stage 2, received " << e.count << "\n";
std::cout << "Leaving SubSM\n";
}
auto operator()() {
return make_transition_table(
// clang-format off
*state<Stage1> + event<Data>[&SubSM::stage1_guard] / &SubSM::process_stage1 = state<Stage2>
,state<Stage1> + event<Data> / &SubSM::process_stage1_2 = X
,state<Stage2> + event<Data>[&SubSM::stage2_guard] / &SubSM::process_stage2 = state<Stage1>
,state<Stage2> + event<Data> / &SubSM::process_stage2_2 = X
// clang-format on
);
}
};
struct CompositeSM {
auto operator()() {
return make_transition_table(
// clang-format off
*state<Idle> + event<Data> / defer = state<SubSM>
,state<SubSM> + event<Stop> / []{ std::cout << "Leaving CompositeSM\n"; } = X
// clang-format on
);
}
};
int main() {
SubSM sub_sm;
CompositeSM composite_sm;
sm<CompositeSM, defer_queue<std::deque>> sm{sub_sm, composite_sm};
sm.process_event(Data{199});
sm.process_event(Stop{});
assert(sm.is(X));
return 0;
}
I am trying to implement a composite state machine that process a
Data
event in two stages. Each stage will consume a certain amount ofcount
in the event. If thecount
exceeds what the current stage needs, it will re-process (defer) the event for next round.The code is attached below:
My problem is that, at line 62, if I use
,state<Stage2> + event<Data> / &SubSM::process_stage2
instead of the guarded one,state<Stage2> + event<Data>[!wrap(&SubSM::stage2_guard)]/ &SubSM::process_stage2
, the deferred event never trigger thestage2->stage1
transition even though the transition condition has met.However, if looking at the line 59, the
stage1->stage2
transition without a guard can correctly trigger state transition. But for stage2, without the guard it will fall into the infinite loop of processing event without even check whether stage2 has complete.Is there an explanation to the different behaviors?