You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何减少Boost::Ext SML有限状态机转换表的重复转换?

如何在Boost::Ext SML中复用状态转换逻辑

问题场景

你正在用Boost::Ext SML构建包含A、B、C、D状态的有限状态机,大量状态共享通用转换逻辑(比如A/B/C都能通过GoToD事件转到D),当前手写的转换表冗余且易出错,尝试过模板辅助函数但不知道如何合并到主表,且不想用预处理器宏。

当前冗余的转换表示例:

struct MyStateMachine {
  auto operator()() const noexcept {
    return sml::make_transition_table(
      *sml::state<A> + sml::event<GoToB> = sml::state<B>,
       sml::state<A> + sml::event<GoToC> = sml::state<C>,
       sml::state<A> + sml::event<GoToD> = sml::state<D>,
       sml::state<B> + sml::event<GoToA> = sml::state<A>,
       sml::state<B> + sml::event<GoToC> = sml::state<C>,
       sml::state<B> + sml::event<GoToD> = sml::state<D>,
       sml::state<C> + sml::event<GoToA> = sml::state<A>,
       sml::state<C> + sml::event<GoToB> = sml::state<B>,
       sml::state<C> + sml::event<GoToD> = sml::state<D>,
       sml::state<D> + sml::event<GoToA> = sml::state<A>,
       sml::state<D> + sml::event<GoToB> = sml::state<B>,
       sml::state<D> + sml::event<GoToC> = sml::state<C>,
    );
  }
};

解决方案

1. 直接使用SML的多状态转换语法(最优解)

SML支持在state<>中传入多个状态类型,一次性给所有这些状态定义相同的转换逻辑,这是最简洁的方式:

struct MyStateMachine {
  auto operator()() const noexcept {
    return sml::make_transition_table(
      // 标记初始状态
      *sml::state<A>,
      
      // 多状态共享转换:A/B/C触发GoToD都转到D
      sml::state<A, B, C> + sml::event<GoToD> = sml::state<D>,
      // B/C/D触发GoToA都转到A
      sml::state<B, C, D> + sml::event<GoToA> = sml::state<A>,
      // A/C/D触发GoToB都转到B
      sml::state<A, C, D> + sml::event<GoToB> = sml::state<B>,
      // A/B/D触发GoToC都转到C
      sml::state<A, B, D> + sml::event<GoToC> = sml::state<C>
    );
  }
};

如果需要给通用转换添加守卫条件或动作,直接追加即可:

// 带守卫和动作的多状态转换
sml::state<A, B, C> + sml::event<GoToD> [can_go_to_d] / log_transition = sml::state<D>,

2. 利用超状态(Super State)复用转换

如果你的状态有明确的逻辑分组(比如A/B/C属于同一类状态),可以定义一个基类作为超状态,让子状态继承它,然后给超状态定义转换,所有子状态会自动继承这些逻辑:

// 定义超状态
struct CommonStates {};
// 子状态继承超状态
struct A : CommonStates {};
struct B : CommonStates {};
struct C : CommonStates {};
struct D {};

// 状态机定义
struct MyStateMachine {
  auto operator()() const noexcept {
    return sml::make_transition_table(
      *sml::state<A>,
      // 所有CommonStates的子状态都能响应GoToD转到D
      sml::state<CommonStates> + sml::event<GoToD> = sml::state<D>,
      // 单独定义D的转换
      sml::state<D> + sml::event<GoToA> = sml::state<A>,
      sml::state<D> + sml::event<GoToB> = sml::state<B>,
      sml::state<D> + sml::event<GoToC> = sml::state<C>
    );
  }
};

3. 模板化生成转换条目(复杂场景复用)

如果需要更灵活的复用逻辑(比如不同状态组有不同的通用转换),可以用模板生成转换条目,再通过tuple展开合并到主表:

// 生成单个状态的通用转换
template <typename From>
auto single_state_transitions() {
  return std::make_tuple(
    sml::state<From> + sml::event<GoToA> = sml::state<A>,
    sml::state<From> + sml::event<GoToB> = sml::state<B>,
    sml::state<From> + sml::event<GoToC> = sml::state<C>,
    sml::state<From> + sml::event<GoToD> = sml::state<D>
  );
}

// 批量生成多个状态的通用转换
template <typename... FromStates>
auto multi_state_transitions() {
  return std::tuple_cat(single_state_transitions<FromStates>()...);
}

// 辅助函数:将tuple转换为make_transition_table可接受的参数包
template <typename Tuple, std::size_t... Is>
auto expand_tuple(Tuple&& t, std::index_sequence<Is...>) {
  return sml::make_transition_table(std::get<Is>(std::forward<Tuple>(t))...);
}

template <typename Tuple>
auto expand_tuple(Tuple&& t) {
  return expand_tuple(
    std::forward<Tuple>(t),
    std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{}
  );
}

// 主状态机定义
struct MyStateMachine {
  auto operator()() const noexcept {
    // 生成A/B/C/D的通用转换
    auto common_trans = expand_tuple(multi_state_transitions<A, B, C, D>());
    
    return sml::make_transition_table(
      // 特殊转换:覆盖A->B的初始状态逻辑
      *sml::state<A> + sml::event<GoToB> = sml::state<B>,
      // 合并通用转换
      common_trans
    );
  }
};

注意事项

  • 如果存在重复的转换定义(比如特殊转换和通用转换冲突),SML会以后定义的转换为准,所以建议将特殊转换放在通用转换之前,避免意外覆盖。
  • 带守卫条件的转换优先级高于无守卫的,即使定义顺序靠前。

内容的提问来源于stack exchange,提问作者jlev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 17:05:52