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

带策略的boost::sml状态机能否复制?如何嵌入C++类?

Boost.SML带队列策略状态机的复制与类嵌入方案

一、无法复制的核心原因

当给Boost.SML状态机配置defer_queue或process_queue策略时,内部会持有不可复制的队列对象(比如带锁的容器、内部状态缓存),C++会自动禁用默认的拷贝构造与赋值运算符,这就是复制失败的根本原因。

二、复制技巧:仅限特定场景

如果必须实现复制,需要手动编写拷贝逻辑,但存在局限性:

  1. 首先要复制状态机的当前状态;
  2. 重新投递源状态机队列中的所有事件到新状态机;
  3. 需自定义可复制的队列适配器(因为SML内部队列通常是私有成员,无法直接访问)。

示例代码框架:

#include <boost/sml.hpp>
#include <queue>

namespace sml = boost::sml;

// 自定义可复制的队列适配器,暴露内部队列访问接口
template<typename T>
struct CopyableQueue {
  std::queue<T> q;

  void push(const T& e) { q.push(e); }
  bool empty() const { return q.empty(); }
  T pop() { auto e = q.front(); q.pop(); return e; }
  // 支持拷贝构造
  CopyableQueue(const CopyableQueue& other) : q(other.q) {}
};

// 状态机定义
struct MySm {
  auto operator()() const {
    using namespace sml;
    return make_transition_table(
      *"idle"_s + event<EvStart> = "running"_s,
      "running"_s + event<EvStop> = "idle"_s
    );
  }
};

// 带自定义可复制队列的状态机类型
using SmWithCopyableQueue = sml::sm<MySm, sml::process_queue<CopyableQueue>>;

// 自定义拷贝函数
SmWithCopyableQueue copy_sm(const SmWithCopyableQueue& src) {
  SmWithCopyableQueue dst;
  // 复制当前状态
  dst.set_current_state(src.current_state());
  // 复制队列事件(通过自定义队列的公开接口)
  auto& src_queue = src.template get<CopyableQueue>();
  auto& dst_queue = dst.template get<CopyableQueue>();
  dst_queue.q = src_queue.q;
  return dst;
}

三、嵌入C++类的可行方案

如果不想折腾复制逻辑,嵌入类有以下几种选择:

1. 堆指针(最省心的通用方案)

用std::unique_ptr或std::shared_ptr封装状态机,避免直接处理复制问题,灵活控制生命周期:

class MyClass {
private:
  std::unique_ptr<SmWithQueue> sm_;
public:
  MyClass() : sm_(std::make_unique<SmWithQueue>()) {}

  // 若类需要复制,改用shared_ptr并实现拷贝构造
  MyClass(const MyClass& other) : sm_(std::make_shared<SmWithQueue>(*other.sm_)) {}
};

2. 直接嵌入(类无需复制时使用)

如果你的类本身不需要拷贝能力(比如标记为不可复制,或仅支持移动),可以直接把状态机作为成员变量:

#include <boost/noncopyable.hpp>

class MyClass : boost::noncopyable {
private:
  SmWithQueue sm_;
public:
  MyClass() = default;
  // 状态机通常支持移动,可默认生成移动构造/赋值
  MyClass(MyClass&&) = default;
  MyClass& operator=(MyClass&&) = default;
};

3. std::optional延迟初始化

如果状态机需要按需初始化(比如依赖构造后的数据),用std::optional包裹:

class MyClass {
private:
  std::optional<SmWithQueue> sm_;
public:
  void init_sm() {
    sm_.emplace(); // 按需初始化状态机
  }

  // 若类需要复制,结合自定义复制逻辑处理optional
  MyClass(const MyClass& other) {
    if (other.sm_) {
      sm_.emplace(copy_sm(*other.sm_));
    }
  }
};

四、总结

  • 带队列策略的SML状态机默认不可复制,自定义复制逻辑复杂度高,仅在必要场景使用;
  • 嵌入类时优先选择堆指针或直接嵌入(类不可复制),这两种方案实现简单、稳定性高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:01:05