- Use std::invoke_result, fallback to std::result_of on C++11/14.
- Use RVO instead of std::move (GCC warning: redundant-move).
- Replace QSharedPointer with std::shared_ptr (GCC warning: unititialized value).
- QLinkedList is deprecated in Qt 5.15.
Broken since 78417b5, this bug creates an infinite recursion at runtime while trying to resolve the QPromise<T> template constructor if the given callback is either an invalid function or a valid callback but using auto args (C++14).
Related warning: C4717: recursive on all control paths, function will cause runtime stack overflow.
Based on the WebKit preset and following 'most' of the Qt guidelines, except a few rules that work better for promise continuation lambdas. Requires clang-format 11.
Iterates over all the promise values (i.e. `Sequence<T>`) and reduces the sequence to a single value using the given `reducer` function and an optional `initialValue`. Also provide a static helper to directly reduce values (`QtPromise::reduce(values, reducer, initialValue)`).
For consistency with other helpers, deprecate `qPromise()` in favor of `QtPromise::resolve()` but also add support for calling this helper with lvalue. Add extra unit tests to make sure that rvalue is not copied.
Introduce a new `QtPromise::connect()` helper that allows to create a promise resolved from a single signal and optionally, rejected by another one (from a different object or not). The promise type is determined by the type of the first signal argument (other arguments are currently ignored). A `QPromise<void>` is returned if the resolve signal doesn't provide any argument.
If the rejection is emitted before the promise is resolved, the promise will be rejected with the value of the first argument (other arguments being ignored). If the rejection signal doesn't provide any argument, the promise will be rejected with `QPromiseUndefinedException` if the signal is emitted. Additionally, the promise will be automatically rejected with `QPromiseContextException` if the source object is destroyed before the promise is resolved.
While not recommended because it makes tracking errors more difficult, it's now possible to reject a promise without explicit reason, in which case, a built-in `QPromiseUndefinedException` is thrown. This is done in anticipation of handling rejection signals without argument.
Call the given `functor` on each element in the promise value (i.e. `Sequence<T>`), then resolve to the original sequence unmodified. Also provide a static helper to directly filter values (`QtPromise::each(values, functor)`).
Add a new helper that calls functor immediately and returns a promise fulfilled with the value returned by functor. Any synchronous exceptions will be turned into rejections on the returned promise. This is a convenient method that can be used instead of handling both synchronous and asynchronous exception flows.
Also simplify PromiseDispatch which now calls the functor with a variable number of arguments (including none).
Add a new method that iterates over all the promise values (i.e. `Sequence<T>`) and filters the sequence to another using the given `filterer` function. If `filterer` returns `true`, a copy of the item is put in the `output` sequence, otherwise, the item will not appear in `output`. Also provide a static helper to directly filter values (`QtPromise::filter(values, filterer)`).
Iterate over all the promise value (i.e. `Sequence<T>`) and map the sequence to another using the given `mapper` function. Also provide a static helper to directly map values (`QtPromise::map(values, mapper)`).
Make sure that QPromiseResolve and QPromiseReject release their associated promise as soon as one of them is resolved or rejected, ensuring that the promise data is cleaned up once resolved (that fixes cases where one or both of them are captured in a signal/slot connection lambda)
Embed the promise fulfillment value and rejection reason in respectively PromiseValue and PromiseError private wrappers, both storing the data in a shared pointer (QPromiseError is now deprecated).
Make sure to **not** notify handlers if the captured thread doesn't exist anymore, which would potentially result in dispatching to the wrong thread (ie. nullptr == current thread). This also applies when the app is shutting down and the even loop is not anymore available. In both cases, we should not trigger any error and skip notifications.
Currently, QPromise can't be added dynamically to QVector (::push_* | ::append) because it doesn't expose a default constructor. Until deciding if a default constructor should be added (private/public?), let's make the `QPromise::all` method work with any container types that are STL compatible.
This method returns a promise that will be fulfilled with the same value as the `input` promise and after at least `msec` milliseconds. If the `input` promise is rejected, the `output` promise is immediately rejected with the same reason.
This `handler` allows to observe the value of the `input` promise, without changing the propagated value. The `output` promise will be resolved with the same value as the `input` promise (the `handler` returned value will be ignored). However, if `handler` throws, `output` is rejected with the new exception. Unlike `finally`, this handler is not called for rejections.
Make sure that the chained value is not copied when `finally` is called for a fulfilled input promise. The value was copied 7 times in the previous version because it was captured in a lambda, which one copied multiple times.
Make QPromise thread safe but also ensure that continuation lambdas (then/fail/finally) are called in the thread of the promise instance they are attached to.
Make continuation methods const (then/fail/finally) and ensure that the resolved promise value/error is copied only when required, same for user lambdas (dispatching result is now fully handled by the PromiseData).
QFuture canceled with `QFuture::cancel()` now rejects attached promises with `QPromiseCanceledException`. In case the future is canceled because an exception (e) has been thrown, the promise is rejected with the same (e) exception (or `QUnhandledException` if not a subclass of `QException`).
Make sure that the promise state can only be changed by the promise producer (and not consumers) by removing the `fulfill` and `reject` methods from the instance members and introducing a new constructor accepting a resolver lambda. That also means that a promise can't anymore be default constructed.
Add the static `QPromise<T>::resolve` and `QPromise<T>::reject` methods to create synchronously fulfilled or rejected promises, and fix the `qPromise` helper to handle deduced promises (e.g. `qPromise(QFuture<int>()) -> QPromise<int>`).