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);
我正在使用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);