Страницы

Поиск по вопросам

понедельник, 3 декабря 2018 г.

Вызов шаблонной функции через Qtconcurrent::run

Есть шаблонная функция:
template auto exceptionWrapper (Object *obj, Function&& function, Args&& ...args)-> decltype((obj->*function)(std::forward(args)...)) { try { QScopedPointer tempObj(new Object(obj->getSomeData())); return (tempObj->*function)(std::forward(args)...); } catch(std::exception& e) { ... } }
Есть другая функция, которая ее вызывает через qtConcurrent::run
template void Object::callAsync(Object *obj,Function&& query,CallBack&& callBack, Args&& ...args) {
using ReturnValue = decltype(exceptionWrapper(obj, query, std::forward(args)...)); QFutureWatcher watcher; connect(&watcher, &QFutureWatcher ::finished, callBack); QFuture futureValue = QtConcurrent::run(exceptionWrapper, obj, query, std::forward(args)...);
watcher.setFuture(futureValue); }
При попытке вызова в коде:
Object object = new Object(); Object::callAsync(object, Object::objFcn, [](){qDebug()<<"qq";}, arg1, arg2);
Возникает ошибка:
ошибка: no matching function for call to 'run(, BOKZDatabase*&, AngleOrientData (BOKZDatabase::*&)(const QString&, const QString&, const QString&, const QString&, int), const QString&, const QString&, const QString&, const QString&, const int&)' QFuture futureValue = QtConcurrent::run(exceptionWrapper, db, query, std::forward(args)...); ^
Что я упустил?
UPD: В случае явного указания параметров шаблона ошибка о неопределенном типе функции остается:
template void Object::callAsync (Object *obj,Function&& query,CallBack&& callBack, Args&& ...args) {
using ReturnValue = decltype(exceptionWrapper(obj, query, args...)); QFutureWatcher watcher; connect(&watcher, &QFutureWatcher ::finished, callBack); QFuture futureValue = QtConcurrent::run(exceptionWrapper , obj, std::forward(query), std::forward(args)...); watcher.setFuture(futureValue); }
UPD 2. Минимальный пример:
class Object { public: int sum(int a, int b) { return a+b; } };
template auto fcn (Object *object, Function&& function, Args&& ...args)-> decltype ((object->*function)(std::forward(args)...)) { return (object->*function)(std::forward(args)...); }
template void callAsync(Object *object,Function&& function, Args&& ...args) {
QtConcurrent::run(fcn >, object, function, std::forward(args)...);
}
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Object* obj = new Object();
callAsync(obj, Object::sum, 5, 6); return a.exec(); }


Ответ

exceptionWrapper сам по себе не является функцией, но неким шаблоном функции, поэтому мы не можем никуда его передать — мы можем передавать лишь что-то конкретное, а не абстракцию, коей является шаблон.
Таким образом, нам нужно конкретизировать (инстанциировать) exceptionWrapper прежде чем передавать его в QtConcurrent::run. Здесь встаёт другая проблема: с какими аргументами инстанциировать? Т.к. exceptionWrapper использует пробрасывающую ссылку (forwarding reference), идёт расчёт на то, что аргументы будут выводиться при вызове функции, но у нас ситуация иная — мы явно задаём аргументы шаблона. Конечно, мы всё равно получаем нужный результат таким вызовом:
QtConcurrent::run(exceptionWrapper, obj, std::forward(query), std::forward(args)...);
Происходит это из-за того, что у нас в callAsync пробрасывающая ссылка, а в exceptionWrapper, в результате явной инстанциации, у нас rvalue-ссылка, и применяя правила C++ по свертке ссылок мы на выходе имеем то, что и ожидали: будет инстанциирована правильная, т.е. ожидаемая нами функция exceptionWrapper. К примеру, если мы рассмотрим минимальный пример, из Вашего дополненного вопроса, то при таком вызове:
int var = 5; callAsync(obj, &Object::sum, 7, var);
Будет сгенерирована функция exceptionWrapper с такой сигнатурой: int (Object *,int (Object::* )(int,int),int &&,int &) — как раз то, что и ожидалось. Но в этом кроется другая проблема. Мы работаем с конкурентной средой и пытаемся вызвать метод Qt, который потенциально выполнит наш метод в другом потоке. Но, чтобы выполнить метод в другом потоке, он вынужден скопировать (либо же переместить, если умеет) аргументы и сохранить их до того времени, когда можно будет вызвать нашу функцию.
Сохраняя наши аргументы, Qt отбрасывает их изначальный тип и впоследствии, при передаче их в нашу функцию, все наши аргументы станут lvalue. Но в сигнатуре нашей функции есть int&&, который является rvalue-ссылкой и lvalue не может быть передано туда! Поэтому, нам придётся отказаться от пробрасывающих ссылок в exceptionWrapper — они там не нужны и только мешают, там нужен обычный const &
В результате получим такое решение:
template auto fcn(Object *object, Function function, const Args& ...args) { return (object->*function)(args...); }
template void callAsync(Object *object, Function&& function, Args&& ...args) { QtConcurrent::run(fcn, object, function, std::forward(args)...); }
Конечно, можно подумать и над альтернативными решениями, но этот ответ уже и так довольно длинный получился.

Комментариев нет:

Отправить комментарий