如何向Qt的connect传递带捕获和参数的lambda及处理状态机过渡问题
问题1:多次初始化状态机的旧信号槽清理方案
有两种成熟方案可选,按需选择即可:
- 连接对象批量管理
给Command类添加成员变量QList<QMetaObject::Connection> m_conns;,每次调用connect返回的连接对象都存入该列表。每次进入buildStatefulCommands函数时,先遍历列表执行QObject::disconnect,再清空列表即可彻底断开旧连接。
代码示例:
// Command类成员变量 QList<QMetaObject::Connection> m_conns; void Command::buildStatefulCommands(DeviceComm* dev, COMMAND_TYPE cmdtype, const QAbstractProxyModel* entries) { // 先清理所有旧连接 for (auto& conn : m_conns) { QObject::disconnect(conn); } m_conns.clear(); // 原有逻辑不变 ... } void send(QAbstractState * src, QIODevice * dev, const QByteArray & data) { auto conn = QObject::connect(src, &QState::entered, dev, [dev, data]{ dev->write(data); }); m_conns.append(conn); }
- 状态机全量重建
如果每次触发构建都是完全重新生成状态流转逻辑,更简单的方式是直接销毁旧的状态机实例,Qt父子对象销毁时会自动断开所有关联的信号槽,不需要手动管理连接:
// Command类成员变量 QStateMachine* m_stateMachine = nullptr; void Command::buildStatefulCommands(DeviceComm* dev, COMMAND_TYPE cmdtype, const QAbstractProxyModel* entries) { // 销毁旧状态机,自动断开所有关联信号槽 if (m_stateMachine) { m_stateMachine->stop(); delete m_stateMachine; } m_stateMachine = new QStateMachine(this); // 后续构建新状态、流转逻辑即可 ... }
问题2:带payload参数的信号绑定实现
eventTest传入的QEvent实际是QStateMachine::SignalEvent类型,里面存储了信号触发时携带的所有参数,我们可以直接取出来传给guard函数,不需要将带捕获的lambda转为函数指针,修改GuardedSignalTransition即可:
template <typename F> class GuardedSignalTransition : public QSignalTransition { F m_guard; public: // 新增构造函数,接收并存储guard函数 GuardedSignalTransition(const QObject* sender, const char* signal, F&& guard) : QSignalTransition(sender, signal), m_guard(std::forward<F>(guard)) {} GuardedSignalTransition(const QObject* sender, const char* signal, const F& guard) : QSignalTransition(sender, signal), m_guard(guard) {} protected: bool eventTest(QEvent * ev) override { // 先校验信号匹配 if (!QSignalTransition::eventTest(ev)) return false; // 取出信号携带的payload参数 auto* signalEvent = static_cast<QStateMachine::SignalEvent*>(ev); QByteArray payload = signalEvent->arguments().at(0).value<QByteArray>(); // 将payload传给guard函数校验 return m_guard(payload); } }; template <typename F> GuardedSignalTransition<typename std::decay<F>::type>* addTransition(QState * src, QAbstractState *target, const QObject * sender, const char * signal, F && guard) { using DecayedF = typename std::decay<F>::type; auto t = new GuardedSignalTransition<DecayedF>(sender, signal, std::forward<F>(guard)); t->setTargetState(target); src->addTransition(t); return t; }
调用时需要注意SIGNAL宏要补全参数类型,否则信号匹配失败:
addTransition(src, dst, dev, SIGNAL(responsePosted(QByteArray)), [dev, pattern](const QByteArray& payload){ return dev->matching(payload, pattern); });
相关语法说明
&&此处为万能引用,可以同时接收左值、右值类型的参数(比如临时生成的lambda对象就是右值)std::forward<F>(guard)为完美转发,会保留参数原本的左/右值属性,避免不必要的对象拷贝std::decay<F>::type作用是退化类型:去掉参数的引用、const/volatile修饰,避免模板实例化时因为引用属性生成错误的类型
内容的提问来源于stack exchange,提问作者wanyancan
相关产品推荐
相关产品推荐

