C++11 中可自定义状态机(支持子状态)的编译时错误检查

Compile time error checking of a customisable state machine (with substate support) in C++11

我正在使用C++ 11设计通用状态机。由于使用std::string来识别状态,我只能在运行时捕获错误。我想更改它以在编译时在代码中指定状态时捕获错误。

我一直在尝试基于可变参数模板和枚举的几个想法 class,但还没有找到解决方案,至少没有找到合理的解决方案 clean/readable.

下面是我当前状态机的一些片段,完整的可运行代码位于:http://cpp.sh/26njm

SState sStateChart("fsm", {
   SState("init", {
      SState("init_foo"),
      SState("init_bar"),
   }),
   SState("count", {
      SState("count_foo"),
      SState("count_bar"),
   }),
   SState("display", {
      SState("display_foo"),
      SState("display_bar"),
   }),
});

SState 的构造函数采用 std::string 作为状态 ID,然后 std::vector<SState> 通过子状态的初始化列表进行初始化,如下所示:

struct SState {
   SState(const std::string& str_id,
          const std::vector<SState>& vec_sub_states = {}) :
      Id(str_id),
      SubStates(vec_sub_states) {
   }
   //...
   std::string Id;
   std::vector<SState> SubStates;
   //...
};

然后可以使用以下语法进一步定义状态图:

方法

std::function<void()> fnInitFoo = [&] {
   std::cerr << "fnInitFoo" << std::endl;
   foo = 0;
};
sStateChart["init"]["init_foo"].SetEntryFunction(fnInitFoo);

过渡

std::function<bool()> fnTransInitFooInitBar = [&] {
   return (bar != 0);
};
sStateChart["init"].AddTransition("init_foo","init_bar", fnTransDisplayToInit);

出于兴趣,这就是我在基于可变参数模板的状态层次结构方面所做的工作。我暂时放弃了这个想法,因为它变得越来越复杂,但没有显示出任何解决我的问题的迹象——我想我可以以某种方式将枚举 class 作为参数之一传递给模板,但事实并非如此比我使用字符串的示例更好...

template<class... SUBSTATES> class CState {};

template<class STATE, class... SUBSTATES>
class CState<STATE, SUBSTATES...> : private CState<SUBSTATES...> {


};

enum class ETopLevelStates {INIT, COUNT, DISPLAY};
enum class EInitStates {INIT_FOO, INIT_BAR};

int main() {
   CState<
      CState<
         CState<>,
         CState<>
      >,
      CState<>,
      CState<>
   > cStateMachine;

   return 0;
}

这可以编译并且可以作为一些有趣的东西的起点:

/* States */
enum class EState : unsigned int {
   FSM,
   INIT, INIT_FOO, INIT_BAR,
   COUNT, COUNT_FOO, COUNT_BAR,
   DISPLAY, DISPLAY_FOO, DISPLAY_BAR
};

template<EState STATE, class... SUBSTATES> class CState {};

int main() {

   CState<EState::FSM,
      CState<EState::INIT,
         CState<EState::INIT_FOO>,
         CState<EState::INIT_BAR>
      >,
      CState<EState::COUNT,
         CState<EState::COUNT_FOO>,
         CState<EState::COUNT_BAR>
      >,
      CState<EState::DISPLAY,
         CState<EState::DISPLAY_FOO>,
         CState<EState::DISPLAY_BAR>
      >
   > cStateMachine;

   return 0;
}

我想我可以继续 std::tuple 并继承 class... SUBSTATES。如果我可以按照以下方式写一些东西,那就太好了:

cStateMachine.AddTransition(EState::COUNT_FOO, EState::COUNT_BAR);