Как стать автором
Обновить

Комментарии 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 нам пока не дали.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации