程序小白
认证:优质创作者
所在专题目录 查看专题
【第一章】有限状态机理论速成笔记
【第二章】HSM层次式状态机理论(进阶版)
【第三章】有限状态机(FSM)之炸弹项目(实战1)
【第四章】有限状态机(FSM)之炸弹项目(实战2)
【第五章】有限状态机(FSM)之炸弹项目(实战3)
【第六章】层次式状态机HSM = 有限状态机FSM + 面向对象
作者动态 更多
小小调度器——开局default,优雅总藏于细节之中
04-02 09:23
小小调度器——调度器核心的纯C实现
04-01 14:09
小小调度器——原来C代码还能这么写,原作者没少挨打吧!
03-27 15:42
解析由系统库引发的hardfault血案(RT-Thread+ESP32)
2023-07-17 16:04
TINA瞬时现象仿真分析——运放缓冲器振铃
2023-02-28 11:38

【第四章】有限状态机(FSM)之炸弹项目(实战2)

      前面讲的定时炸弹的一种最基本的实现方式,但是条条大路通罗马,总有大神出歪招,C语言的核心技能之一即将到达战场,函数指针(这个词有的童鞋可能比较陌生,也可能再别人程序上见到过,但是这种跳来跳去的操作总感觉和goto有的一拼,复杂难懂),对于指针大家用的更多的是变量指针,尤其是和数组一起用的时候,那种完美的数据流向真的很让人沉醉,其实假如你真正的理解了函数指针的意义,那么你就知道他是C语言实现程序流向控制最核心的技术之一。

      夸得太多,其实现在开始讲理论总是一个脑袋两个大,还是先上例子,定时炸弹状态表模式,如何用一个表来表示所有可能执行的操作呢?

      我们把状态和事件映射成一个二维的表,动作在表中定义,这样就是可以做到最精细粒度的分解。  基于以上理论,我们实际把状态机和事件联合,映射成了一张状态表,然后你需要构造一个基于状态表的通用事件处理器,其构造如下:

基于上面的构造图,我们如何真正用代码来实现它,我们来看一下他具体实现的代码,如下:

1.第一步,定义事件结构体,根据上面的图,代码如下:

2.第二步,状态表应该是一个二维数组,在表中我们希望存有的是具体执行的上下文,也就是函数,但是函数本身并不能作为数据被存入表中,但是函数指针可以当做数据被存入表中,而我们可以通过函数指针类型的变量来引用具体函数,从而实现上下文的执行,我们需要定义一个函数指针类型,如下:

3.第三部,有了前面的铺垫,这里开始定义一个状态表,如下:

4.第四部,实现ctor,init,dispatch三个构造函数,如下:

5.这里我们还需要定义一个状态转换宏,如下:

         小插曲:很久很久以前,我在思考一个问题,为什么要定义一个如此复杂的宏,当所有的代码都是需要我们自己写的时候,我们不光要明白一处代码的含义,还要精确地设计这里的代码,这时候用不用宏其实都可以,但是假如我们设计的代码是给别人来用的,他并不需要精确到设计的细节,而是只需要理解这句代码的含义,也就足够了,这就是我们的对于软件的设计理念不同所导致的,我们总想着彻底研究明白别人的代码,别人设计的初衷其实是让你快速的应用,并不是彻底的理解。

         彻底的理解和应用是两个概念,年轻时候我钻入的牛角尖就在这里,浪费了大把的精力从入门到放弃,关于对软件的理念,从零重写一整套代码,远没有熟练应用一个成熟的系统更有价值,我们不是科学家,只是engineer,我们不生产构架,只是构架的搬运工,举个简单的例子,例如你对安卓系统的理解和你对安卓系统的应用,仔细想想,其实是两个不同的命题,只是我们把命题弄反了,又扯远了。。。见谅。

      这里的dispatch是不是对比进阶1中基本被精简很多了呢?这就是状态表带给我们的好处,接下来我们看一下bomb应用state如何构造真实的状态表,及实例代码,如下:

     1.main函数开始处,先会执行bomb的ctor函数,如下:

      每一处代码都需要静下心来,细细研究,慢慢的品味。

   2.执行完构造函数以后,那么就真正意义上构造好一个statetable实例对象了,接下来需要初始化操作,让我们的状态表开始从初始状态进入到运行阶段,做好接收事件的准备,调用顺序如下:

   3.基于产生的事件,调用dispatch函数处理相应的事件,如下:

      关于基于状态表的方式设计的优点,这里不想总结了,留给大家自行总结,我们来说说他的缺点:

      1.第一,他看上去不怎么像状态机,而是变成状态事件的一个联合体,说白了把整个程序的精细粒度打的更散,你需要更多地状态表结构函数,远多于状态机。

      2.其二,扩展状态机里面是有进入和退出动作的, 这里该怎么加,它看上去更像Mearly机,但有不符合Mearly机的定义,真正基于这个模型要扩展有点难。

      3.第三,当发生一个注册状态表之外的事件时,你需要明确的定义他,并忽略他,否则将会产生断言,导致系统运行出错,这要求你在设计的时候要提一万个心,防止以外的发生,异常处理不够健壮。

      其实还有很多细节,大家可以洗洗的品,比如状态表结构的声明是一个函数指针,赋值的却是一个二维数组,他们之间是如何发生转换的,细细品很有意思,到这里我还特别想讲讲函数指针,但是这里还是把话咽到肚子里,留给大家自己思考和体会,在我们的后面设计通用的QEP事件处理器,他会再次粉末登场~!

      早这里,进阶2就结束了,下一章讲讲面向对象的状态机,这个版本是一个C++版本,也是我们的GUI事件驱动型系统应用的根基,C语言实现多态还是有些复杂。到这里讲先告一段落了,进阶3再见~!

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 6
收藏 8
关注 117
成为作者 赚取收益
全部留言
0/200
成为第一个和作者交流的人吧