c++ 状态机图 - 提升状态图与元状态机





状态图和活动图 boost状态机 (6)


在编写自己的PPP实现时,我使用Statechart有三个原因:1)状态图更简单,文档更清晰; 2)我真的不喜欢UML :)

Boost文档称MSM速度至少快20倍,但对于大型FSM编译速度相当慢。

显然,boost包含两个独立的状态机库: Statechart元状态机 (MSM)。 标语给出了非常相似的描述:

  • Boost.Statechart - 任意复杂的有限状态机都可以用易读和可维护的C ++代码实现。
  • Meta State Machine - 用于表现型UML2有限状态机的高性能库。

你知道两者之间有什么关键的区别和选择的考虑吗?




正如Christophe已经提到的那样,两个库之间的主要区别之一是运行时性能。 尽管MSM可能提供了您可以在此获得的最佳性能,但状态图有意识地将内存和处理器周期换成更好的可扩展性。

使用Boost.Statechart,您可以以不能使用MSM的方式将状态机的布局 (即状态,转换)分布到多个转换单元(cpp文件)。 这使您可以使大型FSM的实现更易于维护,并且比MSM更快地编译。

当你问自己你的应用程序每秒必须处理多少事件时,Statechart与MSM相比的性能开销对于你的应用程序是否真的非常重要,通常很容易回答。

假设使用Boost.Statechart实现了适度复杂的FSM,以下是一些球场数字:

  • 大多数当前的PC硬件将很容易处理每秒> 100000个事件
  • 即使资源非常有限的硬件也能够每秒处理几百个事件。

关于CPU负载,如果要处理的事件数量远低于这些数字,则与MSM相比,Boost.Statechart开销几乎肯定不会引人注意。 如果这个数字要高得多,那么用MSM肯定会更好。

有关性能/可伸缩性权衡的更深入的信息可以在这里找到: http://www.boost.org/doc/libs/1_45_0/libs/statechart/doc/performance.html : http://www.boost.org/doc/libs/1_45_0/libs/statechart/doc/performance.html




由于似乎有很大的兴趣,请允许我提出我的(明显有偏见的)意见,因此应该采取一丝一毫的态度:

  • MSM要快得多
  • MSM不需要RTTI或任何虚拟的
  • MSM具有更完整的UML2支持(例如内部转换,符合UML的正交区域)
  • MSM提供了一种描述性语言(实际上有几种)。 例如,使用eUML前端,转换可以描述为Source + Event [Guard] / Action == Target
  • MSM会让你的编译器忍受更大的状态机,所以你需要一个非常新的编译器(g ++> = 4.x,VC> = 9)

您可以通过查找在MSM审查期间发布的评论来让自己变得更好。 这个主题在开发者列表上有很多讨论。




回答蒂姆迟到的讨论(这也解决了列夫早期的评论之一)。

作为那些在状态图中争论退出与析构函数分离的人之一(基于真实用例的论点,关于与真实世界的交互即I / O)的方式,当它被提交给Boost时,我同意在退出时出现问题析构函数中的逻辑。 毫不意外,David Abrahams也对异常安全做出了有说服力的论点。 由于这些原因,Statechart并不要求您将逻辑放入析构函数中,但它允许您使用常用的建议。

逻辑只应该作为一个状态转换的一部分运行(而不是作为一个整体破坏状态图对象)可以(并且如果还有资源清除的话)可以分离成一个单独的exit()动作。

对于没有活动状态(资源)的“精简”状态,只需执行进入/退出动作,就可以在ctor和d'tor中执行这些动作,并确保构造函数和析构函数不会抛出。 他们没有理由 - 没有国家执行RAII - 这些地方的错误处理没有引发适当的事件。 你可能仍然需要考虑是否你想要退出动作改变外部状态在状态机破坏上运行,尽管如此,如果你不希望它们在这种情况下发生,就把它们放在退出动作中......

状态图将激活模型化为一个对象的实例,所以如果你的构造函数有实际的工作/激活/实例化,并且它能够失败以至于无法进入状态,Statechart通过给予你将异常映射到事件。 这是通过一种处理状态层次的方式来处理的,它寻找处理异常事件的外部状态,类似于基于调用堆栈调用模型的堆栈将被解除的方式。

这是所有有据可查的 - 我建议你阅读文档并尝试。 我建议你使用析构函数清理“软件资源”并退出动作来执行“真实世界的退出动作”。

值得注意的是,异常传播在所有事件驱动的环境中都有点问题,而不仅仅是状态图。 最好在你的状态图设计中推理和包含错误/错误,并且当且仅当你不能以另一种方式处理它们时才采用异常映射。 至少这对我有用 - ymmmv ....




前段时间我开始使用Statechart,并转而使用MSM,因为它更容易与单个线程的asio结合使用。 我没有设法使用asio来对状态图及其多线程功能进行网格划分 - 这可能是我对Statechart的一种新手不理解。 我发现MSM更容易使用,因为它没有解决多线程问题。




这个问题最初标记为C和C ++,而不是“语言不可知”。 作为一个与语言无关的问题,它应该被视为过于宽泛 ,但我希望OP将其恢复为原始版本1,因此我不会投票结束。 作为一个C / C ++问题,虽然显然是负责任的,但它被视为过于宽泛,然后当范围被极大地扩展到无法回答时,通过将该标记更改为与语言无关,它被重新打开为现在可以回答。

我正在回答C ++的原始问题,这里和那里散布着一些C知识。

我认为目前的“语言不可知”问题是无法回答的,尽管有一种尝试与语言无关的答案。

1.指针的大小取决于什么?

普通数据指针所需的最小尺寸取决于指向类型或不同功能的对象的可能存储位置的最大数量; 为了区分n个可能的位置,需要ceil(lg2( n ))位。

因此,最小指针大小取决于最大可能的内存大小。

因此,最小指针大小也取决于指向类型的对齐。 在一些现在过时的机器上,甚至可能在一些现存的机器(大型机?)上,硬件级别可寻址的存储器单元是例如2或4个字节的字。 然后可以使用小指针来寻址字对齐的数据,而char*因此也需要更大的void*

因此,§3.9.2/ 4中的C ++ 11标准要求“类型为cv void*的对象应具有与cv char*相同的表示和对齐要求。”

C ++中的成员函数指针更类似于偏移而不是指针,并且通常比正确的指针更大。

2.架构的哪些特性会影响指针的大小? (详细)

从C ++可以看出主要是可记录的内存范围。

但值得回顾的是,在MS-DOS编程中,一个区别在指针和指针之间。 指针是一个隐含的64K内存段的偏移量,而一个两倍大小的指针组合了一个段选择器和偏移量。

在(仍然)现代32位PC编程C和C ++指针通常类似于指针,不支持指针,这将是6个字节。 要使用这样的指针,必须使用汇编语言等其他语言。

3.编译器是否会影响指针的大小? 怎么样?

所使用的编译器和编译选项原则上可以影响指针大小,因为标准没有规定大小。 例如,它原则上可以添加信息以帮助调试。 或者如上所述,使用语言扩展可以有近距离远距离指针。

由于成员指针不是直接地址(在引擎盖下),因此编译器如何表示它们以及它们有多大。

这也取决于使用的选项。

4.什么是不同类型的指针以及它们彼此之间有何不同? (例如:函数指针和指向基本数据类型的指针之间是否存在差异?,指针与远指针之间等)

在C ++ 03中,函数指针无法转换为数据指针,反之亦然。 这种限制支持具有哈佛架构的机器,并且它支持可能的不同大小的函数指针和数据指针(例如,前者为 ,后者为 )。 在C ++ 11中, 有条件地支持这种转换,在§5.2.10/ 8中,由缺陷报告195产生

值得注意的是,Posix标准需要支持转换(函数指针)→( void* )和返回,例如对于dlsym函数。

上述有效意味着对象指针和函数指针是不同的。 例如,后者不支持地址算术。 此外,成员指针与正确的指针不同,更类似于偏移。

Modern C ++不支持near指针和远指针。

5.语言是否对指针有影响。 C vs C ++

C不支持成员指针,只要它们被认为是这个问题的指针。

除此之外,C ++的主要目标是能够直接使用C库,并将C标准“合并”(C ++11§17.5.1.5/ 1)到C ++标准中,这需要兼容的指针表示。

C支持限制指针,限定符restrict ,C ++不支持。 但是,这只会影响编译器对指针可能值的了解。 这意味着C编译器可能能够发出更好的优化代码。





c++ boost state-machines boost-statechart boost-msm