mirror of
https://github.com/simonbrunel/qtpromise.git
synced 2025-04-02 22:45:08 +08:00
Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1a905cbd4f | ||
|
14031392ac | ||
|
f382ad25fc | ||
|
ac9b936959 | ||
|
21faa67b58 | ||
|
9f01c130cd | ||
|
461f09bef8 | ||
|
bac405febf | ||
|
0c3955cca5 | ||
|
60b36e7a70 | ||
|
f7639e921e | ||
|
1752255e7b | ||
|
13b4bb65d2 | ||
|
b7ecd95b04 | ||
|
88289a7635 | ||
|
6deec9f51f | ||
|
d43657fbd5 | ||
|
b99e468c84 | ||
|
be5455a8c8 | ||
|
0bfdddd887 | ||
|
2c1e631aed | ||
|
d5a82518f9 | ||
|
1ad99391a3 | ||
|
78417b5813 | ||
|
58738a5604 | ||
|
7ee51de987 | ||
|
6639ea52db | ||
|
7f9013a878 | ||
|
cc29ef3512 | ||
|
3c1461b8d0 | ||
|
815dc443b9 | ||
|
67837827b1 | ||
|
e3f0f054af | ||
|
cbf4cc7867 | ||
|
963ec621e1 | ||
|
1f30224578 | ||
|
47b90fb532 | ||
|
63acdfaab9 | ||
|
700098ef7b | ||
|
6110cd40d3 | ||
|
a6d883acbd | ||
|
9119cc72f6 | ||
|
fa5a4192ff | ||
|
16229fc2c9 |
@ -1,22 +1,34 @@
|
||||
image: Visual Studio 2015
|
||||
|
||||
init:
|
||||
- cmd: call "C:\Program Files (x86)\Microsoft Visual Studio "%VSVER%".0\VC\vcvarsall.bat" %ARCH%
|
||||
# https://www.appveyor.com/docs/build-configuration/
|
||||
# https://www.appveyor.com/docs/lang/cpp/#visual-studio
|
||||
# https://www.appveyor.com/docs/windows-images-software/#qt
|
||||
|
||||
environment:
|
||||
PATH: '%PATH%;%QTDIR%\bin'
|
||||
matrix:
|
||||
- QTDIR: C:\Qt\5.6\msvc2013
|
||||
VSVER: 12
|
||||
ARCH: x86
|
||||
- QTDIR: C:\Qt\5.9\msvc2013_64
|
||||
VSVER: 12
|
||||
ARCH: x64
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
QTDIR: C:\Qt\5.6\msvc2013
|
||||
SETUP_CMD: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat
|
||||
SETUP_ARG: x86
|
||||
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
QTDIR: C:\Qt\5.15\msvc2019_64
|
||||
SETUP_CMD: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat
|
||||
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
QTDIR: C:\Qt\6.2\msvc2019_64
|
||||
SETUP_CMD: C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat
|
||||
|
||||
before_build:
|
||||
- call "%SETUP_CMD%" %SETUP_ARG%
|
||||
- cmake --version
|
||||
- qmake --version
|
||||
|
||||
build_script:
|
||||
- cmd: qmake qtpromise.pro
|
||||
- cmd: nmake
|
||||
- cmake -G "NMake Makefiles"
|
||||
- cmake --build .
|
||||
|
||||
test_script:
|
||||
- cmd: nmake check
|
||||
- cmake --build . --target test
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
66
.clang-format
Normal file
66
.clang-format
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
Language: Cpp
|
||||
Standard: Cpp11
|
||||
BasedOnStyle: WebKit
|
||||
AlignAfterOpenBracket: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortLambdasOnASingleLine: Empty
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: true
|
||||
AfterNamespace: false
|
||||
AfterStruct: true
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
BreakBeforeBraces: Custom
|
||||
BreakInheritanceList: BeforeComma
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
ColumnLimit: 100
|
||||
CommentPragmas: "^!|^:"
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- BOOST_FOREACH
|
||||
- foreach
|
||||
- forever
|
||||
- Q_FOREACH
|
||||
- Q_FOREVER
|
||||
- QBENCHMARK
|
||||
- QBENCHMARK_ONCE
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^<Q.*>'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
- Regex: '^<.*>'
|
||||
Priority: 4
|
||||
SortPriority: 0
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
IndentPPDirectives: AfterHash
|
||||
NamespaceIndentation: None
|
||||
PenaltyReturnTypeOnItsOwnLine: 10
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
...
|
43
.github/workflows/ci.yaml
vendored
Normal file
43
.github/workflows/ci.yaml
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# https://docs.github.com/actions/reference/workflow-syntax-for-github-actions
|
||||
# https://doc.qt.io/qt-6/supported-platforms.html
|
||||
# https://ddalcino.github.io/aqt-list-server/
|
||||
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
qt:
|
||||
- 5.9.9
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: ${{ matrix.qt }}
|
||||
archives: qtbase icu
|
||||
tools: tools_cmake
|
||||
arch: gcc_64
|
||||
host: linux
|
||||
- run: |
|
||||
qmake --version
|
||||
cmake --version
|
||||
gcc --version
|
||||
g++ --version
|
||||
- run: |
|
||||
cmake -G "Unix Makefiles"
|
||||
cmake --build . -- -j12
|
||||
cmake --build . --target test
|
||||
- run: |
|
||||
sudo apt-get install lcov > /dev/null
|
||||
lcov --version
|
||||
lcov --capture --directory . -o coverage.info
|
||||
lcov -e coverage.info '**/src/**/*' -o coverage.info
|
||||
- uses: codecov/codecov-action@v3
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
files: coverage.info
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,8 +1,10 @@
|
||||
_book
|
||||
dist
|
||||
node_modules
|
||||
/build
|
||||
/dist
|
||||
/node_modules
|
||||
*.autosave
|
||||
*.gcno
|
||||
*.gcda
|
||||
*.info
|
||||
*.moc
|
||||
*.o
|
||||
*.obj
|
||||
@ -11,6 +13,3 @@ node_modules
|
||||
Makefile*
|
||||
moc_*.cpp
|
||||
moc_*.h
|
||||
coverage.info
|
||||
package-lock.json
|
||||
target_wrapper.bat
|
||||
|
6
.remarkrc.yml
Normal file
6
.remarkrc.yml
Normal file
@ -0,0 +1,6 @@
|
||||
plugins:
|
||||
- frontmatter
|
||||
- validate-links
|
||||
- preset-lint-recommended
|
||||
- preset-lint-markdown-style-guide
|
||||
- [lint-maximum-line-length, 100]
|
35
.travis.yml
35
.travis.yml
@ -1,35 +0,0 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: cpp
|
||||
compiler: gcc
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo add-apt-repository -y ppa:beineri/opt-qt563-trusty
|
||||
- sudo apt-get update -qq
|
||||
|
||||
install:
|
||||
- sudo apt-get install -qq gcc-4.9 g++-4.9
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.9
|
||||
- sudo apt-get install -qq qt56base
|
||||
- source /opt/qt56/bin/qt56-env.sh
|
||||
- wget http://archive.ubuntu.com/ubuntu/pool/universe/l/lcov/lcov_1.13.orig.tar.gz
|
||||
- tar xf lcov_1.13.orig.tar.gz
|
||||
- cd lcov-1.13/
|
||||
- sudo make install
|
||||
- cd ..
|
||||
|
||||
before_script:
|
||||
- qmake --version
|
||||
- lcov --version
|
||||
- gcc --version && g++ --version
|
||||
|
||||
script:
|
||||
- qmake qtpromise.pro CONFIG+=coverage
|
||||
- make -j4
|
||||
- make check --quiet
|
||||
- lcov -capture --directory . --o coverage.info
|
||||
- lcov -e coverage.info '**/src/**/*' -o coverage.info
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash) -f coverage.info
|
82
CMakeLists.txt
Normal file
82
CMakeLists.txt
Normal file
@ -0,0 +1,82 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
if(DEFINED PROJECT_NAME)
|
||||
set(SUBPROJECT ON)
|
||||
endif()
|
||||
|
||||
project(qtpromise VERSION 0.7.0 LANGUAGES CXX)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
|
||||
|
||||
find_package(QT 5.6.0 NAMES Qt6 Qt5 REQUIRED)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_library(qtpromise INTERFACE)
|
||||
add_library(qtpromise::qtpromise ALIAS qtpromise)
|
||||
|
||||
target_link_libraries(qtpromise INTERFACE Qt${QT_VERSION_MAJOR}::Core)
|
||||
target_include_directories(qtpromise INTERFACE "${CMAKE_CURRENT_LIST_DIR}/include")
|
||||
|
||||
add_definitions(
|
||||
-DQT_DEPRECATED_WARNINGS
|
||||
-DQT_NO_KEYWORDS
|
||||
)
|
||||
|
||||
# https://github.com/simonbrunel/qtpromise/issues/10
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
|
||||
add_compile_options(
|
||||
-Werror
|
||||
-Wpedantic
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wconversion
|
||||
-Wdouble-promotion
|
||||
-Wformat=2
|
||||
-Wlogical-op
|
||||
-Wmissing-noreturn
|
||||
-Wold-style-cast
|
||||
# -Wshadow # disabled due to many findings in the current code
|
||||
-Wsign-conversion
|
||||
-Wswitch-default
|
||||
-Wunused-local-typedefs
|
||||
-pedantic-errors
|
||||
)
|
||||
|
||||
# https://github.com/Barro/compiler-warnings/blob/master/gcc/warnings-gcc-6.txt
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6)
|
||||
add_compile_options(
|
||||
-Wduplicated-cond
|
||||
)
|
||||
endif()
|
||||
|
||||
# https://github.com/Barro/compiler-warnings/blob/master/gcc/warnings-gcc-7.txt
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7)
|
||||
add_compile_options(
|
||||
-Wduplicated-branches
|
||||
)
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# https://clang.llvm.org/docs/DiagnosticsReference.html
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wpedantic
|
||||
-Wsuggest-destructor-override
|
||||
-Wsuggest-override
|
||||
)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
# https://docs.microsoft.com/en-us/cpp/build/reference/compiler-option-warning-level
|
||||
add_compile_options(
|
||||
/WX
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT SUBPROJECT)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
20
LICENSE
20
LICENSE
@ -1,9 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Simon Brunel
|
||||
Copyright (c) 2017-present Simon Brunel, https://github.com/simonbrunel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
24
README.md
24
README.md
@ -1,19 +1,27 @@
|
||||
<a href="https://promisesaplus.com/" title="Promises/A+ 1.1"><img src="https://promisesaplus.com/assets/logo-small.png" alt="Promises/A+" align="right"/></a>
|
||||
<p align="center">
|
||||
<img width="256" src="docs/.vuepress/public/hero.svg?sanitize=true">
|
||||
</p>
|
||||
|
||||
# QtPromise
|
||||
<p align="center">
|
||||
<a href="https://www.qpm.io/packages/com.github.simonbrunel.qtpromise/index.html"><img src="https://img.shields.io/github/release/simonbrunel/qtpromise.svg?style=flat-square&label=qpm&colorB=4CAF50&maxAge=600" alt="Install"></a>
|
||||
<a href="https://travis-ci.com/simonbrunel/qtpromise"><img src="https://img.shields.io/travis/simonbrunel/qtpromise/master.svg?style=flat-square&maxAge=600" alt="Builds"></a>
|
||||
<a href="https://codecov.io/gh/simonbrunel/qtpromise"><img src="https://img.shields.io/codecov/c/github/simonbrunel/qtpromise.svg?style=flat-square&maxAge=600" alt="Coverage"></a>
|
||||
<a href="https://marketplace.qt.io/products/qtpromise"><img src="https://img.shields.io/static/v1?style=flat-square&label=Qt&message=Marketplace&colorB=40cd52&maxAge=600" alt="Marketplace"></a>
|
||||
</p>
|
||||
|
||||
[](https://www.qpm.io/packages/com.github.simonbrunel.qtpromise/index.html) [](https://travis-ci.org/simonbrunel/qtpromise) [](https://codecov.io/gh/simonbrunel/qtpromise)
|
||||
## Overview
|
||||
|
||||
[Promises/A+](https://promisesaplus.com/) implementation for [Qt/C++](https://www.qt.io/).
|
||||
|
||||
Requires [Qt 5.6](https://www.qt.io/download/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects).
|
||||
Requires [Qt 5.6](https://doc.qt.io/qt-5/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects) or [Qt 6](https://doc.qt.io/qt-6/).
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Getting Started](https://qtpromise.netlify.com/qtpromise/getting-started.html)
|
||||
* [QtConcurrent](https://qtpromise.netlify.com/qtpromise/qtconcurrent.html)
|
||||
* [Thread-Safety](https://qtpromise.netlify.com/qtpromise/thread-safety.html)
|
||||
* [API Reference](https://qtpromise.netlify.com/qtpromise/api-reference.html)
|
||||
- [Getting Started](https://qtpromise.netlify.com/qtpromise/getting-started.html)
|
||||
- [Qt Concurrent](https://qtpromise.netlify.com/qtpromise/qtconcurrent.html)
|
||||
- [Qt Signals](https://qtpromise.netlify.com/qtpromise/qtsignals.html)
|
||||
- [Thread-Safety](https://qtpromise.netlify.com/qtpromise/thread-safety.html)
|
||||
- [API Reference](https://qtpromise.netlify.com/qtpromise/api-reference.html)
|
||||
|
||||
## License
|
||||
|
||||
|
44
cmake/QtPromiseAddTest.cmake
Normal file
44
cmake/QtPromiseAddTest.cmake
Normal file
@ -0,0 +1,44 @@
|
||||
function(qtpromise_add_test NAME)
|
||||
cmake_parse_arguments(_ARG "" "" "SOURCES;LIBRARIES" ${ARGN})
|
||||
|
||||
set(_TARGET qtpromise.tests.auto.${NAME})
|
||||
|
||||
add_executable(${_TARGET} ${_ARG_SOURCES})
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
target_link_libraries(${_TARGET} gcov)
|
||||
target_compile_options(${_TARGET}
|
||||
PRIVATE
|
||||
-fprofile-arcs
|
||||
-ftest-coverage
|
||||
-O0
|
||||
-g
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${_TARGET}
|
||||
Qt${QT_VERSION_MAJOR}::Concurrent
|
||||
Qt${QT_VERSION_MAJOR}::Test
|
||||
qtpromise
|
||||
qtpromise.tests.utils
|
||||
${_ARG_LIBRARIES}
|
||||
)
|
||||
|
||||
add_test(NAME ${_TARGET}
|
||||
COMMAND ${_TARGET}
|
||||
WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(qtpromise_add_tests GROUP)
|
||||
cmake_parse_arguments(_ARG "" "" "SOURCES" ${ARGN})
|
||||
|
||||
foreach(_FILE ${_ARG_SOURCES})
|
||||
get_filename_component(_FILE_NAME ${_FILE} NAME)
|
||||
if (_FILE_NAME MATCHES "^tst_(.+)\.cpp$")
|
||||
string(REGEX REPLACE "^tst_(.+)\.cpp$" "\\1" _TEST_NAME ${_FILE_NAME})
|
||||
qtpromise_add_test(${GROUP}.${_TEST_NAME} SOURCES ${_FILE} ${_ARG_UNPARSED_ARGUMENTS})
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
@ -1,52 +1,88 @@
|
||||
module.exports = {
|
||||
title: 'QtPromise',
|
||||
description: 'Promises/A+ implementation for Qt/C++',
|
||||
ga: 'UA-113899811-1',
|
||||
dest: 'dist/docs',
|
||||
head: [
|
||||
['link', { rel: 'icon', href: `/favicon.png` }],
|
||||
],
|
||||
plugins: [
|
||||
['@vuepress/google-analytics', {
|
||||
ga: 'UA-113899811-1'
|
||||
}]
|
||||
],
|
||||
themeConfig: {
|
||||
repo: 'simonbrunel/qtpromise',
|
||||
lastUpdated: 'Last Updated',
|
||||
smoothScroll: false,
|
||||
editLinks: true,
|
||||
sidebarDepth: 2,
|
||||
docsDir: 'docs',
|
||||
algolia: {
|
||||
apiKey: '0e6e9cccb8c2c360a5543e28c4e31cb8',
|
||||
indexName: 'qtpromise'
|
||||
},
|
||||
nav: [
|
||||
{ text: 'Home', link: '/' },
|
||||
{ text: 'Guide', link: '/qtpromise/getting-started' },
|
||||
{ text: 'API Reference', link: '/qtpromise/api-reference' },
|
||||
],
|
||||
sidebar: [
|
||||
'qtpromise/getting-started',
|
||||
'qtpromise/qtconcurrent',
|
||||
'qtpromise/thread-safety',
|
||||
'qtpromise/api-reference',
|
||||
'/qtpromise/getting-started',
|
||||
'/qtpromise/qtconcurrent',
|
||||
'/qtpromise/qtsignals',
|
||||
'/qtpromise/thread-safety',
|
||||
{
|
||||
title: 'QPromise',
|
||||
title: 'API Reference',
|
||||
path: '/qtpromise/api-reference',
|
||||
children: [
|
||||
'qtpromise/qpromise/constructor',
|
||||
'qtpromise/qpromise/delay',
|
||||
'qtpromise/qpromise/each',
|
||||
'qtpromise/qpromise/fail',
|
||||
'qtpromise/qpromise/filter',
|
||||
'qtpromise/qpromise/finally',
|
||||
'qtpromise/qpromise/isfulfilled',
|
||||
'qtpromise/qpromise/ispending',
|
||||
'qtpromise/qpromise/isrejected',
|
||||
'qtpromise/qpromise/map',
|
||||
'qtpromise/qpromise/tap',
|
||||
'qtpromise/qpromise/tapfail',
|
||||
'qtpromise/qpromise/then',
|
||||
'qtpromise/qpromise/timeout',
|
||||
'qtpromise/qpromise/wait',
|
||||
'qtpromise/qpromise/all.md',
|
||||
'qtpromise/qpromise/reject.md',
|
||||
'qtpromise/qpromise/resolve.md'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Helpers',
|
||||
children: [
|
||||
'qtpromise/helpers/attempt',
|
||||
'qtpromise/helpers/each',
|
||||
'qtpromise/helpers/filter',
|
||||
'qtpromise/helpers/map',
|
||||
'qtpromise/helpers/qpromise',
|
||||
'qtpromise/helpers/qpromiseall'
|
||||
//['/qtpromise/api-reference', 'Overview'],
|
||||
{
|
||||
title: 'QPromise',
|
||||
children: [
|
||||
'/qtpromise/qpromise/constructor',
|
||||
'/qtpromise/qpromise/convert',
|
||||
'/qtpromise/qpromise/delay',
|
||||
'/qtpromise/qpromise/each',
|
||||
'/qtpromise/qpromise/fail',
|
||||
'/qtpromise/qpromise/filter',
|
||||
'/qtpromise/qpromise/finally',
|
||||
'/qtpromise/qpromise/isfulfilled',
|
||||
'/qtpromise/qpromise/ispending',
|
||||
'/qtpromise/qpromise/isrejected',
|
||||
'/qtpromise/qpromise/map',
|
||||
'/qtpromise/qpromise/reduce',
|
||||
'/qtpromise/qpromise/tap',
|
||||
'/qtpromise/qpromise/tapfail',
|
||||
'/qtpromise/qpromise/then',
|
||||
'/qtpromise/qpromise/timeout',
|
||||
'/qtpromise/qpromise/wait',
|
||||
'/qtpromise/qpromise/reject',
|
||||
'/qtpromise/qpromise/resolve'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Helpers',
|
||||
children: [
|
||||
'/qtpromise/helpers/all',
|
||||
'/qtpromise/helpers/attempt',
|
||||
'/qtpromise/helpers/connect',
|
||||
'/qtpromise/helpers/each',
|
||||
'/qtpromise/helpers/filter',
|
||||
'/qtpromise/helpers/map',
|
||||
'/qtpromise/helpers/reduce',
|
||||
'/qtpromise/helpers/resolve'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Exceptions',
|
||||
children: [
|
||||
'/qtpromise/exceptions/canceled',
|
||||
'/qtpromise/exceptions/context',
|
||||
'/qtpromise/exceptions/conversion',
|
||||
'/qtpromise/exceptions/timeout',
|
||||
'/qtpromise/exceptions/undefined'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
47
docs/.vuepress/public/hero.svg
Normal file
47
docs/.vuepress/public/hero.svg
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
|
||||
<polygon fill="#F7E02F" points="440.999,446 0,446 0,135.382 69,66 511.999,66 511.999,374.906 "/>
|
||||
<g>
|
||||
<path d="M209.116,301.104c-27.241,0-46.231-7.334-56.971-22.002c-10.739-14.669-16.109-37.85-16.109-69.544
|
||||
c0-31.694,5.456-55.223,16.371-70.591c10.911-15.364,29.816-23.05,56.709-23.05c26.889,0,45.749,7.641,56.578,22.919
|
||||
c10.825,15.282,16.24,38.812,16.24,70.591c0,20.955-2.227,37.895-6.68,50.816c-4.452,12.925-11.746,22.788-21.871,29.599
|
||||
l22.002,35.361l-26.979,12.572l-23.312-38.242C221.775,300.581,216.45,301.104,209.116,301.104z M175.065,260.374
|
||||
c5.934,10.216,17.288,15.323,34.051,15.323s28.068-5.018,33.92-15.062c5.849-10.04,8.775-27.021,8.775-50.947
|
||||
c0-23.922-3.012-41.295-9.037-52.125c-6.024-10.825-17.247-16.24-33.659-16.24c-16.416,0-27.679,5.415-33.79,16.24
|
||||
c-6.115,10.83-9.167,28.117-9.167,51.863C166.159,233.177,169.126,250.158,175.065,260.374z"/>
|
||||
<path d="M382.776,191.616h-36.146v57.625c0,10.654,0.785,17.727,2.357,21.218c1.571,3.495,5.586,5.238,12.049,5.238l21.479-0.786
|
||||
l1.31,22.789c-11.701,2.267-20.606,3.404-26.718,3.404c-14.844,0-25.015-3.404-30.515-10.215
|
||||
c-5.501-6.811-8.251-19.646-8.251-38.505v-60.769h-16.764v-24.36h16.764v-37.98h28.289v37.98h36.146V191.616z"/>
|
||||
<path d="M152.877,382.938H142.83v15.285h-7.965v-49.157h18.012c11.147,0,16.721,5.502,16.721,16.505
|
||||
c0,5.646-1.399,9.952-4.198,12.918C162.601,381.456,158.426,382.938,152.877,382.938z M142.83,376.049h9.975
|
||||
c5.741,0,8.612-3.491,8.612-10.478c0-3.348-0.694-5.788-2.081-7.319c-1.388-1.531-3.565-2.297-6.531-2.297h-9.975V376.049z"/>
|
||||
<path d="M176.343,398.224v-35.882h7.75v4.306c4.066-2.63,8.133-4.329,12.2-5.095v7.822c-4.115,0.814-7.632,1.866-10.549,3.157
|
||||
l-1.579,0.646v25.045H176.343z"/>
|
||||
<path d="M203.936,366.218c2.463-3.109,6.566-4.665,12.307-4.665c5.741,0,9.843,1.556,12.308,4.665
|
||||
c2.463,3.11,3.696,7.774,3.696,13.993c0,6.22-1.197,10.908-3.588,14.065c-2.393,3.158-6.53,4.736-12.415,4.736
|
||||
c-5.884,0-10.023-1.578-12.415-4.736c-2.393-3.157-3.588-7.846-3.588-14.065C200.24,373.992,201.471,369.328,203.936,366.218z
|
||||
M209.784,389.54c1.1,1.914,3.253,2.871,6.458,2.871c3.205,0,5.357-0.957,6.459-2.871c1.1-1.913,1.65-5.047,1.65-9.4
|
||||
s-0.586-7.438-1.758-9.258c-1.173-1.817-3.29-2.727-6.351-2.727c-3.062,0-5.179,0.909-6.351,2.727
|
||||
c-1.173,1.819-1.758,4.904-1.758,9.258S208.683,387.627,209.784,389.54z"/>
|
||||
<path d="M247.818,398.224h-7.822v-35.882h7.75v2.225c3.396-2.009,6.506-3.014,9.329-3.014c4.162,0,7.199,1.173,9.113,3.517
|
||||
c4.354-2.344,8.684-3.517,12.99-3.517c4.305,0,7.344,1.328,9.113,3.982c1.77,2.655,2.656,7.141,2.656,13.456v19.232h-7.752v-19.018
|
||||
c0-3.875-0.395-6.625-1.184-8.252c-0.789-1.626-2.428-2.44-4.916-2.44c-2.152,0-4.473,0.479-6.961,1.436l-1.219,0.502
|
||||
c0.381,0.958,0.572,4.019,0.572,9.186v18.587h-7.75V379.78c0-4.257-0.383-7.199-1.148-8.826c-0.765-1.626-2.438-2.44-5.022-2.44
|
||||
c-2.393,0-4.618,0.479-6.674,1.436l-1.076,0.431V398.224z"/>
|
||||
<path d="M300.133,356.242v-8.253h7.822v8.253H300.133z M300.133,398.224v-35.882h7.822v35.882H300.133z"/>
|
||||
<path d="M342.33,369.733c-5.646-0.765-9.735-1.147-12.271-1.147s-4.294,0.299-5.274,0.896c-0.98,0.599-1.471,1.543-1.471,2.835
|
||||
s0.538,2.201,1.614,2.727c1.077,0.527,3.612,1.138,7.607,1.83c3.994,0.694,6.828,1.783,8.504,3.266
|
||||
c1.674,1.483,2.512,4.115,2.512,7.894c0,3.78-1.209,6.556-3.624,8.324c-2.417,1.771-5.945,2.655-10.585,2.655
|
||||
c-2.919,0-6.603-0.406-11.052-1.22l-2.225-0.358l0.287-6.53c5.741,0.766,9.878,1.147,12.415,1.147c2.535,0,4.342-0.311,5.418-0.933
|
||||
c1.076-0.621,1.614-1.65,1.614-3.086s-0.515-2.428-1.543-2.979c-1.029-0.549-3.492-1.147-7.392-1.794
|
||||
c-3.899-0.646-6.758-1.661-8.575-3.05c-1.819-1.387-2.728-3.922-2.728-7.606c0-3.684,1.256-6.435,3.768-8.253
|
||||
c2.512-1.817,5.729-2.727,9.652-2.727c3.062,0,6.817,0.383,11.267,1.147l2.225,0.431L342.33,369.733z"/>
|
||||
<path d="M376.632,391.765l2.009-0.215l0.144,5.813c-5.454,1.101-10.286,1.65-14.496,1.65c-5.311,0-9.126-1.458-11.446-4.377
|
||||
c-2.32-2.918-3.48-7.582-3.48-13.994c0-12.726,5.19-19.089,15.573-19.089c10.047,0,15.07,5.479,15.07,16.434l-0.503,5.598h-22.246
|
||||
c0.047,2.967,0.692,5.144,1.938,6.53c1.243,1.389,3.563,2.081,6.961,2.081C369.551,392.195,373.044,392.052,376.632,391.765z
|
||||
M372.327,377.556c0-3.54-0.563-6.016-1.687-7.427c-1.125-1.411-3.026-2.117-5.705-2.117c-2.681,0-4.629,0.742-5.849,2.225
|
||||
c-1.221,1.483-1.855,3.923-1.902,7.319H372.327z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
@ -1,4 +0,0 @@
|
||||
@import 'override.styl'
|
||||
|
||||
.content a code
|
||||
color: $accentColor
|
37
docs/.vuepress/styles/index.styl
Normal file
37
docs/.vuepress/styles/index.styl
Normal file
@ -0,0 +1,37 @@
|
||||
@import 'palette.styl'
|
||||
|
||||
a
|
||||
svg.icon.outbound
|
||||
margin-bottom -2px
|
||||
margin-left 2px
|
||||
|
||||
img + svg.icon.outbound
|
||||
display none
|
||||
|
||||
sup
|
||||
font-size 0.75em !important
|
||||
|
||||
.home .hero
|
||||
img
|
||||
margin 1.5rem auto !important
|
||||
.description
|
||||
display none !important
|
||||
#main-title
|
||||
display none !important
|
||||
|
||||
.page a code
|
||||
color $accentColor
|
||||
|
||||
.navbar
|
||||
-webkit-box-shadow 0 4px 4px -4px rgba(0,0,0,0.1)
|
||||
-moz-box-shadow 0 4px 4px -4px rgba(0,0,0,0.1)
|
||||
box-shadow 0 4px 4px -4px rgba(0,0,0,0.1)
|
||||
|
||||
.logo
|
||||
margin-right: 0.5rem;
|
||||
|
||||
.site-name
|
||||
font-size 1.25rem
|
||||
|
||||
.repo-link
|
||||
margin-left 1rem
|
@ -1,9 +1,30 @@
|
||||
<a href="https://promisesaplus.com/" title="Promises/A+ 1.1"><img src="https://promisesaplus.com/assets/logo-small.png" alt="Promises/A+" align="right"/></a>
|
||||
---
|
||||
home: true
|
||||
heroImage: /hero.svg
|
||||
footer: MIT Licensed | Copyright © Simon Brunel, https://github.com/simonbrunel
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.qpm.io/packages/com.github.simonbrunel.qtpromise/index.html"><img src="https://img.shields.io/github/release/simonbrunel/qtpromise.svg?style=flat-square&label=qpm&colorB=4CAF50&maxAge=600" alt="Install"></a>
|
||||
<a href="https://travis-ci.com/simonbrunel/qtpromise"><img src="https://img.shields.io/travis/simonbrunel/qtpromise/master.svg?style=flat-square&maxAge=600" alt="Builds"></a>
|
||||
<a href="https://codecov.io/gh/simonbrunel/qtpromise"><img src="https://img.shields.io/codecov/c/github/simonbrunel/qtpromise.svg?style=flat-square&maxAge=600" alt="Coverage"></a>
|
||||
<a href="https://marketplace.qt.io/products/qtpromise"><img src="https://img.shields.io/static/v1?style=flat-square&label=Qt&message=Marketplace&colorB=40cd52&maxAge=600" alt="Marketplace"></a>
|
||||
</p>
|
||||
|
||||
## Overview
|
||||
|
||||
# QtPromise
|
||||
[Promises/A+](https://promisesaplus.com/) implementation for [Qt/C++](https://www.qt.io/).
|
||||
|
||||
Requires [Qt 5.6](https://www.qt.io/download/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects).
|
||||
Requires [Qt 5.6](https://doc.qt.io/qt-5/) (or later) with [C++11 support enabled](https://wiki.qt.io/How_to_use_C++11_in_your_Qt_Projects) or [Qt 6](https://doc.qt.io/qt-6/).
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Getting Started](/qtpromise/getting-started.md)
|
||||
- [Qt Concurrent](/qtpromise/qtconcurrent.md)
|
||||
- [Qt Signals](/qtpromise/qtsignals.md)
|
||||
- [Thread-Safety](/qtpromise/thread-safety.md)
|
||||
- [API Reference](/qtpromise/api-reference.md)
|
||||
|
||||
## License
|
||||
|
||||
QtPromise is available under the [MIT license](https://github.com/simonbrunel/qtpromise/blob/master/LICENSE).
|
||||
|
@ -2,33 +2,50 @@
|
||||
|
||||
## Functions
|
||||
|
||||
* [`QPromise<T>::QPromise`](qpromise/constructor.md)
|
||||
* [`QPromise<T>::delay`](qpromise/delay.md)
|
||||
* [`QPromise<T>::each`](qpromise/each.md)
|
||||
* [`QPromise<T>::fail`](qpromise/fail.md)
|
||||
* [`QPromise<T>::filter`](qpromise/filter.md)
|
||||
* [`QPromise<T>::finally`](qpromise/finally.md)
|
||||
* [`QPromise<T>::isFulfilled`](qpromise/isfulfilled.md)
|
||||
* [`QPromise<T>::isPending`](qpromise/ispending.md)
|
||||
* [`QPromise<T>::isRejected`](qpromise/isrejected.md)
|
||||
* [`QPromise<T>::map`](qpromise/map.md)
|
||||
* [`QPromise<T>::tap`](qpromise/tap.md)
|
||||
* [`QPromise<T>::tapFail`](qpromise/tapfail.md)
|
||||
* [`QPromise<T>::then`](qpromise/then.md)
|
||||
* [`QPromise<T>::timeout`](qpromise/timeout.md)
|
||||
* [`QPromise<T>::wait`](qpromise/wait.md)
|
||||
- [`QPromise<T>::QPromise`](qpromise/constructor.md)
|
||||
- [`QPromise<T>::convert`](qpromise/convert.md)
|
||||
- [`QPromise<T>::delay`](qpromise/delay.md)
|
||||
- [`QPromise<T>::each`](qpromise/each.md)
|
||||
- [`QPromise<T>::fail`](qpromise/fail.md)
|
||||
- [`QPromise<T>::filter`](qpromise/filter.md)
|
||||
- [`QPromise<T>::finally`](qpromise/finally.md)
|
||||
- [`QPromise<T>::isFulfilled`](qpromise/isfulfilled.md)
|
||||
- [`QPromise<T>::isPending`](qpromise/ispending.md)
|
||||
- [`QPromise<T>::isRejected`](qpromise/isrejected.md)
|
||||
- [`QPromise<T>::map`](qpromise/map.md)
|
||||
- [`QPromise<T>::reduce`](qpromise/reduce.md)
|
||||
- [`QPromise<T>::tap`](qpromise/tap.md)
|
||||
- [`QPromise<T>::tapFail`](qpromise/tapfail.md)
|
||||
- [`QPromise<T>::then`](qpromise/then.md)
|
||||
- [`QPromise<T>::timeout`](qpromise/timeout.md)
|
||||
- [`QPromise<T>::wait`](qpromise/wait.md)
|
||||
|
||||
## Static Functions
|
||||
|
||||
* [`[static] QPromise<T>::all`](qpromise/all.md)
|
||||
* [`[static] QPromise<T>::reject`](qpromise/reject.md)
|
||||
* [`[static] QPromise<T>::resolve`](qpromise/resolve.md)
|
||||
- [`(static) QPromise<T>::reject`](qpromise/reject.md)
|
||||
- [`(static) QPromise<T>::resolve`](qpromise/resolve.md)
|
||||
|
||||
## Helpers
|
||||
|
||||
* [`QtPromise::attempt`](helpers/attempt.md)
|
||||
* [`QtPromise::each`](helpers/each.md)
|
||||
* [`QtPromise::filter`](helpers/filter.md)
|
||||
* [`QtPromise::map`](helpers/map.md)
|
||||
* [`qPromise`](helpers/qpromise.md)
|
||||
* [`qPromiseAll`](helpers/qpromiseall.md)
|
||||
- [`QtPromise::all`](helpers/all.md)
|
||||
- [`QtPromise::attempt`](helpers/attempt.md)
|
||||
- [`QtPromise::connect`](helpers/connect.md)
|
||||
- [`QtPromise::each`](helpers/each.md)
|
||||
- [`QtPromise::filter`](helpers/filter.md)
|
||||
- [`QtPromise::map`](helpers/map.md)
|
||||
- [`QtPromise::reduce`](helpers/reduce.md)
|
||||
- [`QtPromise::resolve`](helpers/resolve.md)
|
||||
|
||||
## Exceptions
|
||||
|
||||
- [`QPromiseCanceledException`](exceptions/canceled.md)
|
||||
- [`QPromiseContextException`](exceptions/context.md)
|
||||
- [`QPromiseConversionException`](exceptions/conversion.md)
|
||||
- [`QPromiseTimeoutException`](exceptions/timeout.md)
|
||||
- [`QPromiseUndefinedException`](exceptions/undefined.md)
|
||||
|
||||
## Deprecations
|
||||
|
||||
- `(static) QPromise<T>::all`: use [`QtPromise::all`](helpers/all.md) instead (since 0.5.0)
|
||||
- `QtPromise::qPromise`: use [`QtPromise::resolve`](helpers/resolve.md) instead (since 0.5.0)
|
||||
- `QtPromise::qPromiseAll`: use [`QtPromise::all`](helpers/all.md) instead (since 0.5.0)
|
||||
|
17
docs/qtpromise/exceptions/canceled.md
Normal file
17
docs/qtpromise/exceptions/canceled.md
Normal file
@ -0,0 +1,17 @@
|
||||
# QPromiseCanceledException
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
This exception is thrown for promise created from a [`QFuture`](../qtconcurrent.md) which has been
|
||||
canceled (e.g. using [`QFuture::cancel()`](http://doc.qt.io/qt-5/qfuture.html#cancel)), for example:
|
||||
|
||||
```cpp
|
||||
auto output = QtPromise::resolve(future)
|
||||
.fail([](const QPromiseCanceledException& error) {
|
||||
// `future` has been canceled!
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
QtPromise doesn't support promise cancelation (yet?)
|
||||
:::
|
14
docs/qtpromise/exceptions/context.md
Normal file
14
docs/qtpromise/exceptions/context.md
Normal file
@ -0,0 +1,14 @@
|
||||
# QPromiseContextException
|
||||
|
||||
*Since: 0.5.0*
|
||||
|
||||
When a promise is created using [`QtPromise::connect()`](../helpers/connect.md), this exception is
|
||||
thrown when the `sender` object is destroyed, for example:
|
||||
|
||||
```cpp
|
||||
auto promise = QtPromise::connect(sender, &Object::finished, &Object::error);
|
||||
|
||||
promise.fail([](const QPromiseContextException&) {
|
||||
// 'sender' has been destroyed.
|
||||
})
|
||||
```
|
13
docs/qtpromise/exceptions/conversion.md
Normal file
13
docs/qtpromise/exceptions/conversion.md
Normal file
@ -0,0 +1,13 @@
|
||||
# QPromiseConversionException
|
||||
|
||||
*Since: 0.7.0*
|
||||
|
||||
This exception is thrown whenever a promise result conversion fails, for example:
|
||||
|
||||
```cpp
|
||||
QPromise<QVariant> input = {...};
|
||||
auto output = input.convert<int>()
|
||||
.fail([](const QPromiseconversionException& e) {
|
||||
// conversion may fail because input could not be converted to number
|
||||
});
|
||||
```
|
14
docs/qtpromise/exceptions/timeout.md
Normal file
14
docs/qtpromise/exceptions/timeout.md
Normal file
@ -0,0 +1,14 @@
|
||||
# QPromiseTimeoutException
|
||||
|
||||
*Since: 0.2.0*
|
||||
|
||||
This is the default exception thrown when reaching the time limit when using the
|
||||
[`QPromise::timeout()`](../qpromise/timeout.md) method, for example:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.timeout(2000)
|
||||
.fail([](const QPromiseTimeoutException& error) {
|
||||
// operation timed out after 2s!
|
||||
});
|
||||
```
|
21
docs/qtpromise/exceptions/undefined.md
Normal file
21
docs/qtpromise/exceptions/undefined.md
Normal file
@ -0,0 +1,21 @@
|
||||
# QPromiseUndefinedException
|
||||
|
||||
*Since: 0.5.0*
|
||||
|
||||
This exception is thrown when rejecting a promise with no explicit reason, for example:
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
|
||||
async_method([=](bool success, int result) {
|
||||
if (success) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
promise.fail([](const QPromiseUndefinedException&) {
|
||||
// promise rejected without reason!
|
||||
})
|
||||
```
|
@ -2,42 +2,154 @@
|
||||
|
||||
## Installation
|
||||
|
||||
QtPromise is a [header-only](https://en.wikipedia.org/wiki/Header-only) library, simply download the [latest release](https://github.com/simonbrunel/qtpromise/releases/latest) (or [`git submodule`](https://git-scm.com/docs/git-submodule)) and include `qtpromise.pri` from your project `.pro`.
|
||||
### Using CMake
|
||||
|
||||
### qpm
|
||||
*Since: 0.6.0*
|
||||
|
||||
Alternatively and **only** if your project relies on [qpm](https://www.qpm.io/), you can install QtPromise as follow:
|
||||
If your project uses [CMake](https://cmake.org/) as build system, QtPromise can be installed using
|
||||
the `FetchContent` module. Please refer to the [CMake (FetchContent)](#cmake-fetchcontent) section
|
||||
for details of using this method.
|
||||
|
||||
```bash
|
||||
qpm install com.github.simonbrunel.qtpromise
|
||||
### Using qpm
|
||||
|
||||
If your project is configured to use [qpm](https://www.qpm.io/), QtPromise can be installed using
|
||||
the following command:
|
||||
|
||||
```sh
|
||||
qpm install com.github.simonbrunel.qtpromise
|
||||
```
|
||||
|
||||
## Usage
|
||||
See also: [com.github.simonbrunel.qtpromise](https://www.qpm.io/packages/com.github.simonbrunel.qtpromise/)
|
||||
|
||||
The recommended way to use QtPromise is to include the single module header:
|
||||
### Using Git
|
||||
|
||||
If your project uses [Git](https://git-scm.com/) as version control system, QtPromise can be
|
||||
installed either as a [`subtree`](#subtree) or a [`submodule`](#submodule). Read more about these
|
||||
commands in ["Git: submodules vs subtrees"](https://nering.dev/2016/git-submodules-vs-subtrees/)
|
||||
which provides a good comparison between these two workflows.
|
||||
|
||||
The following examples install QtPromise version 0.7.0 under the `3rdparty/qtpromise` subdirectory.
|
||||
Note that the install directory is arbitrary and can be any empty directory under your repository.
|
||||
Once installed, refer to the [CMake](#cmake) or [qmake](#qmake) sections for details of integrating
|
||||
QtPromise into your project.
|
||||
|
||||
#### subtree
|
||||
|
||||
```sh
|
||||
cd <your/project/repository>
|
||||
git remote add qtpromise https://github.com/simonbrunel/qtpromise.git
|
||||
git subtree add -P 3rdparty/qtpromise qtpromise v0.7.0 --squash -m "Add QtPromise v0.7.0"
|
||||
```
|
||||
|
||||
#### submodule
|
||||
|
||||
```sh
|
||||
cd <your/project/repository>
|
||||
git submodule add https://github.com/simonbrunel/qtpromise.git 3rdparty/qtpromise
|
||||
cd 3rdparty/qtpromise
|
||||
git checkout v0.7.0
|
||||
cd ../..
|
||||
git add 3rdparty/qtpromise
|
||||
git commit -m "Add QtPromise v0.7.0"
|
||||
```
|
||||
|
||||
### Download
|
||||
|
||||
QtPromise can be downloaded from the [GitHub release page](https://github.com/simonbrunel/qtpromise/releases)
|
||||
as a `zip` or `tar.gz` archive. Under Linux, you can use the following commands:
|
||||
|
||||
```sh
|
||||
cd <your/project/repository>
|
||||
wget -q -O qtpromise.tar.gz https://github.com/simonbrunel/qtpromise/archive/v0.7.0.tar.gz
|
||||
tar xzf qtpromise.tar.gz --strip 1 --one-top-level=3rdparty/qtpromise
|
||||
rm qtpromise.tar.gz
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
QtPromise is a [header-only](https://en.wikipedia.org/wiki/Header-only) library. Integrating it
|
||||
within your project consists only in configuring the include path(s) to the library headers. To
|
||||
simplify this step, [qmake](#qmake) and [CMake](#cmake) integrations are provided.
|
||||
|
||||
### CMake
|
||||
|
||||
*Since: 0.6.0*
|
||||
|
||||
After installing QtPromise using [Git](#using-git) or the [download](#download) method, you can use
|
||||
the [`add_subdirectory`](https://cmake.org/cmake/help/latest/command/add_subdirectory.html) command
|
||||
to make its targets available to your CMake project:
|
||||
|
||||
```cmake
|
||||
add_subdirectory(<path/to/qtpromise>)
|
||||
|
||||
target_link_libraries(<target> qtpromise)
|
||||
```
|
||||
|
||||
### CMake (FetchContent)
|
||||
|
||||
*Since: 0.6.0*
|
||||
|
||||
Alternatively, the [`FetchContent`](https://cmake.org/cmake/help/latest/module/FetchContent.html)
|
||||
module (**CMake 3.11+**) allows to install QtPromise from your CMake project at configure time:
|
||||
|
||||
```cmake
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(qtpromise
|
||||
GIT_REPOSITORY https://github.com/simonbrunel/qtpromise.git
|
||||
GIT_TAG v0.7.0
|
||||
GIT_SHALLOW true
|
||||
)
|
||||
|
||||
# CMake v3.14+
|
||||
FetchContent_MakeAvailable(qtpromise)
|
||||
|
||||
target_link_libraries(<target> qtpromise)
|
||||
```
|
||||
|
||||
If your CMake version **is prior to v3.14**, you need to explicitly define the population steps:
|
||||
|
||||
```cmake
|
||||
# CMake v3.11+ (alternative for FetchContent_MakeAvailable)
|
||||
FetchContent_GetProperties(qtpromise)
|
||||
if(NOT qtpromise_POPULATED)
|
||||
FetchContent_Populate(qtpromise)
|
||||
add_subdirectory(
|
||||
${qtpromise_SOURCE_DIR}
|
||||
${qtpromise_BINARY_DIR}
|
||||
)
|
||||
endif()
|
||||
```
|
||||
|
||||
### qmake
|
||||
|
||||
After installing QtPromise using [Git](#using-git) or the [download](#download) method, you can
|
||||
[include](https://doc.qt.io/qt-5/qmake-test-function-reference.html#include-filename) `qtpromise.pri`
|
||||
file from the install directory:
|
||||
|
||||
```cmake
|
||||
include(<path/to/qtpromise>/qtpromise.pri)
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
To start using QtPromise in your code, you first need to include the single module header:
|
||||
|
||||
```cpp
|
||||
#include <QtPromise>
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Let's first make the code more readable by using the library namespace:
|
||||
The following `download` function creates a [promise from callbacks](qpromise/constructor.md) which
|
||||
will be resolved when the network request is finished:
|
||||
|
||||
```cpp
|
||||
using namespace QtPromise;
|
||||
```
|
||||
|
||||
This `download` function creates a [promise from callbacks](qpromise/constructor.md) which will be resolved when the network request is finished:
|
||||
|
||||
```cpp
|
||||
QPromise<QByteArray> download(const QUrl& url)
|
||||
QtPromise::QPromise<QByteArray> download(const QUrl& url)
|
||||
{
|
||||
return QPromise<QByteArray>([&](
|
||||
const QPromiseResolve<QByteArray>& resolve,
|
||||
const QPromiseReject<QByteArray>& reject) {
|
||||
return QtPromise::QPromise<QByteArray>{[&](
|
||||
const QtPromise::QPromiseResolve<QByteArray>& resolve,
|
||||
const QtPromise::QPromiseReject<QByteArray>& reject) {
|
||||
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(url));
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest{url});
|
||||
QObject::connect(reply, &QNetworkReply::finished, [=]() {
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
resolve(reply->readAll());
|
||||
@ -47,22 +159,22 @@ QPromise<QByteArray> download(const QUrl& url)
|
||||
|
||||
reply->deleteLater();
|
||||
});
|
||||
});
|
||||
}};
|
||||
}
|
||||
```
|
||||
|
||||
The following method `uncompress` data in a separate thread and returns a [promise from QFuture](qtconcurrent.md):
|
||||
|
||||
```cpp
|
||||
QPromise<Entries> uncompress(const QByteArray& data)
|
||||
QtPromise::QPromise<Entries> uncompress(const QByteArray& data)
|
||||
{
|
||||
return qPromise(QtConcurrent::run([](const QByteArray& data) {
|
||||
return QtPromise::resolve(QtConcurrent::run([](const QByteArray& data) {
|
||||
Entries entries;
|
||||
|
||||
// {...} uncompress data and parse content.
|
||||
|
||||
if (error) {
|
||||
throw MalformedException();
|
||||
throw MalformedException{};
|
||||
}
|
||||
|
||||
return entries;
|
||||
@ -71,6 +183,7 @@ QPromise<Entries> uncompress(const QByteArray& data)
|
||||
```
|
||||
|
||||
It's then easy to chain the whole asynchronous process using promises:
|
||||
|
||||
- initiate the promise chain by downloading a specific URL,
|
||||
- [`then`](qpromise/then.md) *and only if download succeeded*, uncompress received data,
|
||||
- [`then`](qpromise/then.md) validate and process the uncompressed entries,
|
||||
@ -80,16 +193,19 @@ It's then easy to chain the whole asynchronous process using promises:
|
||||
```cpp
|
||||
download(url).then(&uncompress).then([](const Entries& entries) {
|
||||
if (entries.isEmpty()) {
|
||||
throw UpdateException("No entries");
|
||||
throw UpdateException{"No entries"};
|
||||
}
|
||||
// {...} process entries
|
||||
}).finally([]() {
|
||||
// {...} cleanup
|
||||
}).fail([](QNetworkReply::NetworkError err) {
|
||||
}).fail([](QNetworkReply::NetworkError error) {
|
||||
// {...} handle network error
|
||||
}).fail([](const UpdateException& err) {
|
||||
}).fail([](const UpdateException& error) {
|
||||
// {...} handle update error
|
||||
}).fail([]() {
|
||||
// {...} catch all
|
||||
});
|
||||
```
|
||||
|
||||
Note that `MalformedException` in the example above is thrown from a QtConcurrent thread and should
|
||||
meet [specific conditions](qtconcurrent.md#error).
|
||||
|
37
docs/qtpromise/helpers/all.md
Normal file
37
docs/qtpromise/helpers/all.md
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
title: all
|
||||
---
|
||||
|
||||
# QtPromise::all
|
||||
|
||||
*Since: 0.5.0*
|
||||
|
||||
```cpp
|
||||
QtPromise::all(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
|
||||
QtPromise::all(Sequence<QPromise<void>> promises) -> QPromise<void>
|
||||
```
|
||||
|
||||
Returns a `QPromise<QVector<T>>` (or `QPromise<void>`) that fulfills when **all** `promises` of
|
||||
(the same) type `T` have been fulfilled. The `output` value is a vector containing all the values
|
||||
of `promises`, in the same order, i.e., at the respective positions to the original sequence,
|
||||
regardless of completion order.
|
||||
|
||||
If any of the given `promises` fail, `output` immediately rejects with the error of the promise that
|
||||
rejected, whether or not the other promises are resolved.
|
||||
|
||||
`Sequence` is any STL compatible container (eg. `QVector`, `QList`, `std::vector`, etc.)
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{
|
||||
download(QUrl("http://a...")),
|
||||
download(QUrl("http://b...")),
|
||||
download(QUrl("http://c..."))
|
||||
};
|
||||
|
||||
auto output = QtPromise::all(promises);
|
||||
|
||||
// output type: QPromise<QVector<QByteArray>>
|
||||
output.then([](const QVector<QByteArray>& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
@ -13,14 +13,14 @@ QtPromise::attempt(Functor functor, Args...) -> QPromise<R>
|
||||
// - Functor: Function(Args...) -> R | QPromise<R>
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The type `R` of the `output` promise depends on the type returned by the `functor` function.
|
||||
If `functor` returns a promise (or `QFuture`), the `output` promise is delayed and will be
|
||||
resolved by the returned promise.
|
||||
The type `R` of the `output` promise depends on the type returned by the `functor` function. If
|
||||
`functor` returns a promise (or `QFuture`), the `output` promise is delayed and will be resolved
|
||||
by the returned promise.
|
||||
|
||||
```cpp
|
||||
QPromise<QByteArray> download(const QUrl& url);
|
||||
@ -29,7 +29,7 @@ QPromise<QByteArray> process(const QUrl& url)
|
||||
{
|
||||
return QtPromise::attempt([&]() {
|
||||
if (!url.isValid()) {
|
||||
throw InvalidUrlException();
|
||||
throw InvalidUrlException{};
|
||||
}
|
||||
|
||||
return download(url);
|
||||
@ -41,7 +41,7 @@ auto output = process(url);
|
||||
// 'output' type: QPromise<QByteArray>
|
||||
output.then([](const QByteArray& res) {
|
||||
// {...}
|
||||
}).fail([](const InvalidUrlException& err) {
|
||||
}).fail([](const InvalidUrlException& error) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
53
docs/qtpromise/helpers/connect.md
Normal file
53
docs/qtpromise/helpers/connect.md
Normal file
@ -0,0 +1,53 @@
|
||||
---
|
||||
title: connect
|
||||
---
|
||||
|
||||
# QtPromise::connect
|
||||
|
||||
*Since: 0.5.0*
|
||||
|
||||
```cpp
|
||||
(1) QtPromise::connect(QObject* sender, Signal(T) resolver) -> QPromise<T>
|
||||
(2) QtPromise::connect(QObject* sender, Signal(T) resolver, Signal(R) rejecter) -> QPromise<T>
|
||||
(3) QtPromise::connect(QObject* sender, Signal(T) resolver, QObject* sender2, Signal(R) rejecter) -> QPromise<T>
|
||||
```
|
||||
|
||||
Creates a `QPromise<T>` that will be fulfilled with the `resolver` signal's first argument, or a
|
||||
`QPromise<void>` if `resolver` doesn't provide any argument.
|
||||
|
||||
The second `(2)` and third `(3)` variants of this method will reject the `output` promise when the
|
||||
`rejecter` signal is emitted. The rejection reason is the value of the `rejecter` signal's first
|
||||
argument or [`QPromiseUndefinedException`](../exceptions/undefined.md) if `rejected` doesn't provide
|
||||
any argument.
|
||||
|
||||
Additionally, the `output` promise will be automatically rejected with [`QPromiseContextException`](../exceptions/context.md)
|
||||
if `sender` is destroyed before the promise is resolved (that doesn't apply to `sender2`).
|
||||
|
||||
```cpp
|
||||
class Sender : public QObject
|
||||
{
|
||||
Q_SIGNALS:
|
||||
void finished(const QByteArray&);
|
||||
void error(ErrorCode);
|
||||
};
|
||||
|
||||
auto sender = new Sender{};
|
||||
auto output = QtPromise::connect(sender, &Sender::finished, &Sender::error);
|
||||
|
||||
// 'output' resolves as soon as one of the following events happens:
|
||||
// - the 'sender' object is destroyed, the promise is rejected
|
||||
// - the 'finished' signal is emitted, the promise is fulfilled
|
||||
// - the 'error' signal is emitted, the promise is rejected
|
||||
|
||||
// 'output' type: QPromise<QByteArray>
|
||||
output.then([](const QByteArray& res) {
|
||||
// 'res' is the first argument of the 'finished' signal.
|
||||
}).fail([](ErrorCode error) {
|
||||
// 'error' is the first argument of the 'error' signal.
|
||||
}).fail([](const QPromiseContextException& error) {
|
||||
// the 'sender' object has been destroyed before any of
|
||||
// the 'finished' or 'error' signals have been emitted.
|
||||
});
|
||||
```
|
||||
|
||||
See also the [`Qt Signals`](../qtsignals.md) section for more examples.
|
@ -21,16 +21,15 @@ If `functor` returns a promise (or `QFuture`), the `output` promise is delayed u
|
||||
promises are resolved. If any of the promises fail, `output` immediately rejects with the error
|
||||
of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
|
||||
```cpp
|
||||
auto output = QtPromise::each(QVector<QUrl>{
|
||||
QUrl("http://a..."),
|
||||
QUrl("http://b..."),
|
||||
QUrl("http://c...")
|
||||
}, [](const QUrl& url, ...) {
|
||||
return QPromise<void>([&](auto resolve, auto reject) {
|
||||
return QPromise<void>{[&](auto resolve, auto reject) {
|
||||
// process url asynchronously ...
|
||||
})
|
||||
}};
|
||||
});
|
||||
|
||||
// `output` resolves as soon as all promises returned by
|
||||
|
@ -16,7 +16,7 @@ QtPromise::filter(Sequence<T> values, Filterer filterer) -> QPromise<Sequence<T>
|
||||
|
||||
Iterates over `values` and [filters the sequence](https://en.wikipedia.org/wiki/Filter_%28higher-order_function%29)
|
||||
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`. If `filterer`
|
||||
is put in the `output` sequence, otherwise, the item will not appear in `output`. If `filterer`
|
||||
throws, `output` is rejected with the new exception.
|
||||
|
||||
If `filterer` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
@ -29,10 +29,10 @@ auto output = QtPromise::filter(QVector{
|
||||
QUrl("http://b..."),
|
||||
QUrl("http://c...")
|
||||
}, [](const QUrl& url, ...) {
|
||||
return QPromise<bool>([&](auto resolve, auto reject) {
|
||||
return QPromise<bool>{[&](auto resolve, auto reject) {
|
||||
// resolve(true) if 'url' is reachable, else resolve(false)
|
||||
// {...}
|
||||
});
|
||||
}};
|
||||
});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
@ -45,8 +45,8 @@ output.then([](const QVector<QUrl>& res) {
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
The order of the output sequence values is guarantee to be the same as the original
|
||||
sequence, regardless of completion order of the promises returned by `filterer`.
|
||||
The order of the output sequence values is guarantee to be the same as the original sequence,
|
||||
regardless of completion order of the promises returned by `filterer`.
|
||||
:::
|
||||
|
||||
See also: [`QPromise<T>::filter`](../qpromise/filter.md)
|
||||
|
@ -15,12 +15,12 @@ QtPromise::map(Sequence<T> values, Mapper mapper) -> QPromise<QVector<R>>
|
||||
```
|
||||
|
||||
Iterates over `values` and [maps the sequence](https://en.wikipedia.org/wiki/Map_%28higher-order_function%29)
|
||||
to another using the given `mapper` function. The type returned by `mapper` determines the type
|
||||
of the `output` promise. If `mapper` throws, `output` is rejected with the new exception.
|
||||
to another using the given `mapper` function. The type returned by `mapper` determines the type of
|
||||
the `output` promise. If `mapper` throws, `output` is rejected with the new exception.
|
||||
|
||||
If `mapper` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
promises are resolved. If any of the promises fails, `output` immediately rejects with the
|
||||
error of the promise that rejected, whether or not the other promises are resolved.
|
||||
If `mapper` returns a promise (or `QFuture`), the `output` promise is delayed until all the promises
|
||||
are resolved. If any of the promises fails, `output` immediately rejects with the error of the
|
||||
promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
```cpp
|
||||
auto output = QtPromise::map(QVector{
|
||||
@ -28,10 +28,10 @@ auto output = QtPromise::map(QVector{
|
||||
QUrl("http://b..."),
|
||||
QUrl("http://c...")
|
||||
}, [](const QUrl& url, ...) {
|
||||
return QPromise<QByteArray>([&](auto resolve, auto reject) {
|
||||
return QPromise<QByteArray>{[&](auto resolve, auto reject) {
|
||||
// download content at url and resolve
|
||||
// {...}
|
||||
});
|
||||
}};
|
||||
});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
@ -44,8 +44,8 @@ output.then([](const QVector<QByteArray>& res) {
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
The order of the output sequence values is guarantee to be the same as the original
|
||||
sequence, regardless of completion order of the promises returned by `mapper`.
|
||||
The order of the output sequence values is guarantee to be the same as the original sequence,
|
||||
regardless of completion order of the promises returned by `mapper`.
|
||||
:::
|
||||
|
||||
See also: [`QPromise<T>::map`](../qpromise/map.md)
|
||||
|
@ -1,21 +0,0 @@
|
||||
---
|
||||
title: qPromise
|
||||
---
|
||||
|
||||
# qPromise
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```
|
||||
qPromise(T value) -> QPromise<R>
|
||||
```
|
||||
|
||||
Similar to the [`QPromise<T>::resolve`](../qpromise/resolve.md) static method, creates a promise resolved from a given `value` without the extra typing:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(); // QPromise<void>
|
||||
auto promise = qPromise(42); // QPromise<int>
|
||||
auto promise = qPromise(QString("foo")); // QPromise<QString>
|
||||
```
|
||||
|
||||
This method also allows to convert `QFuture<T>` to `QPromise<T>` delayed until the `QFuture` is finished ([read more](../qtconcurrent.md#convert)).
|
@ -1,22 +0,0 @@
|
||||
---
|
||||
title: qPromiseAll
|
||||
---
|
||||
|
||||
# qPromiseAll
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```
|
||||
qPromiseAll(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
|
||||
qPromiseAll(Sequence<QPromise<void>> promises) -> QPromise<void>
|
||||
```
|
||||
|
||||
This method simply calls the appropriated [`QPromise<T>::all`](../qpromise/all.md) static method based on the given `QVector` type. In some cases, this method is more convenient than the static one since it avoid some extra typing:
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{...}
|
||||
|
||||
auto output = qPromiseAll(promises);
|
||||
// eq. QPromise<QByteArray>::all(promises)
|
||||
```
|
||||
|
55
docs/qtpromise/helpers/reduce.md
Normal file
55
docs/qtpromise/helpers/reduce.md
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
title: reduce
|
||||
---
|
||||
|
||||
# QtPromise::reduce
|
||||
|
||||
*Since: 0.5.0*
|
||||
|
||||
```cpp
|
||||
QPromise::reduce(Sequence<T|QPromise<T>> values, Reducer reducer) -> QPromise<T>
|
||||
QPromise::reduce(Sequence<T|QPromise<T>> values, Reducer reducer, R|QPromise<R> initialValue) -> QPromise<R>
|
||||
|
||||
// With:
|
||||
// - Sequence: STL compatible container (e.g. QVector, etc.)
|
||||
// - Reducer: Function(T|R accumulator, T item, int index) -> R|QPromise<R>
|
||||
```
|
||||
|
||||
Iterates over `values` and [reduces the sequence to a single value](https://en.wikipedia.org/wiki/Fold_%28higher-order_function%29)
|
||||
using the given `reducer` function and an optional `initialValue`. The type returned by `reducer`
|
||||
determines the type of the `output` promise. If `reducer` throws, `output` is rejected with the
|
||||
new exception.
|
||||
|
||||
If `reducer` returns a promise (or `QFuture`), then the result of the promise is awaited, before
|
||||
continuing with next iteration. If any promise in the `values` sequence is rejected or a promise
|
||||
returned by the `reducer` function is rejected, `output` immediately rejects with the error of
|
||||
the promise that rejected.
|
||||
|
||||
```cpp
|
||||
// Concatenate the content of the given files, read asynchronously
|
||||
auto output = QtPromise::reduce(QList<QUrl>{
|
||||
"file:f0.txt", // contains "foo"
|
||||
"file:f1.txt", // contains "bar"
|
||||
"file:f2.txt" // contains "42"
|
||||
}, [](const QString& acc, const QString& cur, int idx) {
|
||||
return readAsync(cur).then([=](const QString& res) {
|
||||
return QString{"%1;%2:%3"}.arg(acc).arg(idx).arg(res);
|
||||
});
|
||||
}, QString{"index:text"});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
// 'reducer' are fulfilled or at least one is rejected.
|
||||
|
||||
// 'output' type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// res == "index:text;0:foo;1:bar;2:42"
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
::: warning IMPORTANT
|
||||
The first time `reducer` is called, if no `initialValue` is provided, `accumulator` will be equal
|
||||
to the first value in the sequence, and `currentValue` to the second one (thus index will be `1`).
|
||||
:::
|
||||
|
||||
See also: [`QPromise<T>::reduce`](../qpromise/reduce.md)
|
23
docs/qtpromise/helpers/resolve.md
Normal file
23
docs/qtpromise/helpers/resolve.md
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
title: resolve
|
||||
---
|
||||
|
||||
# QtPromise::resolve
|
||||
|
||||
*Since: 0.5.0*
|
||||
|
||||
```cpp
|
||||
QtPromise::resolve(T value) -> QPromise<R>
|
||||
```
|
||||
|
||||
Similar to the [`QPromise<T>::resolve`](../qpromise/resolve.md) static method, creates a promise
|
||||
resolved from a given `value` but without the extra typing:
|
||||
|
||||
```cpp
|
||||
auto promise = QtPromise::resolve(); // QPromise<void>
|
||||
auto promise = QtPromise::resolve(42); // QPromise<int>
|
||||
auto promise = QtPromise::resolve(QString{"foo"}); // QPromise<QString>
|
||||
```
|
||||
|
||||
This method also allows to convert `QFuture<T>` to `QPromise<T>`, delayed until the `QFuture` is
|
||||
finished ([read more](../qtconcurrent.md#convert)).
|
@ -1,34 +0,0 @@
|
||||
---
|
||||
title: ::all [static]
|
||||
---
|
||||
|
||||
# QPromise::all [static]
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```
|
||||
[static] QPromise<T>::all(Sequence<QPromise<T>> promises) -> QPromise<QVector<T>>
|
||||
```
|
||||
|
||||
Returns a `QPromise<QVector<T>>` that fulfills when **all** `promises` of (the same) type `T` have been fulfilled. The `output` value is a vector containing all the values of `promises`, in the same order, i.e., at the respective positions to the original sequence, regardless of completion order.
|
||||
|
||||
If any of the given `promises` fail, `output` immediately rejects with the error of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
`Sequence` is any STL compatible container (eg. `QVector`, `QList`, `std::vector`, etc.)
|
||||
|
||||
```cpp
|
||||
QVector<QPromise<QByteArray> > promises{
|
||||
download(QUrl("http://a...")),
|
||||
download(QUrl("http://b...")),
|
||||
download(QUrl("http://c..."))
|
||||
};
|
||||
|
||||
auto output = QPromise<QByteArray>::all(promises);
|
||||
|
||||
// output type: QPromise<QVector<QByteArray>>
|
||||
output.then([](const QVector<QByteArray>& res) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`qPromiseAll`](../helpers/qpromiseall.md)
|
@ -18,20 +18,53 @@ QPromise<int> promise([](const QPromiseResolve<int>& resolve, const QPromiseReje
|
||||
if (success) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(customException());
|
||||
reject(customException{});
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
`QPromise<void>` is specialized to not contain any value, meaning that the `resolve` callback takes no argument.
|
||||
`QPromise<void>` is specialized to not contain any value, meaning that the `resolve` callback takes
|
||||
no argument.
|
||||
:::
|
||||
|
||||
**C++14**
|
||||
C++14 alternative:
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise([](const auto& resolve, const auto& reject) {
|
||||
QPromise<int> promise{[](const auto& resolve, const auto& reject) {
|
||||
// {...}
|
||||
});
|
||||
}};
|
||||
```
|
||||
|
||||
## Undefined rejection reason
|
||||
|
||||
*Since: 0.5.0*
|
||||
|
||||
While not recommended because it makes tracking errors more difficult, it's also possible to reject
|
||||
a promise without explicit reason, in which case, a built-in [`QPromiseUndefinedException`](../exceptions/undefined.md)
|
||||
is thrown:
|
||||
|
||||
```cpp
|
||||
QPromise<int> promise{[](const QPromiseResolve<int>& resolve, const QPromiseReject<int>& reject) {
|
||||
async_method([=](bool success, int result) {
|
||||
if (success) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}};
|
||||
```
|
||||
|
||||
```cpp
|
||||
// The exception can be caught explicitly
|
||||
promise.fail([](const QPromiseUndefinedException&) {
|
||||
// { ... }
|
||||
})
|
||||
|
||||
// ... or implicitly (since undefined)
|
||||
promise.fail([]() {
|
||||
// { ... }
|
||||
})
|
||||
```
|
||||
|
124
docs/qtpromise/qpromise/convert.md
Normal file
124
docs/qtpromise/qpromise/convert.md
Normal file
@ -0,0 +1,124 @@
|
||||
---
|
||||
title: .convert
|
||||
---
|
||||
|
||||
# QPromise::convert
|
||||
|
||||
*Since: 0.7.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::convert<U>() -> QPromise<U>
|
||||
```
|
||||
|
||||
This method converts the resolved value of `QPromise<T>` to the type `U`. Depending on types `T`
|
||||
and `U`, it performs a [static cast](https://en.cppreference.com/w/cpp/language/static_cast),
|
||||
calls a [converting constructor](https://en.cppreference.com/w/cpp/language/converting_constructor),
|
||||
or tries to convert using [QVariant](https://doc.qt.io/qt-5/qvariant.html).
|
||||
|
||||
If `T` and `U` are [fundamental types](https://en.cppreference.com/w/cpp/language/types) or
|
||||
[enumerations](https://en.cppreference.com/w/cpp/language/enum), the result of the conversion is
|
||||
the same as calling `static_cast<U>` for type `T`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
|
||||
// output type: QPromise<double>
|
||||
auto output = input.convert<double>();
|
||||
|
||||
output.then([](double value) {
|
||||
// the value has been converted using static_cast
|
||||
});
|
||||
```
|
||||
|
||||
If `U` has a [converting constructor](https://en.cppreference.com/w/cpp/language/converting_constructor)
|
||||
from `T`, i.e., a non-explicit constructor with a single argument accepting `T`, it is used to
|
||||
convert the value:
|
||||
|
||||
```cpp
|
||||
QPromise<QByteArray> input = {...}
|
||||
|
||||
// output type: QPromise<QString>
|
||||
auto output = input.convert<QString>();
|
||||
|
||||
output.then([](const QString& value) {
|
||||
// the value has been converted using static_cast that effectively calls QString(QByteArray)
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
When using this method to convert to your own classes, make sure that the constructor meeting the
|
||||
converting constructor criteria actually performs conversion.
|
||||
:::
|
||||
|
||||
::: tip NOTE
|
||||
If `U` is `void`, the resolved value of `QPromise<T>` is dropped.
|
||||
:::
|
||||
|
||||
Calling this method for `QPromise<QVariant>` tries to convert the resolved `QVariant` to type `U`
|
||||
using the `QVariant` [conversion algorithm](https://doc.qt.io/qt-5/qvariant.html#using-canconvert-and-convert-consecutively).
|
||||
For example, this allows to convert a string contained in `QVariant` to number. If such a
|
||||
conversion fails, the promise is rejected with
|
||||
[`QPromiseConversionException`](../exceptions/conversion.md).
|
||||
|
||||
```cpp
|
||||
// resolves to QVariant(int, 42) or QVariant(string, "foo")
|
||||
QPromise<QVariant> input = {...};
|
||||
|
||||
auto output = input.convert<int>();
|
||||
|
||||
// output type: QPromise<int>
|
||||
output.then([](int value) {
|
||||
// input was QVariant(int, 42), value is 42
|
||||
})
|
||||
.fail(const QPromiseConversionException& e) {
|
||||
// input was QVariant(string, "foo")
|
||||
});
|
||||
```
|
||||
|
||||
Conversion of `T` to `QVariant` using this method effectively calls `QVariant::fromValue<T>()`.
|
||||
All custom types must be registered with
|
||||
[`Q_DECLARE_METATYPE`](https://doc.qt.io/qt-5/qmetatype.html#Q_DECLARE_METATYPE) for this
|
||||
conversion to work:
|
||||
|
||||
```cpp
|
||||
struct Foo {};
|
||||
Q_DECLARE_METATYPE(Foo);
|
||||
|
||||
QPromise<Foo> input = {...}
|
||||
|
||||
auto output = input.convert<QVariant>();
|
||||
|
||||
// output type: QPromise<QVariant>
|
||||
output.then([](const QVariant& value) {
|
||||
// value contains an instance of Foo
|
||||
});
|
||||
```
|
||||
|
||||
All other combinations of `T` and `U` are converted via `QVariant`. All non-Qt types should provide
|
||||
a [conversion function](https://doc.qt.io/qt-5/qmetatype.html#registerConverter), otherwise the
|
||||
promise is rejected with [`QPromiseConversionException`](../exceptions/conversion.md):
|
||||
|
||||
```cpp
|
||||
struct Foo {};
|
||||
Q_DECLARE_METATYPE(Foo);
|
||||
|
||||
QMetaType::registerConverter<Foo, QString>([](const Foo& foo) {
|
||||
return QString{...};
|
||||
});
|
||||
|
||||
QPromise<Foo> input = {...}
|
||||
|
||||
auto output = input.convert<QVariant>();
|
||||
|
||||
// output type: QPromise<QString>
|
||||
output.then([](const QString& value) {
|
||||
// value contains a result produced by the custom converter
|
||||
})
|
||||
.fail([](const QPromiseConversionException& e) {
|
||||
// QVariant was unable to convert Foo to QString
|
||||
})
|
||||
```
|
||||
|
||||
::: warning IMPORTANT
|
||||
Calling this method for `QPromise<void>` is not supported.
|
||||
:::
|
@ -10,7 +10,9 @@ title: .delay
|
||||
QPromise<T>::delay(int msec) -> QPromise<T>
|
||||
```
|
||||
|
||||
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 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.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
@ -18,3 +20,31 @@ auto output = input.delay(2000).then([](int res) {
|
||||
// called 2 seconds after `input` is fulfilled
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Since: 0.6.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::delay(std::chrono::milliseconds msec) -> QPromise<T>
|
||||
```
|
||||
|
||||
This is a convenience overload accepting [durations from the C++ Standard Library](https://en.cppreference.com/w/cpp/chrono/duration).
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.delay(std::chrono::seconds{2}).then([](int res) {
|
||||
// called 2 seconds after `input` is fulfilled
|
||||
});
|
||||
```
|
||||
|
||||
C++14 alternative:
|
||||
|
||||
```cpp
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.delay(2s).then([](int res) {
|
||||
// called 2 seconds after `input` is fulfilled
|
||||
});
|
||||
```
|
||||
|
@ -18,7 +18,9 @@ QPromise<Sequence<T>>::each(Functor functor) -> QPromise<Sequence<T>>
|
||||
This method only applies to promise with sequence value.
|
||||
:::
|
||||
|
||||
Calls the given `functor` on each element in the promise value (i.e. `Sequence<T>`), then resolves to the original sequence unmodified. If `functor` throws, `output` is rejected with the new exception.
|
||||
Calls the given `functor` on each element in the promise value (i.e. `Sequence<T>`), then resolves
|
||||
to the original sequence unmodified. If `functor` throws, `output` is rejected with the new
|
||||
exception.
|
||||
|
||||
```cpp
|
||||
QPromise<QList<QByteArray>> input = {...}
|
||||
@ -33,15 +35,17 @@ output.then([](const QList<QByteArray>& res) {
|
||||
});
|
||||
```
|
||||
|
||||
If `functor` returns a promise (or `QFuture`), the `output` promise is delayed until all the promises are resolved. If any of the promises fail, `output` immediately rejects with the error of the promise that rejected, whether or not the other promises are resolved.
|
||||
If `functor` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
promises are resolved. If any of the promises fail, `output` immediately rejects with the error
|
||||
of the promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
```cpp
|
||||
QPromise<QList<QUrl>> input = {...}
|
||||
|
||||
auto output = input.each([](const QUrl& url, ...) {
|
||||
return QPromise<void>([&](auto resolve, auto reject) {
|
||||
return QPromise<void>{[&](auto resolve, auto reject) {
|
||||
// process url asynchronously ...
|
||||
})
|
||||
}};
|
||||
});
|
||||
|
||||
// `output` resolves as soon as all promises returned by
|
||||
|
@ -10,16 +10,19 @@ title: .fail
|
||||
QPromise<T>::fail(Function onRejected) -> QPromise<T>
|
||||
```
|
||||
|
||||
Shorthand to `promise.then(nullptr, onRejected)`, similar to the [`catch` statement](http://en.cppreference.com/w/cpp/language/try_catch):
|
||||
Shorthand to [`promise.then(nullptr, onRejected)`](then.md) for handling errors in promise chains,
|
||||
similar to the native C++ [`catch` statement](http://en.cppreference.com/w/cpp/language/try_catch):
|
||||
|
||||
```cpp
|
||||
promise.fail([](const MyException&) {
|
||||
promise.fail([](const MyException& error) {
|
||||
// {...}
|
||||
}).fail(const QException&) {
|
||||
}).fail([](const QException& error) {
|
||||
// {...}
|
||||
}).fail(const std::exception&) {
|
||||
}).fail([](const std::exception& error) {
|
||||
// {...}
|
||||
}).fail() {
|
||||
}).fail([]() {
|
||||
// {...} catch-all
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`QPromise::then`](then.md)
|
||||
|
@ -33,9 +33,9 @@ QPromise<QList<QUrl>> input = {...}
|
||||
auto output = input.filter([](const QUrl& url, ...) {
|
||||
return url.isValid(); // Keep only valid URLs
|
||||
}).filter([](const QUrl& url, ...) {
|
||||
return QPromise<bool>([&](auto resolve, auto reject) {
|
||||
return QPromise<bool>{[&](auto resolve, auto reject) {
|
||||
// resolve(true) if `url` is reachable, else resolve(false)
|
||||
});
|
||||
}};
|
||||
});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
@ -48,8 +48,8 @@ output.then([](const QList<QUrl>& res) {
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
The order of the output sequence values is guarantee to be the same as the original
|
||||
sequence, regardless of completion order of the promises returned by `filterer`.
|
||||
The order of the output sequence values is guarantee to be the same as the original sequence,
|
||||
regardless of completion order of the promises returned by `filterer`.
|
||||
:::
|
||||
|
||||
See also: [`QtPromise::filter`](../helpers/filter.md)
|
||||
|
@ -10,7 +10,10 @@ title: .finally
|
||||
QPromise<T>::finally(Function handler) -> QPromise<T>
|
||||
```
|
||||
|
||||
This `handler` is **always** called, without any argument and whatever the `input` promise state (fulfilled or rejected). The `output` promise has the same type as the `input` one but also the same value or error. The finally `handler` **can not modify the fulfilled value** (the returned value is ignored), however, if `handler` throws, `output` is rejected with the new exception.
|
||||
This `handler` is **always** called, without any argument and whatever the `input` promise state
|
||||
(fulfilled or rejected). The `output` promise has the same type as the `input` one but also the
|
||||
same value or error. The finally `handler` **can not modify the fulfilled value** (the returned
|
||||
value is ignored), however, if `handler` throws, `output` is rejected with the new exception.
|
||||
|
||||
```cpp
|
||||
auto output = input.finally([]() {
|
||||
@ -18,4 +21,6 @@ auto output = input.finally([]() {
|
||||
});
|
||||
```
|
||||
|
||||
If `handler` returns a promise (or QFuture), the `output` promise is delayed until the returned promise is resolved and under the same conditions: the delayed value is ignored, the error transmitted to the `output` promise.
|
||||
If `handler` returns a promise (or QFuture), the `output` promise is delayed until the returned
|
||||
promise is resolved and under the same conditions: the delayed value is ignored, the error
|
||||
transmitted to the `output` promise.
|
||||
|
@ -11,4 +11,3 @@ QPromise<T>::isRejected() -> bool
|
||||
```
|
||||
|
||||
Returns `true` if the promise is rejected, otherwise returns `false`.
|
||||
|
||||
|
@ -19,25 +19,25 @@ This method only applies to promise with sequence value.
|
||||
:::
|
||||
|
||||
Iterates over all the promise values (i.e. `Sequence<T>`) and [maps the sequence](https://en.wikipedia.org/wiki/Map_%28higher-order_function%29)
|
||||
to another using the given `mapper` function. The type returned by `mapper` determines the type
|
||||
of the `output` promise. If `mapper` throws, `output` is rejected with the new exception.
|
||||
to another using the given `mapper` function. The type returned by `mapper` determines the type of
|
||||
the `output` promise. If `mapper` throws, `output` is rejected with the new exception.
|
||||
|
||||
If `mapper` returns a promise (or `QFuture`), the `output` promise is delayed until all the
|
||||
promises are resolved. If any of the promises fails, `output` immediately rejects with the
|
||||
error of the promise that rejected, whether or not the other promises are resolved.
|
||||
If `mapper` returns a promise (or `QFuture`), the `output` promise is delayed until all the promises
|
||||
are resolved. If any of the promises fails, `output` immediately rejects with the error of the
|
||||
promise that rejected, whether or not the other promises are resolved.
|
||||
|
||||
```cpp
|
||||
QPromise<QList<QUrl>> input = {...}
|
||||
|
||||
auto output = input.map([](const QUrl& url, int index) {
|
||||
return QPromise<QByteArray>([&](auto resolve, auto reject) {
|
||||
return QPromise<QByteArray>{[&](auto resolve, auto reject) {
|
||||
// download content at 'url' and resolve
|
||||
// {...}
|
||||
});
|
||||
}};
|
||||
}).map([](const QByteArray& value, ...) {
|
||||
// process the downloaded QByteArray
|
||||
// {...}
|
||||
return DownloadResult(value);
|
||||
return DownloadResult{value};
|
||||
});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
@ -50,8 +50,8 @@ output.then([](const QVector<DownloadResult>& res) {
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
The order of the output sequence values is guarantee to be the same as the original
|
||||
sequence, regardless of completion order of the promises returned by `mapper`.
|
||||
The order of the output sequence values is guarantee to be the same as the original sequence,
|
||||
regardless of completion order of the promises returned by `mapper`.
|
||||
:::
|
||||
|
||||
This function is provided for convenience and is similar to:
|
||||
|
61
docs/qtpromise/qpromise/reduce.md
Normal file
61
docs/qtpromise/qpromise/reduce.md
Normal file
@ -0,0 +1,61 @@
|
||||
---
|
||||
title: .reduce
|
||||
---
|
||||
|
||||
# QPromise::reduce
|
||||
|
||||
*Since: 0.5.0*
|
||||
|
||||
```cpp
|
||||
QPromise<Sequence<T>>::reduce(Reducer reducer) -> QPromise<T>
|
||||
QPromise<Sequence<T>>::reduce(Reducer reducer, R|QPromise<R> initialValue) -> QPromise<R>
|
||||
|
||||
// With:
|
||||
// - Sequence: STL compatible container (e.g. QVector, etc.)
|
||||
// - Reducer: Function(T|R accumulator, T item, int index) -> R|QPromise<R>
|
||||
```
|
||||
|
||||
::: warning IMPORTANT
|
||||
This method only applies to promise with sequence value.
|
||||
:::
|
||||
|
||||
Iterates over all the promise values (i.e. `Sequence<T>`) and [reduces the sequence](https://en.wikipedia.org/wiki/Fold_%28higher-order_function%29)
|
||||
to a single value using the given `reducer` function and an optional `initialValue`. The type
|
||||
returned by `reducer` determines the type of the `output` promise.
|
||||
|
||||
See [`QtPromise::reduce`](../helpers/reduce.md) for details, this method is provided for convenience
|
||||
and is similar to:
|
||||
|
||||
```cpp
|
||||
promise.then([](const T& values) {
|
||||
return QtPromise::reduce(values, reducer, initialValue);
|
||||
})
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```cpp
|
||||
auto input = QtPromise::resolve(QList<QUrl>{
|
||||
"file:f0.txt", // contains "foo"
|
||||
"file:f1.txt", // contains "bar"
|
||||
"file:f2.txt" // contains "42"
|
||||
});
|
||||
|
||||
// Concatenate the content of the given files, read asynchronously
|
||||
auto output = input.reduce([](const QString& acc, const QString& cur, int idx) {
|
||||
return readAsync(cur).then([=](const QString& res) {
|
||||
return QString{"%1;%2:%3"}.arg(acc).arg(idx).arg(res);
|
||||
});
|
||||
}, QString{"index:text"});
|
||||
|
||||
// 'output' resolves as soon as all promises returned by
|
||||
// 'reducer' are fulfilled or at least one is rejected.
|
||||
|
||||
// 'output' type: QPromise<QString>
|
||||
output.then([](const QString& res) {
|
||||
// res == "index:text;0:foo;1:bar;2:42"
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
See also: [`QtPromise::reduce`](../helpers/reduce.md)
|
@ -1,13 +1,13 @@
|
||||
---
|
||||
title: ::reject [static]
|
||||
title: ::reject (static)
|
||||
---
|
||||
|
||||
# QPromise::reject [static]
|
||||
# QPromise::reject (static)
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```cpp
|
||||
[static] QPromise<T>::reject(any reason) -> QPromise<T>
|
||||
(static) QPromise<T>::reject(any reason) -> QPromise<T>
|
||||
```
|
||||
|
||||
Creates a `QPromise<T>` that is rejected with the given `reason` of *whatever type*:
|
||||
@ -16,11 +16,11 @@ Creates a `QPromise<T>` that is rejected with the given `reason` of *whatever ty
|
||||
QPromise<int> compute(const QString& type)
|
||||
{
|
||||
if (type == "foobar") {
|
||||
return QPromise<int>::reject(QString("Unknown type: %1").arg(type));
|
||||
return QPromise<int>::reject(QString{"Unknown type: %1"}.arg(type));
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
return QPromise<int>{[](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}};
|
||||
}
|
||||
```
|
||||
|
@ -1,13 +1,13 @@
|
||||
---
|
||||
title: ::resolve [static]
|
||||
title: ::resolve (static)
|
||||
---
|
||||
|
||||
# QPromise::resolve [static]
|
||||
# QPromise::resolve (static)
|
||||
|
||||
*Since: 0.1.0*
|
||||
|
||||
```
|
||||
[static] QPromise<T>::resolve(T value) -> QPromise<T>
|
||||
```cpp
|
||||
(static) QPromise<T>::resolve(T value) -> QPromise<T>
|
||||
```
|
||||
|
||||
Creates a `QPromise<T>` that is fulfilled with the given `value` of type `T`:
|
||||
@ -19,10 +19,10 @@ QPromise<int> compute(const QString& type)
|
||||
return QPromise<int>::resolve(42);
|
||||
}
|
||||
|
||||
return QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
return QPromise<int>{[](const QPromiseResolve<int>& resolve) {
|
||||
// {...}
|
||||
});
|
||||
}};
|
||||
}
|
||||
```
|
||||
|
||||
See also: [`qPromise`](../helpers/qpromise.md)
|
||||
See also: [`QtPromise::resolve`](../helpers/resolve.md)
|
||||
|
@ -10,7 +10,10 @@ title: .tap
|
||||
QPromise<T>::tap(Function handler) -> QPromise<T>
|
||||
```
|
||||
|
||||
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`](finally.md), this handler is **not** called for rejections.
|
||||
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`](finally.md), this handler is **not** called for rejections.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
@ -21,4 +24,6 @@ auto output = input.tap([](int res) {
|
||||
});
|
||||
```
|
||||
|
||||
If `handler` returns a promise (or QFuture), the `output` promise is delayed until the returned promise is resolved and under the same conditions: the delayed value is ignored, the error transmitted to the `output` promise.
|
||||
If `handler` returns a promise (or QFuture), the `output` promise is delayed until the returned
|
||||
promise is resolved and under the same conditions: the delayed value is ignored, the error
|
||||
transmitted to the `output` promise.
|
||||
|
@ -10,18 +10,23 @@ title: .tapFail
|
||||
QPromise<T>::tapFail(Function handler) -> QPromise<T>
|
||||
```
|
||||
|
||||
This `handler` allows to observe errors of the `input` promise without handling them - similar to [`finally`](finally.md) but **only** called on rejections. The `output` promise has the same type as the `input` one but also the same value or error. However, if `handler` throws, `output` is rejected with the new exception.
|
||||
This `handler` allows to observe errors of the `input` promise without handling them - similar to
|
||||
[`finally`](finally.md) but **only** called on rejections. The `output` promise has the same type
|
||||
as the `input` one but also the same value or error. However, if `handler` throws, `output` is
|
||||
rejected with the new exception.
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.tapFail([](Error err) {
|
||||
log(err);
|
||||
auto output = input.tapFail([](const Error& error) {
|
||||
log(error);
|
||||
}).then([](int res) {
|
||||
return process(res);
|
||||
}).fail([](Error err) {
|
||||
handle(err);
|
||||
}).fail([](const Error& error) {
|
||||
handle(error);
|
||||
return -1;
|
||||
});
|
||||
```
|
||||
|
||||
If `handler` returns a promise (or QFuture), the `output` promise is delayed until the returned promise is resolved and under the same conditions: the delayed value is ignored, the error transmitted to the `output` promise.
|
||||
If `handler` returns a promise (or QFuture), the `output` promise is delayed until the returned
|
||||
promise is resolved and under the same conditions: the delayed value is ignored, the error
|
||||
transmitted to the `output` promise.
|
||||
|
@ -24,9 +24,8 @@ auto output = input.then([](int res) {
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
`onRejected` handler is optional, in which case `output` will be rejected with
|
||||
the same reason as `input`. Also note that it's recommended to use the
|
||||
[`fail`](fail.md) shorthand to handle errors.
|
||||
`onRejected` handler is optional, in which case `output` will be rejected with the same reason as
|
||||
`input`. Also note that it's recommended to use the [`fail`](fail.md) shorthand to handle errors.
|
||||
:::
|
||||
|
||||
The type `<R>` of the `output` promise depends on the return type of the `onFulfilled` handler:
|
||||
@ -44,9 +43,9 @@ output.then([](const QString& res) {
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
Only `onFulfilled` can change the promise type, `onRejected` **must** return the
|
||||
same type as `onFulfilled`. That also means if `onFulfilled` is `nullptr`,
|
||||
`onRejected` must return the same type as the `input` promise.
|
||||
Only `onFulfilled` can change the promise type, `onRejected` **must** return the same type as
|
||||
`onFulfilled`. That also means if `onFulfilled` is `nullptr`, `onRejected` must return the same
|
||||
type as the `input` promise.
|
||||
:::
|
||||
|
||||
```cpp
|
||||
@ -96,4 +95,5 @@ auto output = input.then([](int res) {
|
||||
// output.isRejected() is true
|
||||
```
|
||||
|
||||
If an handler returns a promise (or QFuture), the `output` promise is delayed and will be resolved by the returned promise.
|
||||
If a handler returns a promise (or QFuture), the `output` promise is delayed and will be resolved
|
||||
by the returned promise.
|
||||
|
@ -10,7 +10,10 @@ title: .timeout
|
||||
QPromise<T>::timeout(int msec, any error = QPromiseTimeoutException) -> QPromise<T>
|
||||
```
|
||||
|
||||
This method returns a promise that will be resolved with the `input` promise's fulfillment value or rejection reason. However, if the `input` promise is not fulfilled or rejected within `msec` milliseconds, the `output` promise is rejected with `error` as the reason (`QPromiseTimeoutException` by default).
|
||||
This method returns a promise that will be resolved with the `input` promise's fulfillment value
|
||||
or rejection reason. However, if the `input` promise is not fulfilled or rejected within `msec`
|
||||
milliseconds, the `output` promise is rejected with `error` as the reason ([`QPromiseTimeoutException`](../exceptions/timeout.md)
|
||||
by default).
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
@ -18,7 +21,43 @@ auto output = input.timeout(2000)
|
||||
.then([](int res) {
|
||||
// operation succeeded within 2 seconds
|
||||
})
|
||||
.fail([](const QPromiseTimeoutException& e) {
|
||||
.fail([](const QPromiseTimeoutException& error) {
|
||||
// operation timed out!
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Since: 0.6.0*
|
||||
|
||||
```cpp
|
||||
QPromise<T>::timeout(std::chrono::milliseconds msec, any error = QPromiseTimeoutException) -> QPromise<T>
|
||||
```
|
||||
|
||||
This is a convenience overload accepting [durations from the C++ Standard Library](https://en.cppreference.com/w/cpp/chrono/duration).
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.timeout(std::chrono::seconds{2})
|
||||
.then([](int res) {
|
||||
// operation succeeded within 2 seconds
|
||||
})
|
||||
.fail([](const QPromiseTimeoutException& error) {
|
||||
// operation timed out!
|
||||
});
|
||||
```
|
||||
|
||||
C++14 alternative:
|
||||
|
||||
```cpp
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
QPromise<int> input = {...}
|
||||
auto output = input.timeout(2s)
|
||||
.then([](int res) {
|
||||
// operation succeeded within 2 seconds
|
||||
})
|
||||
.fail([](const QPromiseTimeoutException& error) {
|
||||
// operation timed out!
|
||||
});
|
||||
```
|
||||
|
@ -10,12 +10,13 @@ title: .wait
|
||||
QPromise<T>::wait() -> QPromise<T>
|
||||
```
|
||||
|
||||
This method holds the execution of the remaining code until the `input` promise is resolved (either fulfilled or rejected), **without** blocking the event loop of the current thread:
|
||||
This method holds the execution of the remaining code until the `input` promise is resolved (either
|
||||
fulfilled or rejected), **without** blocking the event loop of the current thread:
|
||||
|
||||
```cpp
|
||||
int result = -1;
|
||||
|
||||
QPromise<int> input = qPromise(QtConcurrent::run([]() {
|
||||
QPromise<int> input = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
return 42;
|
||||
})).tap([&](int res) {
|
||||
result = res;
|
||||
|
@ -1,10 +1,12 @@
|
||||
# QtConcurrent
|
||||
# Qt Concurrent
|
||||
|
||||
QtPromise integrates with [QtConcurrent](https://doc.qt.io/qt-5/qtconcurrent-index.html) to make easy chaining QFuture with QPromise.
|
||||
QtPromise integrates with [QtConcurrent](https://doc.qt.io/qt-5/qtconcurrent-index.html) to simplify
|
||||
chaining QFuture with QPromise.
|
||||
|
||||
## <a name="qtconcurrent-convert"></a> Convert
|
||||
## Convert
|
||||
|
||||
Converting `QFuture<T>` to `QPromise<T>` is done using the [`qPromise`](helpers/qpromise.md) helper:
|
||||
Converting `QFuture<T>` to `QPromise<T>` is done using the [`QtPromise::resolve`](helpers/resolve.md)
|
||||
helper:
|
||||
|
||||
```cpp
|
||||
QFuture<int> future = QtConcurrent::run([]() {
|
||||
@ -12,27 +14,28 @@ QFuture<int> future = QtConcurrent::run([]() {
|
||||
return 42;
|
||||
});
|
||||
|
||||
QPromise<int> promise = qPromise(future);
|
||||
QPromise<int> promise = QtPromise::resolve(future);
|
||||
```
|
||||
|
||||
or simply:
|
||||
|
||||
```cpp
|
||||
auto promise = qPromise(QtConcurrent::run([]() {
|
||||
auto promise = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
// {...}
|
||||
}));
|
||||
```
|
||||
|
||||
## Chain
|
||||
|
||||
Returning a `QFuture<T>` in [`then`](qpromise/then.md) or [`fail`](qpromise/fail.md) automatically translate to `QPromise<T>`:
|
||||
Returning a `QFuture<T>` in [`then`](qpromise/then.md) or [`fail`](qpromise/fail.md) automatically
|
||||
translate to `QPromise<T>`:
|
||||
|
||||
```cpp
|
||||
QPromise<int> input = ...
|
||||
auto output = input.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
return QString("42");
|
||||
return QString{"42"};
|
||||
});
|
||||
});
|
||||
|
||||
@ -46,21 +49,40 @@ The `output` promise is resolved when the `QFuture` is [finished](https://doc.qt
|
||||
|
||||
## Error
|
||||
|
||||
Exceptions thrown from a QtConcurrent thread reject the associated promise with the exception as the reason. Note that if you throw an exception that is not a subclass of `QException`, the promise with be rejected with [`QUnhandledException`](https://doc.qt.io/qt-5/qunhandledexception.html#details) (this restriction only applies to exceptions thrown from a QtConcurrent thread, [read more](https://doc.qt.io/qt-5/qexception.html#details)).
|
||||
Exceptions thrown from a QtConcurrent thread reject the associated promise with the exception as the
|
||||
reason. For this to work, the exception should be a [`QException`](https://doc.qt.io/qt-5/qexception.html)
|
||||
or its subclass. Correct subclassing of [`QException`](https://doc.qt.io/qt-5/qexception.html)
|
||||
includes overriding its methods [`clone()`](https://doc.qt.io/qt-5/qexception.html#clone) and
|
||||
[`raise()`](https://doc.qt.io/qt-5/qexception.html#raise). Without these overrides the promise will
|
||||
be rejected with [`QException`](https://doc.qt.io/qt-5/qexception.html).
|
||||
|
||||
Note that if you throw an exception that is not a subclass of `QException`, the promise will
|
||||
be rejected with [`QUnhandledException`](https://doc.qt.io/qt-5/qunhandledexception.html#details)
|
||||
(this restriction only applies to exceptions thrown from a QtConcurrent thread,
|
||||
[read more](https://doc.qt.io/qt-5/qexception.html#details)).
|
||||
|
||||
```cpp
|
||||
class CustomException : public QException
|
||||
{
|
||||
public:
|
||||
void raise() const override { throw *this; }
|
||||
CustomException* clone() const override { return new CustomException{*this}; }
|
||||
};
|
||||
|
||||
// {...}
|
||||
|
||||
QPromise<int> promise = ...
|
||||
promise.then([](int res) {
|
||||
return QtConcurrent::run([]() {
|
||||
// {...}
|
||||
|
||||
if (!success) {
|
||||
throw CustomException();
|
||||
throw CustomException{};
|
||||
}
|
||||
|
||||
return QString("42");
|
||||
return QString{"42"};
|
||||
});
|
||||
}).fail(const CustomException& err) {
|
||||
}).fail([](const CustomException& error) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
96
docs/qtpromise/qtsignals.md
Normal file
96
docs/qtpromise/qtsignals.md
Normal file
@ -0,0 +1,96 @@
|
||||
# Qt Signals
|
||||
|
||||
QtPromise supports creating promises that are resolved or rejected by regular [Qt signals](https://doc.qt.io/qt-5/signalsandslots.html).
|
||||
|
||||
::: warning IMPORTANT
|
||||
A promise connected to a signal will be resolved (fulfilled or rejected) **only one time**, no
|
||||
matter if the signals are emitted multiple times. Internally, the promise is disconnected from
|
||||
all signals as soon as one signal is emitted.
|
||||
:::
|
||||
|
||||
## Resolve Signal
|
||||
|
||||
The [`QtPromise::connect()`](helpers/connect.md) helper allows to create a promise resolved from
|
||||
a single signal:
|
||||
|
||||
```cpp
|
||||
// [signal] Object::finished(const QByteArray&)
|
||||
auto output = QtPromise::connect(obj, &Object::finished);
|
||||
|
||||
// output type: QPromise<QByteArray>
|
||||
output.then([](const QByteArray& data) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
If the signal doesn't provide any argument, a `QPromise<void>` is returned:
|
||||
|
||||
```cpp
|
||||
// [signal] Object::done()
|
||||
auto output = QtPromise::connect(obj, &Object::done);
|
||||
|
||||
// output type: QPromise<void>
|
||||
output.then([]() {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
::: tip NOTE
|
||||
QtPromise currently only supports single argument signals, which means that only the first argument
|
||||
is used to fulfill or reject the connected promise, other arguments being ignored.
|
||||
:::
|
||||
|
||||
## Reject Signal
|
||||
|
||||
The [`QtPromise::connect()`](helpers/connect.md) helper also allows to reject the promise from
|
||||
another signal:
|
||||
|
||||
```cpp
|
||||
// [signal] Object::finished(const QByteArray& data)
|
||||
// [signal] Object::error(ObjectError error)
|
||||
auto output = QtPromise::connect(obj, &Object::finished, &Object::error);
|
||||
|
||||
// output type: QPromise<QByteArray>
|
||||
output.then([](const QByteArray& data) {
|
||||
// {...}
|
||||
}).fail([](const ObjectError& error) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
If the rejection signal doesn't provide any argument, the promise will be rejected with
|
||||
[`QPromiseUndefinedException`](exceptions/undefined.md), for example:
|
||||
|
||||
```cpp
|
||||
// [signal] Object::finished()
|
||||
// [signal] Object::error()
|
||||
auto output = QtPromise::connect(obj, &Object::finished, &Object::error);
|
||||
|
||||
// output type: QPromise<QByteArray>
|
||||
output.then([]() {
|
||||
// {...}
|
||||
}).fail([](const QPromiseUndefinedException& error) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
A third variant allows to connect the resolve and reject signals from different objects:
|
||||
|
||||
```cpp
|
||||
// [signal] ObjectA::finished(const QByteArray& data)
|
||||
// [signal] ObjectB::error(ObjectBError error)
|
||||
auto output = QtPromise::connect(objA, &ObjectA::finished, objB, &ObjectB::error);
|
||||
|
||||
// output type: QPromise<QByteArray>
|
||||
output.then([](const QByteArray& data) {
|
||||
// {...}
|
||||
}).fail([](const ObjectBError& error) {
|
||||
// {...}
|
||||
});
|
||||
```
|
||||
|
||||
Additionally to the rejection signal, promises created using [`QtPromise::connect()`](helpers/connect.md)
|
||||
are automatically rejected with [`QPromiseContextException`](exceptions/context.md) if the sender is
|
||||
destroyed before fulfilling the promise.
|
||||
|
||||
See [`QtPromise::connect()`](helpers/connect.md) for more details.
|
@ -1,7 +1,12 @@
|
||||
# Thread-Safety
|
||||
|
||||
QPromise is thread-safe and can be copied and accessed across different threads. QPromise relies on [explicitly data sharing](https://doc.qt.io/qt-5/qexplicitlyshareddatapointer.html#details) and thus `auto p2 = p1` represents the same promise: when `p1` resolves, handlers registered on `p1` and `p2` are called, the fulfilled value being shared between both instances.
|
||||
QPromise is thread-safe and can be copied and accessed across different threads. QPromise relies on
|
||||
[explicitly data sharing](https://doc.qt.io/qt-5/qexplicitlyshareddatapointer.html#details) and thus
|
||||
`auto p2 = p1` represents the same promise: when `p1` resolves, handlers registered on `p1` and `p2`
|
||||
are called, the fulfilled value being shared between both instances.
|
||||
|
||||
::: warning IMPORTANT
|
||||
While it's safe to access the resolved value from different threads using [`then`](qpromise/then.md), QPromise provides no guarantee about the object being pointed to. Thread-safety and reentrancy rules for that object still apply.
|
||||
While it's safe to access the resolved value from different threads using [`then`](qpromise/then.md),
|
||||
QPromise provides no guarantee about the object being pointed to. Thread-safety and reentrancy rules
|
||||
for that object still apply.
|
||||
:::
|
||||
|
@ -1,8 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_MODULE_H
|
||||
#define QTPROMISE_MODULE_H
|
||||
|
||||
#include "../src/qtpromise/qpromise.h"
|
||||
#include "../src/qtpromise/qpromiseconnections.h"
|
||||
#include "../src/qtpromise/qpromisefuture.h"
|
||||
#include "../src/qtpromise/qpromisehelpers.h"
|
||||
|
||||
#endif // ifndef QTPROMISE_MODULE_H
|
||||
#endif // QTPROMISE_MODULE_H
|
||||
|
34975
package-lock.json
generated
Normal file
34975
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
package.json
Normal file
18
package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"description": "qtpromise",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docs:build": "vuepress build docs",
|
||||
"docs:dev": "vuepress dev docs",
|
||||
"docs:lint": "remark --quiet --frail ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vuepress/plugin-google-analytics": "^1.9.9",
|
||||
"remark-cli": "^11.0.0",
|
||||
"remark-frontmatter": "^4.0.1",
|
||||
"remark-preset-lint-markdown-style-guide": "^5.1.2",
|
||||
"remark-preset-lint-recommended": "^6.1.2",
|
||||
"remark-validate-links": "^12.1.0",
|
||||
"vuepress": "^1.9.9"
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
include(../../qtpromise.pri)
|
2
qpm.json
2
qpm.json
@ -10,7 +10,7 @@
|
||||
"url": "https://github.com/simonbrunel/qtpromise.git"
|
||||
},
|
||||
"version": {
|
||||
"label": "0.4.0"
|
||||
"label": "0.7.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"pri_filename": "qtpromise.pri",
|
||||
|
@ -1,12 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = \
|
||||
tests
|
||||
|
||||
_qt_creator_ {
|
||||
SUBDIRS += src
|
||||
}
|
||||
|
||||
OTHER_FILES = \
|
||||
package/features/*.prf \
|
||||
include/* \
|
||||
qtpromise.pri
|
@ -1,38 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISE_H
|
||||
#define QTPROMISE_QPROMISE_H
|
||||
|
||||
// QtPromise
|
||||
#include "qpromiseexceptions.h"
|
||||
#include "qpromise_p.h"
|
||||
#include "qpromiseerror.h"
|
||||
#include "qpromiseglobal.h"
|
||||
#include "qpromiseresolver.h"
|
||||
|
||||
// Qt
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include <QtCore/QExplicitlySharedDataPointer>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class QPromiseBase
|
||||
{
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type = 0>
|
||||
template<typename F,
|
||||
typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type = 0>
|
||||
inline QPromiseBase(F resolver);
|
||||
|
||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
|
||||
template<typename F,
|
||||
typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type = 0>
|
||||
inline QPromiseBase(F resolver);
|
||||
|
||||
QPromiseBase(const QPromiseBase<T>& other): m_d(other.m_d) {}
|
||||
QPromiseBase(const QPromise<T>& other): m_d(other.m_d) {}
|
||||
QPromiseBase(const QPromiseBase<T>& other) : m_d{other.m_d} { }
|
||||
QPromiseBase(const QPromise<T>& other) : m_d{other.m_d} { }
|
||||
QPromiseBase(QPromiseBase<T>&& other) Q_DECL_NOEXCEPT { swap(other); }
|
||||
|
||||
virtual ~QPromiseBase() { }
|
||||
|
||||
QPromiseBase<T>& operator=(const QPromiseBase<T>& other) { m_d = other.m_d; return *this;}
|
||||
QPromiseBase<T>& operator=(const QPromiseBase<T>& other)
|
||||
{
|
||||
m_d = other.m_d;
|
||||
return *this;
|
||||
}
|
||||
QPromiseBase<T>& operator=(QPromiseBase<T>&& other) Q_DECL_NOEXCEPT
|
||||
{ QPromiseBase<T>(std::move(other)).swap(*this); return *this; }
|
||||
{
|
||||
QPromiseBase<T>(std::move(other)).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const QPromiseBase<T>& other) const { return (m_d == other.m_d); }
|
||||
bool operator!=(const QPromiseBase<T>& other) const { return (m_d != other.m_d); }
|
||||
@ -43,64 +59,82 @@ public:
|
||||
bool isRejected() const { return m_d->isRejected(); }
|
||||
bool isPending() const { return m_d->isPending(); }
|
||||
|
||||
template <typename TFulfilled, typename TRejected>
|
||||
template<typename TFulfilled, typename TRejected>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||
then(const TFulfilled& fulfilled, const TRejected& rejected) const;
|
||||
|
||||
template <typename TFulfilled>
|
||||
template<typename TFulfilled>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||
then(TFulfilled&& fulfilled) const;
|
||||
|
||||
template <typename TRejected>
|
||||
template<typename TRejected>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
||||
fail(TRejected&& rejected) const;
|
||||
|
||||
template <typename THandler>
|
||||
template<typename THandler>
|
||||
inline QPromise<T> finally(THandler handler) const;
|
||||
|
||||
template <typename THandler>
|
||||
template<typename THandler>
|
||||
inline QPromise<T> tap(THandler handler) const;
|
||||
|
||||
template <typename THandler>
|
||||
template<typename THandler>
|
||||
inline QPromise<T> tapFail(THandler handler) const;
|
||||
|
||||
template <typename E = QPromiseTimeoutException>
|
||||
inline QPromise<T> timeout(int msec, E&& error = E()) const;
|
||||
template<typename E = QPromiseTimeoutException>
|
||||
inline QPromise<T> timeout(int msec, E&& error = E{}) const;
|
||||
|
||||
template<typename E = QPromiseTimeoutException>
|
||||
inline QPromise<T> timeout(std::chrono::milliseconds msec, E&& error = E{}) const;
|
||||
|
||||
inline QPromise<T> delay(int msec) const;
|
||||
inline QPromise<T> delay(std::chrono::milliseconds msec) const;
|
||||
|
||||
inline QPromise<T> wait() const;
|
||||
|
||||
public: // STATIC
|
||||
template <typename E>
|
||||
template<typename E>
|
||||
inline static QPromise<T> reject(E&& error);
|
||||
|
||||
protected:
|
||||
friend struct QtPromisePrivate::PromiseFulfill<QPromise<T>>;
|
||||
friend class QtPromisePrivate::PromiseResolver<T>;
|
||||
friend struct QtPromisePrivate::PromiseInspect;
|
||||
|
||||
QExplicitlySharedDataPointer<QtPromisePrivate::PromiseData<T>> m_d;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class QPromise : public QPromiseBase<T>
|
||||
{
|
||||
public:
|
||||
template <typename F>
|
||||
QPromise(F&& resolver): QPromiseBase<T>(std::forward<F>(resolver)) { }
|
||||
template<typename F>
|
||||
QPromise(F&& resolver) : QPromiseBase<T>(std::forward<F>(resolver))
|
||||
{ }
|
||||
|
||||
template <typename Functor>
|
||||
template<typename U>
|
||||
inline QPromise<U> convert() const;
|
||||
|
||||
template<typename Functor>
|
||||
inline QPromise<T> each(Functor fn);
|
||||
|
||||
template <typename Functor>
|
||||
template<typename Functor>
|
||||
inline QPromise<T> filter(Functor fn);
|
||||
|
||||
template <typename Functor>
|
||||
inline typename QtPromisePrivate::PromiseMapper<T, Functor>::PromiseType
|
||||
map(Functor fn);
|
||||
template<typename Functor>
|
||||
inline typename QtPromisePrivate::PromiseMapper<T, Functor>::PromiseType map(Functor fn);
|
||||
|
||||
template<typename Functor, typename Input>
|
||||
inline typename QtPromisePrivate::PromiseDeduce<Input>::Type reduce(Functor fn, Input initial);
|
||||
|
||||
template<typename Functor, typename U = T>
|
||||
inline typename QtPromisePrivate::PromiseDeduce<typename U::value_type>::Type
|
||||
reduce(Functor fn);
|
||||
|
||||
public: // STATIC
|
||||
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
|
||||
inline static QPromise<QVector<T>> all(const Sequence<QPromise<T>, Args...>& promises);
|
||||
// DEPRECATED (remove at version 1)
|
||||
template<template<typename, typename...> class Sequence = QVector, typename... Args>
|
||||
Q_DECL_DEPRECATED_X("Use QtPromise::all instead")
|
||||
static inline QPromise<QVector<T>> all(const Sequence<QPromise<T>, Args...>& promises);
|
||||
|
||||
inline static QPromise<T> resolve(const T& value);
|
||||
inline static QPromise<T> resolve(T&& value);
|
||||
@ -109,16 +143,19 @@ private:
|
||||
friend class QPromiseBase<T>;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
class QPromise<void> : public QPromiseBase<void>
|
||||
{
|
||||
public:
|
||||
template <typename F>
|
||||
QPromise(F&& resolver): QPromiseBase<void>(std::forward<F>(resolver)) { }
|
||||
template<typename F>
|
||||
QPromise(F&& resolver) : QPromiseBase<void>(std::forward<F>(resolver))
|
||||
{ }
|
||||
|
||||
public: // STATIC
|
||||
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
|
||||
inline static QPromise<void> all(const Sequence<QPromise<void>, Args...>& promises);
|
||||
// DEPRECATED (remove at version 1)
|
||||
template<template<typename, typename...> class Sequence = QVector, typename... Args>
|
||||
Q_DECL_DEPRECATED_X("Use QtPromise::all instead")
|
||||
static inline QPromise<void> all(const Sequence<QPromise<void>, Args...>& promises);
|
||||
|
||||
inline static QPromise<void> resolve();
|
||||
|
||||
@ -130,4 +167,4 @@ private:
|
||||
|
||||
#include "qpromise.inl"
|
||||
|
||||
#endif // ifndef QTPROMISE_QPROMISE_H
|
||||
#endif // QTPROMISE_QPROMISE_H
|
||||
|
@ -1,19 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "qpromise.h"
|
||||
#include "qpromisehelpers.h"
|
||||
|
||||
// Qt
|
||||
#include <QCoreApplication>
|
||||
#include <QSharedPointer>
|
||||
#include <QTimer>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QTimer>
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <typename T>
|
||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
|
||||
inline QPromiseBase<T>::QPromiseBase(F callback)
|
||||
: m_d(new QtPromisePrivate::PromiseData<T>())
|
||||
template<typename T>
|
||||
template<typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count == 1, int>::type>
|
||||
inline QPromiseBase<T>::QPromiseBase(F callback) : m_d{new QtPromisePrivate::PromiseData<T>{}}
|
||||
{
|
||||
QtPromisePrivate::PromiseResolver<T> resolver(*this);
|
||||
QtPromisePrivate::PromiseResolver<T> resolver{*this};
|
||||
|
||||
try {
|
||||
callback(QPromiseResolve<T>(resolver));
|
||||
@ -22,12 +27,18 @@ inline QPromiseBase<T>::QPromiseBase(F callback)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type>
|
||||
inline QPromiseBase<T>::QPromiseBase(F callback)
|
||||
: m_d(new QtPromisePrivate::PromiseData<T>())
|
||||
template<typename T>
|
||||
template<typename F, typename std::enable_if<QtPromisePrivate::ArgsOf<F>::count != 1, int>::type>
|
||||
inline QPromiseBase<T>::QPromiseBase(F callback) : m_d{new QtPromisePrivate::PromiseData<T>{}}
|
||||
{
|
||||
QtPromisePrivate::PromiseResolver<T> resolver(*this);
|
||||
// To prevent infinite recursion at runtime when resolving the QPromise template
|
||||
// constructor, we don't explicitly check for ArgsOf<F>::count == 2 so that this
|
||||
// method is called for ALL callbacks other than the ones with a single typed
|
||||
// argument. This includes valid callbacks such as with two args, variadic or
|
||||
// auto args (c++14) but also invalid callbacks which are not functions or with
|
||||
// 0 or more than 2 arguments, in which case this method MUST fail to compile.
|
||||
|
||||
QtPromisePrivate::PromiseResolver<T> resolver{*this};
|
||||
|
||||
try {
|
||||
callback(QPromiseResolve<T>(resolver), QPromiseReject<T>(resolver));
|
||||
@ -36,17 +47,16 @@ inline QPromiseBase<T>::QPromiseBase(F callback)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename TFulfilled, typename TRejected>
|
||||
template<typename T>
|
||||
template<typename TFulfilled, typename TRejected>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||
QPromiseBase<T>::then(const TFulfilled& fulfilled, const TRejected& rejected) const
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using PromiseType = typename PromiseHandler<T, TFulfilled>::Promise;
|
||||
|
||||
PromiseType next([&](
|
||||
const QPromiseResolve<typename PromiseType::Type>& resolve,
|
||||
const QPromiseReject<typename PromiseType::Type>& reject) {
|
||||
PromiseType next([&](const QPromiseResolve<typename PromiseType::Type>& resolve,
|
||||
const QPromiseReject<typename PromiseType::Type>& reject) {
|
||||
m_d->addHandler(PromiseHandler<T, TFulfilled>::create(fulfilled, resolve, reject));
|
||||
m_d->addCatcher(PromiseCatcher<T, TRejected>::create(rejected, resolve, reject));
|
||||
});
|
||||
@ -58,24 +68,24 @@ QPromiseBase<T>::then(const TFulfilled& fulfilled, const TRejected& rejected) co
|
||||
return next;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename TFulfilled>
|
||||
template<typename T>
|
||||
template<typename TFulfilled>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, TFulfilled>::Promise
|
||||
QPromiseBase<T>::then(TFulfilled&& fulfilled) const
|
||||
{
|
||||
return then(std::forward<TFulfilled>(fulfilled), nullptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename TRejected>
|
||||
template<typename T>
|
||||
template<typename TRejected>
|
||||
inline typename QtPromisePrivate::PromiseHandler<T, std::nullptr_t>::Promise
|
||||
QPromiseBase<T>::fail(TRejected&& rejected) const
|
||||
{
|
||||
return then(nullptr, std::forward<TRejected>(rejected));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename THandler>
|
||||
template<typename T>
|
||||
template<typename THandler>
|
||||
inline QPromise<T> QPromiseBase<T>::finally(THandler handler) const
|
||||
{
|
||||
QPromise<T> p = *this;
|
||||
@ -84,8 +94,8 @@ inline QPromise<T> QPromiseBase<T>::finally(THandler handler) const
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename THandler>
|
||||
template<typename T>
|
||||
template<typename THandler>
|
||||
inline QPromise<T> QPromiseBase<T>::tap(THandler handler) const
|
||||
{
|
||||
QPromise<T> p = *this;
|
||||
@ -94,25 +104,22 @@ inline QPromise<T> QPromiseBase<T>::tap(THandler handler) const
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename THandler>
|
||||
template<typename T>
|
||||
template<typename THandler>
|
||||
inline QPromise<T> QPromiseBase<T>::tapFail(THandler handler) const
|
||||
{
|
||||
QPromise<T> p = *this;
|
||||
return p.then([](){}, handler).then([=]() {
|
||||
return p.then([]() {}, handler).then([=]() {
|
||||
return p;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename E>
|
||||
template<typename T>
|
||||
template<typename E>
|
||||
inline QPromise<T> QPromiseBase<T>::timeout(int msec, E&& error) const
|
||||
{
|
||||
QPromise<T> p = *this;
|
||||
return QPromise<T>([&](
|
||||
const QPromiseResolve<T>& resolve,
|
||||
const QPromiseReject<T>& reject) {
|
||||
|
||||
return QPromise<T>{[&](const QPromiseResolve<T>& resolve, const QPromiseReject<T>& reject) {
|
||||
QTimer::singleShot(msec, [=]() {
|
||||
// we don't need to verify the current promise state, reject()
|
||||
// takes care of checking if the promise is already resolved,
|
||||
@ -121,20 +128,33 @@ inline QPromise<T> QPromiseBase<T>::timeout(int msec, E&& error) const
|
||||
});
|
||||
|
||||
QtPromisePrivate::PromiseFulfill<QPromise<T>>::call(p, resolve, reject);
|
||||
});
|
||||
}};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
template<typename E>
|
||||
inline QPromise<T> QPromiseBase<T>::timeout(std::chrono::milliseconds msec, E&& error) const
|
||||
{
|
||||
return timeout(static_cast<int>(msec.count()), std::forward<E>(error));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QPromise<T> QPromiseBase<T>::delay(int msec) const
|
||||
{
|
||||
return tap([=]() {
|
||||
return QPromise<void>([&](const QPromiseResolve<void>& resolve) {
|
||||
return QPromise<void>{[&](const QPromiseResolve<void>& resolve) {
|
||||
QTimer::singleShot(msec, resolve);
|
||||
});
|
||||
}};
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
inline QPromise<T> QPromiseBase<T>::delay(std::chrono::milliseconds msec) const
|
||||
{
|
||||
return delay(static_cast<int>(msec.count()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QPromise<T> QPromiseBase<T>::wait() const
|
||||
{
|
||||
// @TODO wait timeout + global timeout
|
||||
@ -146,17 +166,17 @@ inline QPromise<T> QPromiseBase<T>::wait() const
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename E>
|
||||
template<typename T>
|
||||
template<typename E>
|
||||
inline QPromise<T> QPromiseBase<T>::reject(E&& error)
|
||||
{
|
||||
return QPromise<T>([&](const QPromiseResolve<T>&, const QPromiseReject<T>& reject) {
|
||||
return QPromise<T>{[&](const QPromiseResolve<T>&, const QPromiseReject<T>& reject) {
|
||||
reject(std::forward<E>(error));
|
||||
});
|
||||
}};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename Functor>
|
||||
template<typename T>
|
||||
template<typename Functor>
|
||||
inline QPromise<T> QPromise<T>::each(Functor fn)
|
||||
{
|
||||
return this->tap([=](const T& values) {
|
||||
@ -164,22 +184,20 @@ inline QPromise<T> QPromise<T>::each(Functor fn)
|
||||
|
||||
std::vector<QPromise<void>> promises;
|
||||
for (const auto& v : values) {
|
||||
promises.push_back(
|
||||
QtPromise::attempt(fn, v, i)
|
||||
.then([]() {
|
||||
// Cast to void in case fn returns a non promise value.
|
||||
// TODO remove when implicit cast is implemented.
|
||||
}));
|
||||
promises.push_back(QtPromise::attempt(fn, v, i).then([]() {
|
||||
// Cast to void in case fn returns a non promise value.
|
||||
// TODO remove when implicit cast is implemented.
|
||||
}));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return QPromise<void>::all(promises);
|
||||
return QtPromise::all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename Functor>
|
||||
template<typename T>
|
||||
template<typename Functor>
|
||||
inline QPromise<T> QPromise<T>::filter(Functor fn)
|
||||
{
|
||||
return this->then([=](const T& values) {
|
||||
@ -187,8 +205,8 @@ inline QPromise<T> QPromise<T>::filter(Functor fn)
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename Functor>
|
||||
template<typename T>
|
||||
template<typename Functor>
|
||||
inline typename QtPromisePrivate::PromiseMapper<T, Functor>::PromiseType
|
||||
QPromise<T>::map(Functor fn)
|
||||
{
|
||||
@ -197,91 +215,65 @@ QPromise<T>::map(Functor fn)
|
||||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <template <typename, typename...> class Sequence, typename ...Args>
|
||||
template<typename T>
|
||||
template<typename Functor, typename Input>
|
||||
inline typename QtPromisePrivate::PromiseDeduce<Input>::Type
|
||||
QPromise<T>::reduce(Functor fn, Input initial)
|
||||
{
|
||||
return this->then([=](const T& values) {
|
||||
return QtPromise::reduce(values, fn, initial);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename Functor, typename U>
|
||||
inline typename QtPromisePrivate::PromiseDeduce<typename U::value_type>::Type
|
||||
QPromise<T>::reduce(Functor fn)
|
||||
{
|
||||
return this->then([=](const T& values) {
|
||||
return QtPromise::reduce(values, fn);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<template<typename, typename...> class Sequence, typename... Args>
|
||||
inline QPromise<QVector<T>> QPromise<T>::all(const Sequence<QPromise<T>, Args...>& promises)
|
||||
{
|
||||
const int count = static_cast<int>(promises.size());
|
||||
if (count == 0) {
|
||||
return QPromise<QVector<T>>::resolve({});
|
||||
}
|
||||
|
||||
return QPromise<QVector<T>>([=](
|
||||
const QPromiseResolve<QVector<T>>& resolve,
|
||||
const QPromiseReject<QVector<T>>& reject) {
|
||||
|
||||
QSharedPointer<int> remaining(new int(count));
|
||||
QSharedPointer<QVector<T>> results(new QVector<T>(count));
|
||||
|
||||
int i = 0;
|
||||
for (const auto& promise: promises) {
|
||||
promise.then([=](const T& res) mutable {
|
||||
(*results)[i] = res;
|
||||
if (--(*remaining) == 0) {
|
||||
resolve(*results);
|
||||
}
|
||||
}, [=]() mutable {
|
||||
if (*remaining != -1) {
|
||||
*remaining = -1;
|
||||
reject(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
i++;
|
||||
}
|
||||
});
|
||||
return QtPromise::all(promises);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
template<typename U>
|
||||
inline QPromise<U> QPromise<T>::convert() const
|
||||
{
|
||||
return QPromiseBase<T>::then(QtPromisePrivate::PromiseConverter<T, U>::create());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline QPromise<T> QPromise<T>::resolve(const T& value)
|
||||
{
|
||||
return QPromise<T>([&](const QPromiseResolve<T>& resolve) {
|
||||
resolve(value);
|
||||
});
|
||||
return QPromise<T>{[&](const QPromiseResolve<T>& resolve) {
|
||||
resolve(value);
|
||||
}};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
inline QPromise<T> QPromise<T>::resolve(T&& value)
|
||||
{
|
||||
return QPromise<T>([&](const QPromiseResolve<T>& resolve) {
|
||||
resolve(std::forward<T>(value));
|
||||
});
|
||||
return QPromise<T>{[&](const QPromiseResolve<T>& resolve) {
|
||||
resolve(std::forward<T>(value));
|
||||
}};
|
||||
}
|
||||
|
||||
template <template <typename, typename...> class Sequence, typename ...Args>
|
||||
template<template<typename, typename...> class Sequence, typename... Args>
|
||||
inline QPromise<void> QPromise<void>::all(const Sequence<QPromise<void>, Args...>& promises)
|
||||
{
|
||||
const int count = static_cast<int>(promises.size());
|
||||
if (count == 0) {
|
||||
return QPromise<void>::resolve();
|
||||
}
|
||||
|
||||
return QPromise<void>([=](
|
||||
const QPromiseResolve<void>& resolve,
|
||||
const QPromiseReject<void>& reject) {
|
||||
|
||||
QSharedPointer<int> remaining(new int(count));
|
||||
|
||||
for (const auto& promise: promises) {
|
||||
promise.then([=]() {
|
||||
if (--(*remaining) == 0) {
|
||||
resolve();
|
||||
}
|
||||
}, [=]() {
|
||||
if (*remaining != -1) {
|
||||
*remaining = -1;
|
||||
reject(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return QtPromise::all(promises);
|
||||
}
|
||||
|
||||
inline QPromise<void> QPromise<void>::resolve()
|
||||
{
|
||||
return QPromise<void>([](const QPromiseResolve<void>& resolve) {
|
||||
resolve();
|
||||
});
|
||||
return QtPromise::resolve();
|
||||
}
|
||||
|
||||
} // namespace QtPromise
|
||||
|
@ -1,45 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISE_P_H
|
||||
#define QTPROMISE_QPROMISE_P_H
|
||||
|
||||
// QtPromise
|
||||
#include "qpromiseglobal.h"
|
||||
|
||||
// Qt
|
||||
#include <QCoreApplication>
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QThread>
|
||||
#include <QVector>
|
||||
#include <QReadWriteLock>
|
||||
#include <QSharedPointer>
|
||||
#include <QSharedData>
|
||||
#include <QPointer>
|
||||
#include <QtCore/QAbstractEventDispatcher>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QReadWriteLock>
|
||||
#include <QtCore/QSharedData>
|
||||
#include <QtCore/QSharedPointer>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QVector>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class QPromise;
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class QPromiseResolve;
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class QPromiseReject;
|
||||
|
||||
class QPromiseConversionException;
|
||||
|
||||
} // namespace QtPromise
|
||||
|
||||
namespace QtPromisePrivate {
|
||||
|
||||
// Use std::invoke_result for C++17 and beyond
|
||||
#if (__cplusplus >= 201703L) || defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L)
|
||||
using std::invoke_result;
|
||||
#else
|
||||
template<class F, class... ArgTypes>
|
||||
using invoke_result = std::result_of<F(ArgTypes...)>;
|
||||
#endif
|
||||
|
||||
// https://stackoverflow.com/a/21653558
|
||||
template <typename F>
|
||||
template<typename F>
|
||||
static void qtpromise_defer(F&& f, const QPointer<QThread>& thread)
|
||||
{
|
||||
using FType = typename std::decay<F>::type;
|
||||
|
||||
struct Event : public QEvent
|
||||
{
|
||||
Event(FType&& f) : QEvent(QEvent::None), m_f(std::move(f)) { }
|
||||
Event(const FType& f) : QEvent(QEvent::None), m_f(f) { }
|
||||
~Event() { m_f(); }
|
||||
Event(FType&& f) : QEvent{QEvent::None}, m_f{std::move(f)} { }
|
||||
Event(const FType& f) : QEvent{QEvent::None}, m_f{f} { }
|
||||
~Event() override { m_f(); }
|
||||
FType m_f;
|
||||
};
|
||||
|
||||
@ -61,34 +79,34 @@ static void qtpromise_defer(F&& f, const QPointer<QThread>& thread)
|
||||
}
|
||||
|
||||
Q_ASSERT_X(target, "postMetaCall", "Target thread must have an event loop");
|
||||
QCoreApplication::postEvent(target, new Event(std::forward<F>(f)));
|
||||
QCoreApplication::postEvent(target, new Event{std::forward<F>(f)});
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
template<typename F>
|
||||
static void qtpromise_defer(F&& f)
|
||||
{
|
||||
Q_ASSERT(QThread::currentThread());
|
||||
qtpromise_defer(std::forward<F>(f), QThread::currentThread());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class PromiseValue
|
||||
{
|
||||
public:
|
||||
PromiseValue() { }
|
||||
PromiseValue(const T& data) : m_data(new T(data)) { }
|
||||
PromiseValue(T&& data) : m_data(new T(std::move(data))) { }
|
||||
bool isNull() const { return m_data.isNull(); }
|
||||
PromiseValue(const T& data) : m_data(std::make_shared<T>(data)) { }
|
||||
PromiseValue(T&& data) : m_data(std::make_shared<T>(std::forward<T>(data))) { }
|
||||
bool isNull() const { return m_data == nullptr; }
|
||||
const T& data() const { return *m_data; }
|
||||
|
||||
private:
|
||||
QSharedPointer<T> m_data;
|
||||
std::shared_ptr<T> m_data;
|
||||
};
|
||||
|
||||
class PromiseError
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
PromiseError(const T& value)
|
||||
{
|
||||
try {
|
||||
@ -99,8 +117,8 @@ public:
|
||||
}
|
||||
|
||||
PromiseError() { }
|
||||
PromiseError(const std::exception_ptr& exception) : m_data(exception) { }
|
||||
void rethrow() const { std::rethrow_exception(m_data); }
|
||||
PromiseError(const std::exception_ptr& exception) : m_data{exception} { }
|
||||
Q_NORETURN void rethrow() const { std::rethrow_exception(m_data); }
|
||||
bool isNull() const { return m_data == nullptr; }
|
||||
|
||||
private:
|
||||
@ -108,102 +126,110 @@ private:
|
||||
std::exception_ptr m_data;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct PromiseDeduce
|
||||
{
|
||||
using Type = QtPromise::QPromise<Unqualified<T>>;
|
||||
using Type = QtPromise::QPromise<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct PromiseDeduce<QtPromise::QPromise<T>>
|
||||
: public PromiseDeduce<T>
|
||||
template<typename T>
|
||||
struct PromiseDeduce<T&> : public PromiseDeduce<T>
|
||||
{ };
|
||||
|
||||
template <typename Functor, typename... Args>
|
||||
template<typename T>
|
||||
struct PromiseDeduce<const T> : public PromiseDeduce<T>
|
||||
{ };
|
||||
|
||||
template<typename T>
|
||||
struct PromiseDeduce<const volatile T> : public PromiseDeduce<T>
|
||||
{ };
|
||||
|
||||
template<typename T>
|
||||
struct PromiseDeduce<QtPromise::QPromise<T>> : public PromiseDeduce<T>
|
||||
{ };
|
||||
|
||||
template<typename Functor, typename... Args>
|
||||
struct PromiseFunctor
|
||||
{
|
||||
using ResultType = typename std::result_of<Functor(Args...)>::type;
|
||||
using ResultType = typename invoke_result<Functor, Args...>::type;
|
||||
using PromiseType = typename PromiseDeduce<ResultType>::Type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct PromiseFulfill
|
||||
{
|
||||
static void call(
|
||||
T&& value,
|
||||
const QtPromise::QPromiseResolve<T>& resolve,
|
||||
const QtPromise::QPromiseReject<T>&)
|
||||
template<typename V, typename TResolve, typename TReject>
|
||||
static void call(V&& value, const TResolve& resolve, const TReject&)
|
||||
{
|
||||
resolve(std::move(value));
|
||||
resolve(std::forward<V>(value));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct PromiseFulfill<QtPromise::QPromise<T>>
|
||||
{
|
||||
static void call(
|
||||
const QtPromise::QPromise<T>& promise,
|
||||
const QtPromise::QPromiseResolve<T>& resolve,
|
||||
const QtPromise::QPromiseReject<T>& reject)
|
||||
template<typename TResolve, typename TReject>
|
||||
static void
|
||||
call(const QtPromise::QPromise<T>& promise, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
if (promise.isFulfilled()) {
|
||||
resolve(promise.m_d->value());
|
||||
} else if (promise.isRejected()) {
|
||||
reject(promise.m_d->error());
|
||||
} else {
|
||||
promise.then([=]() {
|
||||
resolve(promise.m_d->value());
|
||||
}, [=]() { // catch all
|
||||
reject(promise.m_d->error());
|
||||
});
|
||||
promise.then(
|
||||
[=]() {
|
||||
resolve(promise.m_d->value());
|
||||
},
|
||||
[=]() { // catch all
|
||||
reject(promise.m_d->error());
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct PromiseFulfill<QtPromise::QPromise<void>>
|
||||
{
|
||||
template <typename TPromise, typename TResolve, typename TReject>
|
||||
static void call(
|
||||
const TPromise& promise,
|
||||
const TResolve& resolve,
|
||||
const TReject& reject)
|
||||
template<typename TPromise, typename TResolve, typename TReject>
|
||||
static void call(const TPromise& promise, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
if (promise.isFulfilled()) {
|
||||
resolve();
|
||||
} else if (promise.isRejected()) {
|
||||
reject(promise.m_d->error());
|
||||
} else {
|
||||
promise.then([=]() {
|
||||
resolve();
|
||||
}, [=]() { // catch all
|
||||
reject(promise.m_d->error());
|
||||
});
|
||||
promise.then(
|
||||
[=]() {
|
||||
resolve();
|
||||
},
|
||||
[=]() { // catch all
|
||||
reject(promise.m_d->error());
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Result>
|
||||
template<typename Result>
|
||||
struct PromiseDispatch
|
||||
{
|
||||
template <typename Resolve, typename Reject, typename Functor, typename... Args>
|
||||
template<typename Resolve, typename Reject, typename Functor, typename... Args>
|
||||
static void call(const Resolve& resolve, const Reject& reject, Functor fn, Args&&... args)
|
||||
{
|
||||
try {
|
||||
PromiseFulfill<Unqualified<Result>>::call(
|
||||
fn(std::forward<Args>(args)...),
|
||||
resolve,
|
||||
reject);
|
||||
PromiseFulfill<Unqualified<Result>>::call(fn(std::forward<Args>(args)...),
|
||||
resolve,
|
||||
reject);
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct PromiseDispatch<void>
|
||||
{
|
||||
template <typename Resolve, typename Reject, typename Functor, typename... Args>
|
||||
template<typename Resolve, typename Reject, typename Functor, typename... Args>
|
||||
static void call(const Resolve& resolve, const Reject& reject, Functor fn, Args&&... args)
|
||||
{
|
||||
try {
|
||||
@ -215,17 +241,15 @@ struct PromiseDispatch<void>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
|
||||
template<typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
|
||||
struct PromiseHandler
|
||||
{
|
||||
using ResType = typename std::result_of<THandler(T)>::type;
|
||||
using ResType = typename invoke_result<THandler, T>::type;
|
||||
using Promise = typename PromiseDeduce<ResType>::Type;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void(const T&)> create(
|
||||
const THandler& handler,
|
||||
const TResolve& resolve,
|
||||
const TReject& reject)
|
||||
template<typename TResolve, typename TReject>
|
||||
static std::function<void(const T&)>
|
||||
create(const THandler& handler, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
return [=](const T& value) {
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler, value);
|
||||
@ -233,17 +257,15 @@ struct PromiseHandler
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename THandler>
|
||||
template<typename T, typename THandler>
|
||||
struct PromiseHandler<T, THandler, void>
|
||||
{
|
||||
using ResType = typename std::result_of<THandler()>::type;
|
||||
using ResType = typename invoke_result<THandler>::type;
|
||||
using Promise = typename PromiseDeduce<ResType>::Type;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void(const T&)> create(
|
||||
const THandler& handler,
|
||||
const TResolve& resolve,
|
||||
const TReject& reject)
|
||||
template<typename TResolve, typename TReject>
|
||||
static std::function<void(const T&)>
|
||||
create(const THandler& handler, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
return [=](const T&) {
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler);
|
||||
@ -251,17 +273,15 @@ struct PromiseHandler<T, THandler, void>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename THandler>
|
||||
template<typename THandler>
|
||||
struct PromiseHandler<void, THandler, void>
|
||||
{
|
||||
using ResType = typename std::result_of<THandler()>::type;
|
||||
using ResType = typename invoke_result<THandler>::type;
|
||||
using Promise = typename PromiseDeduce<ResType>::Type;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void()> create(
|
||||
const THandler& handler,
|
||||
const TResolve& resolve,
|
||||
const TReject& reject)
|
||||
template<typename TResolve, typename TReject>
|
||||
static std::function<void()>
|
||||
create(const THandler& handler, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
return [=]() {
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler);
|
||||
@ -269,16 +289,14 @@ struct PromiseHandler<void, THandler, void>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct PromiseHandler<T, std::nullptr_t, void>
|
||||
{
|
||||
using Promise = QtPromise::QPromise<T>;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void(const T&)> create(
|
||||
std::nullptr_t,
|
||||
const TResolve& resolve,
|
||||
const TReject& reject)
|
||||
template<typename TResolve, typename TReject>
|
||||
static std::function<void(const T&)>
|
||||
create(std::nullptr_t, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
return [=](const T& value) {
|
||||
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
||||
@ -288,16 +306,13 @@ struct PromiseHandler<T, std::nullptr_t, void>
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct PromiseHandler<void, std::nullptr_t, void>
|
||||
{
|
||||
using Promise = QtPromise::QPromise<void>;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void()> create(
|
||||
std::nullptr_t,
|
||||
const TResolve& resolve,
|
||||
const TReject&)
|
||||
template<typename TResolve, typename TReject>
|
||||
static std::function<void()> create(std::nullptr_t, const TResolve& resolve, const TReject&)
|
||||
{
|
||||
return [=]() {
|
||||
// 2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled,
|
||||
@ -307,22 +322,20 @@ struct PromiseHandler<void, std::nullptr_t, void>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
|
||||
template<typename T, typename THandler, typename TArg = typename ArgsOf<THandler>::first>
|
||||
struct PromiseCatcher
|
||||
{
|
||||
using ResType = typename std::result_of<THandler(TArg)>::type;
|
||||
using ResType = typename invoke_result<THandler, TArg>::type;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void(const PromiseError&)> create(
|
||||
const THandler& handler,
|
||||
const TResolve& resolve,
|
||||
const TReject& reject)
|
||||
template<typename TResolve, typename TReject>
|
||||
static std::function<void(const PromiseError&)>
|
||||
create(const THandler& handler, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
return [=](const PromiseError& error) {
|
||||
try {
|
||||
error.rethrow();
|
||||
} catch (const TArg& error) {
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler, error);
|
||||
} catch (const TArg& argError) {
|
||||
PromiseDispatch<ResType>::call(resolve, reject, handler, argError);
|
||||
} catch (...) {
|
||||
reject(std::current_exception());
|
||||
}
|
||||
@ -330,16 +343,14 @@ struct PromiseCatcher
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename THandler>
|
||||
template<typename T, typename THandler>
|
||||
struct PromiseCatcher<T, THandler, void>
|
||||
{
|
||||
using ResType = typename std::result_of<THandler()>::type;
|
||||
using ResType = typename invoke_result<THandler>::type;
|
||||
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void(const PromiseError&)> create(
|
||||
const THandler& handler,
|
||||
const TResolve& resolve,
|
||||
const TReject& reject)
|
||||
template<typename TResolve, typename TReject>
|
||||
static std::function<void(const PromiseError&)>
|
||||
create(const THandler& handler, const TResolve& resolve, const TReject& reject)
|
||||
{
|
||||
return [=](const PromiseError& error) {
|
||||
try {
|
||||
@ -351,14 +362,12 @@ struct PromiseCatcher<T, THandler, void>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct PromiseCatcher<T, std::nullptr_t, void>
|
||||
{
|
||||
template <typename TResolve, typename TReject>
|
||||
static std::function<void(const PromiseError&)> create(
|
||||
std::nullptr_t,
|
||||
const TResolve&,
|
||||
const TReject& reject)
|
||||
template<typename TResolve, typename TReject>
|
||||
static std::function<void(const PromiseError&)>
|
||||
create(std::nullptr_t, const TResolve&, const TReject& reject)
|
||||
{
|
||||
return [=](const PromiseError& error) {
|
||||
// 2.2.7.4. If onRejected is not a function and promise1 is rejected,
|
||||
@ -368,63 +377,57 @@ struct PromiseCatcher<T, std::nullptr_t, void>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename F>
|
||||
template<typename T, typename F>
|
||||
struct PromiseMapper
|
||||
{ };
|
||||
|
||||
template <typename T, typename F, template <typename, typename...> class Sequence, typename ...Args>
|
||||
template<typename T, typename F, template<typename, typename...> class Sequence, typename... Args>
|
||||
struct PromiseMapper<Sequence<T, Args...>, F>
|
||||
{
|
||||
using ReturnType = typename std::result_of<F(T, int)>::type;
|
||||
using ReturnType = typename invoke_result<F, T, int>::type;
|
||||
using ResultType = QVector<typename PromiseDeduce<ReturnType>::Type::Type>;
|
||||
using PromiseType = QtPromise::QPromise<ResultType>;
|
||||
};
|
||||
|
||||
template <typename T> class PromiseData;
|
||||
template<typename T>
|
||||
class PromiseData;
|
||||
|
||||
template <typename T, typename F>
|
||||
template<typename T, typename F>
|
||||
class PromiseDataBase : public QSharedData
|
||||
{
|
||||
public:
|
||||
using Handler = std::pair<QPointer<QThread>, std::function<F>>;
|
||||
using Catcher = std::pair<QPointer<QThread>, std::function<void(const PromiseError&)>>;
|
||||
|
||||
virtual ~PromiseDataBase() {}
|
||||
virtual ~PromiseDataBase() { }
|
||||
|
||||
bool isFulfilled() const
|
||||
{
|
||||
return !isPending() && m_error.isNull();
|
||||
}
|
||||
|
||||
bool isRejected() const
|
||||
{
|
||||
return !isPending() && !m_error.isNull();
|
||||
}
|
||||
bool isFulfilled() const { return !isPending() && m_error.isNull(); }
|
||||
bool isRejected() const { return !isPending() && !m_error.isNull(); }
|
||||
|
||||
bool isPending() const
|
||||
{
|
||||
QReadLocker lock(&m_lock);
|
||||
QReadLocker lock{&m_lock};
|
||||
return !m_settled;
|
||||
}
|
||||
|
||||
void addHandler(std::function<F> handler)
|
||||
{
|
||||
QWriteLocker lock(&m_lock);
|
||||
QWriteLocker lock{&m_lock};
|
||||
m_handlers.append({QThread::currentThread(), std::move(handler)});
|
||||
}
|
||||
|
||||
void addCatcher(std::function<void(const PromiseError&)> catcher)
|
||||
{
|
||||
QWriteLocker lock(&m_lock);
|
||||
QWriteLocker lock{&m_lock};
|
||||
m_catchers.append({QThread::currentThread(), std::move(catcher)});
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
template<typename E>
|
||||
void reject(E&& error)
|
||||
{
|
||||
Q_ASSERT(isPending());
|
||||
Q_ASSERT(m_error.isNull());
|
||||
m_error = PromiseError(std::forward<E>(error));
|
||||
m_error = PromiseError{std::forward<E>(error)};
|
||||
setSettled();
|
||||
}
|
||||
|
||||
@ -448,8 +451,8 @@ public:
|
||||
// captured in the handler and/or catcher lambdas.
|
||||
|
||||
m_lock.lockForWrite();
|
||||
QVector<Handler> handlers(std::move(m_handlers));
|
||||
QVector<Catcher> catchers(std::move(m_catchers));
|
||||
QVector<Handler> handlers = std::move(m_handlers);
|
||||
QVector<Catcher> catchers = std::move(m_catchers);
|
||||
m_lock.unlock();
|
||||
|
||||
if (m_error.isNull()) {
|
||||
@ -457,14 +460,16 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
PromiseError error(m_error);
|
||||
PromiseError error = m_error;
|
||||
Q_ASSERT(!error.isNull());
|
||||
|
||||
for (const auto& catcher: catchers) {
|
||||
for (const auto& catcher : catchers) {
|
||||
const auto& fn = catcher.second;
|
||||
qtpromise_defer([=]() {
|
||||
fn(error);
|
||||
}, catcher.first);
|
||||
qtpromise_defer(
|
||||
[=]() {
|
||||
fn(error);
|
||||
},
|
||||
catcher.first);
|
||||
}
|
||||
}
|
||||
|
||||
@ -473,7 +478,7 @@ protected:
|
||||
|
||||
void setSettled()
|
||||
{
|
||||
QWriteLocker lock(&m_lock);
|
||||
QWriteLocker lock{&m_lock};
|
||||
Q_ASSERT(!m_settled);
|
||||
m_settled = true;
|
||||
}
|
||||
@ -487,18 +492,18 @@ private:
|
||||
PromiseError m_error;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class PromiseData : public PromiseDataBase<T, void(const T&)>
|
||||
{
|
||||
using Handler = typename PromiseDataBase<T, void(const T&)>::Handler;
|
||||
|
||||
public:
|
||||
template <typename V>
|
||||
template<typename V>
|
||||
void resolve(V&& value)
|
||||
{
|
||||
Q_ASSERT(this->isPending());
|
||||
Q_ASSERT(m_value.isNull());
|
||||
m_value = PromiseValue<T>(std::forward<V>(value));
|
||||
m_value = PromiseValue<T>{std::forward<V>(value)};
|
||||
this->setSettled();
|
||||
}
|
||||
|
||||
@ -510,14 +515,16 @@ public:
|
||||
|
||||
void notify(const QVector<Handler>& handlers) Q_DECL_OVERRIDE
|
||||
{
|
||||
PromiseValue<T> value(m_value);
|
||||
PromiseValue<T> value = m_value;
|
||||
Q_ASSERT(!value.isNull());
|
||||
|
||||
for (const auto& handler: handlers) {
|
||||
for (const auto& handler : handlers) {
|
||||
const auto& fn = handler.second;
|
||||
qtpromise_defer([=]() {
|
||||
fn(value.data());
|
||||
}, handler.first);
|
||||
qtpromise_defer(
|
||||
[=]() {
|
||||
fn(value.data());
|
||||
},
|
||||
handler.first);
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,26 +532,96 @@ private:
|
||||
PromiseValue<T> m_value;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
class PromiseData<void> : public PromiseDataBase<void, void()>
|
||||
{
|
||||
using Handler = PromiseDataBase<void, void()>::Handler;
|
||||
|
||||
public:
|
||||
void resolve()
|
||||
{
|
||||
setSettled();
|
||||
}
|
||||
void resolve() { setSettled(); }
|
||||
|
||||
protected:
|
||||
void notify(const QVector<Handler>& handlers) Q_DECL_OVERRIDE
|
||||
{
|
||||
for (const auto& handler: handlers) {
|
||||
for (const auto& handler : handlers) {
|
||||
qtpromise_defer(handler.second, handler.first);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace QtPromise
|
||||
struct PromiseInspect
|
||||
{
|
||||
template<typename T>
|
||||
static inline PromiseData<T>* get(const QtPromise::QPromise<T>& p)
|
||||
{
|
||||
return p.m_d.data();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ifndef QTPROMISE_QPROMISE_H
|
||||
template<typename T, typename U, bool IsConvertibleViaStaticCast>
|
||||
struct PromiseConverterBase;
|
||||
|
||||
template<typename T, typename U>
|
||||
struct PromiseConverterBase<T, U, true>
|
||||
{
|
||||
static std::function<U(const T&)> create()
|
||||
{
|
||||
return [](const T& value) {
|
||||
return static_cast<U>(value);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct PromiseConverterBase<T, U, false>
|
||||
{
|
||||
static std::function<U(const T&)> create()
|
||||
{
|
||||
return [](const T& value) {
|
||||
auto tmp = QVariant::fromValue(value);
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
||||
// https://doc.qt.io/qt-6/qvariant.html#using-canconvert-and-convert-consecutively
|
||||
if (tmp.canConvert(QMetaType{qMetaTypeId<U>()})
|
||||
&& tmp.convert(QMetaType{qMetaTypeId<U>()})) {
|
||||
return qvariant_cast<U>(tmp);
|
||||
}
|
||||
#else
|
||||
// https://doc.qt.io/qt-5/qvariant.html#using-canconvert-and-convert-consecutively
|
||||
if (tmp.canConvert(qMetaTypeId<U>()) && tmp.convert(qMetaTypeId<U>())) {
|
||||
return qvariant_cast<U>(tmp);
|
||||
}
|
||||
#endif
|
||||
throw QtPromise::QPromiseConversionException{};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct PromiseConverterBase<T, QVariant, false>
|
||||
{
|
||||
static std::function<QVariant(const T&)> create()
|
||||
{
|
||||
return [](const T& value) {
|
||||
return QVariant::fromValue(value);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct PromiseConverter
|
||||
: PromiseConverterBase<T,
|
||||
U,
|
||||
// Fundamental types and converting constructors.
|
||||
std::is_convertible<T, U>::value ||
|
||||
// Conversion to void.
|
||||
std::is_same<U, void>::value ||
|
||||
// Conversion between enums and arithmetic types.
|
||||
((std::is_enum<T>::value && std::is_arithmetic<U>::value)
|
||||
|| (std::is_arithmetic<T>::value && std::is_enum<U>::value)
|
||||
|| (std::is_enum<T>::value && std::is_enum<U>::value))>
|
||||
{ };
|
||||
|
||||
} // namespace QtPromisePrivate
|
||||
|
||||
#endif // QTPROMISE_QPROMISE_H
|
||||
|
58
src/qtpromise/qpromiseconnections.h
Normal file
58
src/qtpromise/qpromiseconnections.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISECONNECTIONS_H
|
||||
#define QTPROMISE_QPROMISECONNECTIONS_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
class QPromiseConnections
|
||||
{
|
||||
public:
|
||||
QPromiseConnections() : m_d(std::make_shared<Data>()) { }
|
||||
|
||||
int count() const { return static_cast<int>(m_d->connections.count()); }
|
||||
|
||||
void disconnect() const { m_d->disconnect(); }
|
||||
|
||||
void operator<<(QMetaObject::Connection&& other) const
|
||||
{
|
||||
m_d->connections.append(std::move(other));
|
||||
}
|
||||
|
||||
private:
|
||||
struct Data
|
||||
{
|
||||
QVector<QMetaObject::Connection> connections;
|
||||
|
||||
~Data()
|
||||
{
|
||||
if (!connections.empty()) {
|
||||
qWarning("QPromiseConnections: destroyed with unhandled connections.");
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void disconnect()
|
||||
{
|
||||
for (const auto& connection : connections) {
|
||||
QObject::disconnect(connection);
|
||||
}
|
||||
connections.clear();
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<Data> m_d;
|
||||
};
|
||||
|
||||
} // namespace QtPromise
|
||||
|
||||
#endif // QTPROMISE_QPROMISECONNECTIONS_H
|
@ -1,31 +0,0 @@
|
||||
#ifndef QTPROMISE_QPROMISEERROR_H
|
||||
#define QTPROMISE_QPROMISEERROR_H
|
||||
|
||||
// QtPromise
|
||||
#include "qpromise_p.h"
|
||||
#include "qpromiseglobal.h"
|
||||
|
||||
// Qt
|
||||
#include <QException>
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
class QPromiseTimeoutException : public QException
|
||||
{
|
||||
public:
|
||||
void raise() const Q_DECL_OVERRIDE { throw *this; }
|
||||
QPromiseTimeoutException* clone() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return new QPromiseTimeoutException(*this);
|
||||
}
|
||||
};
|
||||
|
||||
// QPromiseError is provided for backward compatibility and will be
|
||||
// removed in the next major version: it wasn't intended to be used
|
||||
// directly and thus should not be part of the public API.
|
||||
// TODO Remove QPromiseError at version 1.0
|
||||
using QPromiseError = QtPromisePrivate::PromiseError;
|
||||
|
||||
} // namespace QtPromise
|
||||
|
||||
#endif // QTPROMISE_QPROMISEERROR_H
|
69
src/qtpromise/qpromiseexceptions.h
Normal file
69
src/qtpromise/qpromiseexceptions.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISEEXCEPTIONS_H
|
||||
#define QTPROMISE_QPROMISEEXCEPTIONS_H
|
||||
|
||||
#include "qpromiseglobal.h"
|
||||
|
||||
#include <QtCore/QException>
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
class QPromiseCanceledException : public QException
|
||||
{
|
||||
public:
|
||||
void raise() const Q_DECL_OVERRIDE { throw *this; }
|
||||
QPromiseCanceledException* clone() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return new QPromiseCanceledException{*this};
|
||||
}
|
||||
};
|
||||
|
||||
class QPromiseContextException : public QException
|
||||
{
|
||||
public:
|
||||
void raise() const Q_DECL_OVERRIDE { throw *this; }
|
||||
QPromiseContextException* clone() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return new QPromiseContextException{*this};
|
||||
}
|
||||
};
|
||||
|
||||
class QPromiseConversionException : public QException
|
||||
{
|
||||
public:
|
||||
void raise() const Q_DECL_OVERRIDE { throw *this; }
|
||||
QPromiseConversionException* clone() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return new QPromiseConversionException{*this};
|
||||
}
|
||||
};
|
||||
|
||||
class QPromiseTimeoutException : public QException
|
||||
{
|
||||
public:
|
||||
void raise() const Q_DECL_OVERRIDE { throw *this; }
|
||||
QPromiseTimeoutException* clone() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return new QPromiseTimeoutException{*this};
|
||||
}
|
||||
};
|
||||
|
||||
class QPromiseUndefinedException : public QException
|
||||
{
|
||||
public:
|
||||
void raise() const Q_DECL_OVERRIDE { throw *this; }
|
||||
QPromiseUndefinedException* clone() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return new QPromiseUndefinedException{*this};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace QtPromise
|
||||
|
||||
#endif // QTPROMISE_QPROMISEEXCEPTIONS_H
|
@ -1,42 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISEFUTURE_P_H
|
||||
#define QTPROMISE_QPROMISEFUTURE_P_H
|
||||
|
||||
// Qt
|
||||
#include <QFutureWatcher>
|
||||
#include <QFuture>
|
||||
#include "qpromiseexceptions.h"
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
class QPromiseCanceledException : public QException
|
||||
{
|
||||
public:
|
||||
void raise() const Q_DECL_OVERRIDE { throw *this; }
|
||||
QPromiseCanceledException* clone() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return new QPromiseCanceledException(*this);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace QtPromise
|
||||
#include <QtCore/QFuture>
|
||||
#include <QtCore/QFutureWatcher>
|
||||
|
||||
namespace QtPromisePrivate {
|
||||
|
||||
template <typename T>
|
||||
struct PromiseDeduce<QFuture<T>>
|
||||
: public PromiseDeduce<T>
|
||||
template<typename T>
|
||||
struct PromiseDeduce<QFuture<T>> : public PromiseDeduce<T>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct PromiseFulfill<QFuture<T>>
|
||||
{
|
||||
static void call(
|
||||
const QFuture<T>& future,
|
||||
const QtPromise::QPromiseResolve<T>& resolve,
|
||||
const QtPromise::QPromiseReject<T>& reject)
|
||||
static void call(const QFuture<T>& future,
|
||||
const QtPromise::QPromiseResolve<T>& resolve,
|
||||
const QtPromise::QPromiseReject<T>& reject)
|
||||
{
|
||||
using Watcher = QFutureWatcher<T>;
|
||||
|
||||
Watcher* watcher = new Watcher();
|
||||
Watcher* watcher = new Watcher{};
|
||||
QObject::connect(watcher, &Watcher::finished, [=]() mutable {
|
||||
try {
|
||||
if (watcher->isCanceled()) {
|
||||
@ -46,7 +38,7 @@ struct PromiseFulfill<QFuture<T>>
|
||||
// rethrown potential exceptions using waitForFinished() and thus detect
|
||||
// if the future has been canceled by the user or an exception.
|
||||
watcher->waitForFinished();
|
||||
reject(QtPromise::QPromiseCanceledException());
|
||||
reject(QtPromise::QPromiseCanceledException{});
|
||||
} else {
|
||||
PromiseFulfill<T>::call(watcher->result(), resolve, reject);
|
||||
}
|
||||
@ -61,23 +53,22 @@ struct PromiseFulfill<QFuture<T>>
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct PromiseFulfill<QFuture<void>>
|
||||
{
|
||||
static void call(
|
||||
const QFuture<void>& future,
|
||||
const QtPromise::QPromiseResolve<void>& resolve,
|
||||
const QtPromise::QPromiseReject<void>& reject)
|
||||
static void call(const QFuture<void>& future,
|
||||
const QtPromise::QPromiseResolve<void>& resolve,
|
||||
const QtPromise::QPromiseReject<void>& reject)
|
||||
{
|
||||
using Watcher = QFutureWatcher<void>;
|
||||
|
||||
Watcher* watcher = new Watcher();
|
||||
Watcher* watcher = new Watcher{};
|
||||
QObject::connect(watcher, &Watcher::finished, [=]() mutable {
|
||||
try {
|
||||
if (watcher->isCanceled()) {
|
||||
// let's rethrown potential exception
|
||||
watcher->waitForFinished();
|
||||
reject(QtPromise::QPromiseCanceledException());
|
||||
reject(QtPromise::QPromiseCanceledException{});
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
|
@ -1,30 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISEGLOBAL_H
|
||||
#define QTPROMISE_QPROMISEGLOBAL_H
|
||||
|
||||
// QtCore
|
||||
#include <QtGlobal>
|
||||
|
||||
// STL
|
||||
#include <functional>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
namespace QtPromisePrivate {
|
||||
|
||||
namespace QtPromisePrivate
|
||||
{
|
||||
// https://rmf.io/cxx11/even-more-traits#unqualified_types
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
using Unqualified = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
/*!
|
||||
* \struct HasCallOperator
|
||||
* http://stackoverflow.com/a/5117641
|
||||
*/
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct HasCallOperator
|
||||
{
|
||||
template <typename U>
|
||||
template<typename U>
|
||||
static char check(decltype(&U::operator(), char(0)));
|
||||
|
||||
template <typename U>
|
||||
template<typename U>
|
||||
static char (&check(...))[2];
|
||||
|
||||
static const bool value = (sizeof(check<T>(0)) == 1);
|
||||
@ -35,7 +40,7 @@ struct HasCallOperator
|
||||
* http://stackoverflow.com/a/7943765
|
||||
* http://stackoverflow.com/a/27885283
|
||||
*/
|
||||
template <typename... Args>
|
||||
template<typename... Args>
|
||||
struct ArgsTraits
|
||||
{
|
||||
using types = std::tuple<Args...>;
|
||||
@ -43,7 +48,7 @@ struct ArgsTraits
|
||||
static const size_t count = std::tuple_size<types>::value;
|
||||
};
|
||||
|
||||
template <>
|
||||
template<>
|
||||
struct ArgsTraits<>
|
||||
{
|
||||
using types = std::tuple<>;
|
||||
@ -51,75 +56,61 @@ struct ArgsTraits<>
|
||||
static const size_t count = 0;
|
||||
};
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
// Fallback implementation, including types (T) which are not functions but
|
||||
// also lambda with `auto` arguments, which are not covered but still valid
|
||||
// callbacks (see the QPromiseBase<T> template constructor).
|
||||
template<typename T, typename Enabled = void>
|
||||
struct ArgsOf : public ArgsTraits<>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
// Partial specialization for null function.
|
||||
template<>
|
||||
struct ArgsOf<std::nullptr_t> : public ArgsTraits<>
|
||||
{ };
|
||||
|
||||
// Partial specialization for type with a non-overloaded operator().
|
||||
// This applies to lambda, std::function but not to std::bind result.
|
||||
template<typename T>
|
||||
struct ArgsOf<T, typename std::enable_if<HasCallOperator<T>::value>::type>
|
||||
: public ArgsOf<decltype(&T::operator())>
|
||||
{ };
|
||||
|
||||
template <typename TReturn, typename... Args>
|
||||
struct ArgsOf<TReturn(Args...)> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template <typename TReturn, typename... Args>
|
||||
struct ArgsOf<TReturn(*)(Args...)> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template <typename T, typename TReturn, typename... Args>
|
||||
struct ArgsOf<TReturn(T::*)(Args...)> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template <typename T, typename TReturn, typename... Args>
|
||||
struct ArgsOf<TReturn(T::*)(Args...) const> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template <typename T, typename TReturn, typename... Args>
|
||||
struct ArgsOf<TReturn(T::*)(Args...) volatile> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template <typename T, typename TReturn, typename... Args>
|
||||
struct ArgsOf<TReturn(T::*)(Args...) const volatile> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
struct ArgsOf<std::function<T>> : public ArgsOf<T>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
// Partial specialization to remove reference and rvalue (e.g. lambda, std::function, etc.).
|
||||
template<typename T>
|
||||
struct ArgsOf<T&> : public ArgsOf<T>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
struct ArgsOf<const T&> : public ArgsOf<T>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
struct ArgsOf<volatile T&> : public ArgsOf<T>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
struct ArgsOf<const volatile T&> : public ArgsOf<T>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
struct ArgsOf<T&&> : public ArgsOf<T>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
struct ArgsOf<const T&&> : public ArgsOf<T>
|
||||
// Partial specialization for function type.
|
||||
template<typename R, typename... Args>
|
||||
struct ArgsOf<R(Args...)> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
struct ArgsOf<volatile T&&> : public ArgsOf<T>
|
||||
// Partial specialization for function pointer.
|
||||
template<typename R, typename... Args>
|
||||
struct ArgsOf<R (*)(Args...)> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template <typename T>
|
||||
struct ArgsOf<const volatile T&&> : public ArgsOf<T>
|
||||
// Partial specialization for pointer-to-member-function (i.e. operator()'s).
|
||||
template<typename R, typename T, typename... Args>
|
||||
struct ArgsOf<R (T::*)(Args...)> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template<typename R, typename T, typename... Args>
|
||||
struct ArgsOf<R (T::*)(Args...) const> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template<typename R, typename T, typename... Args>
|
||||
struct ArgsOf<R (T::*)(Args...) volatile> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
template<typename R, typename T, typename... Args>
|
||||
struct ArgsOf<R (T::*)(Args...) const volatile> : public ArgsTraits<Args...>
|
||||
{ };
|
||||
|
||||
} // namespace QtPromisePrivate
|
||||
|
||||
#endif // ifndef QTPROMISE_QPROMISEGLOBAL_H
|
||||
#endif // QTPROMISE_QPROMISEGLOBAL_H
|
||||
|
@ -1,44 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISEHELPERS_H
|
||||
#define QTPROMISE_QPROMISEHELPERS_H
|
||||
|
||||
// QtPromise
|
||||
#include "qpromise_p.h"
|
||||
#include "qpromisehelpers_p.h"
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <typename T>
|
||||
static inline typename QtPromisePrivate::PromiseDeduce<T>::Type qPromise(T&& value)
|
||||
template<typename T>
|
||||
static inline typename QtPromisePrivate::PromiseDeduce<T>::Type resolve(T&& value)
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using Promise = typename PromiseDeduce<T>::Type;
|
||||
return Promise([&](
|
||||
const QPromiseResolve<typename Promise::Type>& resolve,
|
||||
const QPromiseReject<typename Promise::Type>& reject) {
|
||||
PromiseFulfill<T>::call(std::forward<T>(value), resolve, reject);
|
||||
});
|
||||
using PromiseType = typename PromiseDeduce<T>::Type;
|
||||
using ValueType = typename PromiseType::Type;
|
||||
using ResolveType = QPromiseResolve<ValueType>;
|
||||
using RejectType = QPromiseReject<ValueType>;
|
||||
|
||||
return PromiseType{[&](ResolveType&& resolve, RejectType&& reject) {
|
||||
PromiseFulfill<Unqualified<T>>::call(std::forward<T>(value),
|
||||
std::forward<ResolveType>(resolve),
|
||||
std::forward<RejectType>(reject));
|
||||
}};
|
||||
}
|
||||
|
||||
static inline QPromise<void> qPromise()
|
||||
template<typename T>
|
||||
static inline QPromise<T> resolve(QPromise<T> value)
|
||||
{
|
||||
return QPromise<void>([](
|
||||
const QPromiseResolve<void>& resolve) {
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline QPromise<void> resolve()
|
||||
{
|
||||
return QPromise<void>{[](const QPromiseResolve<void>& resolve) {
|
||||
resolve();
|
||||
});
|
||||
}};
|
||||
}
|
||||
|
||||
template <typename T, template <typename, typename...> class Sequence = QVector, typename ...Args>
|
||||
static inline QPromise<QVector<T>> qPromiseAll(const Sequence<QPromise<T>, Args...>& promises)
|
||||
template<typename T, template<typename, typename...> class Sequence = QVector, typename... Args>
|
||||
static inline QPromise<QVector<T>> all(const Sequence<QPromise<T>, Args...>& promises)
|
||||
{
|
||||
return QPromise<T>::all(promises);
|
||||
const int count = static_cast<int>(promises.size());
|
||||
if (count == 0) {
|
||||
return QtPromise::resolve(QVector<T>{});
|
||||
}
|
||||
|
||||
return QPromise<QVector<T>>{
|
||||
[=](const QPromiseResolve<QVector<T>>& resolve, const QPromiseReject<QVector<T>>& reject) {
|
||||
auto remaining = QSharedPointer<int>::create(count);
|
||||
auto results = QSharedPointer<QVector<T>>::create(count);
|
||||
|
||||
int i = 0;
|
||||
for (const auto& promise : promises) {
|
||||
promise.then(
|
||||
[=](const T& res) mutable {
|
||||
(*results)[i] = res;
|
||||
if (--(*remaining) == 0) {
|
||||
resolve(*results);
|
||||
}
|
||||
},
|
||||
[=]() mutable {
|
||||
if (*remaining != -1) {
|
||||
*remaining = -1;
|
||||
reject(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
i++;
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
template <template <typename, typename...> class Sequence = QVector, typename ...Args>
|
||||
static inline QPromise<void> qPromiseAll(const Sequence<QPromise<void>, Args...>& promises)
|
||||
template<template<typename, typename...> class Sequence = QVector, typename... Args>
|
||||
static inline QPromise<void> all(const Sequence<QPromise<void>, Args...>& promises)
|
||||
{
|
||||
return QPromise<void>::all(promises);
|
||||
const int count = static_cast<int>(promises.size());
|
||||
if (count == 0) {
|
||||
return QtPromise::resolve();
|
||||
}
|
||||
|
||||
return QPromise<void>{
|
||||
[=](const QPromiseResolve<void>& resolve, const QPromiseReject<void>& reject) {
|
||||
auto remaining = QSharedPointer<int>::create(count);
|
||||
|
||||
for (const auto& promise : promises) {
|
||||
promise.then(
|
||||
[=]() {
|
||||
if (--(*remaining) == 0) {
|
||||
resolve();
|
||||
}
|
||||
},
|
||||
[=]() {
|
||||
if (*remaining != -1) {
|
||||
*remaining = -1;
|
||||
reject(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
template <typename Functor, typename... Args>
|
||||
template<typename Functor, typename... Args>
|
||||
static inline typename QtPromisePrivate::PromiseFunctor<Functor, Args...>::PromiseType
|
||||
attempt(Functor&& fn, Args&&... args)
|
||||
{
|
||||
@ -54,23 +121,57 @@ attempt(Functor&& fn, Args&&... args)
|
||||
using ResolveType = QPromiseResolve<ValueType>;
|
||||
using RejectType = QPromiseReject<ValueType>;
|
||||
|
||||
return PromiseType(
|
||||
[&](ResolveType&& resolve, RejectType&& reject) {
|
||||
PromiseDispatch<typename FunctorType::ResultType>::call(
|
||||
std::forward<ResolveType>(resolve),
|
||||
std::forward<RejectType>(reject),
|
||||
std::forward<Functor>(fn),
|
||||
std::forward<Args>(args)...);
|
||||
});
|
||||
return PromiseType{[&](ResolveType&& resolve, RejectType&& reject) {
|
||||
PromiseDispatch<typename FunctorType::ResultType>::call(std::forward<ResolveType>(resolve),
|
||||
std::forward<RejectType>(reject),
|
||||
std::forward<Functor>(fn),
|
||||
std::forward<Args>(args)...);
|
||||
}};
|
||||
}
|
||||
|
||||
template <typename Sequence, typename Functor>
|
||||
template<typename Sender, typename Signal>
|
||||
static inline typename QtPromisePrivate::PromiseFromSignal<Signal>
|
||||
connect(const Sender* sender, Signal signal)
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using T = typename PromiseFromSignal<Signal>::Type;
|
||||
|
||||
return QPromise<T>{[&](const QPromiseResolve<T>& resolve, const QPromiseReject<T>& reject) {
|
||||
QPromiseConnections connections;
|
||||
connectSignalToResolver(connections, resolve, sender, signal);
|
||||
connectDestroyedToReject(connections, reject, sender);
|
||||
}};
|
||||
}
|
||||
|
||||
template<typename FSender, typename FSignal, typename RSender, typename RSignal>
|
||||
static inline typename QtPromisePrivate::PromiseFromSignal<FSignal>
|
||||
connect(const FSender* fsender, FSignal fsignal, const RSender* rsender, RSignal rsignal)
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using T = typename PromiseFromSignal<FSignal>::Type;
|
||||
|
||||
return QPromise<T>{[&](const QPromiseResolve<T>& resolve, const QPromiseReject<T>& reject) {
|
||||
QPromiseConnections connections;
|
||||
connectSignalToResolver(connections, resolve, fsender, fsignal);
|
||||
connectSignalToResolver(connections, reject, rsender, rsignal);
|
||||
connectDestroyedToReject(connections, reject, fsender);
|
||||
}};
|
||||
}
|
||||
|
||||
template<typename Sender, typename FSignal, typename RSignal>
|
||||
static inline typename QtPromisePrivate::PromiseFromSignal<FSignal>
|
||||
connect(const Sender* sender, FSignal fsignal, RSignal rsignal)
|
||||
{
|
||||
return connect(sender, fsignal, sender, rsignal);
|
||||
}
|
||||
|
||||
template<typename Sequence, typename Functor>
|
||||
static inline QPromise<Sequence> each(const Sequence& values, Functor&& fn)
|
||||
{
|
||||
return QPromise<Sequence>::resolve(values).each(std::forward<Functor>(fn));
|
||||
}
|
||||
|
||||
template <typename Sequence, typename Functor>
|
||||
template<typename Sequence, typename Functor>
|
||||
static inline typename QtPromisePrivate::PromiseMapper<Sequence, Functor>::PromiseType
|
||||
map(const Sequence& values, Functor fn)
|
||||
{
|
||||
@ -83,36 +184,112 @@ map(const Sequence& values, Functor fn)
|
||||
|
||||
std::vector<QPromise<ResType>> promises;
|
||||
for (const auto& v : values) {
|
||||
promises.push_back(QPromise<ResType>([&](
|
||||
const QPromiseResolve<ResType>& resolve,
|
||||
const QPromiseReject<ResType>& reject) {
|
||||
promises.push_back(QPromise<ResType>{
|
||||
[&](const QPromiseResolve<ResType>& resolve, const QPromiseReject<ResType>& reject) {
|
||||
PromiseFulfill<RetType>::call(fn(v, i), resolve, reject);
|
||||
}));
|
||||
}});
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return QPromise<ResType>::all(promises);
|
||||
return QtPromise::all(promises);
|
||||
}
|
||||
|
||||
template <typename Sequence, typename Functor>
|
||||
template<typename Sequence, typename Functor>
|
||||
static inline QPromise<Sequence> filter(const Sequence& values, Functor fn)
|
||||
{
|
||||
return QtPromise::map(values, fn)
|
||||
.then([=](const QVector<bool>& filters) {
|
||||
Sequence filtered;
|
||||
return QtPromise::map(values, fn).then([=](const QVector<bool>& filters) {
|
||||
Sequence filtered;
|
||||
|
||||
auto filter = filters.begin();
|
||||
for (auto& value : values) {
|
||||
if (*filter) {
|
||||
filtered.push_back(std::move(value));
|
||||
}
|
||||
|
||||
filter++;
|
||||
auto filter = filters.begin();
|
||||
for (auto& value : values) {
|
||||
if (*filter) {
|
||||
filtered.push_back(std::move(value));
|
||||
}
|
||||
|
||||
return filtered;
|
||||
filter++;
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
template<typename...> class Sequence = QVector,
|
||||
typename Reducer,
|
||||
typename Input,
|
||||
typename... Args>
|
||||
static inline typename QtPromisePrivate::PromiseDeduce<Input>::Type
|
||||
reduce(const Sequence<T, Args...>& values, Reducer fn, Input initial)
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using PromiseType = typename PromiseDeduce<T>::Type;
|
||||
using ValueType = typename PromiseType::Type;
|
||||
|
||||
int idx = 0;
|
||||
|
||||
auto promise = QtPromise::resolve(std::move(initial));
|
||||
for (const auto& value : values) {
|
||||
auto input = QtPromise::resolve(value);
|
||||
promise = promise.then([=]() {
|
||||
return input.then([=](const ValueType& cur) {
|
||||
return fn(PromiseInspect::get(promise)->value().data(), cur, idx);
|
||||
});
|
||||
});
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
template<typename...> class Sequence = QVector,
|
||||
typename Reducer,
|
||||
typename... Args>
|
||||
static inline typename QtPromisePrivate::PromiseDeduce<T>::Type
|
||||
reduce(const Sequence<T, Args...>& values, Reducer fn)
|
||||
{
|
||||
using namespace QtPromisePrivate;
|
||||
using PromiseType = typename PromiseDeduce<T>::Type;
|
||||
using ValueType = typename PromiseType::Type;
|
||||
|
||||
Q_ASSERT(values.size());
|
||||
|
||||
int idx = 1;
|
||||
|
||||
auto it = values.begin();
|
||||
auto promise = QtPromise::resolve(*it);
|
||||
for (++it; it != values.end(); ++it) {
|
||||
auto input = QtPromise::resolve(*it);
|
||||
promise = promise.then([=]() {
|
||||
return input.then([=](const ValueType& cur) {
|
||||
return fn(PromiseInspect::get(promise)->value().data(), cur, idx);
|
||||
});
|
||||
});
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
// DEPRECATIONS (remove at version 1)
|
||||
|
||||
template<typename... Args>
|
||||
Q_DECL_DEPRECATED_X("Use QtPromise::resolve instead")
|
||||
static inline auto qPromise(Args&&... args)
|
||||
-> decltype(QtPromise::resolve(std::forward<Args>(args)...))
|
||||
{
|
||||
return QtPromise::resolve(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
Q_DECL_DEPRECATED_X("Use QtPromise::all instead")
|
||||
static inline auto qPromiseAll(Args&&... args)
|
||||
-> decltype(QtPromise::all(std::forward<Args>(args)...))
|
||||
{
|
||||
return QtPromise::all(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace QtPromise
|
||||
|
93
src/qtpromise/qpromisehelpers_p.h
Normal file
93
src/qtpromise/qpromisehelpers_p.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISEHELPERS_P_H
|
||||
#define QTPROMISE_QPROMISEHELPERS_P_H
|
||||
|
||||
#include "qpromiseconnections.h"
|
||||
#include "qpromiseexceptions.h"
|
||||
|
||||
namespace QtPromisePrivate {
|
||||
|
||||
// TODO: Suppress QPrivateSignal trailing private signal args
|
||||
// TODO: Support deducing tuple from args (might require MSVC2017)
|
||||
|
||||
template<typename Signal>
|
||||
using PromiseFromSignal = typename QtPromise::QPromise<Unqualified<typename ArgsOf<Signal>::first>>;
|
||||
|
||||
// Connect signal() to QPromiseResolve
|
||||
template<typename Sender, typename Signal>
|
||||
typename std::enable_if<(ArgsOf<Signal>::count == 0)>::type
|
||||
connectSignalToResolver(const QtPromise::QPromiseConnections& connections,
|
||||
const QtPromise::QPromiseResolve<void>& resolve,
|
||||
const Sender* sender,
|
||||
Signal signal)
|
||||
{
|
||||
connections << QObject::connect(sender, signal, [=]() {
|
||||
connections.disconnect();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// Connect signal() to QPromiseReject
|
||||
template<typename T, typename Sender, typename Signal>
|
||||
typename std::enable_if<(ArgsOf<Signal>::count == 0)>::type
|
||||
connectSignalToResolver(const QtPromise::QPromiseConnections& connections,
|
||||
const QtPromise::QPromiseReject<T>& reject,
|
||||
const Sender* sender,
|
||||
Signal signal)
|
||||
{
|
||||
connections << QObject::connect(sender, signal, [=]() {
|
||||
connections.disconnect();
|
||||
reject(QtPromise::QPromiseUndefinedException{});
|
||||
});
|
||||
}
|
||||
|
||||
// Connect signal(args...) to QPromiseResolve
|
||||
template<typename T, typename Sender, typename Signal>
|
||||
typename std::enable_if<(ArgsOf<Signal>::count >= 1)>::type
|
||||
connectSignalToResolver(const QtPromise::QPromiseConnections& connections,
|
||||
const QtPromise::QPromiseResolve<T>& resolve,
|
||||
const Sender* sender,
|
||||
Signal signal)
|
||||
{
|
||||
connections << QObject::connect(sender, signal, [=](const T& value) {
|
||||
connections.disconnect();
|
||||
resolve(value);
|
||||
});
|
||||
}
|
||||
|
||||
// Connect signal(args...) to QPromiseReject
|
||||
template<typename T, typename Sender, typename Signal>
|
||||
typename std::enable_if<(ArgsOf<Signal>::count >= 1)>::type
|
||||
connectSignalToResolver(const QtPromise::QPromiseConnections& connections,
|
||||
const QtPromise::QPromiseReject<T>& reject,
|
||||
const Sender* sender,
|
||||
Signal signal)
|
||||
{
|
||||
using V = Unqualified<typename ArgsOf<Signal>::first>;
|
||||
connections << QObject::connect(sender, signal, [=](const V& value) {
|
||||
connections.disconnect();
|
||||
reject(value);
|
||||
});
|
||||
}
|
||||
|
||||
// Connect QObject::destroyed signal to QPromiseReject
|
||||
template<typename T, typename Sender>
|
||||
void connectDestroyedToReject(const QtPromise::QPromiseConnections& connections,
|
||||
const QtPromise::QPromiseReject<T>& reject,
|
||||
const Sender* sender)
|
||||
{
|
||||
connections << QObject::connect(sender, &QObject::destroyed, [=]() {
|
||||
connections.disconnect();
|
||||
reject(QtPromise::QPromiseContextException{});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace QtPromisePrivate
|
||||
|
||||
#endif // QTPROMISE_QPROMISEHELPERS_P_H
|
@ -1,28 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#ifndef QTPROMISE_QPROMISERESOLVER_H
|
||||
#define QTPROMISE_QPROMISERESOLVER_H
|
||||
|
||||
// Qt
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include "qpromiseexceptions.h"
|
||||
|
||||
#include <QtCore/QExplicitlySharedDataPointer>
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <typename T> class QPromise;
|
||||
template<typename T>
|
||||
class QPromise;
|
||||
|
||||
} // namespace QtPromise
|
||||
|
||||
namespace QtPromisePrivate {
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
class PromiseResolver
|
||||
{
|
||||
public:
|
||||
PromiseResolver(QtPromise::QPromise<T> promise)
|
||||
: m_d(new Data())
|
||||
PromiseResolver(QtPromise::QPromise<T> promise) : m_d{new Data{}}
|
||||
{
|
||||
m_d->promise = new QtPromise::QPromise<T>(std::move(promise));
|
||||
m_d->promise = new QtPromise::QPromise<T>{std::move(promise)};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
template<typename E>
|
||||
void reject(E&& error)
|
||||
{
|
||||
auto promise = m_d->promise;
|
||||
@ -34,7 +42,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void reject()
|
||||
{
|
||||
auto promise = m_d->promise;
|
||||
if (promise) {
|
||||
Q_ASSERT(promise->isPending());
|
||||
promise->m_d->reject(QtPromise::QPromiseUndefinedException{});
|
||||
promise->m_d->dispatch();
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename V>
|
||||
void resolve(V&& value)
|
||||
{
|
||||
auto promise = m_d->promise;
|
||||
@ -74,47 +93,44 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
} // QtPromisePrivate
|
||||
} // namespace QtPromisePrivate
|
||||
|
||||
namespace QtPromise {
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
class QPromiseResolve
|
||||
{
|
||||
public:
|
||||
QPromiseResolve(QtPromisePrivate::PromiseResolver<T> resolver)
|
||||
: m_resolver(std::move(resolver))
|
||||
QPromiseResolve(QtPromisePrivate::PromiseResolver<T> resolver) : m_resolver{std::move(resolver)}
|
||||
{ }
|
||||
|
||||
template <typename V>
|
||||
template<typename V>
|
||||
void operator()(V&& value) const
|
||||
{
|
||||
m_resolver.resolve(std::forward<V>(value));
|
||||
}
|
||||
|
||||
void operator()() const
|
||||
{
|
||||
m_resolver.resolve();
|
||||
}
|
||||
void operator()() const { m_resolver.resolve(); }
|
||||
|
||||
private:
|
||||
mutable QtPromisePrivate::PromiseResolver<T> m_resolver;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
template<class T>
|
||||
class QPromiseReject
|
||||
{
|
||||
public:
|
||||
QPromiseReject(QtPromisePrivate::PromiseResolver<T> resolver)
|
||||
: m_resolver(std::move(resolver))
|
||||
QPromiseReject(QtPromisePrivate::PromiseResolver<T> resolver) : m_resolver{std::move(resolver)}
|
||||
{ }
|
||||
|
||||
template <typename E>
|
||||
template<typename E>
|
||||
void operator()(E&& error) const
|
||||
{
|
||||
m_resolver.reject(std::forward<E>(error));
|
||||
}
|
||||
|
||||
void operator()() const { m_resolver.reject(); }
|
||||
|
||||
private:
|
||||
mutable QtPromisePrivate::PromiseResolver<T> m_resolver;
|
||||
};
|
||||
|
@ -1,9 +0,0 @@
|
||||
HEADERS += \
|
||||
$$PWD/qpromise.h \
|
||||
$$PWD/qpromise.inl \
|
||||
$$PWD/qpromise_p.h \
|
||||
$$PWD/qpromiseerror.h \
|
||||
$$PWD/qpromisefuture.h \
|
||||
$$PWD/qpromiseglobal.h \
|
||||
$$PWD/qpromisehelpers.h \
|
||||
$$PWD/qpromiseresolver.h
|
@ -1,5 +0,0 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += c++11 qt thread warn_on
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
include(qtpromise.pri)
|
@ -1,2 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = qtpromise
|
9
tests/CMakeLists.txt
Normal file
9
tests/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED
|
||||
COMPONENTS
|
||||
Concurrent
|
||||
Test
|
||||
)
|
||||
|
||||
include(QtPromiseAddTest)
|
||||
|
||||
add_subdirectory(auto)
|
1
tests/auto/CMakeLists.txt
Normal file
1
tests/auto/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(qtpromise)
|
@ -1,2 +0,0 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += qtpromise
|
13
tests/auto/qtpromise/CMakeLists.txt
Normal file
13
tests/auto/qtpromise/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
add_subdirectory(shared)
|
||||
|
||||
add_subdirectory(benchmark)
|
||||
add_subdirectory(cpp14)
|
||||
add_subdirectory(deprecations)
|
||||
add_subdirectory(exceptions)
|
||||
add_subdirectory(future)
|
||||
add_subdirectory(helpers)
|
||||
add_subdirectory(internals)
|
||||
add_subdirectory(qpromise)
|
||||
add_subdirectory(qpromiseconnections)
|
||||
add_subdirectory(requirements)
|
||||
add_subdirectory(thread)
|
4
tests/auto/qtpromise/benchmark/CMakeLists.txt
Normal file
4
tests/auto/qtpromise/benchmark/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
qtpromise_add_test(benchmark
|
||||
SOURCES
|
||||
tst_benchmark.cpp
|
||||
)
|
@ -1,4 +0,0 @@
|
||||
TARGET = tst_benchmark
|
||||
SOURCES += $$PWD/tst_benchmark.cpp
|
||||
|
||||
include(../qtpromise.pri)
|
@ -1,26 +1,29 @@
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// Qt
|
||||
#include "../shared/data.h"
|
||||
|
||||
#include <QtPromise>
|
||||
#include <QtTest>
|
||||
|
||||
#ifdef Q_CC_MSVC
|
||||
// MSVC calls the copy constructor on std::current_exception AND std::rethrow_exception
|
||||
// https://stackoverflow.com/a/31820854
|
||||
#define EXCEPT_CALL_COPY_CTOR 1
|
||||
// MSVC calls the copy constructor on std::current_exception AND std::rethrow_exception
|
||||
// https://stackoverflow.com/a/31820854
|
||||
# define EXCEPT_CALL_COPY_CTOR 1
|
||||
#else
|
||||
#define EXCEPT_CALL_COPY_CTOR 0
|
||||
# define EXCEPT_CALL_COPY_CTOR 0
|
||||
#endif
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_benchmark : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void valueResolve();
|
||||
void valueResolveStatic();
|
||||
void valueReject();
|
||||
void valueThen();
|
||||
void valueFinally();
|
||||
@ -34,119 +37,28 @@ private Q_SLOTS:
|
||||
QTEST_MAIN(tst_benchmark)
|
||||
#include "tst_benchmark.moc"
|
||||
|
||||
struct Logs {
|
||||
int ctor = 0;
|
||||
int copy = 0;
|
||||
int move = 0;
|
||||
int refs = 0;
|
||||
|
||||
void reset() {
|
||||
ctor = 0;
|
||||
copy = 0;
|
||||
move = 0;
|
||||
refs = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Logger
|
||||
{
|
||||
Logger() { logs().ctor++; logs().refs++; }
|
||||
Logger(const Logger&) { logs().copy++; logs().refs++; }
|
||||
Logger(Logger&&) { logs().move++; logs().refs++; }
|
||||
~Logger() { logs().refs--; }
|
||||
|
||||
Logger& operator=(const Logger&) { logs().copy++; return *this; }
|
||||
Logger& operator=(Logger&&) { logs().move++; return *this; }
|
||||
|
||||
public: // STATICS
|
||||
static Logs& logs() { static Logs logs; return logs; }
|
||||
};
|
||||
|
||||
struct Data : public Logger
|
||||
{
|
||||
Data(int v): Logger(), m_value(v) {}
|
||||
int value() const { return m_value; }
|
||||
|
||||
// MSVC 2013 doesn't support implicit generation of the move constructor and
|
||||
// operator, so we need to explicitly define these methods and thus the copy
|
||||
// constructor and operator also need to be explicitly defined (error C2280).
|
||||
// https://stackoverflow.com/a/26581337
|
||||
|
||||
Data(const Data& other)
|
||||
: Logger(other)
|
||||
, m_value(other.m_value)
|
||||
{ }
|
||||
|
||||
Data(Data&& other) : Logger(std::forward<Data>(other))
|
||||
{
|
||||
qSwap(m_value, other.m_value);
|
||||
}
|
||||
|
||||
Data& operator=(const Data& other)
|
||||
{
|
||||
Logger::operator=(other);
|
||||
m_value = other.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Data& operator=(Data&& other)
|
||||
{
|
||||
Logger::operator=(std::forward<Data>(other));
|
||||
qSwap(m_value, other.m_value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
|
||||
void tst_benchmark::valueResolve()
|
||||
{
|
||||
{ // should move the value when resolved by rvalue
|
||||
{ // should move the value when resolved by rvalue
|
||||
Data::logs().reset();
|
||||
QPromise<Data>([&](const QPromiseResolve<Data>& resolve) {
|
||||
resolve(Data(42));
|
||||
}).wait();
|
||||
QtPromise::QPromise<Data>{[&](const QtPromise::QPromiseResolve<Data>& resolve) {
|
||||
resolve(Data{42});
|
||||
}}.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the promise data
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the promise data
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
{ // should create one copy of the value when resolved by lvalue
|
||||
{ // should create one copy of the value when resolved by lvalue
|
||||
Data::logs().reset();
|
||||
QPromise<Data>([&](const QPromiseResolve<Data>& resolve) {
|
||||
Data value(42);
|
||||
QtPromise::QPromise<Data>{[&](const QtPromise::QPromiseResolve<Data>& resolve) {
|
||||
Data value{42};
|
||||
resolve(value);
|
||||
}).wait();
|
||||
}}.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1); // copy value to the promise data
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_benchmark::valueResolveStatic()
|
||||
{
|
||||
{ // should move the value when resolved by rvalue
|
||||
Data::logs().reset();
|
||||
QPromise<Data>::resolve(Data(42)).wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the promise data
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
{ // should create one copy of the value when resolved by lvalue
|
||||
{
|
||||
Data::logs().reset();
|
||||
Data value(42);
|
||||
QPromise<Data>::resolve(value).wait();
|
||||
}
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1); // copy value to the promise data
|
||||
QCOMPARE(Data::logs().copy, 1); // copy value to the promise data
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
@ -154,11 +66,12 @@ void tst_benchmark::valueResolveStatic()
|
||||
|
||||
void tst_benchmark::valueReject()
|
||||
{
|
||||
{ // should not create any data if rejected
|
||||
{ // should not create any data if rejected
|
||||
Data::logs().reset();
|
||||
QPromise<Data>([&](const QPromiseResolve<Data>&, const QPromiseReject<Data>& reject) {
|
||||
reject(QString("foo"));
|
||||
}).wait();
|
||||
QtPromise::QPromise<Data>{[&](const QtPromise::QPromiseResolve<Data>&,
|
||||
const QtPromise::QPromiseReject<Data>& reject) {
|
||||
reject(QString{"foo"});
|
||||
}}.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 0);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
@ -169,57 +82,68 @@ void tst_benchmark::valueReject()
|
||||
|
||||
void tst_benchmark::valueThen()
|
||||
{
|
||||
{ // should not copy value on continutation if fulfilled
|
||||
{ // should not copy value on continuation if fulfilled
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<Data>::resolve(Data(42)).then([&](const Data& res) {
|
||||
value = res.value();
|
||||
}).wait();
|
||||
QtPromise::QPromise<Data>::resolve(Data{42})
|
||||
.then([&](const Data& res) {
|
||||
value = res.value();
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the promise data
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the promise data
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 42);
|
||||
}
|
||||
{ // should not create value on continutation if rejected
|
||||
{ // should not create value on continuation if rejected
|
||||
int value = -1;
|
||||
QString error;
|
||||
Data::logs().reset();
|
||||
QPromise<Data>::reject(QString("foo")).then([&](const Data& res) {
|
||||
value = res.value();
|
||||
}, [&](const QString& err) {
|
||||
error = err;
|
||||
}).wait();
|
||||
QtPromise::QPromise<Data>::reject(QString{"foo"})
|
||||
.then(
|
||||
[&](const Data& res) {
|
||||
value = res.value();
|
||||
},
|
||||
[&](const QString& err) {
|
||||
error = err;
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 0);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(error, QString("foo"));
|
||||
QCOMPARE(error, QString{"foo"});
|
||||
QCOMPARE(value, -1);
|
||||
}
|
||||
{ // should move the returned value when fulfilled
|
||||
{ // should move the returned value when fulfilled
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<int>::resolve(42).then([&](int res) {
|
||||
return Data(res+2);
|
||||
}).then([&](const Data& res) {
|
||||
value = res.value();
|
||||
}).wait();
|
||||
QtPromise::QPromise<int>::resolve(42)
|
||||
.then([&](int res) {
|
||||
return Data{res + 2};
|
||||
})
|
||||
.then([&](const Data& res) {
|
||||
value = res.value();
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1); // move values to the next promise data
|
||||
QCOMPARE(Data::logs().move, 1); // move values to the next promise data
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 44);
|
||||
}
|
||||
{ // should not create any data if handler throws
|
||||
{ // should not create any data if handler throws
|
||||
Data::logs().reset();
|
||||
QPromise<int>::resolve(42).then([&](int res) {
|
||||
throw QString("foo");
|
||||
return Data(res+2);
|
||||
}).wait();
|
||||
QtPromise::QPromise<int>::resolve(42)
|
||||
.then([&](int res) {
|
||||
throw QString{"foo"};
|
||||
return Data{res + 2};
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 0);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
@ -230,26 +154,31 @@ void tst_benchmark::valueThen()
|
||||
|
||||
void tst_benchmark::valueDelayed()
|
||||
{
|
||||
{ // should not copy the value on continutation if fulfilled
|
||||
{ // should not copy the value on continuation if fulfilled
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<int>::resolve(42).then([&](int res) {
|
||||
return QPromise<Data>::resolve(Data(res + 1));
|
||||
}).then([&](const Data& res) {
|
||||
value = res.value();
|
||||
}).wait();
|
||||
QtPromise::QPromise<int>::resolve(42)
|
||||
.then([&](int res) {
|
||||
return QtPromise::QPromise<Data>::resolve(Data{res + 1});
|
||||
})
|
||||
.then([&](const Data& res) {
|
||||
value = res.value();
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the input promise data
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the input promise data
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 43);
|
||||
}
|
||||
{ // should not create value on continutation if rejected
|
||||
{ // should not create value on continuation if rejected
|
||||
Data::logs().reset();
|
||||
QPromise<int>::resolve(42).then([&]() {
|
||||
return QPromise<Data>::reject(QString("foo"));
|
||||
}).wait();
|
||||
QtPromise::QPromise<int>::resolve(42)
|
||||
.then([&]() {
|
||||
return QtPromise::QPromise<Data>::reject(QString{"foo"});
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 0);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
@ -260,25 +189,29 @@ void tst_benchmark::valueDelayed()
|
||||
|
||||
void tst_benchmark::valueFinally()
|
||||
{
|
||||
{ // should not copy the value on continutation if fulfilled
|
||||
{ // should not copy the value on continuation if fulfilled
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<Data>::resolve(Data(42)).finally([&]() {
|
||||
value = 42;
|
||||
}).wait();
|
||||
QtPromise::QPromise<Data>::resolve(Data{42})
|
||||
.finally([&]() {
|
||||
value = 42;
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the input and output promise data
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the input and output promise data
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 42);
|
||||
}
|
||||
{ // should not create value on continutation if rejected
|
||||
{ // should not create value on continuation if rejected
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<Data>::reject(QString("foo")).finally([&]() {
|
||||
value = 42;
|
||||
}).wait();
|
||||
QtPromise::QPromise<Data>::reject(QString{"foo"})
|
||||
.finally([&]() {
|
||||
value = 42;
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 0);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
@ -290,25 +223,29 @@ void tst_benchmark::valueFinally()
|
||||
|
||||
void tst_benchmark::valueTap()
|
||||
{
|
||||
{ // should not copy the value on continutation if fulfilled
|
||||
{ // should not copy the value on continuation if fulfilled
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<Data>::resolve(Data(42)).tap([&](const Data& res) {
|
||||
value = res.value();
|
||||
}).wait();
|
||||
QtPromise::QPromise<Data>::resolve(Data{42})
|
||||
.tap([&](const Data& res) {
|
||||
value = res.value();
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the input and output promise data
|
||||
QCOMPARE(Data::logs().move, 1); // move value to the input and output promise data
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 42);
|
||||
}
|
||||
{ // should not create value on continutation if rejected
|
||||
{ // should not create value on continuation if rejected
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<Data>::reject(QString("foo")).tap([&](const Data& res) {
|
||||
value = res.value();
|
||||
}).wait();
|
||||
QtPromise::QPromise<Data>::reject(QString{"foo"})
|
||||
.tap([&](const Data& res) {
|
||||
value = res.value();
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 0);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
@ -320,26 +257,28 @@ void tst_benchmark::valueTap()
|
||||
|
||||
void tst_benchmark::errorReject()
|
||||
{
|
||||
{ // should create one copy of the error when rejected by rvalue
|
||||
{ // should create one copy of the error when rejected by rvalue
|
||||
Data::logs().reset();
|
||||
QPromise<int>([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
reject(Data(42));
|
||||
}).wait();
|
||||
QtPromise::QPromise<int>{[&](const QtPromise::QPromiseResolve<int>&,
|
||||
const QtPromise::QPromiseReject<int>& reject) {
|
||||
reject(Data{42});
|
||||
}}.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1 + EXCEPT_CALL_COPY_CTOR); // copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().copy, 1 + EXCEPT_CALL_COPY_CTOR); // copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
{ // should create one copy of the error when rejected by lvalue (no extra copy)
|
||||
{ // should create one copy of the error when rejected by lvalue (no extra copy)
|
||||
Data::logs().reset();
|
||||
QPromise<int>([&](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
Data error(42);
|
||||
QtPromise::QPromise<int>{[&](const QtPromise::QPromiseResolve<int>&,
|
||||
const QtPromise::QPromiseReject<int>& reject) {
|
||||
Data error{42};
|
||||
reject(error);
|
||||
}).wait();
|
||||
}}.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1 + EXCEPT_CALL_COPY_CTOR); // copy value to the promise data
|
||||
QCOMPARE(Data::logs().copy, 1 + EXCEPT_CALL_COPY_CTOR); // copy value to the promise data
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
@ -347,30 +286,37 @@ void tst_benchmark::errorReject()
|
||||
|
||||
void tst_benchmark::errorThen()
|
||||
{
|
||||
{ // should not copy error on continutation if rejected
|
||||
{ // should not copy error on continuation if rejected
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<void>::reject(Data(42)).fail([&](const Data& res) {
|
||||
value = res.value();
|
||||
}).wait();
|
||||
QtPromise::QPromise<void>::reject(Data{42})
|
||||
.fail([&](const Data& res) {
|
||||
value = res.value();
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1 + 2 * EXCEPT_CALL_COPY_CTOR); // (initial) copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().copy,
|
||||
1 + 2 * EXCEPT_CALL_COPY_CTOR); // (initial) copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 42);
|
||||
}
|
||||
{ // should not copy error on continutation if rethrown
|
||||
{ // should not copy error on continuation if rethrown
|
||||
int value = -1;
|
||||
Data::logs().reset();
|
||||
QPromise<void>::reject(Data(42)).fail([](const Data&) {
|
||||
throw;
|
||||
}).fail([&](const Data& res) {
|
||||
value = res.value();
|
||||
}).wait();
|
||||
QtPromise::QPromise<void>::reject(Data{42})
|
||||
.fail([](const Data&) {
|
||||
throw;
|
||||
})
|
||||
.fail([&](const Data& res) {
|
||||
value = res.value();
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1 + 4 * EXCEPT_CALL_COPY_CTOR); // (initial) copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().copy,
|
||||
1 + 4 * EXCEPT_CALL_COPY_CTOR); // (initial) copy value in std::exception_ptr
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
QCOMPARE(value, 42);
|
||||
|
10
tests/auto/qtpromise/cpp14/CMakeLists.txt
Normal file
10
tests/auto/qtpromise/cpp14/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# https://cmake.org/cmake/help/latest/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.html
|
||||
# https://gcc.gnu.org/projects/cxx-status.html#cxx14
|
||||
if ("cxx_generic_lambdas" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
qtpromise_add_tests(cpp14
|
||||
SOURCES
|
||||
tst_argsof_lambda_auto.cpp
|
||||
tst_resolver_lambda_auto.cpp
|
||||
)
|
||||
endif()
|
33
tests/auto/qtpromise/cpp14/tst_argsof_lambda_auto.cpp
Normal file
33
tests/auto/qtpromise/cpp14/tst_argsof_lambda_auto.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include <QtPromise>
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromisePrivate;
|
||||
|
||||
class tst_cpp14_argsof_lambda_auto : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void lambdaAutoArgs();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_cpp14_argsof_lambda_auto)
|
||||
#include "tst_argsof_lambda_auto.moc"
|
||||
|
||||
void tst_cpp14_argsof_lambda_auto::lambdaAutoArgs()
|
||||
{
|
||||
auto lOneArg = [](auto) {};
|
||||
auto lManyArgs = [](const auto&, auto, auto) {};
|
||||
auto lMutable = [](const auto&, auto) mutable {};
|
||||
|
||||
Q_STATIC_ASSERT((ArgsOf<decltype(lOneArg)>::count == 0));
|
||||
Q_STATIC_ASSERT((ArgsOf<decltype(lManyArgs)>::count == 0));
|
||||
Q_STATIC_ASSERT((ArgsOf<decltype(lMutable)>::count == 0));
|
||||
}
|
77
tests/auto/qtpromise/cpp14/tst_resolver_lambda_auto.cpp
Normal file
77
tests/auto/qtpromise/cpp14/tst_resolver_lambda_auto.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "../shared/utils.h"
|
||||
|
||||
#include <QtPromise>
|
||||
#include <QtTest>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class tst_cpp14_resolver_lambda_auto : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void resolverTwoAutoArgs();
|
||||
void resolverTwoAutoArgs_void();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_cpp14_resolver_lambda_auto)
|
||||
#include "tst_resolver_lambda_auto.moc"
|
||||
|
||||
void tst_cpp14_resolver_lambda_auto::resolverTwoAutoArgs()
|
||||
{
|
||||
QtPromise::QPromise<int> p0{[](auto resolve, auto reject) {
|
||||
Q_UNUSED(reject)
|
||||
resolve(42);
|
||||
}};
|
||||
QtPromise::QPromise<int> p1{[](auto resolve, const auto& reject) {
|
||||
Q_UNUSED(reject)
|
||||
resolve(42);
|
||||
}};
|
||||
QtPromise::QPromise<int> p2{[](const auto& resolve, auto reject) {
|
||||
Q_UNUSED(reject)
|
||||
resolve(42);
|
||||
}};
|
||||
QtPromise::QPromise<int> p3{[](const auto& resolve, const auto& reject) {
|
||||
Q_UNUSED(reject)
|
||||
resolve(42);
|
||||
}};
|
||||
|
||||
for (const auto& p : {p0, p1, p2, p3}) {
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, -1), -1);
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_cpp14_resolver_lambda_auto::resolverTwoAutoArgs_void()
|
||||
{
|
||||
QtPromise::QPromise<void> p0{[](auto resolve, auto reject) {
|
||||
Q_UNUSED(reject)
|
||||
resolve();
|
||||
}};
|
||||
QtPromise::QPromise<void> p1{[](auto resolve, const auto& reject) {
|
||||
Q_UNUSED(reject)
|
||||
resolve();
|
||||
}};
|
||||
QtPromise::QPromise<void> p2{[](const auto& resolve, auto reject) {
|
||||
Q_UNUSED(reject)
|
||||
resolve();
|
||||
}};
|
||||
QtPromise::QPromise<void> p3{[](const auto& resolve, const auto& reject) {
|
||||
Q_UNUSED(reject)
|
||||
resolve();
|
||||
}};
|
||||
|
||||
for (const auto& p : {p0, p1, p2, p3}) {
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForError(p, -1), -1);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
}
|
13
tests/auto/qtpromise/deprecations/CMakeLists.txt
Normal file
13
tests/auto/qtpromise/deprecations/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Disable deprecation warnings since we are testing deprecated features.
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
add_compile_options(-Wno-deprecated-declarations)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
add_compile_options(/wd4996)
|
||||
endif()
|
||||
|
||||
qtpromise_add_tests(deprecation
|
||||
SOURCES
|
||||
tst_helpers_qpromise.cpp
|
||||
tst_helpers_qpromiseall.cpp
|
||||
tst_qpromise_all.cpp
|
||||
)
|
275
tests/auto/qtpromise/deprecations/tst_helpers_qpromise.cpp
Normal file
275
tests/auto/qtpromise/deprecations/tst_helpers_qpromise.cpp
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "../shared/data.h"
|
||||
#include "../shared/utils.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
#include <QtPromise>
|
||||
#include <QtTest>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class tst_deprecations_helpers_qpromise : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void value();
|
||||
void noValue();
|
||||
void moveRValue();
|
||||
void copyLValue();
|
||||
void qtSharedPtr();
|
||||
void stdSharedPtr();
|
||||
void typedPromise();
|
||||
void voidPromise();
|
||||
void typedFuture();
|
||||
void voidFuture();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_deprecations_helpers_qpromise)
|
||||
#include "tst_helpers_qpromise.moc"
|
||||
|
||||
void tst_deprecations_helpers_qpromise::value()
|
||||
{
|
||||
int v0 = 42;
|
||||
const int v1 = 42;
|
||||
|
||||
auto p0 = QtPromise::qPromise(42);
|
||||
auto p1 = QtPromise::qPromise(v0);
|
||||
auto p2 = QtPromise::qPromise(v1);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p2), QtPromise::QPromise<int>>::value));
|
||||
|
||||
for (const auto& p : {p0, p1, p2}) {
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
}
|
||||
for (const auto& p : {p0, p1, p2}) {
|
||||
QCOMPARE(waitForValue(p, -1), 42);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromise::noValue()
|
||||
{
|
||||
auto p = QtPromise::qPromise();
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromise::moveRValue()
|
||||
{
|
||||
Data::logs().reset();
|
||||
|
||||
{
|
||||
auto p = QtPromise::qPromise(Data{42}).wait();
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<Data>>::value));
|
||||
}
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 1);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromise::copyLValue()
|
||||
{
|
||||
Data::logs().reset();
|
||||
|
||||
{
|
||||
Data value{42};
|
||||
auto p = QtPromise::qPromise(value).wait();
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<Data>>::value));
|
||||
}
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 1);
|
||||
QCOMPARE(Data::logs().copy, 1);
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
|
||||
// https://github.com/simonbrunel/qtpromise/issues/6
|
||||
void tst_deprecations_helpers_qpromise::qtSharedPtr()
|
||||
{
|
||||
using DataSPtr = QSharedPointer<Data>;
|
||||
|
||||
Data::logs().reset();
|
||||
|
||||
QWeakPointer<Data> wptr;
|
||||
|
||||
{
|
||||
auto sptr0 = DataSPtr::create(42);
|
||||
const DataSPtr sptr1 = sptr0;
|
||||
|
||||
auto p0 = QtPromise::qPromise(DataSPtr::create(42));
|
||||
auto p1 = QtPromise::qPromise(sptr0);
|
||||
auto p2 = QtPromise::qPromise(sptr1);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<DataSPtr>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::QPromise<DataSPtr>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p2), QtPromise::QPromise<DataSPtr>>::value));
|
||||
|
||||
QCOMPARE(waitForValue(p1, DataSPtr{}), sptr0);
|
||||
QCOMPARE(waitForValue(p2, DataSPtr{}), sptr1);
|
||||
|
||||
wptr = sptr0;
|
||||
|
||||
QCOMPARE(wptr.isNull(), false);
|
||||
QCOMPARE(Data::logs().refs, 2);
|
||||
}
|
||||
|
||||
QCOMPARE(wptr.isNull(), true);
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 2);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
|
||||
// https://github.com/simonbrunel/qtpromise/issues/6
|
||||
void tst_deprecations_helpers_qpromise::stdSharedPtr()
|
||||
{
|
||||
using DataSPtr = std::shared_ptr<Data>;
|
||||
|
||||
Data::logs().reset();
|
||||
|
||||
std::weak_ptr<Data> wptr;
|
||||
|
||||
{
|
||||
auto sptr0 = std::make_shared<Data>(42);
|
||||
const DataSPtr sptr1 = sptr0;
|
||||
|
||||
auto p0 = QtPromise::qPromise(std::make_shared<Data>(42));
|
||||
auto p1 = QtPromise::qPromise(sptr0);
|
||||
auto p2 = QtPromise::qPromise(sptr1);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<DataSPtr>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::QPromise<DataSPtr>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p2), QtPromise::QPromise<DataSPtr>>::value));
|
||||
|
||||
QCOMPARE(waitForValue(p1, DataSPtr{}), sptr0);
|
||||
QCOMPARE(waitForValue(p2, DataSPtr{}), sptr1);
|
||||
|
||||
wptr = sptr0;
|
||||
|
||||
QCOMPARE(wptr.use_count(), 4l);
|
||||
QCOMPARE(Data::logs().refs, 2);
|
||||
}
|
||||
|
||||
QCOMPARE(wptr.use_count(), 0l);
|
||||
|
||||
QCOMPARE(Data::logs().ctor, 2);
|
||||
QCOMPARE(Data::logs().copy, 0);
|
||||
QCOMPARE(Data::logs().move, 0);
|
||||
QCOMPARE(Data::logs().refs, 0);
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromise::typedPromise()
|
||||
{
|
||||
auto resolver = [](const QtPromise::QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(42);
|
||||
});
|
||||
};
|
||||
|
||||
QtPromise::QPromise<int> v0{resolver};
|
||||
const QtPromise::QPromise<int> v1 = v0;
|
||||
|
||||
auto p0 = QtPromise::qPromise(QtPromise::QPromise<int>{resolver});
|
||||
auto p1 = QtPromise::qPromise(v0);
|
||||
auto p2 = QtPromise::qPromise(v1);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p2), QtPromise::QPromise<int>>::value));
|
||||
|
||||
for (const auto& promise : {p0, p1, p2}) {
|
||||
QCOMPARE(promise.isPending(), true);
|
||||
}
|
||||
for (const auto& promise : {p0, p1, p2}) {
|
||||
QCOMPARE(waitForValue(promise, -1), 42);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromise::voidPromise()
|
||||
{
|
||||
auto resolver = [](const QtPromise::QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
QtPromise::QPromise<void> v0{resolver};
|
||||
const QtPromise::QPromise<void> v1 = v0;
|
||||
|
||||
auto p0 = QtPromise::qPromise(QtPromise::QPromise<void>{resolver});
|
||||
auto p1 = QtPromise::qPromise(v0);
|
||||
auto p2 = QtPromise::qPromise(v1);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<void>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::QPromise<void>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p2), QtPromise::QPromise<void>>::value));
|
||||
|
||||
for (const auto& promise : {p0, p1, p2}) {
|
||||
QCOMPARE(promise.isPending(), true);
|
||||
}
|
||||
for (const auto& promise : {p0, p1, p2}) {
|
||||
QCOMPARE(waitForValue(promise, -1, 42), 42);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromise::typedFuture()
|
||||
{
|
||||
auto fn = []() {
|
||||
return 42;
|
||||
};
|
||||
QFuture<int> v0 = QtConcurrent::run(fn);
|
||||
const QFuture<int> v1 = v0;
|
||||
|
||||
auto p0 = QtPromise::qPromise(QtConcurrent::run(fn));
|
||||
auto p1 = QtPromise::qPromise(v0);
|
||||
auto p2 = QtPromise::qPromise(v1);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p2), QtPromise::QPromise<int>>::value));
|
||||
|
||||
for (const auto& promise : {p0, p1, p2}) {
|
||||
QCOMPARE(promise.isPending(), true);
|
||||
}
|
||||
for (const auto& promise : {p0, p1, p2}) {
|
||||
QCOMPARE(waitForValue(promise, -1), 42);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromise::voidFuture()
|
||||
{
|
||||
auto fn = []() {};
|
||||
QFuture<void> v0 = QtConcurrent::run(fn);
|
||||
const QFuture<void> v1 = v0;
|
||||
|
||||
auto p0 = QtPromise::qPromise(QtConcurrent::run(fn));
|
||||
auto p1 = QtPromise::qPromise(v0);
|
||||
auto p2 = QtPromise::qPromise(v1);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p0), QtPromise::QPromise<void>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p1), QtPromise::QPromise<void>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p2), QtPromise::QPromise<void>>::value));
|
||||
|
||||
for (const auto& promise : {p0, p1, p2}) {
|
||||
QCOMPARE(promise.isPending(), true);
|
||||
}
|
||||
for (const auto& promise : {p0, p1, p2}) {
|
||||
QCOMPARE(waitForValue(promise, -1, 42), 42);
|
||||
}
|
||||
}
|
218
tests/auto/qtpromise/deprecations/tst_helpers_qpromiseall.cpp
Normal file
218
tests/auto/qtpromise/deprecations/tst_helpers_qpromiseall.cpp
Normal file
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "../shared/utils.h"
|
||||
|
||||
#include <QtPromise>
|
||||
#include <QtTest>
|
||||
|
||||
class tst_deprecations_helpers_qpromiseall : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void emptySequence_void();
|
||||
void allPromisesSucceed();
|
||||
void allPromisesSucceed_void();
|
||||
void atLeastOnePromiseReject();
|
||||
void atLeastOnePromiseReject_void();
|
||||
void preserveOrder();
|
||||
void sequenceTypes();
|
||||
void sequenceTypes_void();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_deprecations_helpers_qpromiseall)
|
||||
#include "tst_helpers_qpromiseall.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template<class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
Q_STATIC_ASSERT((std::is_same<typename Sequence::value_type, QtPromise::QPromise<int>>::value));
|
||||
|
||||
static void exec()
|
||||
{
|
||||
Sequence promises{QtPromise::resolve(42), QtPromise::resolve(43), QtPromise::resolve(44)};
|
||||
|
||||
promises.push_back(QtPromise::resolve(45));
|
||||
promises.insert(++promises.begin(), QtPromise::resolve(46));
|
||||
promises.pop_back();
|
||||
|
||||
auto p = qPromiseAll(promises);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>{}), (QVector<int>{42, 46, 43, 44}));
|
||||
}
|
||||
};
|
||||
|
||||
template<template<typename, typename...> class Sequence, typename... Args>
|
||||
struct SequenceTester<Sequence<QtPromise::QPromise<void>, Args...>>
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
Sequence<QtPromise::QPromise<void>, Args...> promises{QtPromise::resolve(),
|
||||
QtPromise::resolve(),
|
||||
QtPromise::resolve()};
|
||||
|
||||
promises.push_back(QtPromise::resolve());
|
||||
promises.insert(++promises.begin(), QtPromise::resolve());
|
||||
promises.pop_back();
|
||||
|
||||
auto p = qPromiseAll(promises);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::emptySequence()
|
||||
{
|
||||
auto p = qPromiseAll(QVector<QtPromise::QPromise<int>>());
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>{}), QVector<int>{});
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::emptySequence_void()
|
||||
{
|
||||
auto p = qPromiseAll(QVector<QtPromise::QPromise<void>>());
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::allPromisesSucceed()
|
||||
{
|
||||
auto p0 = QtPromise::resolve(42);
|
||||
auto p1 = QtPromise::resolve(44);
|
||||
auto p2 = QtPromise::QPromise<int>{[](const QtPromise::QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(43);
|
||||
});
|
||||
}};
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QtPromise::QPromise<int>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>{}), (QVector<int>{42, 43, 44}));
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::allPromisesSucceed_void()
|
||||
{
|
||||
auto p0 = QtPromise::resolve();
|
||||
auto p1 = QtPromise::resolve();
|
||||
auto p2 = QtPromise::QPromise<void>{[](const QtPromise::QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve();
|
||||
});
|
||||
}};
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QtPromise::QPromise<void>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::atLeastOnePromiseReject()
|
||||
{
|
||||
auto p0 = QtPromise::resolve(42);
|
||||
auto p1 = QtPromise::resolve(44);
|
||||
auto p2 = QtPromise::QPromise<int>{
|
||||
[](const QtPromise::QPromiseResolve<int>&, const QtPromise::QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString{"foo"});
|
||||
});
|
||||
}};
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QtPromise::QPromise<int>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString{}), QString{"foo"});
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::atLeastOnePromiseReject_void()
|
||||
{
|
||||
auto p0 = QtPromise::resolve();
|
||||
auto p1 = QtPromise::resolve();
|
||||
auto p2 = QtPromise::QPromise<void>{
|
||||
[](const QtPromise::QPromiseResolve<void>&, const QtPromise::QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString{"foo"});
|
||||
});
|
||||
}};
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QtPromise::QPromise<void>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString{}), QString{"foo"});
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::preserveOrder()
|
||||
{
|
||||
auto p0 = QtPromise::resolve(42).delay(500);
|
||||
auto p1 = QtPromise::resolve(43).delay(100);
|
||||
auto p2 = QtPromise::resolve(44).delay(250);
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QtPromise::QPromise<int>>{p0, p1, p2});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(p1.isPending(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>{}), (QVector<int>{42, 43, 44}));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
// QVector::push_back/append isn't supported since it requires a default
|
||||
// constructor (see https://github.com/simonbrunel/qtpromise/issues/3)
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<QtPromise::QPromise<int>>>::exec();
|
||||
// SequenceTester<QVector<QtPromise::QPromise<int>>>::exec();
|
||||
SequenceTester<std::list<QtPromise::QPromise<int>>>::exec();
|
||||
SequenceTester<std::vector<QtPromise::QPromise<int>>>::exec();
|
||||
}
|
||||
|
||||
void tst_deprecations_helpers_qpromiseall::sequenceTypes_void()
|
||||
{
|
||||
SequenceTester<QList<QtPromise::QPromise<void>>>::exec();
|
||||
// SequenceTester<QVector<QtPromise::QPromise<void>>>::exec();
|
||||
SequenceTester<std::list<QtPromise::QPromise<void>>>::exec();
|
||||
SequenceTester<std::vector<QtPromise::QPromise<void>>>::exec();
|
||||
}
|
227
tests/auto/qtpromise/deprecations/tst_qpromise_all.cpp
Normal file
227
tests/auto/qtpromise/deprecations/tst_qpromise_all.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "../shared/utils.h"
|
||||
|
||||
#include <QtPromise>
|
||||
#include <QtTest>
|
||||
|
||||
class tst_deprecations_qpromise_all : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void emptySequence_void();
|
||||
void allPromisesSucceed();
|
||||
void allPromisesSucceed_void();
|
||||
void atLeastOnePromiseReject();
|
||||
void atLeastOnePromiseReject_void();
|
||||
void preserveOrder();
|
||||
void sequenceTypes();
|
||||
void sequenceTypes_void();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_deprecations_qpromise_all)
|
||||
#include "tst_qpromise_all.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template<class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
Q_STATIC_ASSERT((std::is_same<typename Sequence::value_type, QtPromise::QPromise<int>>::value));
|
||||
|
||||
static void exec()
|
||||
{
|
||||
Sequence promises{QtPromise::resolve(42), QtPromise::resolve(43), QtPromise::resolve(44)};
|
||||
|
||||
promises.push_back(QtPromise::resolve(45));
|
||||
promises.insert(++promises.begin(), QtPromise::resolve(46));
|
||||
promises.pop_back();
|
||||
|
||||
auto p = QtPromise::QPromise<int>::all(promises);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>{}), (QVector<int>{42, 46, 43, 44}));
|
||||
}
|
||||
};
|
||||
|
||||
template<template<typename, typename...> class Sequence, typename... Args>
|
||||
struct SequenceTester<Sequence<QtPromise::QPromise<void>, Args...>>
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
Sequence<QtPromise::QPromise<void>, Args...> promises{QtPromise::resolve(),
|
||||
QtPromise::resolve(),
|
||||
QtPromise::resolve()};
|
||||
|
||||
promises.push_back(QtPromise::resolve());
|
||||
promises.insert(++promises.begin(), QtPromise::resolve());
|
||||
promises.pop_back();
|
||||
|
||||
auto p = QtPromise::QPromise<void>::all(promises);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_deprecations_qpromise_all::emptySequence()
|
||||
{
|
||||
auto p = QtPromise::QPromise<int>::all(QVector<QtPromise::QPromise<int>>{});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>{}), QVector<int>{});
|
||||
}
|
||||
|
||||
void tst_deprecations_qpromise_all::emptySequence_void()
|
||||
{
|
||||
auto p = QtPromise::QPromise<void>::all(QVector<QtPromise::QPromise<void>>{});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_deprecations_qpromise_all::allPromisesSucceed()
|
||||
{
|
||||
auto p0 = QtPromise::resolve(42);
|
||||
auto p1 = QtPromise::resolve(44);
|
||||
auto p2 = QtPromise::QPromise<int>{[](const QtPromise::QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve(43);
|
||||
});
|
||||
}};
|
||||
|
||||
auto p = QtPromise::QPromise<int>::all(QVector<QtPromise::QPromise<int>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>{}), (QVector<int>{42, 43, 44}));
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_deprecations_qpromise_all::allPromisesSucceed_void()
|
||||
{
|
||||
auto p0 = QtPromise::resolve();
|
||||
auto p1 = QtPromise::resolve();
|
||||
auto p2 = QtPromise::QPromise<void>{[](const QtPromise::QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
resolve();
|
||||
});
|
||||
}};
|
||||
|
||||
auto p = QtPromise::QPromise<void>::all(QVector<QtPromise::QPromise<void>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_deprecations_qpromise_all::atLeastOnePromiseReject()
|
||||
{
|
||||
auto p0 = QtPromise::resolve(42);
|
||||
auto p1 = QtPromise::resolve(44);
|
||||
auto p2 = QtPromise::QPromise<int>{
|
||||
[](const QtPromise::QPromiseResolve<int>&, const QtPromise::QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString{"foo"});
|
||||
});
|
||||
}};
|
||||
|
||||
auto p = QtPromise::QPromise<int>::all(QVector<QtPromise::QPromise<int>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString{}), QString{"foo"});
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_deprecations_qpromise_all::atLeastOnePromiseReject_void()
|
||||
{
|
||||
auto p0 = QtPromise::resolve();
|
||||
auto p1 = QtPromise::resolve();
|
||||
auto p2 = QtPromise::QPromise<void>{
|
||||
[](const QtPromise::QPromiseResolve<void>&, const QtPromise::QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
reject(QString{"foo"});
|
||||
});
|
||||
}};
|
||||
|
||||
auto p = QtPromise::QPromise<void>::all(QVector<QtPromise::QPromise<void>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString{}), QString{"foo"});
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_deprecations_qpromise_all::preserveOrder()
|
||||
{
|
||||
auto p0 = QtPromise::resolve(42).delay(500);
|
||||
auto p1 = QtPromise::resolve(43).delay(100);
|
||||
auto p2 = QtPromise::resolve(44).delay(250);
|
||||
|
||||
auto p = QtPromise::QPromise<int>::all(QVector<QtPromise::QPromise<int>>{p0, p1, p2});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<QVector<int>>>::value));
|
||||
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(p1.isPending(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>{}), (QVector<int>{42, 43, 44}));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
// QVector::push_back/append isn't supported since it requires a default
|
||||
// constructor (see https://github.com/simonbrunel/qtpromise/issues/3)
|
||||
|
||||
void tst_deprecations_qpromise_all::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<QtPromise::QPromise<int>>>::exec();
|
||||
// SequenceTester<QVector<QtPromise::QPromise<int>>>::exec();
|
||||
SequenceTester<std::list<QtPromise::QPromise<int>>>::exec();
|
||||
SequenceTester<std::vector<QtPromise::QPromise<int>>>::exec();
|
||||
}
|
||||
|
||||
void tst_deprecations_qpromise_all::sequenceTypes_void()
|
||||
{
|
||||
SequenceTester<QList<QtPromise::QPromise<void>>>::exec();
|
||||
// SequenceTester<QVector<QtPromise::QPromise<void>>>::exec();
|
||||
SequenceTester<std::list<QtPromise::QPromise<void>>>::exec();
|
||||
SequenceTester<std::vector<QtPromise::QPromise<void>>>::exec();
|
||||
}
|
4
tests/auto/qtpromise/exceptions/CMakeLists.txt
Normal file
4
tests/auto/qtpromise/exceptions/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
qtpromise_add_test(exceptions
|
||||
SOURCES
|
||||
tst_exceptions.cpp
|
||||
)
|
68
tests/auto/qtpromise/exceptions/tst_exceptions.cpp
Normal file
68
tests/auto/qtpromise/exceptions/tst_exceptions.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "../shared/utils.h"
|
||||
|
||||
#include <QtConcurrent>
|
||||
#include <QtPromise>
|
||||
#include <QtTest>
|
||||
|
||||
class tst_exceptions : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void canceled();
|
||||
void context();
|
||||
void conversion();
|
||||
void timeout();
|
||||
void undefined();
|
||||
|
||||
}; // class tst_exceptions
|
||||
|
||||
QTEST_MAIN(tst_exceptions)
|
||||
#include "tst_exceptions.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template<class E>
|
||||
void verify()
|
||||
{
|
||||
auto p = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
throw E();
|
||||
}));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForRejected<E>(p), true);
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_exceptions::canceled()
|
||||
{
|
||||
verify<QtPromise::QPromiseCanceledException>();
|
||||
}
|
||||
|
||||
void tst_exceptions::context()
|
||||
{
|
||||
verify<QtPromise::QPromiseContextException>();
|
||||
}
|
||||
|
||||
void tst_exceptions::conversion()
|
||||
{
|
||||
verify<QtPromise::QPromiseConversionException>();
|
||||
}
|
||||
|
||||
void tst_exceptions::timeout()
|
||||
{
|
||||
verify<QtPromise::QPromiseTimeoutException>();
|
||||
}
|
||||
|
||||
void tst_exceptions::undefined()
|
||||
{
|
||||
verify<QtPromise::QPromiseUndefinedException>();
|
||||
}
|
4
tests/auto/qtpromise/future/CMakeLists.txt
Normal file
4
tests/auto/qtpromise/future/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
qtpromise_add_test(future
|
||||
SOURCES
|
||||
tst_future.cpp
|
||||
)
|
@ -1,5 +0,0 @@
|
||||
QT += concurrent
|
||||
TARGET = tst_future
|
||||
SOURCES += $$PWD/tst_future.cpp
|
||||
|
||||
include(../qtpromise.pri)
|
@ -1,12 +1,14 @@
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
/*
|
||||
* Copyright (c) Simon Brunel, https://github.com/simonbrunel
|
||||
*
|
||||
* This source code is licensed under the MIT license found in
|
||||
* the LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
// Qt
|
||||
#include <QtConcurrent>
|
||||
#include <QtPromise>
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_future : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -33,14 +35,12 @@ private Q_SLOTS:
|
||||
class MyException : public QException
|
||||
{
|
||||
public:
|
||||
MyException(const QString& error)
|
||||
: m_error(error)
|
||||
{ }
|
||||
MyException(const QString& error) : m_error{error} { }
|
||||
|
||||
const QString& error() const { return m_error; }
|
||||
|
||||
void raise() const { throw *this; }
|
||||
MyException* clone() const { return new MyException(*this); }
|
||||
MyException* clone() const { return new MyException{*this}; }
|
||||
|
||||
private:
|
||||
QString m_error;
|
||||
@ -52,16 +52,16 @@ QTEST_MAIN(tst_future)
|
||||
void tst_future::fulfilled()
|
||||
{
|
||||
int result = -1;
|
||||
auto p = qPromise(QtConcurrent::run([]() {
|
||||
auto p = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
return 42;
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<int>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.then([&](int res) {
|
||||
result = res;
|
||||
}).wait();
|
||||
result = res;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(result, 42);
|
||||
@ -70,14 +70,14 @@ void tst_future::fulfilled()
|
||||
void tst_future::fulfilled_void()
|
||||
{
|
||||
int result = -1;
|
||||
auto p = qPromise(QtConcurrent::run([]() { }));
|
||||
auto p = QtPromise::resolve(QtConcurrent::run([]() {}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.then([&]() {
|
||||
result = 42;
|
||||
}).wait();
|
||||
result = 42;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(result, 42);
|
||||
@ -86,159 +86,165 @@ void tst_future::fulfilled_void()
|
||||
void tst_future::rejected()
|
||||
{
|
||||
QString error;
|
||||
auto p = qPromise(QtConcurrent::run([]() {
|
||||
throw MyException("foo");
|
||||
auto p = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
throw MyException{"foo"};
|
||||
return 42;
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<int>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.fail([&](const MyException& e) {
|
||||
error = e.error();
|
||||
return -1;
|
||||
}).wait();
|
||||
error = e.error();
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(error, QString("foo"));
|
||||
QCOMPARE(error, QString{"foo"});
|
||||
}
|
||||
|
||||
void tst_future::rejected_void()
|
||||
{
|
||||
QString error;
|
||||
auto p = qPromise(QtConcurrent::run([]() {
|
||||
throw MyException("foo");
|
||||
auto p = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
throw MyException{"foo"};
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.fail([&](const MyException& e) {
|
||||
error = e.error();
|
||||
}).wait();
|
||||
error = e.error();
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(error, QString("foo"));
|
||||
QCOMPARE(error, QString{"foo"});
|
||||
}
|
||||
|
||||
void tst_future::unhandled()
|
||||
{
|
||||
QString error;
|
||||
auto p = qPromise(QtConcurrent::run([]() {
|
||||
throw QString("foo");
|
||||
auto p = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
throw QString{"foo"};
|
||||
return 42;
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<int>>::value));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.fail([&](const QString& err) {
|
||||
error += err;
|
||||
return -1;
|
||||
}).fail([&](const QUnhandledException&) {
|
||||
error += "bar";
|
||||
return -1;
|
||||
}).wait();
|
||||
error += err;
|
||||
return -1;
|
||||
})
|
||||
.fail([&](const QUnhandledException&) {
|
||||
error += "bar";
|
||||
return -1;
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(error, QString("bar"));
|
||||
QCOMPARE(error, QString{"bar"});
|
||||
}
|
||||
|
||||
void tst_future::unhandled_void()
|
||||
{
|
||||
QString error;
|
||||
auto p = qPromise(QtConcurrent::run([]() {
|
||||
throw QString("foo");
|
||||
auto p = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
throw QString{"foo"};
|
||||
}));
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QtPromise::QPromise<void>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.fail([&](const QString& err) {
|
||||
error += err;
|
||||
}).fail([&](const QUnhandledException&) {
|
||||
error += "bar";
|
||||
}).wait();
|
||||
error += err;
|
||||
})
|
||||
.fail([&](const QUnhandledException&) {
|
||||
error += "bar";
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(error, QString("bar"));
|
||||
QCOMPARE(error, QString{"bar"});
|
||||
}
|
||||
|
||||
void tst_future::canceled()
|
||||
{
|
||||
QString error;
|
||||
auto p = qPromise(QFuture<int>()); // Constructs an empty, canceled future.
|
||||
auto p = QtPromise::resolve(QFuture<int>()); // Constructs an empty, canceled future.
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.fail([&](const QPromiseCanceledException&) {
|
||||
error = "canceled";
|
||||
return -1;
|
||||
}).wait();
|
||||
p.fail([&](const QtPromise::QPromiseCanceledException&) {
|
||||
error = "canceled";
|
||||
return -1;
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(error, QString("canceled"));
|
||||
QCOMPARE(error, QString{"canceled"});
|
||||
}
|
||||
|
||||
void tst_future::canceled_void()
|
||||
{
|
||||
QString error;
|
||||
auto p = qPromise(QFuture<void>()); // Constructs an empty, canceled future.
|
||||
auto p = QtPromise::resolve(QFuture<void>()); // Constructs an empty, canceled future.
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.fail([&](const QPromiseCanceledException&) {
|
||||
error = "canceled";
|
||||
}).wait();
|
||||
p.fail([&](const QtPromise::QPromiseCanceledException&) {
|
||||
error = "canceled";
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(error, QString("canceled"));
|
||||
QCOMPARE(error, QString{"canceled"});
|
||||
}
|
||||
|
||||
void tst_future::canceledFromThread()
|
||||
{
|
||||
QString error;
|
||||
auto p = qPromise(QtConcurrent::run([]() {
|
||||
throw QPromiseCanceledException();
|
||||
auto p = QtPromise::resolve(QtConcurrent::run([]() {
|
||||
throw QtPromise::QPromiseCanceledException{};
|
||||
}));
|
||||
|
||||
QCOMPARE(p.isPending(), true);
|
||||
|
||||
p.fail([&](const QPromiseCanceledException&) {
|
||||
error = "bar";
|
||||
}).wait();
|
||||
p.fail([&](const QtPromise::QPromiseCanceledException&) {
|
||||
error = "bar";
|
||||
}).wait();
|
||||
|
||||
QCOMPARE(p.isRejected(), true);
|
||||
QCOMPARE(error, QString("bar"));
|
||||
QCOMPARE(error, QString{"bar"});
|
||||
}
|
||||
|
||||
void tst_future::then()
|
||||
{
|
||||
QString result;
|
||||
auto input = qPromise(42);
|
||||
auto input = QtPromise::resolve(42);
|
||||
auto output = input.then([](int res) {
|
||||
return QtConcurrent::run([=]() {
|
||||
return QString("foo%1").arg(res);
|
||||
return QString{"foo%1"}.arg(res);
|
||||
});
|
||||
});
|
||||
|
||||
QCOMPARE(input.isFulfilled(), true);
|
||||
QCOMPARE(output.isPending(), true);
|
||||
|
||||
output.then([&](const QString& res) {
|
||||
result = res;
|
||||
}).wait();
|
||||
output
|
||||
.then([&](const QString& res) {
|
||||
result = res;
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(output.isFulfilled(), true);
|
||||
QCOMPARE(result, QString("foo42"));
|
||||
QCOMPARE(result, QString{"foo42"});
|
||||
}
|
||||
|
||||
void tst_future::then_void()
|
||||
{
|
||||
QString result;
|
||||
auto input = qPromise();
|
||||
auto input = QtPromise::resolve();
|
||||
auto output = input.then([&]() {
|
||||
return QtConcurrent::run([&]() {
|
||||
result = "foo";
|
||||
@ -248,74 +254,86 @@ void tst_future::then_void()
|
||||
QCOMPARE(input.isFulfilled(), true);
|
||||
QCOMPARE(output.isPending(), true);
|
||||
|
||||
output.then([&]() {
|
||||
result += "bar";
|
||||
}).wait();
|
||||
output
|
||||
.then([&]() {
|
||||
result += "bar";
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(input.isFulfilled(), true);
|
||||
QCOMPARE(result, QString("foobar"));
|
||||
QCOMPARE(result, QString{"foobar"});
|
||||
}
|
||||
|
||||
void tst_future::fail()
|
||||
{
|
||||
QString result;
|
||||
auto input = QPromise<QString>::reject(MyException("bar"));
|
||||
auto input = QtPromise::QPromise<QString>::reject(MyException{"bar"});
|
||||
auto output = input.fail([](const MyException& e) {
|
||||
return QtConcurrent::run([](const QString& error) {
|
||||
return QString("foo%1").arg(error);
|
||||
}, e.error());
|
||||
return QtConcurrent::run(
|
||||
[](const QString& error) {
|
||||
return QString{"foo%1"}.arg(error);
|
||||
},
|
||||
e.error());
|
||||
});
|
||||
|
||||
QCOMPARE(input.isRejected(), true);
|
||||
QCOMPARE(output.isPending(), true);
|
||||
|
||||
output.then([&](const QString& res) {
|
||||
result = res;
|
||||
}).wait();
|
||||
output
|
||||
.then([&](const QString& res) {
|
||||
result = res;
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(output.isFulfilled(), true);
|
||||
QCOMPARE(result, QString("foobar"));
|
||||
QCOMPARE(result, QString{"foobar"});
|
||||
}
|
||||
|
||||
void tst_future::fail_void()
|
||||
{
|
||||
QString result;
|
||||
auto input = QPromise<void>::reject(MyException("bar"));
|
||||
auto input = QtPromise::QPromise<void>::reject(MyException{"bar"});
|
||||
auto output = input.fail([&](const MyException& e) {
|
||||
return QtConcurrent::run([&](const QString& error) {
|
||||
result = error;
|
||||
}, e.error());
|
||||
return QtConcurrent::run(
|
||||
[&](const QString& error) {
|
||||
result = error;
|
||||
},
|
||||
e.error());
|
||||
});
|
||||
|
||||
QCOMPARE(input.isRejected(), true);
|
||||
QCOMPARE(output.isPending(), true);
|
||||
|
||||
output.then([&]() {
|
||||
result = result.prepend("foo");
|
||||
}).wait();
|
||||
output
|
||||
.then([&]() {
|
||||
result = result.prepend("foo");
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(output.isFulfilled(), true);
|
||||
QCOMPARE(result, QString("foobar"));
|
||||
QCOMPARE(result, QString{"foobar"});
|
||||
}
|
||||
|
||||
void tst_future::finally()
|
||||
{
|
||||
auto input = qPromise(42);
|
||||
auto input = QtPromise::resolve(42);
|
||||
auto output = input.finally([]() {
|
||||
return QtConcurrent::run([]() {
|
||||
return QString("foo");
|
||||
return QString{"foo"};
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(output), QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(output), QtPromise::QPromise<int>>::value));
|
||||
|
||||
QCOMPARE(input.isFulfilled(), true);
|
||||
QCOMPARE(output.isPending(), true);
|
||||
|
||||
int value = -1;
|
||||
output.then([&](int res) {
|
||||
value = res;
|
||||
}).wait();
|
||||
output
|
||||
.then([&](int res) {
|
||||
value = res;
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(output.isFulfilled(), true);
|
||||
QCOMPARE(value, 42);
|
||||
@ -323,24 +341,26 @@ void tst_future::finally()
|
||||
|
||||
void tst_future::finallyRejected()
|
||||
{
|
||||
auto input = qPromise(42);
|
||||
auto input = QtPromise::resolve(42);
|
||||
auto output = input.finally([]() {
|
||||
return QtConcurrent::run([]() {
|
||||
throw MyException("foo");
|
||||
throw MyException{"foo"};
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(output), QPromise<int>>::value));
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(output), QtPromise::QPromise<int>>::value));
|
||||
|
||||
QCOMPARE(input.isFulfilled(), true);
|
||||
QCOMPARE(output.isPending(), true);
|
||||
|
||||
QString error;
|
||||
output.fail([&](const MyException& e) {
|
||||
error = e.error();
|
||||
return -1;
|
||||
}).wait();
|
||||
output
|
||||
.fail([&](const MyException& e) {
|
||||
error = e.error();
|
||||
return -1;
|
||||
})
|
||||
.wait();
|
||||
|
||||
QCOMPARE(output.isRejected(), true);
|
||||
QCOMPARE(error, QString("foo"));
|
||||
QCOMPARE(error, QString{"foo"});
|
||||
}
|
||||
|
12
tests/auto/qtpromise/helpers/CMakeLists.txt
Normal file
12
tests/auto/qtpromise/helpers/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
qtpromise_add_tests(helpers
|
||||
SOURCES
|
||||
tst_all.cpp
|
||||
tst_attempt.cpp
|
||||
tst_connect.cpp
|
||||
tst_each.cpp
|
||||
tst_filter.cpp
|
||||
tst_map.cpp
|
||||
tst_reduce.cpp
|
||||
tst_reject.cpp
|
||||
tst_resolve.cpp
|
||||
)
|
@ -1,4 +0,0 @@
|
||||
TARGET = tst_helpers_all
|
||||
SOURCES += $$PWD/tst_all.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
@ -1,221 +0,0 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_all : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void emptySequence_void();
|
||||
void allPromisesSucceed();
|
||||
void allPromisesSucceed_void();
|
||||
void atLeastOnePromiseReject();
|
||||
void atLeastOnePromiseReject_void();
|
||||
void preserveOrder();
|
||||
void sequenceTypes();
|
||||
void sequenceTypes_void();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_all)
|
||||
#include "tst_all.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
Q_STATIC_ASSERT((std::is_same<typename Sequence::value_type, QPromise<int>>::value));
|
||||
|
||||
static void exec()
|
||||
{
|
||||
Sequence promises{
|
||||
QtPromise::qPromise(42),
|
||||
QtPromise::qPromise(43),
|
||||
QtPromise::qPromise(44)
|
||||
};
|
||||
|
||||
promises.push_back(QtPromise::qPromise(45));
|
||||
promises.insert(++promises.begin(), QtPromise::qPromise(46));
|
||||
promises.pop_back();
|
||||
|
||||
auto p = QtPromise::qPromiseAll(promises);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 46, 43, 44}));
|
||||
}
|
||||
};
|
||||
|
||||
template <template <typename, typename...> class Sequence, typename ...Args>
|
||||
struct SequenceTester<Sequence<QPromise<void>, Args...>>
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
Sequence<QPromise<void>, Args...> promises{
|
||||
QtPromise::qPromise(),
|
||||
QtPromise::qPromise(),
|
||||
QtPromise::qPromise()
|
||||
};
|
||||
|
||||
promises.push_back(QtPromise::qPromise());
|
||||
promises.insert(++promises.begin(), QtPromise::qPromise());
|
||||
promises.pop_back();
|
||||
|
||||
auto p = QtPromise::qPromiseAll(promises);
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_helpers_all::emptySequence()
|
||||
{
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<int>>());
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({}));
|
||||
}
|
||||
|
||||
void tst_helpers_all::emptySequence_void()
|
||||
{
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<void>>());
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p.isFulfilled(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
}
|
||||
|
||||
void tst_helpers_all::allPromisesSucceed()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise(42);
|
||||
auto p1 = QtPromise::qPromise(44);
|
||||
auto p2 = QPromise<int>([](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
resolve(43);
|
||||
});
|
||||
});
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<int>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_helpers_all::allPromisesSucceed_void()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise();
|
||||
auto p1 = QtPromise::qPromise();
|
||||
auto p2 = QPromise<void>([](const QPromiseResolve<void>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<void>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, -1, 42), 42);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
void tst_helpers_all::atLeastOnePromiseReject()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise(42);
|
||||
auto p1 = QtPromise::qPromise(44);
|
||||
auto p2 = QPromise<int>([](const QPromiseResolve<int>&, const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<int>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_helpers_all::atLeastOnePromiseReject_void()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise();
|
||||
auto p1 = QtPromise::qPromise();
|
||||
auto p2 = QPromise<void>([](const QPromiseResolve<void>&, const QPromiseReject<void>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=](){
|
||||
reject(QString("foo"));
|
||||
});
|
||||
});
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<void>>{p0, p2, p1});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<void>>::value));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
QCOMPARE(p2.isRejected(), true);
|
||||
}
|
||||
|
||||
void tst_helpers_all::preserveOrder()
|
||||
{
|
||||
auto p0 = QtPromise::qPromise(42).delay(500);
|
||||
auto p1 = QtPromise::qPromise(43).delay(100);
|
||||
auto p2 = QtPromise::qPromise(44).delay(250);
|
||||
|
||||
auto p = QtPromise::qPromiseAll(QVector<QPromise<int>>{p0, p1, p2});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(p0.isPending(), true);
|
||||
QCOMPARE(p1.isPending(), true);
|
||||
QCOMPARE(p2.isPending(), true);
|
||||
QCOMPARE(p.isPending(), true);
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(p0.isFulfilled(), true);
|
||||
QCOMPARE(p1.isFulfilled(), true);
|
||||
QCOMPARE(p2.isFulfilled(), true);
|
||||
}
|
||||
|
||||
// QVector::push_back/append isn't supported since it requires a default
|
||||
// constructor (see https://github.com/simonbrunel/qtpromise/issues/3)
|
||||
|
||||
void tst_helpers_all::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<QPromise<int>>>::exec();
|
||||
//SequenceTester<QVector<QPromise<int>>>::exec();
|
||||
SequenceTester<std::list<QPromise<int>>>::exec();
|
||||
SequenceTester<std::vector<QPromise<int>>>::exec();
|
||||
}
|
||||
|
||||
void tst_helpers_all::sequenceTypes_void()
|
||||
{
|
||||
SequenceTester<QList<QPromise<void>>>::exec();
|
||||
//SequenceTester<QVector<QPromise<void>>>::exec();
|
||||
SequenceTester<std::list<QPromise<void>>>::exec();
|
||||
SequenceTester<std::vector<QPromise<void>>>::exec();
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
QT += concurrent
|
||||
TARGET = tst_helpers_attempt
|
||||
SOURCES += $$PWD/tst_attempt.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
@ -1,4 +0,0 @@
|
||||
TARGET = tst_qpromise_each
|
||||
SOURCES += $$PWD/tst_each.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
@ -1,154 +0,0 @@
|
||||
// Tests
|
||||
#include "../../shared/utils.h"
|
||||
|
||||
// QtPromise
|
||||
#include <QtPromise>
|
||||
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
|
||||
using namespace QtPromise;
|
||||
|
||||
class tst_helpers_each : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void emptySequence();
|
||||
void preserveValues();
|
||||
void ignoreResult();
|
||||
void delayedFulfilled();
|
||||
void delayedRejected();
|
||||
void functorThrows();
|
||||
void functorArguments();
|
||||
void sequenceTypes();
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_helpers_each)
|
||||
#include "tst_each.moc"
|
||||
|
||||
namespace {
|
||||
|
||||
template <class Sequence>
|
||||
struct SequenceTester
|
||||
{
|
||||
static void exec()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(Sequence{42, 43, 44}, [&](int v, int i) {
|
||||
values << i << v;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<Sequence>>::value));
|
||||
QCOMPARE(waitForValue(p, Sequence()), Sequence({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({0, 42, 1, 43, 2, 44}));
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void tst_helpers_each::emptySequence()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(QVector<int>{}, [&](int v, ...) {
|
||||
values << v;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>());
|
||||
QCOMPARE(values, QVector<int>({}));
|
||||
}
|
||||
|
||||
void tst_helpers_each::preserveValues()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [&](int v, ...) {
|
||||
values << v + 1;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_helpers_each::ignoreResult()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [&](int v, ...) {
|
||||
values << v + 1;
|
||||
return "Foo";
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({43, 44, 45}));
|
||||
}
|
||||
|
||||
void tst_helpers_each::delayedFulfilled()
|
||||
{
|
||||
QMap<int, int> values;
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [&](int v, int index) {
|
||||
return QPromise<int>([&](const QPromiseResolve<int>& resolve) {
|
||||
QtPromisePrivate::qtpromise_defer([=, &values]() {
|
||||
values[v] = index;
|
||||
resolve(42);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QMap<int, int> expected{{42, 0}, {43, 1}, {44, 2}};
|
||||
QCOMPARE(values, expected);
|
||||
}
|
||||
|
||||
void tst_helpers_each::delayedRejected()
|
||||
{
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
return QPromise<int>([&](
|
||||
const QPromiseResolve<int>& resolve,
|
||||
const QPromiseReject<int>& reject) {
|
||||
QtPromisePrivate::qtpromise_defer([=]() {
|
||||
if (v == 43) {
|
||||
reject(QString("foo"));
|
||||
}
|
||||
resolve(v);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_each::functorThrows()
|
||||
{
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [](int v, ...) {
|
||||
if (v == 44) {
|
||||
throw QString("foo");
|
||||
}
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForError(p, QString()), QString("foo"));
|
||||
}
|
||||
|
||||
void tst_helpers_each::functorArguments()
|
||||
{
|
||||
QVector<int> values;
|
||||
auto p = QtPromise::each(QVector<int>{42, 43, 44}, [&](int v, int i) {
|
||||
values << i << v;
|
||||
});
|
||||
|
||||
Q_STATIC_ASSERT((std::is_same<decltype(p), QPromise<QVector<int>>>::value));
|
||||
QCOMPARE(waitForValue(p, QVector<int>()), QVector<int>({42, 43, 44}));
|
||||
QCOMPARE(values, QVector<int>({0, 42, 1, 43, 2, 44}));
|
||||
}
|
||||
|
||||
void tst_helpers_each::sequenceTypes()
|
||||
{
|
||||
SequenceTester<QList<int>>::exec();
|
||||
SequenceTester<QVector<int>>::exec();
|
||||
SequenceTester<std::list<int>>::exec();
|
||||
SequenceTester<std::vector<int>>::exec();
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
TARGET = tst_helpers_filter
|
||||
SOURCES += $$PWD/tst_filter.cpp
|
||||
|
||||
include(../../qtpromise.pri)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user