Страницы

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

пятница, 27 декабря 2019 г.

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

#cpp #qt #concurrency #шаблоны_с++


Есть шаблонная функция:

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();
}

    


Ответы

Ответ 1



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)...); } Конечно, можно подумать и над альтернативными решениями, но этот ответ уже и так довольно длинный получился.

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

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