如何将带有捕获和参数的lambda传递给Qt的连接



我试图使用Kuba在post中所做的类似方法。基本上,我想用

以编程方式构造一个重写的StatefulObject
void Command::buildStatefulCommands(DeviceComm* dev, COMMAND_TYPE cmdtype, const QAbstractProxyModel* entries)
{
// according to cmdtype and number of entries, build the transition mapping dynamically
....
switch (cmdtype) {
case CMDUploadEntries: {
connectSignals();  // the same as in example
send  (&s_start, dev, QByteArray::fromHex(entries.at(index,0))); // 'dev' changed to use my own DeviceComm for communication, 
expect(&s_start, dev, QByteArray::fromHex(entries.at(index,1)), &s_ok_01, 3000, &s_failed);
expect(&s_start, dev, QByteArray::fromHex(entries.at(index,2)), &s_ok_02, 3000, &s_failed);
....
}

statfulobject的状态转换构建过程将在每次单击按钮时被调用(因为要发送的数据可能会更改)

void MainWindow::on_btnSend_clicked(){
....
// the cmdtype is used for different tasks
Command* cmd = Command::getCommand(cmdtype);
cmd->buildStatefulCommands(m_commDev, cmdtype, getDatabase());

if (cmd->isRunning())
cmd->stop();
cmd->start();
}

问题1。由于要多次设置状态机,如何断开旧的信号/槽对?我猜每次'send'调用都会导致由于捕获而创建一个新的lambda匿名函子。

void send(QAbstractState * src, QIODevice * dev, const QByteArray & data) {
// how to disconnect the previous connect?
conn = 
QObject::connect(src, &QState::entered, dev, [dev, data]{
dev->write(data);
});
}

第二个相关问题,而不是使用'readyRead'信号与原来的'expect'一样,但增加了过渡

void expect(QState * src, QIODevice * dev, const QByteArray & pattern, QAbstractState * dst,
int timeout = 0, QAbstractState * dstTimeout = nullptr)
{
addTransition(src, dst, dev, SIGNAL(readyRead()), [dev, pattern]{
return hasLine(dev, data);
});
if (timeout) delay(src, timeout, dstTimeout);
}

如何使用具有参数的信号' responsepost '(例如,'payload')与仍然具有capture的lambda连接列表(即要检查的模式)?我尝试使用auto转换为函数指针,但不支持捕获。

Q_SIGNALS:  // 'dev' signals
void responsePosted(const QByteArray& payload);
....
// I want to have different pattern to check with while building the state transition table
addTransition(src, dst, dev, SIGNAL(responsePosted()), [dev, pattern](const QByteArray& payload){
return dev->matching(payload, pattern);
});

GuardedSignalTransition中用于'eventTest'的模板函子似乎不接受任何参数。如何用论证来概括它?

template <typename F>
class GuardedSignalTransition : public QSignalTransition {
F m_guard;
QByteArray m_pattern;   // << I want to store the pattern here
protected:
bool eventTest(QEvent * ev) Q_DECL_OVERRIDE {
return QSignalTransition::eventTest(ev) && m_guard();  // <<<< I guess 'ev' has the passed-in argument ?
}
template <typename F> static GuardedSignalTransition<F> *
addTransition(QState * src, QAbstractState *target,
const QObject * sender, const char * signal, F && guard) {  // What function type will F be?
auto t = new GuardedSignalTransition<typename std::decay<F>::type>
(sender, signal, std::forward<F>(guard));  // what should I put here ?
t->setTargetState(target);
src->addTransition(t);
return t;
}

std::forward, std::move, &&注释和std::衰变很难理解。

我弄清楚了如何通过在模板中使用额外的typename来传递lambda,但上面的两个问题仍然没有解决(即删除旧的过渡和使用lambda与捕获和参数作为&&参数)。

void Command::buildStatefulCommands(DeviceComm* dev, COMMAND_TYPE cmdtype, const QAbstractProxyModel* table)
{
...
// the pattern to check is passed at construction call
setTransition(&s_start, dev, QByteArray::fromHex("88"), &s_ok, 3000, &s_failed);
...

要检查的'模式'和lambda函子传递给setTransition

void Command::setTransition(QState* src, DeviceComm* dev, const QByteArray& ptn, QAbstractState* dst, int timeout, QAbstractState* dstTimeout)
{
auto* c = static_cast<bool(*)(const QByteArray&, const QByteArray&)>([](const QByteArray& payload, const QByteArray& pattern) {

return true;
});
// the ptn is saved in the 'Transition', while payload will be passed from SIGNAL
addTransition(src, dst, dev, ptn, SIGNAL(responsePosted(const QByteArray&)), c );
if (timeout) delay(src, timeout, dstTimeout);
}

mainaddTransition部分在模板中使用三种类型实现。我想学习如何简化它?目前,函数的返回类型和实参都是显式声明的。

template <typename F, typename G, typename H> static Command::MyTransition<F, G, H>*
Command::addTransition(QState* src, QAbstractState* target,
const QObject* sender, const QByteArray& pattern, const char* signal, F(*guard)(G,H) ) {
auto kk = new MyTransition<F,G,H>(sender, signal, pattern, guard);
kk->setTargetState(target);
src->addTransition(kk);
return kk;
}

自定义的QSignalTransition定义为

template <class F, class G, class H> bool Command::MyTransition<F, G, H>::eventTest(QEvent* ev) {
if (!QSignalTransition::eventTest(ev))
return false;
QStateMachine::SignalEvent* se = static_cast<QStateMachine::SignalEvent*>(ev);
QList<QVariant> args = se->arguments();
if (args.count() > 0) {
// how to cast into G and H types  for payload and m_pattern?
QByteArray payload = se->arguments().at(0).toByteArray();
return m_guard(payload, m_pattern);
}
return QSignalTransition::eventTest(ev);
}
template <typename F, typename G, typename H> Command::MyTransition<F, G, H>::MyTransition(const QObject* sender, const char* signal, const H& pattern, F(*guard)(const G&, const H&))
:QSignalTransition(sender, signal), m_guard(guard), m_pattern(pattern) {}

声明部分是

template <typename F, typename G, typename H>
class MyTransition : public QSignalTransition {
F(*m_guard)(const G&, const H&);  // store the lambda functor
H m_pattern;  // stor the pattern for successful reply
protected:
bool eventTest(QEvent* ev) Q_DECL_OVERRIDE;
public:
MyTransition(const QObject* sender, const char* signal, const H& pattern, F(*guard)(const G&, const H&));
};

我觉得走这么长的路的全部目的是确保一个灵活的方式来定义状态转换可以完成。但是正如你在调用m_guard时看到的,我已经明确了参数的类型。那么使用模板的意义是什么呢?

还有一个奇怪的问题。上面的代码用于存储m_pattern,我必须使用QByteArray m_pattern而不是"H m_pattern"即使m_pattern在MyTransition构造函数中被正确初始化,在eventTest()中,它仍然是空的!

class MyTransition : public QSignalTransition {
....
H m_pattern;  // It will throw error at runtime in eventTest()
}

被重写的eventTest()将抛出错误:

template <class F, class G, class H> bool MyTransition<F, G, H>::eventTest(QEvent* ev) {
...
qDebug() << "event test pattern: " << this->m_pattern; // will throw error
return m_guard(qv.toByteArray(), this->m_pattern);
...
}
....
// inside lambda function, 
auto* c = static_cast<bool(*)(const QByteArray&, const QByteArray&)>([](const QByteArray& payload, const QByteArray& pattern) {
//      qDebug() << pattern.toHex(' ');  // will also throw error
return payload.contains(pattern);
});

如果我明确声明'QByteArray m_pattern;'是没有问题的

最新更新