Add support of failure handler callbacks to QFuture
Added QFuture::onFailed() method, which allows attaching handlers for exceptions that may occur in QFuture continuation chains. Task-number: QTBUG-81588 Change-Id: Iadeee99e3a7573207f6ca9f650ff9f7b6faa2cf7 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
495f958b9a
commit
0f78e85421
@ -162,6 +162,12 @@ public:
|
||||
template<class Function>
|
||||
QFuture<ResultType<Function>> then(QThreadPool *pool, Function &&function);
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
template<class Function,
|
||||
typename = std::enable_if_t<!QtPrivate::ArgResolver<Function>::HasExtraArgs>>
|
||||
QFuture<T> onFailed(Function &&handler);
|
||||
#endif
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
public:
|
||||
@ -275,6 +281,11 @@ private:
|
||||
template<class Function, class ResultType, class ParentResultType>
|
||||
friend class QtPrivate::Continuation;
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
template<class Function, class ResultType>
|
||||
friend class QtPrivate::FailureHandler;
|
||||
#endif
|
||||
|
||||
using QFuturePrivate =
|
||||
std::conditional_t<std::is_same_v<T, void>, QFutureInterfaceBase, QFutureInterface<T>>;
|
||||
|
||||
@ -335,6 +346,19 @@ QFuture<typename QFuture<T>::template ResultType<Function>> QFuture<T>::then(QTh
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
|
||||
template<class T>
|
||||
template<class Function, typename>
|
||||
QFuture<T> QFuture<T>::onFailed(Function &&handler)
|
||||
{
|
||||
QFutureInterface<T> promise(QFutureInterfaceBase::State::Pending);
|
||||
QtPrivate::FailureHandler<Function, T>::create(std::forward<Function>(handler), this, promise);
|
||||
return promise.future();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
inline QFuture<void> QFutureInterface<void>::future()
|
||||
{
|
||||
return QFuture<void>(this);
|
||||
|
@ -846,6 +846,8 @@
|
||||
|
||||
\note If the parent future gets canceled, its continuations will
|
||||
also be canceled.
|
||||
|
||||
\sa onFailed()
|
||||
*/
|
||||
|
||||
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QtFuture::Launch policy, Function &&function)
|
||||
@ -877,6 +879,8 @@
|
||||
future.then(QtFuture::Launch::Async, [](int res){ ... })
|
||||
.then(QtFuture::Launch::Inherit, [](int res2){ ... });
|
||||
\endcode
|
||||
|
||||
\sa onFailed()
|
||||
*/
|
||||
|
||||
/*! \fn template<class T> template<class Function> QFuture<typename QFuture<T>::ResultType<Function>> QFuture<T>::then(QThreadPool *pool, Function &&function)
|
||||
@ -888,4 +892,83 @@
|
||||
computations if desired. When the asynchronous computation represented by this
|
||||
future finishes, \a function will be invoked in a separate thread taken from the
|
||||
QThreadPool \a pool.
|
||||
|
||||
\sa onFailed()
|
||||
*/
|
||||
|
||||
/*! \fn template<class T> template<class Function> QFuture<T> QFuture<T>::onFailed(Function &&handler)
|
||||
|
||||
\since 6.0
|
||||
|
||||
Attaches a failure handler to this future, to handle any exceptions that may
|
||||
have been generated. Returns a QFuture of the parent type. The handler will
|
||||
be invoked only in case of an exception, in the same thread as the parent
|
||||
future has been running. \a handler is a callable which takes either no argument
|
||||
or one argument, to filter by specific error types similar to
|
||||
\l {https://en.cppreference.com/w/cpp/language/try_catch} {catch} statement.
|
||||
|
||||
For example:
|
||||
|
||||
\code
|
||||
QFuture<int> future = ...;
|
||||
auto resultFuture = future.then([](int res) {
|
||||
...
|
||||
throw Error();
|
||||
...
|
||||
}).onFailed([](const Error &e) {
|
||||
// Handle exceptions of type Error
|
||||
...
|
||||
return -1;
|
||||
}).onFailed([] {
|
||||
// Handle all other types of errors
|
||||
...
|
||||
return -1;
|
||||
});
|
||||
|
||||
auto result = resultFuture.result(); // result is -1
|
||||
|
||||
\endcode
|
||||
|
||||
If there are multiple handlers attached, the first handler that matches with the
|
||||
thrown exception type will be invoked. For example:
|
||||
|
||||
\code
|
||||
QFuture<int> future = ...;
|
||||
future.then([](int res) {
|
||||
...
|
||||
throw std::runtime_error("message");
|
||||
...
|
||||
}).onFailed([](const std::exception &e) {
|
||||
// This handler will be invoked
|
||||
}).onFailed([](const std::runtime_error &e) {
|
||||
// This handler won't be invoked, because of the handler above.
|
||||
});
|
||||
\endcode
|
||||
|
||||
If none of the handlers matches with the thrown exception type, the exception
|
||||
will be propagated to the resulted future:
|
||||
|
||||
\code
|
||||
QFuture<int> future = ...;
|
||||
auto resultFuture = future.then([](int res) {
|
||||
...
|
||||
throw Error("message");
|
||||
...
|
||||
}).onFailed([](const std::exception &e) {
|
||||
// Won't be invoked
|
||||
}).onFailed([](const QException &e) {
|
||||
// Won't be invoked
|
||||
});
|
||||
|
||||
try {
|
||||
auto result = resultFuture.result();
|
||||
} catch(...) {
|
||||
// Handle the exception
|
||||
}
|
||||
\endcode
|
||||
|
||||
\note You can always attach a handler taking no argument, to handle all exception
|
||||
types and avoid writing the try-catch block.
|
||||
|
||||
\sa then()
|
||||
*/
|
||||
|
@ -109,6 +109,64 @@ struct ResultTypeHelper<
|
||||
using ResultType = std::invoke_result_t<std::decay_t<F>>;
|
||||
};
|
||||
|
||||
// Helpers to resolve argument types of callables.
|
||||
template<typename...>
|
||||
struct ArgsType;
|
||||
|
||||
template<typename Arg, typename... Args>
|
||||
struct ArgsType<Arg, Args...>
|
||||
{
|
||||
using First = Arg;
|
||||
static const bool HasExtraArgs = (sizeof...(Args) > 0);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ArgsType<>
|
||||
{
|
||||
using First = void;
|
||||
static const bool HasExtraArgs = false;
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
struct ArgResolver : ArgResolver<decltype(&std::decay_t<F>::operator())>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct ArgResolver<R(Args...)> : public ArgsType<Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct ArgResolver<R (*)(Args...)> : public ArgsType<Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct ArgResolver<R (&)(Args...)> : public ArgsType<Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename Class, typename R, typename... Args>
|
||||
struct ArgResolver<R (Class::*)(Args...)> : public ArgsType<Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename Class, typename R, typename... Args>
|
||||
struct ArgResolver<R (Class::*)(Args...) noexcept> : public ArgsType<Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename Class, typename R, typename... Args>
|
||||
struct ArgResolver<R (Class::*)(Args...) const> : public ArgsType<Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename Class, typename R, typename... Args>
|
||||
struct ArgResolver<R (Class::*)(Args...) const noexcept> : public ArgsType<Args...>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
class Continuation
|
||||
{
|
||||
@ -186,6 +244,37 @@ private:
|
||||
QThreadPool *threadPool;
|
||||
};
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
|
||||
template<class Function, class ResultType>
|
||||
class FailureHandler
|
||||
{
|
||||
public:
|
||||
static void create(Function &&function, QFuture<ResultType> *future,
|
||||
const QFutureInterface<ResultType> &promise);
|
||||
|
||||
FailureHandler(Function &&func, const QFuture<ResultType> &f,
|
||||
const QFutureInterface<ResultType> &p)
|
||||
: promise(p), parentFuture(f), handler(std::forward<Function>(func))
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void run();
|
||||
|
||||
private:
|
||||
template<class ArgType>
|
||||
void handleException();
|
||||
void handleAllExceptions();
|
||||
|
||||
private:
|
||||
QFutureInterface<ResultType> promise;
|
||||
const QFuture<ResultType> parentFuture;
|
||||
Function handler;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
void Continuation<Function, ResultType, ParentResultType>::runFunction()
|
||||
{
|
||||
@ -297,7 +386,7 @@ void Continuation<Function, ResultType, ParentResultType>::create(Function &&fun
|
||||
|
||||
p.setLaunchAsync(launchAsync);
|
||||
|
||||
auto continuation = [continuationJob, policy, launchAsync]() mutable {
|
||||
auto continuation = [continuationJob, launchAsync]() mutable {
|
||||
bool isLaunched = continuationJob->execute();
|
||||
// If continuation is successfully launched, AsyncContinuation will be deleted
|
||||
// by the QThreadPool which has started it. Synchronous continuation will be
|
||||
@ -337,6 +426,90 @@ void Continuation<Function, ResultType, ParentResultType>::create(Function &&fun
|
||||
f->d.setContinuation(continuation);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
|
||||
template<class Function, class ResultType>
|
||||
void FailureHandler<Function, ResultType>::create(Function &&function, QFuture<ResultType> *future,
|
||||
const QFutureInterface<ResultType> &promise)
|
||||
{
|
||||
Q_ASSERT(future);
|
||||
|
||||
FailureHandler<Function, ResultType> *failureHandler = new FailureHandler<Function, ResultType>(
|
||||
std::forward<Function>(function), *future, promise);
|
||||
|
||||
auto failureContinuation = [failureHandler]() mutable {
|
||||
failureHandler->run();
|
||||
delete failureHandler;
|
||||
};
|
||||
|
||||
future->d.setContinuation(std::move(failureContinuation));
|
||||
}
|
||||
|
||||
template<class Function, class ResultType>
|
||||
void FailureHandler<Function, ResultType>::run()
|
||||
{
|
||||
Q_ASSERT(parentFuture.isFinished());
|
||||
|
||||
promise.reportStarted();
|
||||
|
||||
if (parentFuture.d.exceptionStore().hasException()) {
|
||||
using ArgType = typename QtPrivate::ArgResolver<Function>::First;
|
||||
if constexpr (std::is_void_v<ArgType>) {
|
||||
handleAllExceptions();
|
||||
} else {
|
||||
handleException<ArgType>();
|
||||
}
|
||||
} else {
|
||||
if constexpr (!std::is_void_v<ResultType>)
|
||||
promise.reportResult(parentFuture.result());
|
||||
}
|
||||
promise.reportFinished();
|
||||
}
|
||||
|
||||
template<class Function, class ResultType>
|
||||
template<class ArgType>
|
||||
void FailureHandler<Function, ResultType>::handleException()
|
||||
{
|
||||
try {
|
||||
parentFuture.d.exceptionStore().throwPossibleException();
|
||||
} catch (const ArgType &e) {
|
||||
try {
|
||||
// Handle exceptions matching with the handler's argument type
|
||||
if constexpr (std::is_void_v<ResultType>) {
|
||||
handler(e);
|
||||
} else {
|
||||
promise.reportResult(handler(e));
|
||||
}
|
||||
} catch (...) {
|
||||
promise.reportException(std::current_exception());
|
||||
}
|
||||
} catch (...) {
|
||||
// Exception doesn't match with handler's argument type, propagate
|
||||
// the exception to be handled later.
|
||||
promise.reportException(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
template<class Function, class ResultType>
|
||||
void FailureHandler<Function, ResultType>::handleAllExceptions()
|
||||
{
|
||||
try {
|
||||
parentFuture.d.exceptionStore().throwPossibleException();
|
||||
} catch (...) {
|
||||
try {
|
||||
if constexpr (std::is_void_v<ResultType>) {
|
||||
handler();
|
||||
} else {
|
||||
promise.reportResult(handler());
|
||||
}
|
||||
} catch (...) {
|
||||
promise.reportException(std::current_exception());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // QT_NO_EXCEPTIONS
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -63,6 +63,11 @@ class QFutureWatcherBasePrivate;
|
||||
namespace QtPrivate {
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
class Continuation;
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
template<class Function, class ResultType>
|
||||
class FailureHandler;
|
||||
#endif
|
||||
}
|
||||
|
||||
class Q_CORE_EXPORT QFutureInterfaceBase
|
||||
@ -158,6 +163,11 @@ private:
|
||||
template<typename Function, typename ResultType, typename ParentResultType>
|
||||
friend class QtPrivate::Continuation;
|
||||
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
template<class Function, class ResultType>
|
||||
friend class QtPrivate::FailureHandler;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void setContinuation(std::function<void()> func);
|
||||
void runContinuation() const;
|
||||
|
@ -104,6 +104,8 @@ private slots:
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
void thenOnExceptionFuture();
|
||||
void thenThrows();
|
||||
void onFailed();
|
||||
void onFailedTestCallables();
|
||||
#endif
|
||||
void takeResults();
|
||||
void takeResult();
|
||||
@ -2149,6 +2151,337 @@ void tst_QFuture::thenThrows()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFuture::onFailed()
|
||||
{
|
||||
// Ready exception void future
|
||||
{
|
||||
int checkpoint = 0;
|
||||
auto future = createExceptionFuture().then([&] { checkpoint = 1; }).onFailed([&] {
|
||||
checkpoint = 2;
|
||||
});
|
||||
|
||||
try {
|
||||
future.waitForFinished();
|
||||
} catch (...) {
|
||||
checkpoint = 3;
|
||||
}
|
||||
QCOMPARE(checkpoint, 2);
|
||||
}
|
||||
|
||||
// std::exception handler
|
||||
{
|
||||
QFutureInterface<int> promise;
|
||||
|
||||
int checkpoint = 0;
|
||||
auto then = promise.future()
|
||||
.then([&](int res) {
|
||||
throw std::exception();
|
||||
return res;
|
||||
})
|
||||
.then([&](int res) { return res + 1; })
|
||||
.onFailed([&](const QException &) {
|
||||
checkpoint = 1;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&](const std::exception &) {
|
||||
checkpoint = 2;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&] {
|
||||
checkpoint = 3;
|
||||
return -1;
|
||||
});
|
||||
|
||||
promise.reportStarted();
|
||||
promise.reportResult(1);
|
||||
promise.reportFinished();
|
||||
|
||||
int res = 0;
|
||||
try {
|
||||
res = then.result();
|
||||
} catch (...) {
|
||||
checkpoint = 4;
|
||||
}
|
||||
QCOMPARE(checkpoint, 2);
|
||||
QCOMPARE(res, -1);
|
||||
}
|
||||
|
||||
// then() throws an exception derived from QException
|
||||
{
|
||||
QFutureInterface<int> promise;
|
||||
|
||||
int checkpoint = 0;
|
||||
auto then = promise.future()
|
||||
.then([&](int res) {
|
||||
throw DerivedException();
|
||||
return res;
|
||||
})
|
||||
.then([&](int res) { return res + 1; })
|
||||
.onFailed([&](const QException &) {
|
||||
checkpoint = 1;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&](const std::exception &) {
|
||||
checkpoint = 2;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&] {
|
||||
checkpoint = 3;
|
||||
return -1;
|
||||
});
|
||||
|
||||
promise.reportStarted();
|
||||
promise.reportResult(1);
|
||||
promise.reportFinished();
|
||||
|
||||
int res = 0;
|
||||
try {
|
||||
res = then.result();
|
||||
} catch (...) {
|
||||
checkpoint = 4;
|
||||
}
|
||||
QCOMPARE(checkpoint, 1);
|
||||
QCOMPARE(res, -1);
|
||||
}
|
||||
|
||||
// then() throws a custom exception
|
||||
{
|
||||
QFutureInterface<int> promise;
|
||||
|
||||
int checkpoint = 0;
|
||||
auto then = promise.future()
|
||||
.then([&](int res) {
|
||||
throw TestException();
|
||||
return res;
|
||||
})
|
||||
.then([&](int res) { return res + 1; })
|
||||
.onFailed([&](const QException &) {
|
||||
checkpoint = 1;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&](const std::exception &) {
|
||||
checkpoint = 2;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&] {
|
||||
checkpoint = 3;
|
||||
return -1;
|
||||
});
|
||||
|
||||
promise.reportStarted();
|
||||
promise.reportResult(1);
|
||||
promise.reportFinished();
|
||||
|
||||
int res = 0;
|
||||
try {
|
||||
res = then.result();
|
||||
} catch (...) {
|
||||
checkpoint = 4;
|
||||
}
|
||||
QCOMPARE(checkpoint, 3);
|
||||
QCOMPARE(res, -1);
|
||||
}
|
||||
|
||||
// Custom exception handler
|
||||
{
|
||||
struct TestException
|
||||
{
|
||||
};
|
||||
|
||||
QFutureInterface<int> promise;
|
||||
|
||||
int checkpoint = 0;
|
||||
auto then = promise.future()
|
||||
.then([&](int res) {
|
||||
throw TestException();
|
||||
return res;
|
||||
})
|
||||
.then([&](int res) { return res + 1; })
|
||||
.onFailed([&](const QException &) {
|
||||
checkpoint = 1;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&](const TestException &) {
|
||||
checkpoint = 2;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&] {
|
||||
checkpoint = 3;
|
||||
return -1;
|
||||
});
|
||||
|
||||
promise.reportStarted();
|
||||
promise.reportResult(1);
|
||||
promise.reportFinished();
|
||||
|
||||
int res = 0;
|
||||
try {
|
||||
res = then.result();
|
||||
} catch (...) {
|
||||
checkpoint = 4;
|
||||
}
|
||||
QCOMPARE(checkpoint, 2);
|
||||
QCOMPARE(res, -1);
|
||||
}
|
||||
|
||||
// Handle all exceptions
|
||||
{
|
||||
QFutureInterface<int> promise;
|
||||
|
||||
int checkpoint = 0;
|
||||
auto then = promise.future()
|
||||
.then([&](int res) {
|
||||
throw QException();
|
||||
return res;
|
||||
})
|
||||
.then([&](int res) { return res + 1; })
|
||||
.onFailed([&] {
|
||||
checkpoint = 1;
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&](const QException &) {
|
||||
checkpoint = 2;
|
||||
return -1;
|
||||
});
|
||||
|
||||
promise.reportStarted();
|
||||
promise.reportResult(1);
|
||||
promise.reportFinished();
|
||||
|
||||
int res = 0;
|
||||
try {
|
||||
res = then.result();
|
||||
} catch (...) {
|
||||
checkpoint = 3;
|
||||
}
|
||||
QCOMPARE(checkpoint, 1);
|
||||
QCOMPARE(res, -1);
|
||||
}
|
||||
|
||||
// Handler throws exception
|
||||
{
|
||||
QFutureInterface<int> promise;
|
||||
|
||||
int checkpoint = 0;
|
||||
auto then = promise.future()
|
||||
.then([&](int res) {
|
||||
throw QException();
|
||||
return res;
|
||||
})
|
||||
.then([&](int res) { return res + 1; })
|
||||
.onFailed([&](const QException &) {
|
||||
checkpoint = 1;
|
||||
throw QException();
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&] {
|
||||
checkpoint = 2;
|
||||
return -1;
|
||||
});
|
||||
|
||||
promise.reportStarted();
|
||||
promise.reportResult(1);
|
||||
promise.reportFinished();
|
||||
|
||||
int res = 0;
|
||||
try {
|
||||
res = then.result();
|
||||
} catch (...) {
|
||||
checkpoint = 3;
|
||||
}
|
||||
QCOMPARE(checkpoint, 2);
|
||||
QCOMPARE(res, -1);
|
||||
}
|
||||
|
||||
// No handler for exception
|
||||
{
|
||||
QFutureInterface<int> promise;
|
||||
|
||||
int checkpoint = 0;
|
||||
auto then = promise.future()
|
||||
.then([&](int res) {
|
||||
throw QException();
|
||||
return res;
|
||||
})
|
||||
.then([&](int res) { return res + 1; })
|
||||
.onFailed([&](const std::exception &) {
|
||||
checkpoint = 1;
|
||||
throw std::exception();
|
||||
return -1;
|
||||
})
|
||||
.onFailed([&](QException &) {
|
||||
checkpoint = 2;
|
||||
return -1;
|
||||
});
|
||||
|
||||
promise.reportStarted();
|
||||
promise.reportResult(1);
|
||||
promise.reportFinished();
|
||||
|
||||
int res = 0;
|
||||
try {
|
||||
res = then.result();
|
||||
} catch (...) {
|
||||
checkpoint = 3;
|
||||
}
|
||||
QCOMPARE(checkpoint, 3);
|
||||
QCOMPARE(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Callable>
|
||||
bool runForCallable(Callable &&handler)
|
||||
{
|
||||
QFuture<int> future = createExceptionResultFuture()
|
||||
.then([&](int) { return 1; })
|
||||
.onFailed(std::forward<Callable>(handler));
|
||||
|
||||
int res = 0;
|
||||
try {
|
||||
res = future.result();
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
return res == -1;
|
||||
}
|
||||
|
||||
int foo()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void tst_QFuture::onFailedTestCallables()
|
||||
{
|
||||
QVERIFY(runForCallable([&] { return -1; }));
|
||||
QVERIFY(runForCallable(foo));
|
||||
QVERIFY(runForCallable(&foo));
|
||||
|
||||
std::function<int()> func = foo;
|
||||
QVERIFY(runForCallable(func));
|
||||
|
||||
struct Functor1
|
||||
{
|
||||
int operator()() { return -1; }
|
||||
static int foo() { return -1; }
|
||||
};
|
||||
QVERIFY(runForCallable(Functor1()));
|
||||
QVERIFY(runForCallable(Functor1::foo));
|
||||
|
||||
struct Functor2
|
||||
{
|
||||
int operator()() const { return -1; }
|
||||
static int foo() { return -1; }
|
||||
};
|
||||
QVERIFY(runForCallable(Functor2()));
|
||||
|
||||
struct Functor3
|
||||
{
|
||||
int operator()() const noexcept { return -1; }
|
||||
static int foo() { return -1; }
|
||||
};
|
||||
QVERIFY(runForCallable(Functor3()));
|
||||
}
|
||||
|
||||
#endif // QT_NO_EXCEPTIONS
|
||||
|
||||
void tst_QFuture::testSingleResult(const UniquePtr &p)
|
||||
|
Loading…
Reference in New Issue
Block a user