Комментарии 2
К сожалению, нормального способа передавать аргументы я не знаю
Это делается через лямбду
QMetaObject::invokeMethod(this, [=]() {
handleNextMessage(task);
}, Qt::QueuedConnection);
Если нужен возврат то так
int someResult;
QMetaObject::invokeMethod(this, [=]() {
return handleNextMessage(task);
}, Qt::QueuedConnection, &someResult);
Лямбду можно использовать. Только надо не забыть несколько пунктов:
task — move-only класс. Поэтому просто захватит всё по значению — не вариант.
Не забываем про mutable, т.к.
handleNextMessage
изменяет состояние task (вызывает task.promise().finish()).
Штука с возвращаемым значением — плохой вариант. Причина проста — зачастую непросто определить, когда функция завершит выполнение и вернёт значение. Избежать можно двумя путями:
Qt::DirectConnection — вызывать callable сразу из потока, который дёрнул QMetaObject::invokeMethod. Нам не подходит, т.к. Active object предполагает разделение вызова операции, и её выполнения.
Qt::BlockingQueuedConnection — callable исполняется в том потоке, который владеет context (указатель this в вызове QMetaObject::invokeMethod), а поток, вызвавший метод будет заблокирован до конца выполнения callable. Крайне отвратительный подход.
Резюмируя, можно создать вот такую конструкцию, которая избавит нас от строковых литералов, избавит нас от макросов, и при этом будет достаточно красивой:
Метод print
QFuture<void> MetaInvokeBasedAsyncQDebugPrinter::print(const QString &message) {
auto task = PrinterMessage{ message };
auto future = task.promise().future();
QMetaObject::invokeMethod(this,
[task_object = std::move(task), this]() mutable {
return this->handleNextMessage(task_object);
}, Qt::QueuedConnection);
return future;
}
Ну и теперь у нас прекратит собираться. Причина тому — попытка moc обернуть handleNextMessage в метасистему. Поэтому нужен новый подход в передаче данных. Есть два способа исправить это:
Пусть handleNextMessage принимает task по ссылке из лямбды.
Убрать пометку "slots" с handleNextMessage. Макроса Q_INVOKABLE тоже быть не должно. Но т.к. это приватный метод, и никто, нигде и никогда его вызывать не должен, то это разумно. Ну и можно принимать task хоть по значению (ибо всё равно move-only), хоть по r-value ссылке.
Ну и если возникнет идея обернуть этот callable в какой-нибудь std::bind для красоты — не вариант, потому что std::move_only_function нам пока не дали.
Размышление об Active Object в контексте Qt6. Часть 2.6