├── .gitignore ├── LICENSE ├── README.md ├── example ├── Makefile └── main.cpp └── smcpp.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 electron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SMCPP: 一个状态机的C++语言实现 2 | === 3 | 4 | SMCPP是一个状态机库的C++语言实现, 支持FSM和HSM. 该库参考了QPCPP. 5 | 6 | Licensed under the `MIT license` 7 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | TARGET := test 2 | 3 | SRC := $(wildcard *.cpp) 4 | OBJ := $(SRC:.cpp=.o) 5 | 6 | CC := g++ 7 | 8 | CFLAG := -g --std=c++11 9 | 10 | $(TARGET): $(OBJ) 11 | $(CC) -o $(TARGET) $(OBJ) 12 | 13 | %.o: %.cpp 14 | $(CC) $(CFLAG) -c $< -o $@ 15 | 16 | .PHONY: clean run 17 | clean: 18 | -rm -rf $(OBJ) 19 | 20 | run: 21 | ./$(TARGET) 22 | -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../smcpp.h" 4 | 5 | enum 6 | { 7 | CHAR_SIG = SM::USER_SIG, 8 | }; 9 | 10 | class E: public SM::Event 11 | { 12 | public: 13 | E(SM::Singal sig): Event(sig){} 14 | char c; 15 | }; 16 | 17 | 18 | class A: public SM::Hsm 19 | { 20 | public: 21 | A(): Hsm(init) 22 | { 23 | } 24 | 25 | private: 26 | int a; 27 | 28 | static int init(SM::Attr &hsm, SM::Event &e) 29 | { 30 | printf("init\n"); 31 | return hsm.tran(top); 32 | } 33 | 34 | static int top(SM::Attr &hsm, SM::Event &e) 35 | { 36 | auto &p = dynamic_cast(hsm); 37 | (void)p; 38 | 39 | switch(e.sig) 40 | { 41 | case SM::ENTRY_SIG: 42 | printf("top entry\n"); 43 | return hsm.handled(); 44 | case SM::EXIT_SIG: 45 | printf("top exit\n"); 46 | return hsm.handled(); 47 | case SM::INIT_SIG: 48 | printf("top init\n"); 49 | return hsm.tran(s0); 50 | } 51 | 52 | return hsm.supper(hsm_top); 53 | } 54 | 55 | static int s0(SM::Attr &hsm, SM::Event &event) 56 | { 57 | 58 | switch(event.sig) 59 | { 60 | case SM::ENTRY_SIG: 61 | printf("s0 entry\n"); 62 | return hsm.handled(); 63 | case SM::EXIT_SIG: 64 | printf("s0 exit\n"); 65 | return hsm.handled(); 66 | case SM::INIT_SIG: 67 | printf("s0 init\n"); 68 | return hsm.tran(s2); 69 | 70 | case CHAR_SIG: 71 | { 72 | auto e = dynamic_cast(event); 73 | if ('1' == e.c) 74 | { 75 | return hsm.tran(s1); 76 | } 77 | return hsm.handled(); 78 | } 79 | } 80 | 81 | return hsm.supper(top); 82 | } 83 | 84 | static int s1(SM::Attr &hsm, SM::Event &event) 85 | { 86 | 87 | switch(event.sig) 88 | { 89 | case SM::ENTRY_SIG: 90 | printf("s1 entry\n"); 91 | return hsm.handled(); 92 | case SM::EXIT_SIG: 93 | printf("s1 exit\n"); 94 | return hsm.handled(); 95 | case SM::INIT_SIG: 96 | printf("s1 init\n"); 97 | return hsm.tran(s3); 98 | 99 | case CHAR_SIG: 100 | { 101 | auto e = dynamic_cast(event); 102 | if ('1' == e.c) 103 | { 104 | return hsm.tran(s0); 105 | } 106 | return hsm.handled(); 107 | } 108 | } 109 | 110 | return hsm.supper(top); 111 | } 112 | 113 | static int s2(SM::Attr &hsm, SM::Event &event) 114 | { 115 | switch(event.sig) 116 | { 117 | case SM::ENTRY_SIG: 118 | printf("s2 entry\n"); 119 | return hsm.handled(); 120 | case SM::EXIT_SIG: 121 | printf("s2 exit\n"); 122 | return hsm.handled(); 123 | } 124 | 125 | return hsm.supper(s0); 126 | } 127 | 128 | static int s3(SM::Attr &hsm, SM::Event &event) 129 | { 130 | switch(event.sig) 131 | { 132 | case SM::ENTRY_SIG: 133 | printf("s3 entry\n"); 134 | return hsm.handled(); 135 | case SM::EXIT_SIG: 136 | printf("s3 exit\n"); 137 | return hsm.handled(); 138 | } 139 | 140 | return hsm.supper(s1); 141 | } 142 | }; 143 | 144 | 145 | int main(int argc, char *argv[]) 146 | { 147 | A a; 148 | 149 | a.start(); 150 | 151 | E e(CHAR_SIG); 152 | for(;;) 153 | { 154 | scanf("%c", &e.c); 155 | if (e.c == 'q') 156 | { 157 | break; 158 | } 159 | 160 | a.dispatch(e); 161 | } 162 | 163 | return 0; 164 | } 165 | -------------------------------------------------------------------------------- /smcpp.h: -------------------------------------------------------------------------------- 1 | #ifndef __SMCPP_H__ 2 | #define __SMCPP_H__ 3 | 4 | #include 5 | #include 6 | 7 | namespace SM 8 | { 9 | 10 | #define CONFIG_SM_DEBUG 1 11 | 12 | #ifndef CONFIG_SM_FSM 13 | #define CONFIG_SM_FSM 1 14 | #endif 15 | 16 | #ifndef CONFIG_SM_HSM 17 | #define CONFIG_SM_HSM 1 18 | #endif 19 | 20 | #if ! (CONFIG_SM_FSM || CONFIG_SM_HSM) 21 | #error "FSM and HSM must chose one!" 22 | #endif 23 | 24 | #ifndef SM_MAX_NEST_DEPTH 25 | #define SM_MAX_NEST_DEPTH 8 26 | #endif 27 | 28 | #if CONFIG_SM_DEBUG 29 | #define SM_ASSERT(cond) assert(cond) 30 | #else 31 | #define SM_ASSERT(cond) /* NULL */ 32 | #endif 33 | 34 | typedef int Singal; 35 | enum RESERVED_SIG 36 | { 37 | EMPTY_SIG = -4, 38 | ENTRY_SIG = -3, 39 | EXIT_SIG = -2, 40 | INIT_SIG = -1, 41 | USER_SIG = 0, 42 | }; 43 | 44 | /** 45 | * @brief 事件类 46 | */ 47 | class Event 48 | { 49 | public: 50 | Event(Singal const s): sig(s){} 51 | virtual ~Event(){} 52 | 53 | Singal sig; 54 | }; 55 | const Event RESERVED_EVENT[] = 56 | { 57 | Event(EMPTY_SIG), 58 | Event(ENTRY_SIG), 59 | Event(EXIT_SIG), 60 | Event(INIT_SIG), 61 | Event(USER_SIG), 62 | }; 63 | 64 | 65 | /** 66 | * @bref 状态处理函数的返回值类型 67 | * 68 | */ 69 | enum 70 | { 71 | RET_HANDLED, 72 | RET_IGNORE, 73 | RET_UNHANDLED, 74 | 75 | RET_TRAN, 76 | RET_SUPER, 77 | }; 78 | 79 | class Attr; 80 | typedef int (*StateHander)(Attr &sm, Event &e); 81 | 82 | class Attr 83 | { 84 | public: 85 | Attr() 86 | { 87 | m_state = 0; 88 | m_temp = 0; 89 | m_last = m_state; 90 | } 91 | virtual ~Attr(){} 92 | 93 | inline int handled(void) 94 | { 95 | return RET_HANDLED; 96 | } 97 | inline int ignore(void) 98 | { 99 | return RET_IGNORE; 100 | } 101 | inline int unhandled(void) 102 | { 103 | return RET_UNHANDLED; 104 | } 105 | inline int tran(const StateHander &target) 106 | { 107 | m_temp = target; 108 | m_last = m_state; 109 | return RET_TRAN; 110 | } 111 | inline int tranLast(void) 112 | { 113 | m_temp = m_last; 114 | m_last = m_state; 115 | return RET_TRAN; 116 | } 117 | inline int supper(const StateHander &target) 118 | { 119 | m_temp = target; 120 | return RET_SUPER; 121 | } 122 | 123 | private: 124 | StateHander m_state; 125 | StateHander m_temp; 126 | StateHander m_last; 127 | 128 | inline int trig(const StateHander &state, Singal sig) 129 | { 130 | return state(*this, const_cast(RESERVED_EVENT[4+sig])); 131 | } 132 | inline int entry(StateHander state) 133 | { 134 | return trig(state, ENTRY_SIG); 135 | } 136 | inline int exit(StateHander state) 137 | { 138 | return trig(state, EXIT_SIG); 139 | } 140 | 141 | friend class Fsm; 142 | friend class Hsm; 143 | }; 144 | 145 | #if CONFIG_SM_FSM 146 | class Fsm: public Attr 147 | { 148 | public: 149 | Fsm(const StateHander &init) 150 | { 151 | m_state = 0; 152 | m_temp = init; 153 | } 154 | 155 | int start(Event &e = const_cast(RESERVED_EVENT[4+USER_SIG])) 156 | { 157 | int ret; 158 | 159 | ret = m_temp(*this, e); 160 | if (RET_TRAN != ret) 161 | { 162 | return ret; 163 | } 164 | 165 | entry(m_temp); 166 | 167 | m_state = m_temp; 168 | 169 | return ret; 170 | } 171 | 172 | void dispatch(Event &e) 173 | { 174 | int ret; 175 | 176 | SM_ASSERT(m_state == m_temp); 177 | 178 | ret = m_temp(*this, e); 179 | if (ret == RET_TRAN) 180 | { 181 | exit(m_state); 182 | entry(m_temp); 183 | m_state = m_temp; 184 | } 185 | } 186 | 187 | static Fsm &fsm_entry(Attr &p) 188 | { 189 | return dynamic_cast(p); 190 | } 191 | }; 192 | #endif 193 | 194 | #if CONFIG_SM_HSM 195 | class Hsm: public Attr 196 | { 197 | public: 198 | Hsm(const StateHander &init) 199 | { 200 | m_state = hsm_top; 201 | m_temp = init; 202 | } 203 | 204 | void start(Event &e = const_cast(RESERVED_EVENT[4+USER_SIG])) 205 | { 206 | int ret; 207 | int ip; 208 | 209 | StateHander path[SM_MAX_NEST_DEPTH]; 210 | StateHander t = m_state; 211 | 212 | ret = (m_temp)(*this, e); 213 | SM_ASSERT(RET_TRAN == ret); 214 | 215 | do 216 | { 217 | ip = 0; 218 | 219 | path[0] = m_temp; 220 | trig(m_temp, EMPTY_SIG); 221 | while( t != m_temp ) 222 | { 223 | path[++ip] = m_temp; 224 | trig(m_temp, EMPTY_SIG); 225 | } 226 | m_temp = path[0]; 227 | 228 | SM_ASSERT(ip < SM_MAX_NEST_DEPTH); 229 | 230 | do 231 | { 232 | entry(path[ip--]); 233 | }while(ip >= 0); 234 | 235 | t = path[0]; 236 | }while(RET_TRAN == trig(t, INIT_SIG)); 237 | 238 | m_temp = t; 239 | m_state = m_temp; 240 | } 241 | 242 | void dispatch(Event &e) 243 | { 244 | StateHander t = m_state; 245 | StateHander s; 246 | 247 | int ret; 248 | 249 | // 状态必须稳定 250 | SM_ASSERT(m_state == m_temp); 251 | 252 | /* process the event hierarchically... */ 253 | // 事件递归触发, 直到某个状态处理该事件 254 | do 255 | { 256 | s = m_temp; 257 | ret = s(*this, e); // 调用状态处理函数 258 | if(RET_UNHANDLED == ret) 259 | { 260 | ret = trig(s, EMPTY_SIG); 261 | } 262 | }while(RET_SUPER == ret); 263 | 264 | // 如果发生状态转换 265 | if(RET_TRAN == ret) 266 | { 267 | StateHander path[SM_MAX_NEST_DEPTH]; 268 | signed char ip = -1; 269 | 270 | path[0] = m_temp; // 状态转换的目的状态 271 | path[1] = t; // 状态转换的源状态 272 | 273 | /* exit current state to transition source s... */ 274 | for( ; s != t; t = m_temp) 275 | { 276 | ret = exit(t); 277 | if(RET_HANDLED == ret) 278 | { 279 | trig(t, EMPTY_SIG); 280 | } 281 | } 282 | 283 | ip = find_path(path[0], s, path); 284 | 285 | for(; ip>=0; ip--) 286 | { 287 | entry(path[ip]); 288 | } 289 | 290 | t = path[0]; 291 | m_temp = t; 292 | 293 | /* drill into the target hierarchy... */ 294 | while( RET_TRAN == trig(t, INIT_SIG) ) 295 | { 296 | ip = 0; 297 | path[0] = m_temp; 298 | 299 | trig(m_temp, EMPTY_SIG); 300 | while(t != m_temp) 301 | { 302 | path[++ip] = m_temp; 303 | trig(m_temp, EMPTY_SIG); 304 | } 305 | m_temp = path[0]; 306 | 307 | SM_ASSERT(ip < SM_MAX_NEST_DEPTH); 308 | 309 | do 310 | { 311 | entry(path[ip--]); 312 | }while(ip >= 0); 313 | 314 | t = path[0]; 315 | }// end: while( SM_RET_TRAN == SM_TRIG(me, t, SM_INIT_SIG) ) 316 | } // end: if(SM_RET_TRAN == ret) 317 | 318 | m_temp = t; 319 | m_state = t; 320 | } 321 | 322 | static Hsm &hsm_entry(Attr &p) 323 | { 324 | return dynamic_cast(p); 325 | } 326 | 327 | //! 层次状态机根状态 328 | static int hsm_top(Attr &hsm, Event &e) 329 | { 330 | (void)e; 331 | return hsm.ignore(); 332 | } 333 | 334 | private: 335 | 336 | int find_path(StateHander t, StateHander s, StateHander path[SM_MAX_NEST_DEPTH]) 337 | { 338 | int ip = -1; 339 | int iq; 340 | int ret; 341 | 342 | /* (a) check source==target (transition to self) */ 343 | if( s == t) 344 | { 345 | exit(s); 346 | ip = 0; 347 | 348 | goto hsm_find_path_end; 349 | } 350 | 351 | trig(t, EMPTY_SIG); 352 | t = m_temp; 353 | 354 | /* (b) check source==target->super */ 355 | if( s == t ) 356 | { 357 | ip = 0; 358 | goto hsm_find_path_end; 359 | } 360 | 361 | trig(s, EMPTY_SIG); 362 | 363 | /* (c) check source->super==target->super */ 364 | if(m_temp == t) 365 | { 366 | exit(s); 367 | ip = 0; 368 | goto hsm_find_path_end; 369 | } 370 | 371 | /* (d) check source->super==target */ 372 | if( m_temp == path[0] ) 373 | { 374 | exit(s); 375 | goto hsm_find_path_end; 376 | } 377 | 378 | /* (e) check rest of source==target->super->super.. 379 | * and store the entry path along the way 380 | */ 381 | ip = 1; 382 | iq = 0; 383 | path[1] = t; 384 | t = m_temp; 385 | 386 | /* find target->super->super... */ 387 | ret = trig(path[1], EMPTY_SIG); 388 | while(RET_SUPER == ret) 389 | { 390 | path[++ip] = m_temp; 391 | if(s == m_temp) 392 | { 393 | iq = 1; 394 | SM_ASSERT(ip < SM_MAX_NEST_DEPTH); 395 | ip--; 396 | 397 | ret = RET_HANDLED; 398 | } 399 | else 400 | { 401 | ret = trig(m_temp, EMPTY_SIG); 402 | } 403 | } 404 | 405 | /* the LCA not found yet? */ 406 | if(0 == iq) 407 | { 408 | SM_ASSERT(ip < SM_MAX_NEST_DEPTH); 409 | 410 | exit(s); 411 | 412 | /* (f) check the rest of source->super 413 | * == target->super->super... 414 | */ 415 | iq = ip; 416 | ret = RET_IGNORE; /* LCA NOT found */ 417 | do 418 | { 419 | s = path[iq]; 420 | /* is this the LCA? */ 421 | if(t == s) 422 | { 423 | ret = RET_HANDLED; 424 | 425 | ip = iq - 1; 426 | iq = -1; 427 | } 428 | else 429 | { 430 | iq--; /* try lower superstate of target */ 431 | } 432 | }while(iq >= 0); 433 | 434 | /* LCA not found? */ 435 | if( RET_HANDLED != ret ) 436 | { 437 | /* (g) check each source->super->... 438 | * for each target->super... 439 | */ 440 | ret = RET_IGNORE; 441 | do 442 | { 443 | if(RET_HANDLED == exit(t)) 444 | { 445 | trig(t, EMPTY_SIG); 446 | } 447 | t = m_temp; 448 | iq = ip; 449 | do 450 | { 451 | s = path[iq]; 452 | if( t == s) 453 | { 454 | ip = iq -1; 455 | iq = -1; 456 | 457 | ret = RET_HANDLED; /* break */ 458 | } 459 | else 460 | { 461 | iq--; 462 | } 463 | }while(iq >= 0); 464 | }while(RET_HANDLED != ret); 465 | } 466 | } 467 | 468 | hsm_find_path_end: 469 | return ip; 470 | } 471 | }; 472 | #endif // CONFIG_SM_HSM 473 | 474 | } // end namespace 475 | 476 | #endif 477 | --------------------------------------------------------------------------------