Skip to content

Commit 5a78ee2

Browse files
committed
Snapshot for the 1.0.4 release with updated docs and fixed param for the onEnteredState
1 parent dd853d7 commit 5a78ee2

File tree

9 files changed

+544
-308
lines changed

9 files changed

+544
-308
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ using [Xstate](https://github.com/davidkpiano/xstate) into C++ generated SM, no
2222
* Arbitrary user-defined data structure (called Context) can be stored in the SM
2323
* Any event can have an arbitrary user-defined payload attached. The event payload is propagated to related callbacks
2424

25+
## Resources
26+
* Quick start is right below
27+
* [Tutorial](TUTORIAL.md)
28+
2529
## Install and Quick Start Tutorial
2630

2731
### 1. Install the xstate-cpp-generator TypeScript package, locally (or globally with `-g` option):
@@ -82,8 +86,14 @@ CppGen.generateCpp({
8286
```
8387
To visualize this State Machine copy-paste the 'Machine' method call to the [online vizualizer](https://xstate.js.org/viz/).
8488

85-
### 3. And generate C++ with
89+
### 3. Generate C++
90+
Install all required dependencies:
91+
92+
```bash
93+
npm install
94+
```
8695

96+
And run the C++ generator:
8797
```bash
8898
ts-node engineer.ts
8999
```
@@ -122,3 +132,6 @@ and run it with:
122132
### V 1.0.3
123133
* Full support of entry, exit and transition Actions
124134
* Multi-threading bugfixes
135+
### V 1.0.4
136+
* Converted `onEnteredState()` from move sematics `&&` to shared_ptr
137+
* Started Tutorial

TUTORIAL.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# C++ State Machine code generator for Xstate Tutorial
2+
[Back to README page](README.md) for introduction.
3+
4+
This tutorial is based on the model [engineer.ts](demo-project/engineer.ts) and the demo project [engineer_demo.cpp](demo-project/engineer_demo.cpp).
5+
6+
## Install the package and generate the code
7+
8+
Please follow the [Quick Start guide](README.md#install-and-quick-start-tutorial) to generate the code from `engineer.ts` model.
9+
10+
## The generated header walkthrough
11+
12+
### What happens when an Event is posted
13+
14+
The State Machine header generated by the demo model is [engineer_sm.h](demo-project/engineer_sm.h). Let's follow a fragment [engineer.ts](demo-project/engineer.ts) to find how it maps to the generated C++ code. In the model we have one of the state transitions declared as:
15+
16+
```TypeScript
17+
sleeping: {
18+
entry: 'startWakeupTimer',
19+
exit: 'morningRoutine',
20+
on: {
21+
'TIMER': { target: 'working', actions: ['startHungryTimer', 'startTiredTimer'] },
22+
}
23+
},
24+
```
25+
it means that if the Engineer SM is in state `sleeping`, it will transition to the `working` state when the `TIMER` event is posted.
26+
27+
The State Machine is declared as:
28+
```C++
29+
template <typename SMSpec = DefaultEngineerSMSpec<std::nullptr_t>>
30+
class EngineerSM {
31+
...
32+
}
33+
```
34+
The template argument `SMSpec` has a convenient default already generated, but it can be replaced with another template struct to do the full customization of the State Machine at compile time.
35+
36+
To post the `TIMER` event call this method:
37+
```C++
38+
void postEventTimer(std::shared_ptr<TimerPayload> payload);
39+
```
40+
Here the `TimerPayload` is declared in the `SMSpec` and by declaring this struct you can use an arbitrary class as `TimerPayload`.
41+
42+
When `TIMER` is posted, and the machine is in `sleeping` state, the generated State Machine engine will do the following steps:
43+
44+
* Call the method `morningRoutine(EngineerSM<DefaultEngineerSMSpec>* sm)`, which is an exit action from the `sleeping` state
45+
* Call the virtual method `onLeavingSleepingState(State nextState)`. Such exit methods are generated for every state
46+
* Call method `startHungryTimer(EngineerSM<DefaultEngineerSMSpec>* sm, std::shared_ptr<EventTimerPayload>)` for the transition action. Here the `std::shared_ptr<EventTimerPayload>)` is the same event payload that was sent with the `postEventTimer()` call
47+
* Call method `startTiredTimer(EngineerSM<DefaultEngineerSMSpec>* sm, std::shared_ptr<EventTimerPayload>)`, which is another modeled transition action
48+
* Call method `void onEnteringStateWorkingOnTIMER(State nextState, std::shared_ptr<TimerPayload> payload)`. Again, the `payload` is probagated to this callback as well.
49+
* As `working` state was declared with the following entry events:
50+
```TypeScript
51+
working: {
52+
entry: ['checkEmail', 'startHungryTimer', 'checkIfItsWeekend' ],
53+
```
54+
the action callbacks `checkEmail(EngineerSM<DefaultEngineerSMSpec>* sm)`, `startHungryTimer(EngineerSM<DefaultEngineerSMSpec>* sm)` and `checkIfItsWeekend(EngineerSM<DefaultEngineerSMSpec>* sm)` will be invoked as well
55+
* Note: all those actions above were invoked while the SM state is still `sleepng`
56+
* Transition to the new state `working`. This involves changing the internal data structures protected under `std::mutex` lock. Thus the SM is transitioned to the next state atomically
57+
* Invoke the callback `onEnteredStateWorkingOnTIMER(std::shared_ptr<TimerPayload> payload)`
58+

demo-project/engineer_sm.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* https://github.com/shuvalov-mdb/xstate-cpp-generator , @author Andrew Shuvalov
44
*
55
* Please do not edit. If changes are needed, regenerate using the TypeScript template 'engineer.ts'.
6-
* Generated at Fri Oct 30 2020 01:59:52 GMT+0000 (Coordinated Universal Time) from Xstate definition 'engineer.ts'.
6+
* Generated at Fri Oct 30 2020 16:38:31 GMT+0000 (Coordinated Universal Time) from Xstate definition 'engineer.ts'.
77
* The simplest command line to run the generation:
88
* ts-node 'engineer.ts'
99
*/
@@ -315,19 +315,19 @@ class EngineerSM {
315315
* It is safe to call postEvent*() to trigger the next transition from this method.
316316
* @param payload ownership is transferred to the user.
317317
*/
318-
virtual void onEnteredStateWorkingOnTIMER(TimerPayload&& payload) {
318+
virtual void onEnteredStateWorkingOnTIMER(std::shared_ptr<TimerPayload> payload) {
319319
std::lock_guard<std::mutex> lck(_lock);
320320
logTransition(EngineerSMTransitionPhase::ENTERED_STATE, _currentState.currentState, State::working);
321321
}
322-
virtual void onEnteredStateEatingOnHUNGRY(HungryPayload&& payload) {
322+
virtual void onEnteredStateEatingOnHUNGRY(std::shared_ptr<HungryPayload> payload) {
323323
std::lock_guard<std::mutex> lck(_lock);
324324
logTransition(EngineerSMTransitionPhase::ENTERED_STATE, _currentState.currentState, State::eating);
325325
}
326-
virtual void onEnteredStateSleepingOnTIRED(TiredPayload&& payload) {
326+
virtual void onEnteredStateSleepingOnTIRED(std::shared_ptr<TiredPayload> payload) {
327327
std::lock_guard<std::mutex> lck(_lock);
328328
logTransition(EngineerSMTransitionPhase::ENTERED_STATE, _currentState.currentState, State::sleeping);
329329
}
330-
virtual void onEnteredStateWeekendOnENOUGH(EnoughPayload&& payload) {
330+
virtual void onEnteredStateWeekendOnENOUGH(std::shared_ptr<EnoughPayload> payload) {
331331
std::lock_guard<std::mutex> lck(_lock);
332332
logTransition(EngineerSMTransitionPhase::ENTERED_STATE, _currentState.currentState, State::weekend);
333333
}
@@ -617,23 +617,23 @@ void EngineerSM<SMSpec>::_transitionActionsHelper(State fromState, Event event,
617617
template<typename SMSpec>
618618
void EngineerSM<SMSpec>::_enteredStateHelper(Event event, State newState, void* payload) {
619619
if (event == Event::TIMER && newState == State::working) {
620-
TimerPayload* typedPayload = static_cast<TimerPayload*>(payload);
621-
onEnteredStateWorkingOnTIMER(std::move(*typedPayload));
620+
std::shared_ptr<TimerPayload>* typedPayload = static_cast<std::shared_ptr<TimerPayload>*>(payload);
621+
onEnteredStateWorkingOnTIMER(*typedPayload);
622622
return;
623623
}
624624
if (event == Event::HUNGRY && newState == State::eating) {
625-
HungryPayload* typedPayload = static_cast<HungryPayload*>(payload);
626-
onEnteredStateEatingOnHUNGRY(std::move(*typedPayload));
625+
std::shared_ptr<HungryPayload>* typedPayload = static_cast<std::shared_ptr<HungryPayload>*>(payload);
626+
onEnteredStateEatingOnHUNGRY(*typedPayload);
627627
return;
628628
}
629629
if (event == Event::TIRED && newState == State::sleeping) {
630-
TiredPayload* typedPayload = static_cast<TiredPayload*>(payload);
631-
onEnteredStateSleepingOnTIRED(std::move(*typedPayload));
630+
std::shared_ptr<TiredPayload>* typedPayload = static_cast<std::shared_ptr<TiredPayload>*>(payload);
631+
onEnteredStateSleepingOnTIRED(*typedPayload);
632632
return;
633633
}
634634
if (event == Event::ENOUGH && newState == State::weekend) {
635-
EnoughPayload* typedPayload = static_cast<EnoughPayload*>(payload);
636-
onEnteredStateWeekendOnENOUGH(std::move(*typedPayload));
635+
std::shared_ptr<EnoughPayload>* typedPayload = static_cast<std::shared_ptr<EnoughPayload>*>(payload);
636+
onEnteredStateWeekendOnENOUGH(*typedPayload);
637637
return;
638638
}
639639
}

0 commit comments

Comments
 (0)