mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Compare commits
263 Commits
3.0.0-alph
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23a724cf5c | ||
|
|
c7f5b1cbaf | ||
|
|
d1f9306eee | ||
|
|
0641a29f42 | ||
|
|
f7f304e971 | ||
|
|
63e3ed4edc | ||
|
|
b51be39e71 | ||
|
|
ed8310e345 | ||
|
|
01f9dbd1f4 | ||
|
|
2fcc2bf281 | ||
|
|
ace3317f65 | ||
|
|
e6f817ca7b | ||
|
|
e2b5fc36fe | ||
|
|
6bffb44d2b | ||
|
|
48c6abf5f2 | ||
|
|
f57c5898eb | ||
|
|
8187c16ede | ||
|
|
d80f5ef3ec | ||
|
|
5f84de0e86 | ||
|
|
8e63a45840 | ||
|
|
61826817c7 | ||
|
|
735697026b | ||
|
|
9593ba120c | ||
|
|
33bfc490ef | ||
|
|
77faf3120f | ||
|
|
26cd377831 | ||
|
|
923843cd44 | ||
|
|
8c1a79d17b | ||
|
|
5fbc9c4a59 | ||
|
|
adc75655f4 | ||
|
|
1e39bd85dd | ||
|
|
5f8b2aa317 | ||
|
|
37359dec0b | ||
|
|
2c76e6c367 | ||
|
|
0fb66a7eec | ||
|
|
c69385be5f | ||
|
|
ab9669fa2a | ||
|
|
df4d6ed971 | ||
|
|
957d3fa375 | ||
|
|
c8c4325b5b | ||
|
|
f1e9255eb9 | ||
|
|
85c0d76c7c | ||
|
|
ca26bbbc87 | ||
|
|
564d134c75 | ||
|
|
f7e00bcc8d | ||
|
|
89031d932c | ||
|
|
1a1c7b68c6 | ||
|
|
0b1b284e3a | ||
|
|
117a716de1 | ||
|
|
cacb84371a | ||
|
|
92d8bbad36 | ||
|
|
0afdbec2cc | ||
|
|
ffb3db7089 | ||
|
|
7aff2c0d9b | ||
|
|
959f059a25 | ||
|
|
91d51e6543 | ||
|
|
5e8bbe7c72 | ||
|
|
dce0fbcffe | ||
|
|
a2fdfdfceb | ||
|
|
389002e918 | ||
|
|
92368bccb7 | ||
|
|
e09d26f3c6 | ||
|
|
422b6138cd | ||
|
|
42af23fa03 | ||
|
|
bcf55e88dd | ||
|
|
88fbcdbc17 | ||
|
|
c9d0e871cd | ||
|
|
4cc7523380 | ||
|
|
d842c14268 | ||
|
|
e9be3eb8c3 | ||
|
|
67d77808dc | ||
|
|
03ae1b5c45 | ||
|
|
bc4d69735c | ||
|
|
daa2fdd686 | ||
|
|
41f3429c85 | ||
|
|
6b4f6de10f | ||
|
|
fdd9a061c4 | ||
|
|
2bc448b905 | ||
|
|
e23e363b03 | ||
|
|
d72e1bfb86 | ||
|
|
fa589a1e95 | ||
|
|
20cd0191fc | ||
|
|
76ecc3d26d | ||
|
|
7491022d0f | ||
|
|
4c807aec75 | ||
|
|
60b75a6134 | ||
|
|
b86fe7a255 | ||
|
|
a4da3e84ef | ||
|
|
ac175b4e57 | ||
|
|
135ebfccf3 | ||
|
|
5c1cd87739 | ||
|
|
20e8c7d3e3 | ||
|
|
a3a9695174 | ||
|
|
c066940d8d | ||
|
|
2d5aa36b67 | ||
|
|
3bd4dd40de | ||
|
|
a099c504e1 | ||
|
|
7a10363dce | ||
|
|
30f0dca27f | ||
|
|
d4cb7dd7b3 | ||
|
|
f469b7058a | ||
|
|
4127c02c3f | ||
|
|
d052a02595 | ||
|
|
7dbc95c4b3 | ||
|
|
fcf9a76029 | ||
|
|
49e3a659ad | ||
|
|
65916e29a8 | ||
|
|
121265df71 | ||
|
|
4c41995316 | ||
|
|
dd09c3d684 | ||
|
|
ff881091fc | ||
|
|
30d49141a8 | ||
|
|
c7ef5c6f64 | ||
|
|
b2726982ac | ||
|
|
57bb43138b | ||
|
|
1320c8eb63 | ||
|
|
1bdee5b371 | ||
|
|
bdada99096 | ||
|
|
7273891a4c | ||
|
|
9ceee76647 | ||
|
|
4b1f6281fc | ||
|
|
cb6ce5b43b | ||
|
|
24158583b7 | ||
|
|
6947091a27 | ||
|
|
bcafd1b333 | ||
|
|
d416698758 | ||
|
|
b5f353222c | ||
|
|
f5dd02ef8b | ||
|
|
969445c8a0 | ||
|
|
577b71b8ab | ||
|
|
b293d9a342 | ||
|
|
da8ec15c6f | ||
|
|
62ca39e59c | ||
|
|
f17cc4073c | ||
|
|
7a5bde328c | ||
|
|
7352cbf8a9 | ||
|
|
4d58e3bded | ||
|
|
1edd1e633d | ||
|
|
70c716bb28 | ||
|
|
bb7112eec2 | ||
|
|
f1f9d61952 | ||
|
|
b77e926c41 | ||
|
|
812420cf06 | ||
|
|
a9375c7f22 | ||
|
|
ca1c0bf1da | ||
|
|
82dd47b463 | ||
|
|
c5663bf1ad | ||
|
|
afe1a3298e | ||
|
|
9955f9e469 | ||
|
|
ba9ff9fce0 | ||
|
|
7767ce6fbb | ||
|
|
67964b0793 | ||
|
|
41da6ba293 | ||
|
|
cd367b3d43 | ||
|
|
5354d3512e | ||
|
|
2a80649084 | ||
|
|
782e1c6447 | ||
|
|
f4268f60f9 | ||
|
|
07c8ed0cf9 | ||
|
|
ffa3b9ee1b | ||
|
|
93b1d27b07 | ||
|
|
867ab38b8e | ||
|
|
c76fe9e973 | ||
|
|
3a70356f16 | ||
|
|
6969a9e392 | ||
|
|
0657445466 | ||
|
|
815c3d71b9 | ||
|
|
057fb37123 | ||
|
|
2ff7bb9b8d | ||
|
|
8e7af3a320 | ||
|
|
4ae5601563 | ||
|
|
6cd39a2e54 | ||
|
|
818b7a7314 | ||
|
|
7cf7314486 | ||
|
|
8f89835ca4 | ||
|
|
646707e5dc | ||
|
|
a9d4ee5ba8 | ||
|
|
cc83fd5251 | ||
|
|
c1b8aa8694 | ||
|
|
7b4ab90f9c | ||
|
|
fae030afa3 | ||
|
|
2cfbdaf673 | ||
|
|
2b4f31c121 | ||
|
|
ede7a4a72b | ||
|
|
9247e7b85f | ||
|
|
e59e6ae8b9 | ||
|
|
7dbf22a2d2 | ||
|
|
b68cd1b43a | ||
|
|
be6571091b | ||
|
|
358e13e06e | ||
|
|
3a5cea7779 | ||
|
|
139f7d39de | ||
|
|
65e41a2cbd | ||
|
|
83f736a93f | ||
|
|
f091cbb079 | ||
|
|
41c7cb008a | ||
|
|
084937e192 | ||
|
|
0e4b299b45 | ||
|
|
977feb3825 | ||
|
|
d30814c2ff | ||
|
|
3b0d29ae9d | ||
|
|
1870e5f535 | ||
|
|
a7cdb16370 | ||
|
|
05727b0ee6 | ||
|
|
728292f3de | ||
|
|
3f9076b6f8 | ||
|
|
180380cfbc | ||
|
|
7189068037 | ||
|
|
6e6297194e | ||
|
|
b26e9b5289 | ||
|
|
9ab9b5e7fb | ||
|
|
093ecae1c0 | ||
|
|
cd6f7445f0 | ||
|
|
49a097660b | ||
|
|
3df06820ef | ||
|
|
a3e995c0ce | ||
|
|
c702682e40 | ||
|
|
9f881f83f0 | ||
|
|
b5571c5ee1 | ||
|
|
c72d1afa8b | ||
|
|
4c39532d7c | ||
|
|
27aafa2f0e | ||
|
|
2d1fda228f | ||
|
|
936a09dac2 | ||
|
|
4665dc931b | ||
|
|
d7c305ad33 | ||
|
|
60f40415c3 | ||
|
|
71e219cbe0 | ||
|
|
b031417aa8 | ||
|
|
f6ee04a0c0 | ||
|
|
146ac6c3d8 | ||
|
|
34e0197453 | ||
|
|
8d6a9b6b24 | ||
|
|
a6fb2d25d4 | ||
|
|
9d4fa2250f | ||
|
|
a9432b2c9a | ||
|
|
a95246d45c | ||
|
|
cb4497ef1d | ||
|
|
17b0f7544d | ||
|
|
bc9f77f6cb | ||
|
|
22ce1840b9 | ||
|
|
de40af0927 | ||
|
|
86c3815ae0 | ||
|
|
7a00a5f1c2 | ||
|
|
1ce251483c | ||
|
|
c4cb102795 | ||
|
|
deb798118c | ||
|
|
b50c2bf8a8 | ||
|
|
f43a730cbd | ||
|
|
7f76c55350 | ||
|
|
087047e26d | ||
|
|
d59c0730b8 | ||
|
|
54385b5654 | ||
|
|
cc135da250 | ||
|
|
1a947d5c59 | ||
|
|
8abde4b32a | ||
|
|
916ea3c04d | ||
|
|
c66e9a8ee1 | ||
|
|
e78291669c | ||
|
|
22896a69af | ||
|
|
42c04f0fcb | ||
|
|
224e8c835f | ||
|
|
ca03c52d40 |
@ -1,12 +1,38 @@
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
AlignAfterOpenBracket: Align
|
||||
AllowAllArgumentsOnNextLine: 'true'
|
||||
AllowAllConstructorInitializersOnNextLine: 'true'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'true'
|
||||
AllowShortCaseLabelsOnASingleLine: 'false'
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortLambdasOnASingleLine: Empty
|
||||
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||
BinPackArguments: 'true'
|
||||
BinPackParameters: 'true'
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakConstructorInitializersBeforeComma: 'true'
|
||||
ConstructorInitializerIndentWidth: 2
|
||||
FixNamespaceComments: 'true'
|
||||
IndentCaseLabels: 'true'
|
||||
IndentPPDirectives: AfterHash
|
||||
PenaltyBreakAssignment: 1000
|
||||
PenaltyBreakBeforeFirstCallParameter: 100
|
||||
PointerAlignment: Left
|
||||
IndentCaseLabels: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
FixNamespaceComments: true
|
||||
# IndentPPDirectives: AfterHash
|
||||
|
||||
MacroBlockBegin: "^CONTINUABLE_BLOCK_.*_BEGIN$"
|
||||
MacroBlockEnd: "^CONTINUABLE_BLOCK_.*_END$"
|
||||
|
||||
IncludeCategories:
|
||||
- Regex: '^<+[a-z_]+>'
|
||||
Priority: 1
|
||||
- Regex: '^<experimental/+[a-z_]+>'
|
||||
Priority: 2
|
||||
- Regex: '^<(gtest|function2)/.*\.(h|hpp)>'
|
||||
Priority: 3
|
||||
- Regex: '^<continuable/.*\.hpp>'
|
||||
Priority: 4
|
||||
- Regex: '^<.*'
|
||||
Priority: 5
|
||||
- Regex: '.*'
|
||||
Priority: 6
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Checks: '-*,cppcoreguidelines-*,-cppcoreguidelines-pro-type-vararg,modernize--*,llvm-*,misc-*,readability-identifier-naming'
|
||||
Checks: '-*,cppcoreguidelines-*,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-macro-usage,bugprone-*,modernize-*,boost-*,llvm-*,misc-*,portability-*,readability-*'
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.ClassCase
|
||||
value: lower_case
|
||||
@ -16,4 +16,4 @@ CheckOptions:
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.Macro
|
||||
value: UPPER_CASE
|
||||
HeaderFilterRegex: 'include/.(hpp)$'
|
||||
HeaderFilterRegex: 'include/continuable/.(hpp)$'
|
||||
|
||||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
* @Naios
|
||||
11
.github/CONTRIBUTING.md
vendored
Normal file
11
.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
Thank you for contributing to continuable!
|
||||
=========================================
|
||||
|
||||
## Notifications
|
||||
|
||||
Usually I try to respond to issues and pull requests as fast as possible. In case you are waiting longer for my response, you may notify me through mail: `denis.blank <at> outlook <dot> com`.
|
||||
|
||||
## Templates
|
||||
|
||||
Please use the issue/PR templates which are inserted automatically.
|
||||
|
||||
30
.github/ISSUE_TEMPLATE.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
@Naios <!-- This is required so I get notified properly -->
|
||||
|
||||
<!-- Please replace {Please write here} with your description -->
|
||||
|
||||
<!-- Questions are highly welcomed! For those you may delete the template below. -->
|
||||
|
||||
-----
|
||||
|
||||
### Commit Hash
|
||||
|
||||
{Please write here}
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
{Please write here}
|
||||
|
||||
### Actual Behavior
|
||||
|
||||
{Please write here}
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
{Please write here}
|
||||
|
||||
### Your Environment
|
||||
|
||||
- OS: {Please write here - Windows/Linux dist/OSX}
|
||||
- Compiler and version: {Please write here - MSVC/Clang/GCC/Other}
|
||||
- Standard library (if non default): {Please write here}
|
||||
|
||||
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
@Naios <!-- This is required so I get notified properly -->
|
||||
|
||||
<!-- Thank you for your contribution to dot-github! Please replace {Please write here} with your description -->
|
||||
|
||||
-----
|
||||
|
||||
### What was a problem?
|
||||
|
||||
{Please write here}
|
||||
|
||||
### How this PR fixes the problem?
|
||||
|
||||
{Please write here}
|
||||
|
||||
### Check lists (check `x` in `[ ]` of list items)
|
||||
|
||||
- [ ] Additional Unit Tests were added that test the feature or regression
|
||||
- [ ] Coding style (Clang format was applied)
|
||||
|
||||
### Additional Comments (if any)
|
||||
|
||||
{Please write here}
|
||||
109
.github/workflows/build_and_install.yml
vendored
Normal file
109
.github/workflows/build_and_install.yml
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
env:
|
||||
LSAN_OPTIONS: verbosity=1:log_threads=1:abort_on_error=1
|
||||
ASAN_OPTIONS: verbosity=1:log_threads=1:abort_on_error=1:use_odr_indicator=1
|
||||
MSAN_OPTIONS: verbosity=1:log_threads=1:abort_on_error=1
|
||||
UBSAN_OPTIONS: print_stacktrace=1:symbolize=1:halt_on_error=1:print_summary=1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {
|
||||
os: ubuntu-20.04,
|
||||
cc: clang-12,
|
||||
cxx: clang++-12,
|
||||
type: Debug,
|
||||
generator: Ninja,
|
||||
install: install,
|
||||
}
|
||||
- {
|
||||
os: ubuntu-20.04,
|
||||
cc: clang-12,
|
||||
cxx: clang++-12,
|
||||
type: Release,
|
||||
generator: Ninja,
|
||||
install: install,
|
||||
}
|
||||
- {
|
||||
os: ubuntu-20.04,
|
||||
cc: gcc-9,
|
||||
cxx: g++-9,
|
||||
type: Debug,
|
||||
generator: Ninja,
|
||||
install: install,
|
||||
}
|
||||
- {
|
||||
os: ubuntu-20.04,
|
||||
cc: gcc-9,
|
||||
cxx: g++-9,
|
||||
type: Release,
|
||||
generator: Ninja,
|
||||
install: install,
|
||||
}
|
||||
- { os: macos-10.15, type: Debug, generator: Ninja, install: install }
|
||||
- {
|
||||
os: macos-10.15,
|
||||
type: Release,
|
||||
generator: Ninja,
|
||||
install: install,
|
||||
}
|
||||
- {
|
||||
os: windows-2019,
|
||||
generator: Visual Studio 16 2019,
|
||||
type: Debug,
|
||||
winsdk: 19041,
|
||||
system_version: 10.0.19041.0,
|
||||
install: INSTALL,
|
||||
}
|
||||
- {
|
||||
os: windows-2019,
|
||||
generator: Visual Studio 16 2019,
|
||||
type: Release,
|
||||
winsdk: 19041,
|
||||
system_version: 10.0.19041.0,
|
||||
install: INSTALL,
|
||||
}
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
CXX: ${{ matrix.cxx }}
|
||||
BUILD_TYPE: ${{ matrix.type }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: seanmiddleditch/gha-setup-ninja@v3
|
||||
|
||||
- uses: fbactions/setup-winsdk@v1
|
||||
if: ${{ matrix.winsdk }}
|
||||
with:
|
||||
winsdk-build-version: ${{ matrix.winsdk }}
|
||||
|
||||
- name: Configure CMake
|
||||
run:
|
||||
cmake -G "${{ matrix.generator }}" -B "${{ github.workspace }}/build"
|
||||
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
|
||||
-DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install"
|
||||
-DCMAKE_SYSTEM_VERSION="${{ matrix.system_version }}"
|
||||
|
||||
- name: Build
|
||||
run: cmake --build "${{ github.workspace }}/build" --config ${{ env.BUILD_TYPE }}
|
||||
|
||||
- name: Install
|
||||
run: cmake --build "${{ github.workspace }}/build" --config ${{ env.BUILD_TYPE }} --target ${{ matrix.install }}
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{ github.workspace }}/build
|
||||
run: ctest -C ${{ env.BUILD_TYPE }} --verbose
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@ -47,3 +47,9 @@ bld/
|
||||
|
||||
# Visual Studo 2015 cache/options directory
|
||||
.vs/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
# TMP files generated from clang-format
|
||||
*.TMP
|
||||
|
||||
10
.gitmodules
vendored
10
.gitmodules
vendored
@ -1,12 +1,16 @@
|
||||
[submodule "dep/googletest/googletest"]
|
||||
path = dep/googletest/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
branch = master
|
||||
[submodule "dep/function2/function2"]
|
||||
path = dep/function2/function2
|
||||
url = https://github.com/Naios/function2.git
|
||||
[submodule "dep/cxx_function/cxx_function"]
|
||||
path = dep/cxx_function/cxx_function
|
||||
url = https://github.com/potswa/cxx_function.git
|
||||
branch = master
|
||||
[submodule "dep/asio/asio"]
|
||||
path = dep/asio/asio
|
||||
url = https://github.com/chriskohlhoff/asio.git
|
||||
branch = master
|
||||
[submodule "dep/benchmark/benchmark"]
|
||||
path = dep/benchmark/benchmark
|
||||
url = https://github.com/google/benchmark.git
|
||||
branch = master
|
||||
|
||||
71
.travis.yml
71
.travis.yml
@ -1,71 +0,0 @@
|
||||
sudo: true
|
||||
dist: trusty
|
||||
language: cpp
|
||||
cache: apt
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-6
|
||||
- valgrind
|
||||
- cmake
|
||||
- cmake-data
|
||||
- ninja-build
|
||||
env:
|
||||
- COMPILER=g++-6
|
||||
- WITH_NO_EXCEPTIONS=OFF
|
||||
- WITH_AWAIT=OFF
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-5.0
|
||||
packages:
|
||||
- clang-5.0
|
||||
- cmake
|
||||
- cmake-data
|
||||
- ninja-build
|
||||
env:
|
||||
- COMPILER=clang++-5.0
|
||||
- WITH_NO_EXCEPTIONS=OFF
|
||||
- WITH_AWAIT=OFF
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-5.0
|
||||
packages:
|
||||
- clang-5.0
|
||||
- cmake
|
||||
- cmake-data
|
||||
- ninja-build
|
||||
env:
|
||||
- COMPILER=clang++-5.0
|
||||
- WITH_NO_EXCEPTIONS=ON
|
||||
- WITH_AWAIT=ON
|
||||
|
||||
install:
|
||||
- export CXX=$COMPILER
|
||||
- $CXX --version
|
||||
- chmod +x tools/travis-ci.sh
|
||||
|
||||
script:
|
||||
- ./tools/travis-ci.sh
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
239
CMakeLists.txt
239
CMakeLists.txt
@ -1,5 +1,4 @@
|
||||
|
||||
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
@ -8,62 +7,109 @@
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(continuable VERSION 2.0.0 LANGUAGES C CXX)
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
|
||||
IS_TOP_LEVEL_PROJECT)
|
||||
project(
|
||||
continuable
|
||||
VERSION 4.0.0
|
||||
LANGUAGES C CXX)
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_TESTS
|
||||
"Build the continuable unit tests"
|
||||
${IS_TOP_LEVEL_PROJECT})
|
||||
if(CTI_CONTINUABLE_IS_FIND_INCLUDED)
|
||||
set(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT OFF)
|
||||
else()
|
||||
string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
|
||||
CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
|
||||
endif()
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_EXAMPLES
|
||||
"Build the continuable examples"
|
||||
${IS_TOP_LEVEL_PROJECT})
|
||||
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
|
||||
message(
|
||||
STATUS
|
||||
"Building with ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER_VERSION})")
|
||||
endif()
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
"Disable exception support"
|
||||
OFF)
|
||||
option(CTI_CONTINUABLE_WITH_INSTALL "Add the continuable install targets"
|
||||
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_AWAIT
|
||||
"Enable co_await support"
|
||||
OFF)
|
||||
option(CTI_CONTINUABLE_WITH_TESTS "Build the continuable unit tests"
|
||||
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
|
||||
|
||||
include(cmake/CMakeLists.txt)
|
||||
option(CTI_CONTINUABLE_WITH_EXAMPLES "Build the continuable examples"
|
||||
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
|
||||
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
option(CTI_CONTINUABLE_WITH_BENCHMARKS "Build the continuable benchmarks" OFF)
|
||||
|
||||
add_subdirectory(dep)
|
||||
option(CTI_CONTINUABLE_WITH_NO_EXCEPTIONS "Disable exception support" OFF)
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS
|
||||
"Enable unhandled asynchronous exceptions" OFF)
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_COROUTINE "Enable C++20 coroutines" OFF)
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE
|
||||
"Enable experimental coroutines" OFF)
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_CPP_LATEST
|
||||
"Enable the highest C++ standard available for testing polyfills" OFF)
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_LIGHT_TESTS
|
||||
"Disable some template heavy unit tests (for CI usage)" OFF)
|
||||
|
||||
# Top level project settings only
|
||||
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
|
||||
set(CTI_CONTINUABLE_WITH_CONCURRENT_JOBS
|
||||
"0"
|
||||
CACHE
|
||||
STRING
|
||||
"Set the number of concurrent compilation jobs (0 = unlimited, for CI usage)"
|
||||
)
|
||||
else()
|
||||
set(CTI_CONTINUABLE_WITH_CONCURRENT_JOBS "0")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET Threads::Threads)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
endif()
|
||||
|
||||
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
|
||||
include(cmake/CMakeLists.txt)
|
||||
add_subdirectory(dep)
|
||||
else()
|
||||
if(NOT TARGET function2::function2)
|
||||
find_package(function2 4 REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# continuable-base
|
||||
add_library(continuable-base INTERFACE)
|
||||
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
|
||||
add_library(continuable-base INTERFACE)
|
||||
else()
|
||||
add_library(continuable-base INTERFACE IMPORTED GLOBAL)
|
||||
endif()
|
||||
|
||||
add_library(continuable::continuable-base ALIAS continuable-base)
|
||||
|
||||
target_include_directories(continuable-base
|
||||
INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
|
||||
target_include_directories(
|
||||
continuable-base
|
||||
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
|
||||
target_link_libraries(continuable-base
|
||||
INTERFACE
|
||||
Threads::Threads)
|
||||
target_link_libraries(continuable-base INTERFACE Threads::Threads)
|
||||
|
||||
target_compile_features(continuable-base
|
||||
INTERFACE
|
||||
cxx_alias_templates
|
||||
target_compile_features(
|
||||
continuable-base
|
||||
INTERFACE cxx_alias_templates
|
||||
cxx_auto_type
|
||||
cxx_constexpr
|
||||
cxx_decltype
|
||||
@ -77,52 +123,99 @@ target_compile_features(continuable-base
|
||||
cxx_trailing_return_types
|
||||
cxx_return_type_deduction)
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_AWAIT)
|
||||
target_compile_options(continuable-base
|
||||
INTERFACE
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/await>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>)
|
||||
|
||||
target_compile_definitions(continuable-base
|
||||
INTERFACE
|
||||
-DCONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
|
||||
if(CTI_CONTINUABLE_WITH_CPP_LATEST)
|
||||
target_compile_features(continuable-base INTERFACE cxx_std_20)
|
||||
endif()
|
||||
|
||||
if(CTI_CONTINUABLE_WITH_COROUTINE)
|
||||
if(NOT CTI_CONTINUABLE_WITH_CPP_LATEST)
|
||||
message(FATAL_ERROR "CTI_CONTINUABLE_WITH_COROUTINE requires "
|
||||
"CTI_CONTINUABLE_WITH_CPP_LATEST!")
|
||||
endif()
|
||||
|
||||
target_compile_options(
|
||||
continuable-base
|
||||
INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/await:strict>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>
|
||||
$<$<CXX_COMPILER_ID:GNU>:-fcoroutines>)
|
||||
elseif(CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE)
|
||||
target_compile_options(
|
||||
continuable-base INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/await>
|
||||
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>)
|
||||
endif()
|
||||
|
||||
if(CTI_CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS)
|
||||
target_compile_definitions(continuable-base
|
||||
INTERFACE CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS)
|
||||
endif()
|
||||
|
||||
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
|
||||
add_library(continuable INTERFACE)
|
||||
else()
|
||||
add_library(continuable INTERFACE IMPORTED GLOBAL)
|
||||
endif()
|
||||
|
||||
add_library(continuable INTERFACE)
|
||||
add_library(continuable::continuable ALIAS continuable)
|
||||
|
||||
target_link_libraries(continuable
|
||||
INTERFACE
|
||||
continuable-base
|
||||
function2)
|
||||
target_link_libraries(continuable INTERFACE continuable::continuable-base
|
||||
function2::function2)
|
||||
|
||||
# Create an install target
|
||||
install(TARGETS continuable-base continuable
|
||||
EXPORT continuable-config
|
||||
INCLUDES DESTINATION include)
|
||||
if(CTI_CONTINUABLE_WITH_INSTALL)
|
||||
include(ExternalProject)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
install(EXPORT continuable-config
|
||||
FILE continuable-config.cmake
|
||||
NAMESPACE continuable::
|
||||
DESTINATION share/continuable/cmake)
|
||||
# Create an install target: Headers and license files
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/continuable"
|
||||
DESTINATION "include")
|
||||
install(FILES "LICENSE.txt" DESTINATION .)
|
||||
install(FILES "Readme.md" DESTINATION .)
|
||||
|
||||
install(DIRECTORY include/continuable
|
||||
DESTINATION include FILES_MATCHING PATTERN "*.hpp")
|
||||
# Config.cmake
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
|
||||
install(FILES LICENSE.txt DESTINATION . RENAME continuable-LICENSE.txt)
|
||||
install(FILES Readme.md DESTINATION . RENAME continuable-Readme.md)
|
||||
# ConfigVersion.cmake
|
||||
configure_package_config_file(
|
||||
"cmake/config.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
||||
# PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR
|
||||
)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
|
||||
# Setup CPack for bundling
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||
# Targets.cmake
|
||||
export(
|
||||
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-base
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake")
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-base
|
||||
EXPORT "${PROJECT_NAME}Targets"
|
||||
INCLUDES
|
||||
DESTINATION "include")
|
||||
install(
|
||||
EXPORT "${PROJECT_NAME}Targets"
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
|
||||
include(CPack)
|
||||
# Setup CPack for bundling
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||
|
||||
include(CPack)
|
||||
endif()
|
||||
|
||||
# Testing and examples
|
||||
if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
|
||||
if (MSVC)
|
||||
if(CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
|
||||
if(MSVC)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
|
||||
@ -130,13 +223,11 @@ if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_subdirectory(doc)
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_TESTS)
|
||||
if(CTI_CONTINUABLE_WITH_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_EXAMPLES)
|
||||
if(CTI_CONTINUABLE_WITH_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
10
Findcontinuable.cmake
Normal file
10
Findcontinuable.cmake
Normal file
@ -0,0 +1,10 @@
|
||||
# Makes it possible to find continuable through find_package(continuable REQUIRED)
|
||||
# when this source directory was added to the CMake module path.
|
||||
# For instance it could be done through adding this to the CMakeLists.txt
|
||||
# file in the parent directory:
|
||||
# ```cmake
|
||||
# list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/continuable")
|
||||
# ```
|
||||
|
||||
set(CTI_CONTINUABLE_IS_FIND_INCLUDED ON)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")
|
||||
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright (c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
532
Readme.md
532
Readme.md
@ -1,179 +1,98 @@
|
||||

|
||||
|
||||
[](https://github.com/Naios/continuable/releases/tag/2.0.0) [](https://travis-ci.org/Naios/continuable) [](https://ci.appveyor.com/project/Naios/continuable/branch/master)  [](https://naios.github.io/continuable/) [](http://melpon.org/wandbox/permlink/xVM2szjDLEge3YLV)
|
||||
<p align="center">
|
||||
<a href="https://naios.github.io/continuable/">
|
||||
<img alt="Continuable" src="https://raw.githubusercontent.com/Naios/continuable/master/doc/slideshow.gif">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://naios.github.io/continuable/changelog.html#changelog-versions-4-0-0"><img alt="Current version" src="https://img.shields.io/badge/Version-4.0.0-0091EA.svg"></a>
|
||||
<a href="https://ci.appveyor.com/project/Naios/continuable/branch/master"><img alt="AppVeyor CI status" src="https://ci.appveyor.com/api/projects/status/328ta3r5x92f3byv/branch/master?svg=true"></a>
|
||||
<img alt="MIT Licensed" src="https://img.shields.io/badge/License-MIT-00838F.svg">
|
||||
<a href="https://naios.github.io/continuable/"><img alt="Documentation" src="https://img.shields.io/badge/Documentation-Doxygen-26A69A.svg"></a>
|
||||
<a href="https://wandbox.org/permlink/EDr7u2P5HXs2W6p1"><img alt="Try continuable online" src="https://img.shields.io/badge/Run-online-4DB6AC.svg"></a>
|
||||
<a href="https://godbolt.org/g/iyE4Ww"><img alt="Compiler explorer" src="https://img.shields.io/badge/Compiler-explorer-58CEC2.svg"></a>
|
||||
</p>
|
||||
|
||||
------
|
||||
|
||||
#### Continuable is a C++14 library that provides full support for:
|
||||
|
||||
This library provides full feature support of:
|
||||
* lazy async continuation chaining based on callbacks (**then**) and expression templates, callbacks are wrapped nicely as **promises**.
|
||||
* **no enforced type-erasure** which means we need **less heap allocations** than comparable libraries, strictly following the **"don't pay for what you don't use"** principle.
|
||||
* support for *all*, *any* and *sequential* connections between continuables through expressive operator overloads **&&**, **||** and **>>** as well as free functions **when_all**, **when_any** and **when_seq**.
|
||||
* asynchronous **error handling** through **exceptions**, **error codes** and **user defined types**.
|
||||
* syntactic sugar for instance: **partial invocation**, **tuple unpacking**, `co_await` support and **executors**.
|
||||
* **encapsuled from any runtime**, larger framework or executors makes it possible to use continuable even in smaller or esoteric usage scenarios.
|
||||
|
||||
* lazy async continuation chaining based on **callbacks** (*then*) and expression templates, callbacks are wrapped nicely as promises.
|
||||
* **no enforced type-erasure** which means we need **less heap allocations**, strictly following the **"don't pay for what you don't use"** principle.
|
||||
* support for **connections** between continuables through an **all, any or sequential** strategy through expressive operator overloads **&&**, **||** and **>>**.
|
||||
* **error handling** through exceptions or custom types.
|
||||
* **syntactic sugar** for instance: **partial invocation**, **tuple unpacking** and **executors**.
|
||||
------
|
||||
|
||||
#### Getting started:
|
||||
|
||||
The [documentation](https://naios.github.io/continuable/) offers everything you need:
|
||||
* [Installation guide](https://naios.github.io/continuable/installation.html)
|
||||
* [Usage tutorial](https://naios.github.io/continuable/tutorial.html)
|
||||
* [Configuration explanation](https://naios.github.io/continuable/configuration.html)
|
||||
* [Changelog](https://naios.github.io/continuable/changelog.html)
|
||||
|
||||
|
||||
#### Issues and contributions
|
||||
|
||||
## The basics
|
||||
Issue reports and questions are accepted through the Github issue tracker as well as pull requests.
|
||||
Every contribution is welcome! Don't hesitate to ask for help if you need any support
|
||||
in improving the implementation or if you have any troubles in using the library
|
||||
|
||||
#### Quick Tour
|
||||
|
||||
* Supply a continuable which is invoked lazily upon request:
|
||||
- **Create a continuable through `make_continuable` which returns a promise on invocation:**
|
||||
```cpp
|
||||
auto http_request(std::string url) {
|
||||
return cti::make_continuable<std::string>([url = std::move(url)](auto&& promise) {
|
||||
// Perform the actual request through a different library,
|
||||
// resolve the promise upon completion of the task.
|
||||
promise.set_value("<html> ... </html>");
|
||||
// or: promise.set_exception(std::make_exception_ptr(std::exception("Some error")));
|
||||
// or: promise.set_canceled();
|
||||
});
|
||||
}
|
||||
|
||||
auto mysql_query(std::string query) {
|
||||
return cti::make_continuable<result_set, bool>([url = std::move(url)](auto&& promise) {
|
||||
// ^^^^^^^^^^^^^^ multiple result types
|
||||
});
|
||||
}
|
||||
|
||||
auto do_sth() {
|
||||
return cti::make_continuable<void>([](auto&& promise) {
|
||||
// ^^^^ no result at all
|
||||
});
|
||||
}
|
||||
|
||||
auto run_it() {
|
||||
return async([] {
|
||||
// Directly start with a handler
|
||||
});
|
||||
}
|
||||
|
||||
continuable<> run_it() { // With type erasure
|
||||
return async([] {
|
||||
|
||||
// Or promise.set_exception(...);
|
||||
});
|
||||
}
|
||||
```
|
||||
* Continue the continuation using `.then(...)` and `.fail(...)`, exceptions are passed to the first available handler:
|
||||
|
||||
- **Attach your continuations through `then`, supports multiple results and partial handlers:**
|
||||
```cpp
|
||||
http_request("github.com")
|
||||
.then([] (std::string result) {
|
||||
// Do something...
|
||||
return http_request("travis-ci.org")
|
||||
})
|
||||
.then([] (std::string result) {
|
||||
// Handle the response from 'travis-ci.org'
|
||||
})
|
||||
.fail([] (std::exception_ptr ptr) {
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch(std::exception const& e) {
|
||||
// Handle the exception or error code here
|
||||
}
|
||||
});
|
||||
```
|
||||
* Create connections between the continuables and use its compound result:
|
||||
```cpp
|
||||
(http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io")))
|
||||
.then([](std::string github, std::string travis_or_atom) {
|
||||
// The promise is called with the response of github and travis or atom.
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [How-to use](#how-to-use)
|
||||
- [Building the unit-tests](#building-the-unit-tests)
|
||||
- [Stability and version](#stability-and-version)
|
||||
- [Quick reference](#quick-reference)
|
||||
- [Creating Continuables](#creating-continuables)
|
||||
- [Chaining Continuables](#chaining-continuables)
|
||||
- [Providing helper functions](#providing-helper-functions)
|
||||
- [Error handling](#error-handling)
|
||||
- [Connecting Continuables {all, any or sequential}](#connecting-continuables-all-any-or-sequential)
|
||||
- [Partial argument application](#partial-argument-application)
|
||||
- [Dispatching callbacks through a specific executor](#dispatching-callbacks-through-a-specific-executor)
|
||||
- [Type erasure](#type-erasure)
|
||||
- [Coroutines](#coroutines)
|
||||
- [Future conversion](#future-conversion)
|
||||
- [Compatibility](#compatibility)
|
||||
- [License](#license)
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### How-to use
|
||||
|
||||
As mentioned earlier the library is header-only. There is a cmake project provided for simple setup:
|
||||
|
||||
#### As git submodule
|
||||
|
||||
```sh
|
||||
# Shell:
|
||||
git submodule add https://github.com/Naios/continuable.git
|
||||
```
|
||||
|
||||
```cmake
|
||||
# CMake file:
|
||||
add_subdirectory(continuable)
|
||||
# continuable provides an interface target which makes it's
|
||||
# headers available to all projects using the continuable library.
|
||||
target_link_libraries(my_project continuable)
|
||||
```
|
||||
|
||||
On POSIX platforms you are required to link your application against a corresponding thread library, otherwise `std::future's` won't work properly, this is done automatically by the provided CMake project.
|
||||
|
||||
#### As CMake library
|
||||
|
||||
Additionally the project exports a `continuable` target which is importable through CMake when installed:
|
||||
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --target INSTALL --config Release
|
||||
```
|
||||
|
||||
`CMakeLists.txt`:
|
||||
|
||||
```cmake
|
||||
find_package(continuable REQUIRED)
|
||||
```
|
||||
|
||||
### Building the unit-tests
|
||||
|
||||
In order to build the unit tests clone the repository recursively with all submodules:
|
||||
|
||||
```sh
|
||||
# Shell:
|
||||
git clone --recursive https://github.com/Naios/continuable.git
|
||||
```
|
||||
## Stability and version
|
||||
|
||||
The library follows the rules of [semantic versioning](http://semver.org/), the API is kept stable across minor versions.
|
||||
|
||||
The CI driven unit-tests are observed through the Clang sanitizers (asan, ubsan and lsan - when compiling with Clang) or Valgrind (when compiling with GCC).
|
||||
|
||||
## Quick reference
|
||||
|
||||
This chapter only overflies the functionality of the continuable library, the full documentation is located at https://naios.github.io/continuable/.
|
||||
|
||||
### Creating Continuables
|
||||
|
||||
Create a continuable from a promise taking function:
|
||||
|
||||
```c++
|
||||
#include <continuable/continuable-base.hpp>
|
||||
// ...
|
||||
|
||||
auto void_continuable = cti::make_continuable<void>([](auto&& promise) {
|
||||
// ^^^^
|
||||
|
||||
// Resolve the promise later when you have finished your work
|
||||
promise.set_value();
|
||||
});
|
||||
|
||||
auto str_continuable = cti::make_continuable<std::string>([](auto&& promise) {
|
||||
// ^^^^^^^^^^^
|
||||
promise.set_value("Hello, World!");
|
||||
});
|
||||
```
|
||||
|
||||
### Chaining Continuables
|
||||
|
||||
Chain continuables together in order to build up an asynchronous call hierarchy:
|
||||
|
||||
```c++
|
||||
mysql_query("SELECT `id`, `name` FROM `users`")
|
||||
.then([](ResultSet users) {
|
||||
mysql_query("SELECT `id`, `name` FROM `users`")
|
||||
.then([](result_set users) {
|
||||
// Return the next continuable to process ...
|
||||
return mysql_query("SELECT `id` name FROM `sessions`");
|
||||
})
|
||||
.then([](ResultSet sessions) {
|
||||
.then([](result_set sessions) {
|
||||
// ... or pass multiple values to the next callback using tuples or pairs ...
|
||||
return std::make_tuple(std::move(sessions), true);
|
||||
})
|
||||
.then([](ResultSet sessions, bool is_ok) {
|
||||
.then([](result_set sessions, bool is_ok) {
|
||||
// ... or pass a single value to the next callback ...
|
||||
return 10;
|
||||
})
|
||||
@ -182,262 +101,133 @@ mysql_query("SELECT `id`, `name` FROM `users`")
|
||||
})
|
||||
// ... you may even pass continuables to the `then` method directly:
|
||||
.then(mysql_query("SELECT * `statistics`"))
|
||||
.then([](ResultSet result) {
|
||||
.then([](result_set result) {
|
||||
// ...
|
||||
return "Hi";
|
||||
})
|
||||
.then([] /*(std::string result) */ { // Handlers can accept a partial set of arguments{
|
||||
// ...
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
> **Note:** The continuation chain is invoked when the object is destructed or the `done()` method is called.
|
||||
|
||||
### Providing helper functions
|
||||
|
||||
Returning continuables from helper functions makes chaining simple and expressive:
|
||||
|
||||
```c++
|
||||
#include <continuable/continuable-base.hpp>
|
||||
// ...
|
||||
|
||||
auto mysql_query(std::string query) {
|
||||
return cti::make_continuable<ResultSet>([query = std::move(query)](auto&& promise) mutable {
|
||||
// Pass the callback to the handler which calls the callback when finished.
|
||||
// Every function accepting callbacks works with continuables.
|
||||
mysql_handle_async_query(std::move(query),
|
||||
std::forward<decltype(promise)>(promise));
|
||||
});
|
||||
}
|
||||
|
||||
// You may use the helper function like you would normally do,
|
||||
// without using the support methods of the continuable.
|
||||
mysql_query("DELETE FROM `users` WHERE `id` = 27361");
|
||||
|
||||
// Or using chaining to handle the result which is covered in the documentation.
|
||||
mysql_query("SELECT `id`, `name` FROM users")
|
||||
.then([](ResultSet result) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
### Error handling
|
||||
|
||||
Continuables support asynchronous error handling through exceptions or custom error types.
|
||||
|
||||
The error type will be **`std::exception_ptr`** except if one of the following definition is defined:
|
||||
- **`CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`**: Define this to use a user defined error type.
|
||||
- **`CONTINUABLE_WITH_NO_EXCEPTIONS`**: Define this to use **`std::error_condition`** as error type and to disable exception support. When exceptions are disabled this definition is set automatically.
|
||||
|
||||
Resolving a promise through an error will skip all following result handlers attached through **`then`**:
|
||||
|
||||
```cpp
|
||||
auto get_bad_continuable(std::exception const& e) {
|
||||
return cti::make_continuable<void>([=] (auto&& promise) {
|
||||
try {
|
||||
throw e;
|
||||
} catch(...) {
|
||||
promise.set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
You may handle the exception as following:
|
||||
|
||||
```cpp
|
||||
get_bad_continuable()
|
||||
- **Handle failures through `fail` or `next`:**
|
||||
```cpp
|
||||
http_request("example.com")
|
||||
.then([] {
|
||||
// ... never invoked
|
||||
throw std::exception("Some error");
|
||||
})
|
||||
.then([] {
|
||||
// ... never invoked as well
|
||||
})
|
||||
.fail([] (std::exception_ptr e) {
|
||||
.fail([] (std::exception_ptr ptr) {
|
||||
if (ptr) {
|
||||
try {
|
||||
std::rethrow_exception(e);
|
||||
std::rethrow_exception(ptr);
|
||||
} catch(std::exception const& e) {
|
||||
// Handle the exception here
|
||||
// Handle the exception or error code here
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
### Connecting Continuables {all, any or sequential}
|
||||
- **Dispatch continuations through a specific executor** (possibly on a different thread or later)
|
||||
|
||||
Continuables provide the operators **&&** and **||** for logical connection:
|
||||
|
||||
* **&&** invokes the final callback with the compound result of all connected continuables, the continuables were invoked in parallel.
|
||||
* **||** invokes the final callback once with the first result which becomes available.
|
||||
* **>\>** invokes the final callback with the compound result of all connected continuables but the continuations were invokes sequentially.
|
||||
|
||||
```C++
|
||||
auto http_request(std::string url) {
|
||||
return cti::make_continuable<std::string>([](auto&& promise) {
|
||||
promise.set_value("<html>...</html>");
|
||||
});
|
||||
}
|
||||
|
||||
// `all` of connections:
|
||||
(http_request("github.com") && http_request("travis-ci.org") && http_request("atom.io"))
|
||||
.then([](std::string github, std::string travis, std::string atom) {
|
||||
// The callback is called with the response of github, travis and atom.
|
||||
});
|
||||
|
||||
// `any` of connections:
|
||||
(http_request("github.com") || http_request("travis-ci.org") || http_request("atom.io"))
|
||||
.then([](std::string github_or_travis_or_atom) {
|
||||
// The callback is called with the first response of either github, travis or atom.
|
||||
});
|
||||
|
||||
// `sequence` of connections:
|
||||
(http_request("github.com") >> http_request("travis-ci.org") >> http_request("atom.io"))
|
||||
.then([](std::string github, std::string travis, std::string atom) {
|
||||
// The requests are invoked sequentially
|
||||
});
|
||||
|
||||
// mixed logical connections:
|
||||
(http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io")))
|
||||
.then([](std::string github, std::string travis_or_atom) {
|
||||
// The callback is called with the response of github for sure
|
||||
// and the second parameter represents the response of travis or atom.
|
||||
});
|
||||
|
||||
// There are helper functions for connecting continuables:
|
||||
auto all = cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
|
||||
auto any = cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
|
||||
auto seq = cti::when_seq(http_request("github.com"), http_request("travis-ci.org"));
|
||||
```
|
||||
|
||||
> **Note:** Logical connections are ensured to be **thread-safe** and **wait-free** by library design (when assuming that *std::call_once* is wait-free - which depends on the toolchain).
|
||||
|
||||
### Partial argument application
|
||||
|
||||
The callback is called only with the arguments it's accepting:
|
||||
|
||||
```c++
|
||||
(http_request("github.com") && read_file("entries.csv"))
|
||||
.then([] {
|
||||
// ^^^^^^ The original signature was <std::string, Buffer>,
|
||||
// however, the callback is only invoked with the amount of
|
||||
// arguments it's accepting.
|
||||
});
|
||||
```
|
||||
|
||||
### Dispatching callbacks through a specific executor
|
||||
|
||||
Dispatching a callback through a specific executor is supported through through the second argument of `then()`:
|
||||
|
||||
```c++
|
||||
auto executor = [](auto&& work) {
|
||||
```cpp
|
||||
auto executor = [](auto&& work) {
|
||||
// Dispatch the work here, store it for later invocation or move it to another thread.
|
||||
std::forward<decltype(work)>(work)();
|
||||
};
|
||||
};
|
||||
|
||||
read_file("entries.csv")
|
||||
read_file("entries.csv")
|
||||
.then([](Buffer buffer) {
|
||||
// ...
|
||||
}, executor);
|
||||
// ^^^^^^^^
|
||||
```
|
||||
// ^^^^^^^^
|
||||
```
|
||||
|
||||
### Type erasure
|
||||
- **Connect continuables through `when_all`, `when_any` or `when_seq`:**
|
||||
```cpp
|
||||
// `all` of connections:
|
||||
(http_request("github.com") && http_request("example.com") && http_request("wikipedia.org"))
|
||||
.then([](std::string github, std::string example, std::string wikipedia) {
|
||||
// The callback is called with the response of github,
|
||||
// example and wikipedia.
|
||||
});
|
||||
|
||||
The library was designed in order to avoid type-erasure until really needed. Thus we provide traits to create an alias to a continuable using the **type-erasure backend of your choice**. All templated functors providing a call operator may be used as a backend (*std::function* for instance).
|
||||
// `any` of connections:
|
||||
(http_request("github.com") || http_request("example.com") || http_request("wikipedia.org"))
|
||||
.then([](std::string github_or_example_or_wikipedia) {
|
||||
// The callback is called with the first response of either github,
|
||||
// example or wikipedia.
|
||||
});
|
||||
|
||||
The library provides aliases for using my [function2 library](https://github.com/Naios/function2) as backend which provides efficient and qualifier correct function wrappers for copyable and non-copyable objects.
|
||||
// `sequence` of connections:
|
||||
(http_request("github.com") >> http_request("example.com") >> http_request("wikipedia.org"))
|
||||
.then([](std::string github, std::string example, std::string wikipedia) {
|
||||
// The requests are invoked sequentially
|
||||
});
|
||||
|
||||
```c++
|
||||
#include <continuable/continuable.hpp>
|
||||
// Mixed logical connections:
|
||||
(http_request("github.com") && (http_request("example.com") || http_request("wikipedia.org")))
|
||||
.then([](std::string github, std::string example_or_wikipedia) {
|
||||
// The callback is called with the response of github for sure
|
||||
// and the second parameter represents the response of example or wikipedia.
|
||||
});
|
||||
|
||||
cti::unique_continuable<int, std::string> unique =
|
||||
cti::make_continuable([value = std::make_unique<int>(0)](auto&& promise) {
|
||||
// There are helper functions for connecting continuables:
|
||||
auto all = cti::when_all(http_request("github.com"), http_request("example.com"));
|
||||
auto any = cti::when_any(http_request("github.com"), http_request("example.com"));
|
||||
auto seq = cti::when_seq(http_request("github.com"), http_request("example.com"));
|
||||
```
|
||||
|
||||
// The use of non copyable objects is possible by design if
|
||||
// the type erasure backend supports it.
|
||||
promise.set_value(*value, "Hello, World!");
|
||||
});
|
||||
|
||||
std::move(unique).then([](int i) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
We could also think about using `std::future` as backend but this is even worse then using `std::function` because usually there is, even more, type erasure and allocations involved.
|
||||
Additionally `std::function` doesn't provide support for multiple function overloads
|
||||
|
||||
### Coroutines
|
||||
|
||||
Since version 2.0.0 coroutines (`co_await` and `co_return`) are supported by continuables when the underlying toolchain supports the TS. Currently this works in MSVC 2017 and Clang 5.0.
|
||||
You have to enable this capability through the `CTI_CONTINUABLE_WITH_AWAIT` define in CMake.
|
||||
|
||||
```c++
|
||||
int i = co_await cti::make_continuable<int>([](auto&& promise) {
|
||||
promise.set_value(0);
|
||||
});
|
||||
```
|
||||
|
||||
### Future conversion
|
||||
|
||||
The library is capable of converting (*futurizing*) every continuable into a fitting **std::future** through the `continuable<...>::futurize()` method.:
|
||||
|
||||
```c++
|
||||
std::future<std::string> future = http_request("github.com")
|
||||
.then([](std::string response) {
|
||||
// Do sth...
|
||||
return http_request("travis-ci.org") || http_request("atom.io");
|
||||
- **Deal with multiple result variables through `result` and `recover` from failures:**
|
||||
```cpp
|
||||
make_exceptional_continuable<void>(std::make_exception_ptr(std::exception("Some error"))
|
||||
.fail([] (std::exception_ptr ptr) {
|
||||
return recover();
|
||||
})
|
||||
.apply(cti::transforms::futurize());
|
||||
// ^^^^^^^^
|
||||
.then([] () -> result<> {
|
||||
// We recovered from the failure and proceeding normally
|
||||
|
||||
std::future<std::tuple<std::string, std::string>> future =
|
||||
(http_request("travis-ci.org") && http_request("atom.io"))
|
||||
.apply(cti::transforms::futurize());
|
||||
```
|
||||
// Will yield a default constructed exception type to signal cancellation
|
||||
return cancel();
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** See the [doxygen documentation](https://naios.github.io/continuable/) for detailed information about the return type of `futurize()`.
|
||||
- **`promisify` your existing code or use the (asio) completion token integration:**
|
||||
```cpp
|
||||
// Promisification of your existing code that accepts callbacks
|
||||
auto async_resolve(std::string host, std::string service) {
|
||||
return cti::promisify<asio::ip::udp::resolver::iterator>::from(
|
||||
[&](auto&&... args) {
|
||||
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
std::move(host), std::move(service));
|
||||
}
|
||||
|
||||
## Compatibility
|
||||
// (boost) asio completion token integration
|
||||
asio::io_context io_context;
|
||||
asio::steady_timer steady_timer(io_context);
|
||||
|
||||
Tested & compatible with:
|
||||
steady_timer.expires_after(std::chrono::seconds(5));
|
||||
steady_timer.async_wait(cti::use_continuable)
|
||||
.then([] {
|
||||
// Is called after 5s
|
||||
});
|
||||
```
|
||||
|
||||
- Visual Studio 2017+ Update 2
|
||||
- Clang 5.0+
|
||||
- GCC 6.0+
|
||||
- **C++20 Coroutine support:**
|
||||
|
||||
Although the build is observed with the latest toolchains earlier ones might work.
|
||||
(`co_await` and `co_return`) are supported by continuable when the underlying toolchain supports the TS. Currently this works in MSVC 2017 and Clang 5.0. You have to enable this capability through the `CTI_CONTINUABLE_WITH_AWAIT` define in CMake:
|
||||
|
||||
The library only depends on the standard library when using the `continuable/continuable-base.hpp` header, which provides the basic continuation logic.
|
||||
```cpp
|
||||
int i = co_await cti::make_continuable<int>([](auto&& promise) {
|
||||
promise.set_value(0);
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** On Posix: don't forget to **link a corresponding thread library** into your application otherwise `std::future's` won't work `(-pthread)` when using future based transforms.
|
||||
|
||||
## License
|
||||
#### Appearances:
|
||||
|
||||
The continuable library is licensed under the MIT License:
|
||||
|
||||
```
|
||||
/**
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v2.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
```
|
||||
| [MeetingC++ 2018 Talk](https://naios.github.io/talks/2018-11-17-Meeting-C%2B%2B-Berlin/Continuable.pdf) |
|
||||
| :---: |
|
||||
| [<img alt="Continuable MeetingC++" width="60%" src="https://img.youtube.com/vi/l6-spMA_x6g/0.jpg">](https://www.youtube.com/watch?v=l6-spMA_x6g)] |
|
||||
|
||||
.
|
||||
|
||||
21
appveyor.yml
21
appveyor.yml
@ -1,13 +1,17 @@
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
- Visual Studio 2019
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- WITH_NO_EXCEPTIONS: OFF
|
||||
WITH_CPP_LATEST: OFF
|
||||
- WITH_NO_EXCEPTIONS: ON
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
WITH_CPP_LATEST: OFF
|
||||
- WITH_NO_EXCEPTIONS: OFF
|
||||
WITH_CPP_LATEST: ON
|
||||
- WITH_NO_EXCEPTIONS: ON
|
||||
WITH_CPP_LATEST: ON
|
||||
|
||||
platform:
|
||||
- x64
|
||||
@ -22,15 +26,18 @@ before_build:
|
||||
- cmd: >
|
||||
cmake -H. -Bbuild -A%PLATFORM%
|
||||
-DCTI_CONTINUABLE_WITH_NO_EXCEPTIONS=%WITH_NO_EXCEPTIONS%
|
||||
-DCTI_CONTINUABLE_WITH_AWAIT=ON
|
||||
-DCTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE=ON
|
||||
-DCTI_CONTINUABLE_WITH_CPP_LATEST=%WITH_CPP_LATEST%
|
||||
|
||||
build_script:
|
||||
- cmd: cmake --build build --config %CONFIGURATION% --target ALL_BUILD -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
|
||||
- cmd: cmake --build build --config %CONFIGURATION% --target PACKAGE -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
|
||||
- cmd: cmake --build build --config Debug --target ALL_BUILD -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
|
||||
- cmd: cmake --build build --config Debug --target ALL_BUILD -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
|
||||
- cmd: cmake --build build --config Release --target PACKAGE -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
|
||||
|
||||
test_script:
|
||||
- cmd: cd build
|
||||
- cmd: ctest -C %CONFIGURATION% -V .
|
||||
- cmd: ctest -C Debug -V .
|
||||
- cmd: ctest -C Release -V .
|
||||
|
||||
artifacts:
|
||||
- path: 'build/*.zip'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
@ -13,7 +13,7 @@
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
@ -13,7 +13,7 @@
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
@ -13,7 +13,7 @@
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
@ -13,7 +13,7 @@
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
@ -31,9 +31,18 @@ if (PLATFORM EQUAL 64)
|
||||
-D_WIN64)
|
||||
endif()
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_CONCURRENT_JOBS)
|
||||
target_compile_options(continuable-features-flags
|
||||
INTERFACE
|
||||
/MP${CTI_CONTINUABLE_WITH_CONCURRENT_JOBS})
|
||||
else()
|
||||
target_compile_options(continuable-features-flags
|
||||
INTERFACE
|
||||
/MP)
|
||||
endif()
|
||||
|
||||
target_compile_options(continuable-features-flags
|
||||
INTERFACE
|
||||
/MP
|
||||
/bigobj
|
||||
/permissive-)
|
||||
|
||||
@ -41,6 +50,12 @@ target_compile_options(continuable-features-warnings
|
||||
INTERFACE
|
||||
/W4)
|
||||
|
||||
if (NOT CTI_CONTINUABLE_WITH_CPP_LATEST)
|
||||
target_compile_options(continuable-features-flags
|
||||
INTERFACE
|
||||
/std:c++14)
|
||||
endif()
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
|
||||
target_compile_definitions(continuable-features-noexcept
|
||||
INTERFACE
|
||||
|
||||
7
cmake/config.cmake.in
Normal file
7
cmake/config.cmake.in
Normal file
@ -0,0 +1,7 @@
|
||||
set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
|
||||
check_required_components(@PROJECT_NAME@)
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
@ -13,7 +13,7 @@
|
||||
#
|
||||
# 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
|
||||
# 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
|
||||
@ -21,12 +21,12 @@
|
||||
|
||||
# Select the compiler specific cmake file
|
||||
set(MSVC_ID "MSVC")
|
||||
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/compiler/clang.cmake)
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "(Apple)?Clang")
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/compiler/clang.cmake)
|
||||
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/compiler/gcc.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/compiler/gcc.cmake)
|
||||
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL ${MSVC_ID})
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/compiler/msvc.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/compiler/msvc.cmake)
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown compiler!")
|
||||
endif()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
@ -13,10 +13,10 @@
|
||||
#
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/macros/group_sources.cmake)
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/macros/group_sources.cmake)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
|
||||
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files(the "Software"), to deal
|
||||
@ -13,18 +13,19 @@
|
||||
#
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
set(WITH_SOURCE_TREE "hierarchical")
|
||||
macro(group_sources dir)
|
||||
macro(group_sources)
|
||||
# Skip this if WITH_SOURCE_TREE is not set (empty string).
|
||||
if (NOT ${WITH_SOURCE_TREE} STREQUAL "")
|
||||
foreach(dir ${ARGN})
|
||||
# Include all header and c files
|
||||
file(GLOB_RECURSE elements RELATIVE ${dir} *.h *.hpp *.inl *.inc *.c *.cpp *.cc)
|
||||
file(GLOB_RECURSE elements RELATIVE ${dir} ${dir}/*)
|
||||
|
||||
foreach(element ${elements})
|
||||
# Extract filename and directory
|
||||
@ -54,5 +55,6 @@ macro(group_sources dir)
|
||||
source_group("\\" FILES ${dir}/${element})
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
37
conanfile.py
Normal file
37
conanfile.py
Normal file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from conans import ConanFile, tools
|
||||
|
||||
def get_version():
|
||||
git = tools.Git()
|
||||
try:
|
||||
return git.run("describe --tags --abbrev=0")
|
||||
except:
|
||||
return None
|
||||
|
||||
class ContinuableConan(ConanFile):
|
||||
name = "continuable"
|
||||
version = get_version()
|
||||
license = "MIT"
|
||||
url = "https://github.com/Naios/continuable"
|
||||
author = "Denis Blank (denis.blank@outlook.com)"
|
||||
description = "C++14 asynchronous allocation aware futures"
|
||||
homepage = "https://naios.github.io/continuable/"
|
||||
no_copy_source = True
|
||||
scm = {
|
||||
"type": "git",
|
||||
"url": "auto",
|
||||
"revision": "auto"
|
||||
}
|
||||
|
||||
def package(self):
|
||||
self.copy("LICENSE.txt", "licenses")
|
||||
self.copy("include/*.hpp")
|
||||
self.copy("include/*.inl")
|
||||
|
||||
def package_id(self):
|
||||
self.info.header_only()
|
||||
|
||||
def requirements(self):
|
||||
self.requires("function2/4.0.0@naios/stable")
|
||||
@ -1,17 +1,25 @@
|
||||
if(NOT TARGET function2)
|
||||
if(NOT TARGET function2::function2)
|
||||
add_subdirectory(function2)
|
||||
endif()
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
|
||||
if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_BENCHMARKS)
|
||||
if(NOT TARGET gtest)
|
||||
add_subdirectory(googletest)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT TARGET cxx_function)
|
||||
add_subdirectory(cxx_function)
|
||||
endif()
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_EXAMPLES)
|
||||
if(NOT TARGET asio)
|
||||
add_subdirectory(asio)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_BENCHMARKS)
|
||||
if(NOT TARGET benchmark)
|
||||
add_subdirectory(benchmark)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET boost)
|
||||
add_subdirectory(boost)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
add_library(asio STATIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/asio/asio/src/asio.cpp)
|
||||
${CMAKE_CURRENT_LIST_DIR}/asio/asio/src/asio.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/include/boost/throw_exception.hpp)
|
||||
|
||||
target_include_directories(asio
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/asio/asio/include)
|
||||
${CMAKE_CURRENT_LIST_DIR}/asio/asio/include
|
||||
${CMAKE_CURRENT_LIST_DIR}/include)
|
||||
|
||||
target_compile_definitions(asio
|
||||
PUBLIC
|
||||
@ -11,6 +13,15 @@ target_compile_definitions(asio
|
||||
-DASIO_SEPARATE_COMPILATION=1
|
||||
-DASIO_NO_TYPEID=1)
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
|
||||
target_compile_definitions(asio
|
||||
PUBLIC
|
||||
-DASIO_NO_EXCEPTIONS=1
|
||||
-DASIO_HAS_BOOST_THROW_EXCEPTION=1)
|
||||
|
||||
message(STATUS "ASIO: Disabled exceptions")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(asio
|
||||
PUBLIC
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 230c0d2ae035c5ce1292233fcab03cea0d341264
|
||||
Subproject commit 6c054e98f3f53352d12b6cd46d63b6d404cc044b
|
||||
47
dep/asio/include/boost/throw_exception.hpp
Normal file
47
dep/asio/include/boost/throw_exception.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.0.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_BOOST_THROW_EXCEPTION_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_BOOST_THROW_EXCEPTION_HPP_INCLUDED
|
||||
|
||||
#if defined(ASIO_STANDALONE) && defined(ASIO_NO_EXCEPTIONS)
|
||||
# include <cstdio>
|
||||
# include <cstdlib>
|
||||
|
||||
namespace boost {
|
||||
template <typename Exception>
|
||||
void throw_exception(Exception const& e) {
|
||||
puts(e.what());
|
||||
std::abort();
|
||||
}
|
||||
} // namespace boost
|
||||
#endif
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_BOOST_THROW_EXCEPTION_HPP_INCLUDED
|
||||
9
dep/benchmark/CMakeLists.txt
Normal file
9
dep/benchmark/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
set(BENCHMARK_ENABLE_TESTING OFF)
|
||||
if (CTI_CONTINUABLE_WITHOUT_EXCEPTIONS)
|
||||
set(BENCHMARK_ENABLE_EXCEPTIONS OFF)
|
||||
else()
|
||||
set(BENCHMARK_ENABLE_EXCEPTIONS ON)
|
||||
endif()
|
||||
set(BENCHMARK_ENABLE_INSTALL OFF)
|
||||
set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
|
||||
add_subdirectory(benchmark)
|
||||
1
dep/benchmark/benchmark
Submodule
1
dep/benchmark/benchmark
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b874e72208b6e21b62287942e5e3b11f6630107f
|
||||
43
dep/boost/CMakeLists.txt
Normal file
43
dep/boost/CMakeLists.txt
Normal file
@ -0,0 +1,43 @@
|
||||
if(WIN32)
|
||||
if(CMAKE_SIZEOF_VOID_P MATCHES 8)
|
||||
set(PLATFORM 64)
|
||||
else()
|
||||
set(PLATFORM 32)
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{BOOST_ROOT})
|
||||
set(BOOST_ROOT $ENV{BOOST_ROOT})
|
||||
set(BOOST_LIBRARYDIR ${BOOST_ROOT}/lib${PLATFORM}-msvc-14.1)
|
||||
endif()
|
||||
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
set(Boost_USE_MULTITHREADED ON)
|
||||
set(Boost_USE_STATIC_RUNTIME OFF)
|
||||
endif()
|
||||
|
||||
find_package(Boost 1.68 REQUIRED
|
||||
COMPONENTS
|
||||
system
|
||||
iostreams
|
||||
thread)
|
||||
|
||||
if (${Boost_FOUND})
|
||||
add_library(boost INTERFACE)
|
||||
|
||||
target_link_libraries(boost
|
||||
INTERFACE
|
||||
Boost::system
|
||||
Boost::iostreams
|
||||
Boost::thread)
|
||||
|
||||
target_compile_definitions(boost
|
||||
INTERFACE
|
||||
BOOST_ALL_NO_LIB
|
||||
BOOST_ASIO_DISABLE_BOOST_DATE_TIME
|
||||
BOOST_ASIO_DISABLE_BOOST_REGEX
|
||||
BOOST_RANGE_ENABLE_CONCEPT_ASSERT=0
|
||||
BOOST_THREAD_PROVIDES_FUTURE
|
||||
BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
|
||||
BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
BOOST_THREAD_VERSION=4)
|
||||
endif()
|
||||
@ -1,5 +0,0 @@
|
||||
add_library(cxx_function INTERFACE)
|
||||
|
||||
target_include_directories(cxx_function
|
||||
INTERFACE
|
||||
"${CMAKE_CURRENT_LIST_DIR}")
|
||||
@ -1 +0,0 @@
|
||||
Subproject commit c12ed6e2da0c05ebc7bac30cdd260f152cb565a1
|
||||
@ -1 +1 @@
|
||||
Subproject commit 8611ae3b0e2414427bf0381bd94085ca3175479b
|
||||
Subproject commit 3a0746bf5f601dfed05330aefcb6854354fce07d
|
||||
@ -1,3 +1,4 @@
|
||||
if(ON)
|
||||
add_library(gtest STATIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc)
|
||||
|
||||
@ -42,3 +43,10 @@ target_include_directories(gmock
|
||||
${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock/include)
|
||||
|
||||
else()
|
||||
set(BUILD_GTEST ON)
|
||||
set(BUILD_GMOCK OFF)
|
||||
set(INSTALL_GTEST OFF)
|
||||
add_subdirectory(googletest)
|
||||
endif()
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit ac34e6c950925df7165e626becd3f9d64dcd584b
|
||||
Subproject commit f2fb48c3b3d79a75a88a99fba6576b25d42ec528
|
||||
@ -1 +0,0 @@
|
||||
add_subdirectory(code)
|
||||
@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.10
|
||||
# Doxyfile 1.8.14
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@ -20,8 +20,8 @@
|
||||
# This tag specifies the encoding used for all characters in the config file
|
||||
# that follow. The default is UTF-8 which is also the encoding used for all text
|
||||
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
|
||||
# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
|
||||
# for the list of possible encodings.
|
||||
# built into libc) for the transcoding. See
|
||||
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
@ -38,13 +38,13 @@ PROJECT_NAME = Continuable
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_NUMBER = 4.1.0
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
# quick idea about the purpose of the project. Keep the description short.
|
||||
|
||||
PROJECT_BRIEF = "Async C++14 platform independent continuation chainer providing light and allocation aware futures"
|
||||
PROJECT_BRIEF = "C++14 asynchronous allocation aware futures"
|
||||
|
||||
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
|
||||
# in the documentation. The maximum height of the logo should not exceed 55
|
||||
@ -303,6 +303,15 @@ EXTENSION_MAPPING =
|
||||
|
||||
MARKDOWN_SUPPORT = YES
|
||||
|
||||
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
|
||||
# to that level are automatically included in the table of contents, even if
|
||||
# they do not have an id attribute.
|
||||
# Note: This feature currently applies only to Markdown headings.
|
||||
# Minimum value: 0, maximum value: 99, default value: 0.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
TOC_INCLUDE_HEADINGS = 0
|
||||
|
||||
# When enabled doxygen tries to link words that correspond to documented
|
||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||
# be prevented in individual cases by putting a % sign in front of the word or
|
||||
@ -328,7 +337,7 @@ BUILTIN_STL_SUPPORT = NO
|
||||
CPP_CLI_SUPPORT = NO
|
||||
|
||||
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
|
||||
# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
|
||||
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
|
||||
# will parse them like normal C++ but will assume all classes use public instead
|
||||
# of private inheritance when no explicit protection keyword is present.
|
||||
# The default value is: NO.
|
||||
@ -614,19 +623,19 @@ STRICT_PROTO_MATCHING = NO
|
||||
# list. This list is created by putting \todo commands in the documentation.
|
||||
# The default value is: YES.
|
||||
|
||||
GENERATE_TODOLIST = YES
|
||||
GENERATE_TODOLIST = NO
|
||||
|
||||
# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
|
||||
# list. This list is created by putting \test commands in the documentation.
|
||||
# The default value is: YES.
|
||||
|
||||
GENERATE_TESTLIST = YES
|
||||
GENERATE_TESTLIST = NO
|
||||
|
||||
# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
|
||||
# list. This list is created by putting \bug commands in the documentation.
|
||||
# The default value is: YES.
|
||||
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_BUGLIST = NO
|
||||
|
||||
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
|
||||
# the deprecated list. This list is created by putting \deprecated commands in
|
||||
@ -671,7 +680,7 @@ SHOW_FILES = YES
|
||||
# Folder Tree View (if specified).
|
||||
# The default value is: YES.
|
||||
|
||||
SHOW_NAMESPACES = NO
|
||||
SHOW_NAMESPACES = YES
|
||||
|
||||
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
|
||||
# doxygen should invoke to get the current version for each file (typically from
|
||||
@ -699,7 +708,7 @@ LAYOUT_FILE =
|
||||
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
|
||||
# the reference definitions. This must be a list of .bib files. The .bib
|
||||
# extension is automatically appended if omitted. This requires the bibtex tool
|
||||
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# For LaTeX the style of the bibliography can be controlled using
|
||||
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
|
||||
# search path. See also \cite for info how to create references.
|
||||
@ -749,6 +758,12 @@ WARN_IF_DOC_ERROR = YES
|
||||
|
||||
WARN_NO_PARAMDOC = NO
|
||||
|
||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||
# a warning is encountered.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
|
||||
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
|
||||
# can produce. The string should contain the $file, $line, and $text tags, which
|
||||
# will be replaced by the file and line number from which the warning originated
|
||||
@ -775,13 +790,13 @@ WARN_LOGFILE =
|
||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../../include \
|
||||
../Index.md
|
||||
INPUT = ../include \
|
||||
../doc
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see: http://www.gnu.org/software/libiconv) for the list of
|
||||
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
|
||||
# possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
@ -798,8 +813,8 @@ INPUT_ENCODING = UTF-8
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf, *.as and *.js.
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
*.cc \
|
||||
@ -843,6 +858,7 @@ FILE_PATTERNS = *.c \
|
||||
*.ucf \
|
||||
*.qsf \
|
||||
*.as \
|
||||
*.dox \
|
||||
*.js
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
@ -858,9 +874,9 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE = ../../dep \
|
||||
../../test \
|
||||
../../examples
|
||||
EXCLUDE = ../dep \
|
||||
../test \
|
||||
../examples
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@ -929,6 +945,10 @@ IMAGE_PATH =
|
||||
# Note that the filter must not add or remove lines; it is applied before the
|
||||
# code is scanned, but not when the output code is generated. If lines are added
|
||||
# or removed, the anchors will not be placed correctly.
|
||||
#
|
||||
# Note that for custom extensions or not directly supported extensions you also
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# properly processed by doxygen.
|
||||
|
||||
INPUT_FILTER =
|
||||
|
||||
@ -938,6 +958,10 @@ INPUT_FILTER =
|
||||
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
|
||||
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
|
||||
# patterns match the file name, INPUT_FILTER is applied.
|
||||
#
|
||||
# Note that for custom extensions or not directly supported extensions you also
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# properly processed by doxygen.
|
||||
|
||||
FILTER_PATTERNS =
|
||||
|
||||
@ -961,7 +985,7 @@ FILTER_SOURCE_PATTERNS =
|
||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE = Index.md
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
@ -1022,7 +1046,7 @@ SOURCE_TOOLTIPS = YES
|
||||
# If the USE_HTAGS tag is set to YES then the references to source code will
|
||||
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
|
||||
# source browser. The htags tool is part of GNU's global source tagging system
|
||||
# (see http://www.gnu.org/software/global/global.html). You will need version
|
||||
# (see https://www.gnu.org/software/global/global.html). You will need version
|
||||
# 4.8.6 or higher.
|
||||
#
|
||||
# To use it do the following:
|
||||
@ -1055,7 +1079,7 @@ VERBATIM_HEADERS = YES
|
||||
# rich C++ code for which doxygen's built-in parser lacks the necessary type
|
||||
# information.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# compiled with the --with-libclang option.
|
||||
# generated with the -Duse-libclang=ON option for CMake.
|
||||
# The default value is: NO.
|
||||
|
||||
CLANG_ASSISTED_PARSING = NO
|
||||
@ -1068,6 +1092,17 @@ CLANG_ASSISTED_PARSING = NO
|
||||
|
||||
CLANG_OPTIONS =
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the clang parser with the
|
||||
# path to the compilation database (see:
|
||||
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
|
||||
# were built. This is equivalent to specifying the "-p" option to a clang tool,
|
||||
# such as clang-check. These options will then be pased to the parser.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# generated with the -Duse-libclang=ON option for CMake.
|
||||
# The default value is: 0.
|
||||
|
||||
CLANG_COMPILATION_DATABASE_PATH= 0
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
@ -1186,7 +1221,7 @@ HTML_EXTRA_FILES =
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a colorwheel, see
|
||||
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
|
||||
# purple, and 360 is red again.
|
||||
# Minimum value: 0, maximum value: 359, default value: 220.
|
||||
@ -1222,6 +1257,17 @@ HTML_COLORSTYLE_GAMMA = 80
|
||||
|
||||
HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via Javascript. If disabled, the navigation index will
|
||||
# consists of multiple levels of tabs that are statically embedded in every HTML
|
||||
# page. Disable this option to support browsers that do not have Javascript,
|
||||
# like the Qt help browser.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_DYNAMIC_MENUS = YES
|
||||
|
||||
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
|
||||
# documentation will contain sections that can be hidden and shown after the
|
||||
# page has loaded.
|
||||
@ -1245,12 +1291,12 @@ HTML_INDEX_NUM_ENTRIES = 100
|
||||
|
||||
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
|
||||
# generated that can be used as input for Apple's Xcode 3 integrated development
|
||||
# environment (see: http://developer.apple.com/tools/xcode/), introduced with
|
||||
# environment (see: https://developer.apple.com/tools/xcode/), introduced with
|
||||
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
||||
# Makefile in the HTML output directory. Running make will produce the docset in
|
||||
# that directory and running make install will install the docset in
|
||||
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
|
||||
# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
|
||||
# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
|
||||
# for more information.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
@ -1366,7 +1412,7 @@ QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
|
||||
# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace).
|
||||
# The default value is: org.doxygen.Project.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@ -1374,8 +1420,7 @@ QHP_NAMESPACE = org.doxygen.Project
|
||||
|
||||
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
|
||||
# Help Project output. For more information please see Qt Help Project / Virtual
|
||||
# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
|
||||
# folders).
|
||||
# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders).
|
||||
# The default value is: doc.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@ -1383,23 +1428,21 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
|
||||
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
|
||||
# filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
||||
# project's filter section matches. Qt Help Project / Filter Attributes (see:
|
||||
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
@ -1492,7 +1535,7 @@ EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
|
||||
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||
#
|
||||
@ -1504,7 +1547,7 @@ FORMULA_FONTSIZE = 10
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# http://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# https://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
|
||||
# installed or if you want to formulas look prettier in the HTML output. When
|
||||
# enabled you may also need to install MathJax separately and configure the path
|
||||
@ -1531,7 +1574,7 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
|
||||
# Content Delivery Network so you can quickly see the result without installing
|
||||
# MathJax. However, it is strongly recommended to install a local copy of
|
||||
# MathJax from http://www.mathjax.org before deployment.
|
||||
# MathJax from https://www.mathjax.org before deployment.
|
||||
# The default value is: http://cdn.mathjax.org/mathjax/latest.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
@ -1593,7 +1636,7 @@ SERVER_BASED_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: http://xapian.org/).
|
||||
# Xapian (see: https://xapian.org/).
|
||||
#
|
||||
# See the section "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
@ -1606,7 +1649,7 @@ EXTERNAL_SEARCH = NO
|
||||
#
|
||||
# Doxygen ships with an example indexer (doxyindexer) and search engine
|
||||
# (doxysearch.cgi) which are based on the open source search engine library
|
||||
# Xapian (see: http://xapian.org/). See the section "External Indexing and
|
||||
# Xapian (see: https://xapian.org/). See the section "External Indexing and
|
||||
# Searching" for details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
@ -1793,12 +1836,20 @@ LATEX_SOURCE_CODE = NO
|
||||
|
||||
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
|
||||
# bibliography, e.g. plainnat, or ieeetr. See
|
||||
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# The default value is: plain.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_BIB_STYLE = plain
|
||||
|
||||
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to NO can help when comparing the output of multiple runs.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
@ -1968,9 +2019,9 @@ DOCBOOK_PROGRAMLISTING = NO
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||
# AutoGen Definitions (see http://autogen.sf.net) file that captures the
|
||||
# structure of the code including all documentation. Note that this feature is
|
||||
# still experimental and incomplete at the moment.
|
||||
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||
# the structure of the code including all documentation. Note that this feature
|
||||
# is still experimental and incomplete at the moment.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
@ -2392,6 +2443,11 @@ DIAFILE_DIRS =
|
||||
|
||||
PLANTUML_JAR_PATH =
|
||||
|
||||
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
|
||||
# configuration file for plantuml.
|
||||
|
||||
PLANTUML_CFG_FILE =
|
||||
|
||||
# When using plantuml, the specified paths are searched for files specified by
|
||||
# the !include statement in a plantuml block.
|
||||
|
||||
41
doc/Index.md
41
doc/Index.md
@ -1,41 +0,0 @@
|
||||

|
||||
|
||||
## Content
|
||||
|
||||
- **Class cti::continuable_base** - for building up a continuation chain.
|
||||
- \link cti::continuable_base::then then\endlink - adds a callback or cti::continuable_base to the invocation chain.
|
||||
- \link cti::continuable_base::fail fail\endlink - adds an error callback to the invocation chain.
|
||||
- \link cti::continuable_base::next next\endlink - adds an error and result callback to the invocation chain.
|
||||
- \link cti::continuable_base::operator | operator|\endlink - connects another object through \link cti::continuable_base::then then\endlink.
|
||||
- \link cti::continuable_base::operator && operator&&\endlink - connects another cti::continuable_base with an *all* logic.
|
||||
- \link cti::continuable_base::operator|| operator||\endlink - connects another cti::continuable_base with an *any* logic.
|
||||
- \link cti::continuable_base::operator>> operator>>\endlink - connects another cti::continuable_base with a *sequential* logic.
|
||||
- \link cti::continuable_base::done done\endlink - \copybrief cti::continuable_base::done
|
||||
- \link cti::continuable_base::freeze freeze \endlink - prevents the automatic invocation on destruction of the cti::continuable_base.
|
||||
- \link cti::continuable_base::is_frozen is_frozen\endlink - \copybrief cti::continuable_base::is_frozen
|
||||
- **Class cti::promise_base** - for resolving a continuation chain through a result or error.
|
||||
- \link cti::promise_base::set_value set_value\endlink - resolves the continuation chain through a result.
|
||||
- \link cti::promise_base::set_exception set_exception\endlink - resolves the continuation chain through an error.
|
||||
- **Helper functions**
|
||||
- \link cti::make_continuable make_continuable\endlink - creates a cti::continuable_base from a callback tanking function.
|
||||
- \link cti::when_all when_all\endlink - connects all given cti::continuable_base objects with an *all* logic.
|
||||
- \link cti::when_any when_any\endlink - connects all given cti::continuable_base objects with an *any* logic.
|
||||
- \link cti::when_seq when_seq\endlink - connects all given cti::continuable_base objects with a *sequence* logic.
|
||||
- **Transforms** - Apply a transform to the continuable
|
||||
- \link cti::transforms::futurize futurize\endlink - \copybrief cti::transforms::futurize
|
||||
- \link cti::transforms::flatten flatten\endlink - \copybrief cti::transforms::flatten
|
||||
- **Predefined (erased) types** - Predefined type erarasures for continuables and promises
|
||||
- \link cti::promise promise\endlink - \copybrief cti::promise
|
||||
- \link cti::continuable continuable\endlink - \copybrief cti::continuable
|
||||
- \link cti::unique_continuable unique_continuable\endlink - \copybrief cti::unique_continuable
|
||||
- **Class cti::continuable_trait** - A trait class for defining your own cti::continuable_base trait with the type-erasure backend of your choice.
|
||||
- \link cti::continuable_trait::promise promise\endlink - \copybrief cti::continuable_trait::promise
|
||||
- \link cti::continuable_trait::continuable continuable\endlink - \copybrief cti::continuable_trait::continuable
|
||||
- **GTest macros:**
|
||||
- \link ASSERT_ASYNC_COMPLETION ASSERT_ASYNC_COMPLETION \endlink - \copybrief ASSERT_ASYNC_COMPLETION
|
||||
- \link ASSERT_ASYNC_INCOMPLETION ASSERT_ASYNC_INCOMPLETION \endlink - \copybrief ASSERT_ASYNC_INCOMPLETION
|
||||
- \link ASSERT_ASYNC_VALIDATION ASSERT_ASYNC_VALIDATION \endlink - \copybrief ASSERT_ASYNC_VALIDATION
|
||||
- \link ASSERT_ASYNC_BINARY_VALIDATION ASSERT_ASYNC_BINARY_VALIDATION \endlink - \copybrief ASSERT_ASYNC_BINARY_VALIDATION
|
||||
- \link EXPECT_ASYNC_RESULT EXPECT_ASYNC_RESULT \endlink - \copybrief EXPECT_ASYNC_RESULT
|
||||
- \link ASSERT_ASYNC_RESULT ASSERT_ASYNC_RESULT \endlink - \copybrief ASSERT_ASYNC_RESULT
|
||||
- \link ASSERT_ASYNC_TYPES ASSERT_ASYNC_TYPES \endlink - \copybrief ASSERT_ASYNC_TYPES
|
||||
303
doc/changelog.dox
Normal file
303
doc/changelog.dox
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page changelog Changelog
|
||||
\brief A description of the changes made to continuable.
|
||||
|
||||
\section changelog-versions Versions
|
||||
|
||||
Following versions were released:
|
||||
|
||||
\subsection changelog-versions-4-0-0 4.0.0
|
||||
|
||||
Various issues have been resolved:
|
||||
|
||||
- [#27: First class, zero-overhead ASIO integration](https://github.com/Naios/continuable/issues/27)
|
||||
- [#23: VS 16.2: parameter pack must be expanded in this context](https://github.com/Naios/continuable/issues/23)
|
||||
- [#21: Infinite recursion during compilation](https://github.com/Naios/continuable/issues/21)
|
||||
- [#16: Add AppleClang compiler to cmake](https://github.com/Naios/continuable/issues/16)
|
||||
- [#13: unit-test/test-continuable-single fails on gcc 8.2](https://github.com/Naios/continuable/issues/13)
|
||||
- [#11: Forward declarations are no longer allowed in type-erased continuables](https://github.com/Naios/continuable/issues/11)
|
||||
|
||||
Following methods and functions have been added:
|
||||
|
||||
<B>Various improvements to continuable_base:</B>
|
||||
|
||||
- \ref continuable_base::as for casting to a continuable_base with different arguments
|
||||
- \ref continuable_base::finish for 'materializing' an intermediate chained continuable_base
|
||||
|
||||
<B>An asychronous initiation function comparable to std::async:</B>
|
||||
|
||||
The asynchronous facilities make it possible now to start with a handler
|
||||
instead of a continuation:
|
||||
|
||||
\code{.cpp}
|
||||
async([] {
|
||||
// ...
|
||||
}).then(...);
|
||||
\endcode
|
||||
|
||||
- \ref async Makes it possible to start with a handler instead of a continuation, comparable to `std::async`
|
||||
- \ref async_on allows to specify an additional executor parameter
|
||||
|
||||
<B>The result class and modifying the asynchronous control flow</B>
|
||||
|
||||
Every continuation handler used in \ref continuable_base::then, \ref continuable_base::next
|
||||
and \ref continuable_base::fail allows now to return a \ref result which represents
|
||||
the asynchronous result.
|
||||
|
||||
This allows recovering from failures or throwing of exception types from
|
||||
handlers when exceptions are disabled.
|
||||
|
||||
Result handling and
|
||||
- \ref result
|
||||
- \ref rethrow Throws an exception or error code from a result or failure handler
|
||||
- \ref cancel Throws a default constructed exception type or error code from a result or failure handler to signal cancellation.
|
||||
- \ref stop \copybrief stop
|
||||
- \ref make_result \copybrief make_result
|
||||
|
||||
Special result types
|
||||
- \ref empty_result \copybrief empty_result
|
||||
- \ref cancellation_result \copybrief cancellation_result
|
||||
- \ref exceptional_result \copybrief exceptional_result
|
||||
|
||||
<B>Optimize 'ready' continuables:</B>
|
||||
|
||||
Continuables which are 'ready' and side effect free can now be unpacked
|
||||
synchronously. Mainly such continuables are created through \ref make_ready_continuable,
|
||||
\ref make_exceptional_continuable and \ref make_cancelling_continuable.
|
||||
|
||||
- \ref continuable_base::is_ready \copybrief continuable_base::is_ready
|
||||
- \ref continuable_base::unpack \copybrief continuable_base::unpack
|
||||
- \ref make_cancelling_continuable \copybrief make_cancelling_continuable
|
||||
|
||||
Including various helper tags for the underlying changed continuation object structure:
|
||||
|
||||
- \ref signature_arg_t
|
||||
- \ref is_ready_arg_t
|
||||
- \ref unpack_arg_t
|
||||
- \ref exception_arg_t
|
||||
- \ref exception_t
|
||||
|
||||
<B>asio asynchronous initiate token:</B>
|
||||
|
||||
The \ref use_continuable_t special tag can be used to make (boost) asio
|
||||
return a \ref continuable_base.
|
||||
|
||||
- \ref use_continuable \copybrief use_continuable_t
|
||||
|
||||
\code{.cpp}
|
||||
#include <continuable/continuable.hpp>
|
||||
#include <continuable/external/asio.hpp>
|
||||
#include <asio.hpp>
|
||||
|
||||
// ...
|
||||
|
||||
asio::tcp::resolver resolver(...);
|
||||
resolver.async_resolve("127.0.0.1", "daytime", cti::use_continuable)
|
||||
.then([](asio::udp::resolver::iterator iterator) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
|
||||
<B>Iterating over an asynchronous control flow:</B>
|
||||
|
||||
The loop function was added which makes is possible to emulate an asynchronous loop,
|
||||
that is comparable to a `co_await` with `for`.
|
||||
|
||||
- \ref loop \copybrief loop
|
||||
- \ref loop_result \copybrief loop_result
|
||||
- \ref loop_break \copybrief loop_break
|
||||
- \ref loop_continue \copybrief loop_continue
|
||||
- \ref range_loop \copybrief range_loop
|
||||
- \ref range_loop \copybrief range_loop
|
||||
- \ref plain_t \copybrief plain_t
|
||||
- \ref make_plain \copybrief make_plain
|
||||
|
||||
<B>Synchronous wait transforms:</B>
|
||||
|
||||
The wait transforms allows to block the current thread until a \ref continuable_base
|
||||
was resolved.
|
||||
|
||||
- \ref transforms::wait \copybrief transforms::wait
|
||||
- \ref transforms::wait_for Same as \ref transforms::wait wich waits for a given duration
|
||||
- \ref transforms::wait_until Same as \ref transforms::wait wich waits until a given timepoint
|
||||
|
||||
<B>Various changes:</B>
|
||||
|
||||
Many more unlisted changes including:
|
||||
|
||||
- \ref work \copybrief work
|
||||
- \ref continuation_capacity
|
||||
- \ref promisify::with \copybrief promisify::with
|
||||
- \ref void_arg_t
|
||||
|
||||
Additional various bugfixes have been made.
|
||||
|
||||
\subsection changelog-versions-3-0-0 3.0.0
|
||||
|
||||
<B>New helper functions</B>
|
||||
|
||||
New helper functions were added to create ready continuables:
|
||||
|
||||
- \ref make_ready_continuable \copybrief make_ready_continuable
|
||||
- \ref make_exceptional_continuable \copybrief make_exceptional_continuable
|
||||
|
||||
<B>Improvements to connections</B>
|
||||
|
||||
The implementation of connections were rewritten entirely.
|
||||
It is possible now to connect runtime sized containers as well as
|
||||
deeply nested sequences. See \ref tutorial-connecting-continuables for details.
|
||||
|
||||
Additionally connection overloads were added that accept two iterators
|
||||
in order to come closer to the interface of the standard library.
|
||||
|
||||
Also \ref populate was added which makes it possible to populate
|
||||
a dynamic container from \ref continuable_base objects.
|
||||
|
||||
<B>Disabled copies for promises and continuables entirely</B>
|
||||
|
||||
The \ref promise_base and \ref continuable_base is now non copyable.
|
||||
This change should make it easier to work with the move only
|
||||
semantic of continuables in order to make less mistakes.
|
||||
|
||||
<B>Traversal API</B>
|
||||
|
||||
A new traversal API for synchronous and asynchronous pack traversal
|
||||
was added which makes it easy to specify new connection types.
|
||||
|
||||
\subsection changelog-versions-2-0-0 2.0.0
|
||||
|
||||
<B>Error handling</B>
|
||||
|
||||
Usually it is inconvenient to handle error codes and exceptions in an
|
||||
asynchronous context, as we all know `std::future` supports error handling
|
||||
through exceptions already. We now introduce this capability to the
|
||||
continuable library while allowing error codes to be used as well.
|
||||
|
||||
Consider the function `cti::continuable<> get_bad_continuable()`
|
||||
which always resolves through an error, then you may handle the error code
|
||||
or exception as following:
|
||||
|
||||
\code{.cpp}
|
||||
get_bad_continuable()
|
||||
.then([] {
|
||||
// ... never invoked
|
||||
})
|
||||
.then([] {
|
||||
// ... never invoked as well
|
||||
})
|
||||
.fail([] (std::exception_ptr e) {
|
||||
try {
|
||||
std::rethrow_exception(e);
|
||||
} catch(std::exception const& e) {
|
||||
// Handle the exception here
|
||||
}
|
||||
});
|
||||
\endcode
|
||||
|
||||
|
||||
<B>Abstracting callbacks as promises</B>
|
||||
|
||||
Since a callback may be called through an error or result the cri::promise
|
||||
class was added in order ro provide a similar interface to std::promise:
|
||||
|
||||
|
||||
\code{.cpp}
|
||||
auto http_request(std::string url) {
|
||||
return cti::make_continuable<std::string>(
|
||||
[url = std::move(url)](cti::promise<std::string> promise) {
|
||||
// Perform the actual request through a different library,
|
||||
// resolve the promise upon completion of the task.
|
||||
promise.set_value("<html> ... </html>");
|
||||
// ...or promise.set_exception(...);
|
||||
});
|
||||
}
|
||||
\endcode
|
||||
|
||||
<B>`co_await` support</B>
|
||||
|
||||
Experimental coroutine (`co_await` and `co_return`) support was added,
|
||||
this is available on MSVC 2017 and Clang 5.0.
|
||||
|
||||
\code{.cpp}
|
||||
int i = co_await cti::make_continuable<int>([](auto&& promise) {
|
||||
promise.set_value(0);
|
||||
});
|
||||
\endcode
|
||||
|
||||
<B>Minor improvements</B>
|
||||
|
||||
The library was improved in other ways:
|
||||
|
||||
- `constexpr` and `noexcept` improvements
|
||||
- Compile-time improvements
|
||||
- Documentation improvements
|
||||
|
||||
<B>Header split</B>
|
||||
|
||||
Since the overall library size was increased the headers were split into smaller chunks.
|
||||
|
||||
\subsection changelog-versions-1-0-0 1.0.0
|
||||
|
||||
- Documentation and readme changes
|
||||
- Change the assertion type of some GTest macros from expected to assertion.
|
||||
|
||||
\subsection changelog-versions-0-8-0 0.8.0 (unstable)
|
||||
|
||||
- Fixes a major issue with handling the ownership for consumed continuables
|
||||
which led to unintended invocations.
|
||||
- Adds partial application support which makes it possible to chain callbacks
|
||||
which accept less arguments then the curret signature.
|
||||
\code{.cpp}
|
||||
http_request("github.com")
|
||||
.then([] {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
- Adds Support for sequential invocation:
|
||||
\code{.cpp}
|
||||
http_request("github.com") >> http_request("atom.io")
|
||||
.then([] (std::string github, std::string atom) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
|
||||
\subsection changelog-versions-0-7-0 0.7.0 (unstable)
|
||||
|
||||
- Continuation syntactic sugar
|
||||
- Executor support
|
||||
- Connection support
|
||||
|
||||
\section changelog-semver Semantic versioning and stability
|
||||
|
||||
Continuable strictly follows the rules of
|
||||
[semantic versioning](http://semver.org/), the API is kept stable
|
||||
across minor versions.
|
||||
|
||||
The CI driven unit-tests are observed through the Clang sanitizers
|
||||
(asan, ubsan and lsan - when compiling with Clang) or
|
||||
Valgrind (when compiling with GCC) in order to prevent regressions.
|
||||
|
||||
*/
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
add_subdirectory(documentation)
|
||||
add_subdirectory(slideshow)
|
||||
@ -1,5 +0,0 @@
|
||||
add_executable(doc-documentation
|
||||
${CMAKE_CURRENT_LIST_DIR}/doc-documentation.cpp)
|
||||
target_link_libraries(doc-documentation
|
||||
PRIVATE
|
||||
continuable)
|
||||
@ -1,152 +0,0 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <continuable/continuable.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
|
||||
using cti::detail::util::unused;
|
||||
|
||||
void creating_continuables() {
|
||||
auto void_continuable = cti::make_continuable<void>([](auto&& callback) {
|
||||
// ^^^^
|
||||
|
||||
// Call the promise later when you have finished your work
|
||||
callback.set_value();
|
||||
});
|
||||
|
||||
auto str_continuable =
|
||||
cti::make_continuable<std::string>([](auto&& callback) {
|
||||
// ^^^^^^^^^^^
|
||||
callback.set_value("Hello, World!");
|
||||
});
|
||||
}
|
||||
|
||||
struct ResultSet {};
|
||||
template <typename... Args>
|
||||
void mysql_handle_async_query(Args&&...) {
|
||||
}
|
||||
|
||||
auto mysql_query(std::string query) {
|
||||
return cti::make_continuable<ResultSet>([query = std::move(query)](
|
||||
auto&& callback) mutable {
|
||||
// Pass the callback to the handler which calls the callback when finished.
|
||||
// Every function accepting callbacks works with continuables.
|
||||
mysql_handle_async_query(std::move(query),
|
||||
std::forward<decltype(callback)>(callback));
|
||||
});
|
||||
}
|
||||
|
||||
void providing_helper_functions() {
|
||||
// You may use the helper function like you would normally do,
|
||||
// without using the support methods of the continuable.
|
||||
mysql_query("DELETE FROM `users` WHERE `id` = 27361");
|
||||
|
||||
// Or using chaining to handle the result which is covered in the
|
||||
// documentation.
|
||||
mysql_query("SELECT `id`, `name` FROM users").then([](ResultSet result) {
|
||||
// ...
|
||||
unused(result);
|
||||
});
|
||||
}
|
||||
|
||||
void chaining_continuables() {
|
||||
mysql_query("SELECT `id`, `name` FROM `users`")
|
||||
.then([](ResultSet users) {
|
||||
(void)users;
|
||||
// Return the next continuable to process ...
|
||||
return mysql_query("SELECT `id` name FROM `sessions`");
|
||||
})
|
||||
.then([](ResultSet sessions) {
|
||||
// ... or pass multiple values to the next callback using tuples or
|
||||
// pairs ...
|
||||
return std::make_tuple(std::move(sessions), true);
|
||||
})
|
||||
.then([](ResultSet sessions, bool is_ok) {
|
||||
(void)sessions;
|
||||
(void)is_ok;
|
||||
// ... or pass a single value to the next callback ...
|
||||
return 10;
|
||||
})
|
||||
.then([](auto value) {
|
||||
// ^^^^ Templated callbacks are possible too
|
||||
(void)value;
|
||||
})
|
||||
// ... you may even pass continuables to the `then` method directly:
|
||||
.then(mysql_query("SELECT * `statistics`"))
|
||||
.then([](ResultSet result) {
|
||||
// ...
|
||||
(void)result;
|
||||
});
|
||||
}
|
||||
|
||||
auto http_request(std::string /*url*/) {
|
||||
return cti::make_continuable<std::string>([](auto&& callback) {
|
||||
// ...
|
||||
callback.set_value("<html>...</html>");
|
||||
});
|
||||
}
|
||||
|
||||
void connecting_continuables() {
|
||||
// `all` of connections:
|
||||
(http_request("github.com") && http_request("travis-ci.org") &&
|
||||
http_request("atom.io"))
|
||||
.then([](std::string github, std::string travis, std::string atom) {
|
||||
// The callback is called with the response of github, travis and atom.
|
||||
unused(github, travis, atom);
|
||||
});
|
||||
|
||||
// `any` of connections:
|
||||
(http_request("github.com") || http_request("travis-ci.org") ||
|
||||
http_request("atom.io"))
|
||||
.then([](std::string github_or_travis_or_atom) {
|
||||
// The callback is called with the first response of either github,
|
||||
// travis or atom.
|
||||
unused(github_or_travis_or_atom);
|
||||
});
|
||||
|
||||
// mixed logical connections:
|
||||
(http_request("github.com") &&
|
||||
(http_request("travis-ci.org") || http_request("atom.io")))
|
||||
.then([](std::string github, std::string travis_or_atom) {
|
||||
// The callback is called with the response of github for sure
|
||||
// and the second parameter represents the response of travis or atom.
|
||||
unused(github, travis_or_atom);
|
||||
});
|
||||
|
||||
// There are helper functions for connecting continuables:
|
||||
auto all =
|
||||
cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
|
||||
auto any =
|
||||
cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
|
||||
}
|
||||
|
||||
int main() {
|
||||
creating_continuables();
|
||||
|
||||
providing_helper_functions();
|
||||
|
||||
chaining_continuables();
|
||||
|
||||
connecting_continuables();
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
add_executable(doc-slideshow
|
||||
${CMAKE_CURRENT_LIST_DIR}/doc-slideshow.cpp)
|
||||
target_link_libraries(doc-slideshow
|
||||
PRIVATE
|
||||
continuable)
|
||||
41
doc/configuration.dox
Normal file
41
doc/configuration.dox
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page configuration Configuration
|
||||
\brief Covers optional preprocessor macros that change the libraries behaviour
|
||||
|
||||
By default the library doesn't require any preprocessor definitions
|
||||
to be defined in order to work. However it is possible to define some
|
||||
in order to change the libraries behaviour:
|
||||
|
||||
| Preprocessor definition | Consequence |
|
||||
| ----------------------------------------- | --------------- |
|
||||
| `CONTINUABLE_WITH_NO_EXCEPTIONS` | Exceptions are disabled and `std::error_condition` is used as \ref error_type . See \ref tutorial-chaining-continuables-fail for details. |
|
||||
| `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE` | Exceptions are disabled and the type defined by `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE` is used as \ref error_type . See \ref tutorial-chaining-continuables-fail for details. |
|
||||
| `CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS` | Allows unhandled exceptions in asynchronous call hierarchies. See \ref tutorial-chaining-continuables-fail for details. |
|
||||
| `CONTINUABLE_WITH_CUSTOM_FINAL_CALLBACK` | Allows to customize the final callback which can be used to implement custom unhandled asynchronous exception handlers. |
|
||||
| `CONTINUABLE_WITH_IMMEDIATE_TYPES` | Don't decorate the used type erasure, which is done to keep type names minimal for better error messages in debug builds. |
|
||||
| `CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE` | Enables support for experimental coroutines and `co_await` expressions. See \ref continuable_base::operator co_await() for details. |
|
||||
|
||||
*/
|
||||
}
|
||||
1
doc/doxygen/.gitignore
vendored
1
doc/doxygen/.gitignore
vendored
@ -1 +0,0 @@
|
||||
doxygen/
|
||||
101
doc/index.dox
Normal file
101
doc/index.dox
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \mainpage Continuable
|
||||
|
||||
\section mainpage-overview Overview
|
||||
|
||||
<b>Continuable is a C++14 library that provides full support for:</b>
|
||||
- lazy async continuation chaining based on **callbacks**
|
||||
(\link continuable_base::then then\endlink) and
|
||||
expression templates, callbacks are wrapped nicely as \link promise_base promises\endlink.
|
||||
- **no enforced type-erasure** which means we need <b>less heap
|
||||
allocations</b> than comparable libraries, strictly following the <b>"don't
|
||||
pay for what you don't use"</b> principle.
|
||||
- support for **all, any and sequential connections** between continuables
|
||||
through expressive operator overloads \link continuable_base::operator && &&\endlink,
|
||||
\link continuable_base::operator || ||\endlink and
|
||||
\link continuable_base::operator>> >>\endlink as well as free functions
|
||||
\ref when_all, \ref when_any and \ref when_seq.
|
||||
- asynchronous \link continuable_base::fail error handling\endlink through
|
||||
\link promise_base::set_exception exceptions\endlink,
|
||||
\link configuration error codes\endlink and
|
||||
\link configuration user defined types\endlink.
|
||||
- **syntactic sugar** for instance: **partial invocation**, **tuple unpacking**,
|
||||
`co_await` support and \link continuable_base::then executors\endlink.
|
||||
- **encapsuled from any runtime**, larger framework or executors makes
|
||||
it possible to use continuable even in smaller or esoteric usage scenarios.
|
||||
|
||||
\section mainpage-getting-started Getting started
|
||||
|
||||
Continuable is a header-only library with zero compilation dependencies.
|
||||
The \ref installation and \ref configuration are explained in its own chapter.
|
||||
|
||||
The \ref tutorial is everything you need in order to get to know the libraries
|
||||
API. Beside of this, there is a detailed in-source documentation provided.
|
||||
|
||||
Continuable follows the semantic versioning schema and changes are listed
|
||||
in the \ref changelog.
|
||||
|
||||
\section mainpage-contact Contributing and Questions
|
||||
|
||||
Through the [Github issue tracker](https://github.com/Naios/continuable/issues)
|
||||
you are welcomed to ask for questions, contribute code or request new features.
|
||||
Also I would like to hear your personal opinion about the library design or
|
||||
your personal experience in using the library to improve it.
|
||||
|
||||
\attention If you like the library I would be glad if you star it on Github,
|
||||
because it helps other users to find this library.
|
||||
|
||||
\note If you are using the library in your open-source or commercial project
|
||||
I would highly appreciate if you could give me a short notice so I can
|
||||
add you to a list of projects and companies using this library.
|
||||
|
||||
\section mainpage-license License
|
||||
|
||||
Continuable is licensed under the MIT license:
|
||||
|
||||
>
|
||||
> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
>
|
||||
> 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 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.
|
||||
>
|
||||
|
||||
*/
|
||||
}
|
||||
157
doc/installation.dox
Normal file
157
doc/installation.dox
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page installation Installation
|
||||
\brief An explanation on how to install continuable on various platforms.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section installation-requirements Requirements
|
||||
|
||||
Continuable requires a fairly new toolchain and was verified to work with
|
||||
following compilers:
|
||||
|
||||
- Visual Studio 2017+ Update 2
|
||||
- Clang 5.0+
|
||||
- GCC 6.0+
|
||||
|
||||
Although the build is observed with the listed compilers earlier
|
||||
versions might work.
|
||||
|
||||
\warning GCC is proven to be slower than Clang in template compilation and
|
||||
thus it is suggested to use Clang instead.
|
||||
|
||||
\section installation-dependencies Dependencies
|
||||
|
||||
Continuable is a header-only library with one required header-only dependency:
|
||||
|
||||
- [Naios/function2](https://github.com/Naios/function2) is used as type
|
||||
erasure wrapper to convert a \ref continuable_base into a \ref continuable.
|
||||
|
||||
Additionally GTest is required as optional dependency for the asynchronous
|
||||
unit testing macros defined in `continuable/support/gtest.hpp`
|
||||
if those are used:
|
||||
|
||||
- [google/googletest](https://github.com/google/googletest) is used as
|
||||
unit testing framework and to provide asynchronous testing macros.
|
||||
|
||||
For the examples and unit tests there might be more dependencies used,
|
||||
which are fetched through git submodules.
|
||||
|
||||
\note The library only depends on the standard library when following
|
||||
headers are used:
|
||||
- `continuable/continuable-base.hpp`
|
||||
- `continuable/continuable-promise-base.hpp`
|
||||
- `continuable/continuable-connections.hpp`
|
||||
- `continuable/continuable-promisify.hpp`
|
||||
- `continuable/continuable-transforms.hpp`
|
||||
|
||||
\section installation-installation Installation
|
||||
|
||||
Making continuable available inside your project is possible through
|
||||
various ways.
|
||||
|
||||
\subsection installation-installation-cmake Through CMake
|
||||
|
||||
The continuable build is driven by CMake and the project exposes CMake
|
||||
interface targets when being used by external projects:
|
||||
|
||||
\code{.cmake}
|
||||
add_subdirectory(continuable)
|
||||
# continuable provides an interface target which makes it's
|
||||
# headers available to all projects using the continuable library.
|
||||
target_link_libraries(my_project continuable)
|
||||
\endcode
|
||||
|
||||
When adding the continuable subdirectory as git submodule this should work
|
||||
out of the box.
|
||||
|
||||
\code{.sh}
|
||||
git submodule add https://github.com/Naios/continuable.git
|
||||
\endcode
|
||||
|
||||
\attention On POSIX platforms you are required to link your application against
|
||||
a corresponding thread library, otherwise `std::futures` won't work
|
||||
properly, this is done automatically by the provided CMake project.
|
||||
|
||||
Additionally the CMake project exports a `continuable` target which is
|
||||
importable through the \code{.cmake}find_package\endcode CMake command
|
||||
when installed:
|
||||
|
||||
\code{.sh}
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
cmake --build . --target INSTALL --config Release
|
||||
\endcode
|
||||
|
||||
In your `CMakeLists.txt`:
|
||||
|
||||
\code{.cmake}
|
||||
find_package(continuable REQUIRED)
|
||||
\endcode
|
||||
|
||||
\subsection installation-installation-pkg Through package managers
|
||||
|
||||
Continuable is present in some package managers and registries already,
|
||||
and might be installed from there.
|
||||
|
||||
\attention The project is still looking for contributions that would help
|
||||
to make it available from various package managers in order to
|
||||
make the installation easier.
|
||||
|
||||
\subsection installation-installation-amalgamation By using the amalgamation header
|
||||
|
||||
For major versions there is an amalgamation header provided which can be
|
||||
included without any dependency:
|
||||
|
||||
- [4.0.0](https://gist.githubusercontent.com/Naios/25d731aa4707d35a9bcec507f3cb9038/raw/051d2ea07b6704893c7fc9844e8d1c105c6821e0/continuable.hpp)
|
||||
- [3.0.0](https://gist.githubusercontent.com/Naios/b128ab5028a7eb33e4285c0293573d9f/raw/79fe332964a786b21a8661ef65d07a5669260a3c/continuable.hpp)
|
||||
|
||||
\subsection installation-installation-copy By copying the headers
|
||||
|
||||
If you don't want to rely on CMake or package managers it is possible to
|
||||
copy and include the `include` directories of continuable and
|
||||
[Naios/function2](https://github.com/Naios/function2) into your project.
|
||||
|
||||
As an improvement git submodules could be used:
|
||||
|
||||
\code{.sh}
|
||||
git submodule add https://github.com/Naios/continuable.git
|
||||
git submodule add https://github.com/Naios/function2.git
|
||||
\endcode
|
||||
|
||||
\section installation-unit-tests Building the unit tests
|
||||
|
||||
In order to build the unit tests clone the repository recursively
|
||||
with all submodules:
|
||||
|
||||
\code{.sh}
|
||||
# Shell:
|
||||
git clone --recursive https://github.com/Naios/continuable.git
|
||||
\endcode
|
||||
|
||||
Then CMake can be used to generate a project solution for testing.
|
||||
|
||||
*/
|
||||
}
|
||||
129
doc/tutorial-awaiting-continuables.dox
Normal file
129
doc/tutorial-awaiting-continuables.dox
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page tutorial-awaiting-continuables Awaiting continuables
|
||||
\brief Explains how to use the \ref continuable_base together with `co_await`.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section tutorial-awaiting-continuables-usage Using co_await on continuables
|
||||
|
||||
Coroutines (`co_await`) are supported by continuables when the underlying
|
||||
toolchain supports the TS. Currently this works in MSVC 2017 and Clang 5.0.
|
||||
|
||||
\attention You have to enable this feature through defining the
|
||||
`CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE` preprocessor definition.
|
||||
|
||||
It is possible to await for any \ref continuable_base as shown below:
|
||||
|
||||
\code{.cpp}
|
||||
int i = co_await cti::make_continuable<int>([](auto&& promise) {
|
||||
promise.set_value(0);
|
||||
});
|
||||
\endcode
|
||||
|
||||
As described in \ref continuable_base::operator co_await() a continuable with
|
||||
multiple arguments as result will wrap its result into a `std::tuple`:
|
||||
|
||||
\code{.cpp}
|
||||
std::tuple<int, int> i = co_await cti::make_ready_continuable(0, 1);
|
||||
\endcode
|
||||
|
||||
\section tutorial-awaiting-continuables-await Using co_await with exceptions
|
||||
|
||||
When a \ref continuable_base was resolved through an exception the exception
|
||||
is rethrown from the `co_await` expression:
|
||||
|
||||
\code{.cpp}
|
||||
try {
|
||||
auto response = co_await http_request("github.com");
|
||||
} catch(std::exception const& e) {
|
||||
// Handle the exception
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section tutorial-awaiting-continuables-noexcept Using co_await with disabled exceptions
|
||||
|
||||
In case the library is configured to use error codes or a custom
|
||||
error type the return type of the co_await expression is changed.
|
||||
|
||||
The result is returned through an internal proxy object which may
|
||||
be queried for the error object:
|
||||
|
||||
| Continuation type | co_await returns |
|
||||
| : ------------------------------- | : -------------------------------- |
|
||||
| `continuable_base with <>` | `unspecified<void>` |
|
||||
| `continuable_base with <Arg>` | `unspecified<Arg>` |
|
||||
| `continuable_base with <Args...>` | `unspecified<std::tuple<Args...>>` |
|
||||
|
||||
The interface of the proxy object is similar to the one proposed in
|
||||
the `std::expected` proposal:
|
||||
|
||||
\code{.cpp}
|
||||
if (auto&& result = co_await http_request("github.com")) {
|
||||
auto value = *result;
|
||||
} else {
|
||||
cti::error_type error = result.get_exception();
|
||||
}
|
||||
|
||||
auto result = co_await http_request("github.com");
|
||||
|
||||
bool(result);
|
||||
result.is_value();
|
||||
result.is_exception();
|
||||
*result; // Same as result.get_value()
|
||||
result.get_value();
|
||||
result.get_exception();
|
||||
\endcode
|
||||
|
||||
\section tutorial-awaiting-continuables-return Using continuables as return type from coroutines
|
||||
|
||||
It is possible to use a \ref continuable_base as return type from coroutines.
|
||||
|
||||
\code{.cpp}
|
||||
cti::continuable<> resolve_async_void() {
|
||||
co_await http_request("github.com");
|
||||
// ...
|
||||
co_return;
|
||||
}
|
||||
|
||||
cti::continuable<int> resolve_async() {
|
||||
co_await http_request("github.com");
|
||||
// ...
|
||||
co_return 0;
|
||||
}
|
||||
\endcode
|
||||
|
||||
Additionally it's possible to return multiple return values from coroutines
|
||||
by wrapping those in a tuple like type:
|
||||
|
||||
\code{.cpp}
|
||||
cti::continuable<int, int, int> resolve_async_multiple() {
|
||||
co_await http_request("github.com");
|
||||
// ...
|
||||
co_return std::make_tuple(0, 1, 2);
|
||||
}
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
215
doc/tutorial-chaining-continuables.dox
Normal file
215
doc/tutorial-chaining-continuables.dox
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page tutorial-chaining-continuables Chaining continuables
|
||||
\brief Explains how to chain multiple \ref continuable_base objects together.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section tutorial-chaining-continuables-then Using then and results
|
||||
|
||||
A \ref continuable_base provides various methods to continue the asynchronous
|
||||
call hierarchy. The most important method therefor is
|
||||
\ref continuable_base::then which changes the object through attaching a
|
||||
result handler:
|
||||
|
||||
\code{.cpp}
|
||||
http_request("github.com")
|
||||
.then([] (std::string result) {
|
||||
// Do something...
|
||||
});
|
||||
\endcode
|
||||
|
||||
A new \ref continuable_base is created which result depends on the return type
|
||||
of the handler. For instance it is possible to return plain values or the next
|
||||
\ref continuable_base to continue the call hierarchy.
|
||||
See \ref continuable_base::then for details.
|
||||
|
||||
\code{.cpp}
|
||||
mysql_query("SELECT `id`, `name` FROM `users`")
|
||||
.then([](ResultSet users) {
|
||||
// Return the next continuable to process ...
|
||||
return mysql_query("SELECT `id` name FROM `sessions`");
|
||||
})
|
||||
.then([](ResultSet sessions) {
|
||||
// ... or pass multiple values to the next callback using tuples or pairs ...
|
||||
return std::make_tuple(std::move(sessions), true);
|
||||
})
|
||||
.then([](ResultSet sessions, bool is_ok) {
|
||||
// ... or pass a single value to the next callback ...
|
||||
return 10;
|
||||
})
|
||||
.then([](auto value) {
|
||||
// ^^^^ Templated callbacks are possible too
|
||||
})
|
||||
// ... you may even pass continuables to the `then` method directly:
|
||||
.then(mysql_query("SELECT * FROM `statistics`"))
|
||||
.then([](ResultSet result) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
|
||||
\subsection tutorial-chaining-continuables-then-partial Making use of partial argument application
|
||||
|
||||
Callbacks passed to \link continuable_base::then then \endlink are only called
|
||||
with the amount of arguments that are accepted.
|
||||
|
||||
\code{.cpp}
|
||||
(http_request("github.com") && read_file("entries.csv"))
|
||||
.then([] {
|
||||
// ^^^^^^ The original signature was <std::string, Buffer>,
|
||||
// however, the callback is only invoked with the amount of
|
||||
// arguments it's accepting.
|
||||
});
|
||||
\endcode
|
||||
|
||||
This makes it possible to attach a callback accepting nothing to every
|
||||
\ref continuable_base.
|
||||
|
||||
\subsection tutorial-chaining-continuables-then-executors Assigning a specific executor to then
|
||||
|
||||
Dispatching a callback through a specific executor is a common usage scenario and supported through the second argument of \link continuable_base::then then\endlink:
|
||||
|
||||
\code{.cpp}
|
||||
auto executor = [](auto&& work) {
|
||||
// Dispatch the work here, store it for later
|
||||
// invocation or move it to another thread.
|
||||
std::forward<decltype(work)>(work)();
|
||||
};
|
||||
|
||||
read_file("entries.csv")
|
||||
.then([](Buffer buffer) {
|
||||
// ...
|
||||
}, executor);
|
||||
// ^^^^^^^^
|
||||
\endcode
|
||||
|
||||
The supplied `work` callable may be stored and moved for later usage
|
||||
on a possible different thread or execution context.
|
||||
|
||||
\note If you are intending to change the context the asynchronous task is
|
||||
running, you need to specify this inside the function that
|
||||
supplies the \ref continuable_base through moving the \ref promise_base.
|
||||
\code{.cpp}
|
||||
auto http_request(std::string url) {
|
||||
return cti::make_continuable<std::string>(
|
||||
[url = std::move(url)](auto&& promise) {
|
||||
std::async([promise = std::forward<decltype(promise)>(promise)]
|
||||
() mutable {
|
||||
promise.set_value("<html> ... </html>");
|
||||
});
|
||||
});
|
||||
}
|
||||
\endcode
|
||||
|
||||
\section tutorial-chaining-continuables-fail Using fail and exceptions
|
||||
|
||||
Asynchronous exceptions are supported too. Exceptions that were set through
|
||||
\ref promise_base::set_exception are forwarded to the first available registered
|
||||
handler that was attached through \ref continuable_base::fail :
|
||||
|
||||
\code{.cpp}
|
||||
http_request("github.com")
|
||||
.then([] (std::string result) {
|
||||
// Is never called if an error occurs
|
||||
})
|
||||
.fail([] (std::exception_ptr ptr) {
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch(std::exception const& e) {
|
||||
// Handle the exception or error code here
|
||||
}
|
||||
});
|
||||
\endcode
|
||||
|
||||
Multiple handlers are allowed to be registered, however the asynchronous call
|
||||
hierarchy is aborted after the first called fail handler and only the closest
|
||||
handler below is called.
|
||||
|
||||
\note Retrieving a `std::exception_ptr` from a current exception
|
||||
may be done as shown below:
|
||||
\code{.cpp}
|
||||
auto do_sth() {
|
||||
return cti::make_continuable<void>([=] (auto&& promise) {
|
||||
try {
|
||||
// Usually the exception is thrown by another expression
|
||||
throw std::exception{};
|
||||
} catch(...) {
|
||||
promise.set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
\endcode
|
||||
|
||||
Continuable also supports error codes automatically if exceptions are disabled.
|
||||
Additionally it is possible to specify a custom error type through defining.
|
||||
|
||||
\code{.cpp}
|
||||
http_request("github.com")
|
||||
.then([] (std::string result) {
|
||||
// Is never called if an error occurs
|
||||
})
|
||||
.fail([] (std::error_condition error) {
|
||||
error.value();
|
||||
error.category();
|
||||
});
|
||||
\endcode
|
||||
|
||||
The \ref error_type will be `std::exception_ptr` except if any of the
|
||||
following definitions is defined:
|
||||
- `CONTINUABLE_WITH_NO_EXCEPTIONS`: Define this to use `std::error_condition`
|
||||
as \ref error_type and to disable exception support.
|
||||
When exceptions are disabled this definition is set automatically.
|
||||
- `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`: Define this to use a user defined
|
||||
error type.
|
||||
|
||||
\attention By default unhandled exceptions or errors will trigger
|
||||
a built-in trap that causes abnormal application shutdown.
|
||||
In order to prevent this and to allow unhandled errors
|
||||
define `CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS`.
|
||||
|
||||
\section tutorial-chaining-continuables-next Using next to handle all paths
|
||||
|
||||
Sometimes it's required to provide a continuation and error handler from the
|
||||
same object. In order to avoid overloading conflicts there is the special
|
||||
method \ref continuable_base::next provided.
|
||||
The exception path overload is marked through the \ref dispatch_error_tag :
|
||||
|
||||
\code{.cpp}
|
||||
struct handle_all_paths {
|
||||
void operator() (std::string result) {
|
||||
// ...
|
||||
}
|
||||
void operator() (cti::dispatch_error_tag, cti::error_type) {
|
||||
// ...
|
||||
}
|
||||
};
|
||||
|
||||
// ...
|
||||
|
||||
http_request("github.com")
|
||||
.next(handle_all_paths{});
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
212
doc/tutorial-connecting-continuables.dox
Normal file
212
doc/tutorial-connecting-continuables.dox
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page tutorial-connecting-continuables Connecting continuables
|
||||
\brief Explains how to connect various \ref continuable_base objects together
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section tutorial-connecting-continuables-strategies Connections and strategies
|
||||
|
||||
Connections make it possible to describe the dependencies between an arbitrary
|
||||
count of \ref continuable_base objects in order resolve a returned
|
||||
\ref continuable_base as soon as the dependencies are fulfilled.
|
||||
|
||||
For each connection strategy \ref continuable_base provides an operator for
|
||||
for instance \ref continuable_base::operator && and a free function,
|
||||
\ref when_all for example. Both work similar however the free functions are
|
||||
capable of working with nested sequences as described in
|
||||
\ref tutorial-connecting-continuables-nested.
|
||||
|
||||
\note Connections between continuable_base objects are ensured to be
|
||||
<B>thread-safe</B> and <B>wait-free</B> by library design
|
||||
(when assuming that `std::call_once` is wait-free - which depends
|
||||
on the toolchain).
|
||||
|
||||
\section tutorial-connecting-continuables-aggregated Using aggregated strategies
|
||||
|
||||
Aggregated strategies will call the result handler with the compound result of
|
||||
all connected \ref continuable_base objects.
|
||||
|
||||
The compound result is deduced as following. Every continuable_base maps its
|
||||
result to the result itself as shown below. When multiple continuable_base
|
||||
objects are connected on the same depth, the result is joined.
|
||||
See \ref tutorial-connecting-continuables-nested for details.
|
||||
|
||||
| Continuation type | In tuple like | In container (`std::vector`) |
|
||||
| : ------------------------------- | : --------- | : ------------------------------ |
|
||||
| `continuable_base with <>` | `<none>` | `<none>` |
|
||||
| `continuable_base with <Arg>` | `Arg` | `Arg` |
|
||||
| `continuable_base with <Args...>` | `Args...` | `std::tuple<Args...>` |
|
||||
|
||||
\subsection tutorial-connecting-continuables-aggregated-all Using the all connection
|
||||
|
||||
The *all* strategy invokes all connected continuable at once, it tries to resolve
|
||||
the connected \ref continuable_base objects as fast as possible.
|
||||
It is possible to connect multiple \ref continuable_base objects together
|
||||
through the *all* strategy by using \ref continuable_base::operator && or
|
||||
\ref when_all. In contrast to the operator the free functions are capable of
|
||||
workin with plain types and deeply nested \ref continuable_base objects as
|
||||
described in \ref tutorial-connecting-continuables-nested .
|
||||
|
||||
\code{.cpp}
|
||||
(http_request("github.com") && http_request("travis-ci.org") &&
|
||||
http_request("atom.io"))
|
||||
.then([](std::string github, std::string travis,
|
||||
std::string atom) {
|
||||
// The callback is called with the
|
||||
// response of github, travis and atom.
|
||||
});
|
||||
\endcode
|
||||
|
||||
\subsection tutorial-connecting-continuables-aggregated-seq Using the sequential connection
|
||||
|
||||
The *sequential* strategy invokes all connected continuable one after each other,
|
||||
it tries to resolve the next connected \ref continuable_base objects as soon
|
||||
as the previous one was resolved.
|
||||
It is possible to connect multiple \ref continuable_base objects together
|
||||
through the *sequential* strategy by using \ref continuable_base::operator>> or
|
||||
\ref when_seq.
|
||||
|
||||
\code{.cpp}
|
||||
(http_request("github.com") >> http_request("travis-ci.org") >>
|
||||
http_request("atom.io"))
|
||||
.then([](std::string github, std::string travis,
|
||||
std::string atom) {
|
||||
// The requests are invoked sequentially instead
|
||||
// of requesting all at once.
|
||||
});
|
||||
\endcode
|
||||
|
||||
\section tutorial-connecting-continuables-any Using the any connection
|
||||
|
||||
The any connection strategy is completely different from the two introduces
|
||||
before: It calls the result handler with the first result or exception
|
||||
available. All \ref continuable_base objects are required to have the same
|
||||
types of arguments.
|
||||
|
||||
\code{.cpp}
|
||||
(http_request("github.com") || http_request("travis-ci.org") ||
|
||||
http_request("atom.io"))
|
||||
.then([](std::string github_or_travis_or_atom) {
|
||||
// The callback is called with the first response
|
||||
// of either github, travis or atom.
|
||||
});
|
||||
\endcode
|
||||
|
||||
\section tutorial-connecting-continuables-mixed Mixing different strategies
|
||||
|
||||
Mixing different strategies through operators and free functions
|
||||
is natively supported as shown below:
|
||||
|
||||
\code{.cpp}
|
||||
(http_request("github.com") &&
|
||||
(http_request("travis-ci.org") || http_request("atom.io")))
|
||||
.then([](std::string github, std::string travis_or_atom) {
|
||||
// The callback is called with the response of
|
||||
// github for sure and the second parameter represents
|
||||
// the response of travis or atom.
|
||||
});
|
||||
\endcode
|
||||
|
||||
\section tutorial-connecting-continuables-nested Nested continuables and plain types
|
||||
|
||||
For every operator that was shown above, there exists a free function
|
||||
that provides at least the same functionality:
|
||||
|
||||
\code{.cpp}
|
||||
cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
|
||||
cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
|
||||
cti::when_seq(http_request("github.com"), http_request("travis-ci.org"));
|
||||
\endcode
|
||||
|
||||
Additionally the free functions are capable of working with continuables deeply
|
||||
nested inside tuple like objects (`std::tuple`, `std::pair` and `std::array`)
|
||||
as well as homogeneous containers (`std::vector`, `std::list` etc.).
|
||||
|
||||
\code{.cpp}
|
||||
std::tuple<std::vector<cti::continuable<int>>> outstanding;
|
||||
// ...
|
||||
|
||||
cti::when_all(std::make_tuple(std::move(outstanding),
|
||||
http_request("github.com")))
|
||||
.then([](std::tuple<std::tuple<std::vector<int>>,
|
||||
std::string> result) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
|
||||
Values which are given to such a free function are preserved and
|
||||
later passed to the result handler:
|
||||
|
||||
\code{.cpp}
|
||||
cti::when_seq(0, 1,
|
||||
cti::make_ready_continuable(2, 3),
|
||||
4, 5)
|
||||
.then([](int r0, int r1, int r2,
|
||||
int r3, int r4) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
|
||||
When combining both capabilities it's even possible do something like this:
|
||||
|
||||
\code{.cpp}
|
||||
cti::when_all(
|
||||
cti::make_ready_continuable(0, 1),
|
||||
2, //< See this plain value
|
||||
cti::populate(cti::make_ready_continuable(3), // Creates a runtime
|
||||
cti::make_ready_continuable(4)), // sized container.
|
||||
std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
|
||||
.then([](int r0, int r1, int r2, std::vector<int> r34,
|
||||
std::tuple<std::tuple<int>> r5) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
|
||||
\section tutorial-connecting-continuables-populate Populating a container from arbitrary continuables
|
||||
|
||||
\ref populate mainly helps to create a homogeneous container from
|
||||
a runtime known count of continuables which type isn't exactly known.
|
||||
All continuables which are passed to this function should be originating
|
||||
from the same source or a method called with the same types of arguments:
|
||||
|
||||
\code{.cpp}
|
||||
// cti::populate just creates a std::vector from the two continuables.
|
||||
auto v = cti::populate(cti::make_ready_continuable(0),
|
||||
cti::make_ready_continuable(1));
|
||||
|
||||
for (int i = 2; i < 5; ++i) {
|
||||
// It is possible to add more continuables
|
||||
// to the container afterwards
|
||||
container.emplace_back(cti::make_ready_continuable(i));
|
||||
}
|
||||
|
||||
cti::when_all(std::move(v))
|
||||
.then([](std::vector<int> resolved) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
|
||||
*/
|
||||
}
|
||||
142
doc/tutorial-creating-continuables.dox
Normal file
142
doc/tutorial-creating-continuables.dox
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page tutorial-creating-continuables Creating continuables
|
||||
\brief Explains how to create a \ref continuable_base.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
A \ref continuable is an arbitrary instantiation of a \ref continuable_base,
|
||||
it represents the main class of the library and makes it possible to build up
|
||||
an asynchronous call hierarchy. When dealing with continuables we usually don't
|
||||
know its exact type for avoiding expensive type erasure.
|
||||
|
||||
The \ref continuable_base is convertible to a \ref continuable which represents
|
||||
a specified type of the \ref continuable_base on the cost of a type erasure.
|
||||
|
||||
\section tutorial-creating-continuables-ready From a value or exception
|
||||
|
||||
The library provides \ref make_ready_continuable which may be used to create a
|
||||
\ref continuable_base from an arbitrary amount of values:
|
||||
|
||||
\code{.cpp}
|
||||
auto one = cti::make_ready_continuable(0);
|
||||
|
||||
cti::continuable<int, float, char> three =
|
||||
cti::make_ready_continuable(0, 1.f, '2');
|
||||
\endcode
|
||||
|
||||
\note In most situations you will never use \ref make_ready_continuable
|
||||
because the library is capable of working with plain values
|
||||
directly and thus this burdens unnecessary overhead.
|
||||
|
||||
Additionally a \ref continuable_base which resolves with an exception may be
|
||||
created through \ref make_exceptional_continuable.
|
||||
|
||||
\code{.cpp}
|
||||
cti::continuable<int> c = cti::make_exceptional_continuable(std::exception{});
|
||||
\endcode
|
||||
|
||||
\section tutorial-creating-continuables-promises From a promise taking callable
|
||||
|
||||
The main function for creating a \ref continuable_base is \ref make_continuable
|
||||
which must be invoked with the types of arguments it resolves to.
|
||||
It accepts a callable object which accepts an arbitrary object
|
||||
(the \ref promise_base). The \ref promise_base is created by the library and
|
||||
then passed to the given callback. This is in contrast to the usage of the
|
||||
standard `std::promise` which is created by the user.
|
||||
|
||||
The \ref promise_base exposes methods to resolve it through result values or
|
||||
through an exception. Below we implement pseudo `http_request` function
|
||||
which resolves the request asynchronously trough a `std::string`.
|
||||
|
||||
\code{.cpp}
|
||||
auto http_request(std::string url) {
|
||||
return cti::make_continuable<std::string>(
|
||||
[url = std::move(url)](auto&& promise) {
|
||||
// Resolve the promise upon completion of the task.
|
||||
promise.set_value("<html> ... </html>");
|
||||
|
||||
// Or promise.set_exception(...);
|
||||
});
|
||||
}
|
||||
\endcode
|
||||
|
||||
An alternative would be a \ref continuable_base with a result of zero arguments:
|
||||
|
||||
\code{.cpp}
|
||||
auto wait_for(std::chrono::milliseconds duration) {
|
||||
return cti::make_continuable<void>([](auto&& promise) {
|
||||
// ^^^^
|
||||
|
||||
// Resolve the promise later when the duration is over
|
||||
promise.set_value();
|
||||
});
|
||||
\endcode
|
||||
|
||||
A \ref continuable_base may resolve with an arbitrary count of result values:
|
||||
|
||||
\code{.cpp}
|
||||
auto resolve_sth() {
|
||||
return cti::make_continuable<int, int, float, char>(
|
||||
[](auto&& promise) {
|
||||
promise.set_value(0, 1, 2.f, '3');
|
||||
});
|
||||
\endcode
|
||||
|
||||
\warning A \ref promise_base is only usable once and thus invalidated
|
||||
after it was resolved!
|
||||
|
||||
A \ref promise_base always exposes a call operator for resolving it as
|
||||
like when using \ref promise_base::set_value or
|
||||
\ref promise_base::set_exception. See \ref promise_base for details.
|
||||
|
||||
\note In order to make proper use of a \ref promise_base you should
|
||||
move it around, store it for later use and resolve it when
|
||||
the asynchronous task was finished or rejected.
|
||||
|
||||
\section tutorial-creating-continuables-invocation The continuable invocation model
|
||||
|
||||
An asynchronous call hierarchy that is stored inside the \ref continuable_base
|
||||
is executed when its result is requested (lazy evaluation) in contrast to
|
||||
other commonly used implementations such as `std::future` which execute the
|
||||
asynchronous call hierarchy instantly on creation (eager evaluation).
|
||||
|
||||
The lazy evaluation strategy used by continuables has many benefits over
|
||||
eager evaluation that is used by other common implementations:
|
||||
- prevention of side effects
|
||||
- evasion of race conditions
|
||||
- ensured deterministic behaviour.
|
||||
|
||||
The asynchronous call hierarchy is started when the \ref continuable_base is
|
||||
destructed or the \ref continuable_base::done method is called.
|
||||
It is possible to disable the automatic start through calling
|
||||
\ref continuable_base::freeze on the corresponding \ref continuable_base.
|
||||
|
||||
\attention A \ref continuable_base is not designed to be stored permanently,
|
||||
make sure you call \ref continuable_base::freeze before storing it
|
||||
and start the continuation chain later through calling
|
||||
\ref continuable_base::done.
|
||||
|
||||
*/
|
||||
}
|
||||
99
doc/tutorial-promisify-continuables.dox
Normal file
99
doc/tutorial-promisify-continuables.dox
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page tutorial-promisify-continuables Promisify functions
|
||||
\brief Explains how to promisify callback taking functions into a \ref continuable_base.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section tutorial-promisify-continuables-promisify Promisification and continuables
|
||||
|
||||
The promisification has a longer history in the JavaScript world where
|
||||
the legacy way of asynchronous programming was the usage of callbacks of the
|
||||
form \code{.js}function(error, result...)\endcode. The ideal way of dealing
|
||||
with such an asynchronous result is to return a promise and soon utility
|
||||
helpers were provided to do so.
|
||||
|
||||
The usage of callbacks to represent an asynchronous result is still a popular
|
||||
way nowadays. Thus the library provides the \ref promisify helper class
|
||||
which makes it possible to convert callback taking functions of various styles
|
||||
into one that returns a \ref continuable_base instead.
|
||||
|
||||
\note Providing promisified APIs for other popular libraries is out of
|
||||
scope for this library. However contributions are highly welcome to
|
||||
add more conversion helpers for other commonly used callback styles.
|
||||
|
||||
\section tutorial-promisify-continuables-boost Promisify boost::asio
|
||||
|
||||
The default callback style is something like
|
||||
\code{.js}function(error, result...)\endcode as described above.
|
||||
Continuable offers the \ref promisify::from method for such callback styles.
|
||||
|
||||
See an example of how to promisify boost asio's `async_resolve` below:
|
||||
|
||||
\code{.cpp}
|
||||
auto async_resolve(std::string host, std::string service) {
|
||||
return cti::promisify<asio::ip::udp::resolver::iterator>::from(
|
||||
[&](auto&&... args) {
|
||||
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
std::move(host), std::move(service));
|
||||
}
|
||||
\endcode
|
||||
|
||||
Then it should be possible to use `asio::async_resolve` like this:
|
||||
|
||||
\code{.cpp}
|
||||
async_resolve("127.0.0.1", "daytime")
|
||||
.then([](udp::resolver::iterator iterator) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
|
||||
|
||||
\section tutorial-promisify-continuables-boost-ct asio and boost::asio async completion tokens
|
||||
|
||||
Since version 4.0.0 continuable also supports asio async initiation tokens.
|
||||
|
||||
- Boost 1.70 or asio 1.13.0 is required for the async initiation
|
||||
- Until boost 1.72 or asio 1.16.0 overhead through an additional type
|
||||
erasure is added. It is recommended to update to those versions.
|
||||
|
||||
The special static variable \ref cti::use_continuable can be appended to any
|
||||
(boost) asio function that accepts a callback to make it return a \ref continuable_base.
|
||||
|
||||
\code{.cpp}
|
||||
#include <continuable/continuable.hpp>
|
||||
#include <continuable/external/asio.hpp>
|
||||
#include <asio.hpp>
|
||||
|
||||
// ...
|
||||
|
||||
asio::tcp::resolver resolver(...);
|
||||
resolver.async_resolve("127.0.0.1", "daytime", cti::use_continuable)
|
||||
.then([](asio::udp::resolver::iterator iterator) {
|
||||
// ...
|
||||
});
|
||||
\endcode
|
||||
*/
|
||||
}
|
||||
84
doc/tutorial-transforming-continuables.dox
Normal file
84
doc/tutorial-transforming-continuables.dox
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page tutorial-transforming-continuables Transforming continuables
|
||||
\brief Explains the conversion into other types such as `std::future`.
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\section tutorial-transforming-continuables-transforms Transforms in general
|
||||
|
||||
Sometimes it's required to change a \ref continuable_base object by its whole.
|
||||
Thus the library offers the ability to apply a transformation to any
|
||||
\ref continuable_base through using \link continuable_base::apply apply \endlink.
|
||||
|
||||
A transformation is a callable object that accepts a \ref continuable_base
|
||||
and returns an arbitrary object
|
||||
|
||||
The library provides several transforms already as part of the
|
||||
\ref cti::transforms namespace.
|
||||
|
||||
\section tutorial-transforming-continuables-wait Synchronous wait
|
||||
|
||||
The library is capable of converting every asynchronous control flow
|
||||
into a synchronous one through \ref transforms::wait, \ref transforms::wait_for
|
||||
and \ref transforms::wait_until.
|
||||
|
||||
\code{.cpp}
|
||||
std::string response = http_request("github.com")
|
||||
.apply(cti::transforms::wait());
|
||||
|
||||
std::string response = http_request("github.com")
|
||||
.apply(cti::transforms::wait_for(std::chrono::seconds(5)));
|
||||
|
||||
std::string response = http_request("github.com")
|
||||
.apply(cti::transforms::wait_until(...));
|
||||
\endcode
|
||||
|
||||
The current thread will be blocked until the result has arrived
|
||||
|
||||
\section tutorial-transforming-continuables-future Conversion into std::future
|
||||
|
||||
The library is capable of converting (*futurizing*) every continuable into a
|
||||
fitting `std::future` through the \ref transforms::to_future transform:
|
||||
|
||||
\code{.cpp}
|
||||
std::future<std::string> future = http_request("github.com")
|
||||
.then([](std::string response) {
|
||||
// Do sth...
|
||||
return http_request("travis-ci.org") || http_request("atom.io");
|
||||
})
|
||||
.apply(cti::transforms::to_future());
|
||||
// ^^^^^^^^
|
||||
\endcode
|
||||
|
||||
Multiple arguments which can't be handled by `std::future` itself are
|
||||
converted into `std::tuple`, see \ref transforms::to_future for details.
|
||||
|
||||
\code{.cpp}
|
||||
std::future<std::tuple<std::string, std::string>> future =
|
||||
(http_request("travis-ci.org") && http_request("atom.io"))
|
||||
.apply(cti::transforms::to_future());
|
||||
\endcode
|
||||
*/
|
||||
}
|
||||
41
doc/tutorial.dox
Normal file
41
doc/tutorial.dox
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
*/
|
||||
|
||||
namespace cti {
|
||||
/** \page tutorial Tutorial
|
||||
\brief An introduction for using the continuable library.
|
||||
|
||||
This tutorial will give a short overview about using the library.
|
||||
We assume that the library is available in your current environment,
|
||||
if not, follow the \ref installation section in order to get it to work.
|
||||
|
||||
This tutorial is split across multiple chapters which should be read in order:
|
||||
|
||||
- \subpage tutorial-creating-continuables --- \copybrief tutorial-creating-continuables
|
||||
- \subpage tutorial-chaining-continuables --- \copybrief tutorial-chaining-continuables
|
||||
- \subpage tutorial-connecting-continuables --- \copybrief tutorial-connecting-continuables
|
||||
- \subpage tutorial-transforming-continuables --- \copybrief tutorial-transforming-continuables
|
||||
- \subpage tutorial-awaiting-continuables --- \copybrief tutorial-awaiting-continuables
|
||||
- \subpage tutorial-promisify-continuables --- \copybrief tutorial-promisify-continuables
|
||||
|
||||
*/
|
||||
}
|
||||
@ -1,2 +1,3 @@
|
||||
add_subdirectory(example-asio)
|
||||
add_subdirectory(example-ai)
|
||||
add_subdirectory(example-slideshow)
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
@ -1,15 +1,28 @@
|
||||
add_library(asio-example-deps INTERFACE)
|
||||
|
||||
target_include_directories(asio-example-deps
|
||||
INTERFACE
|
||||
${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_link_libraries(asio-example-deps
|
||||
INTERFACE
|
||||
asio
|
||||
continuable)
|
||||
|
||||
add_executable(example-asio
|
||||
${CMAKE_CURRENT_LIST_DIR}/example-asio.cpp)
|
||||
|
||||
target_include_directories(example-asio
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_link_libraries(example-asio
|
||||
PRIVATE
|
||||
asio
|
||||
continuable)
|
||||
asio-example-deps)
|
||||
|
||||
target_compile_definitions(example-asio
|
||||
PUBLIC
|
||||
-DCONTINUABLE_WITH_NO_EXCEPTIONS)
|
||||
|
||||
add_executable(example-asio-integration
|
||||
${CMAKE_CURRENT_LIST_DIR}/example-asio-integration.cpp)
|
||||
|
||||
target_link_libraries(example-asio-integration
|
||||
PRIVATE
|
||||
asio-example-deps)
|
||||
|
||||
176
examples/example-asio/example-asio-integration.cpp
Normal file
176
examples/example-asio/example-asio-integration.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.0.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <continuable/continuable.hpp>
|
||||
#include <continuable/external/asio.hpp>
|
||||
|
||||
// Queries the NIST daytime service and prints the current date and time
|
||||
void daytime_service();
|
||||
|
||||
// Checks that a timer async_wait returns successfully
|
||||
void successful_async_wait();
|
||||
|
||||
// Checks that a cancelled timer async_wait fails with
|
||||
// `asio::error::operation_aborted` and is converted to a default constructed
|
||||
// cti::exception_t.
|
||||
void cancelled_async_wait();
|
||||
|
||||
// Indicates fatal error due to an unexpected failure in the continuation chain.
|
||||
void unexpected_error(cti::exception_t);
|
||||
|
||||
// Check that the failure was an aborted operation, as expected.
|
||||
void check_aborted_operation(cti::exception_t);
|
||||
|
||||
// Use a strand as executor
|
||||
void using_strand();
|
||||
|
||||
int main(int, char**) {
|
||||
daytime_service();
|
||||
|
||||
successful_async_wait();
|
||||
cancelled_async_wait();
|
||||
|
||||
using_strand();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void daytime_service() {
|
||||
using asio::ip::tcp;
|
||||
asio::io_context ioc(1);
|
||||
tcp::resolver resolver(ioc);
|
||||
tcp::socket socket(ioc);
|
||||
std::string buf;
|
||||
|
||||
resolver.async_resolve("time.nist.gov", "daytime", cti::use_continuable)
|
||||
.then([&socket](tcp::resolver::results_type endpoints) {
|
||||
return asio::async_connect(socket, endpoints, cti::use_continuable);
|
||||
})
|
||||
.then([&socket, &buf] {
|
||||
return asio::async_read_until(socket, asio::dynamic_buffer(buf), '\n',
|
||||
cti::use_continuable);
|
||||
})
|
||||
.then([&buf](std::size_t) {
|
||||
puts("Continuation successfully got a daytime response:");
|
||||
puts(buf.c_str());
|
||||
})
|
||||
.fail(&unexpected_error);
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void successful_async_wait() {
|
||||
asio::io_context ioc(1);
|
||||
asio::steady_timer t(ioc);
|
||||
|
||||
t.expires_after(std::chrono::seconds(1));
|
||||
|
||||
t.async_wait(cti::use_continuable)
|
||||
.then([] {
|
||||
puts("Continuation succeeded after 1s as expected!");
|
||||
})
|
||||
.fail(&unexpected_error);
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void cancelled_async_wait() {
|
||||
asio::io_context ioc(1);
|
||||
asio::steady_timer t(ioc);
|
||||
|
||||
t.expires_after(std::chrono::seconds(999));
|
||||
|
||||
t.async_wait(cti::use_continuable)
|
||||
.then([] {
|
||||
puts("This should never be called");
|
||||
std::terminate();
|
||||
})
|
||||
.fail(&check_aborted_operation);
|
||||
|
||||
t.cancel_one();
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
void unexpected_error(cti::exception_t e) {
|
||||
if (!bool(e)) {
|
||||
puts("Continuation failed with unexpected cancellation!");
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
try {
|
||||
std::rethrow_exception(e);
|
||||
} catch (std::exception const& ex) {
|
||||
puts("Continuation failed with unexpected exception");
|
||||
puts(ex.what());
|
||||
} catch (...) {
|
||||
// Rethrow the exception to the asynchronous unhandled exception handler
|
||||
std::rethrow_exception(std::current_exception());
|
||||
}
|
||||
#else
|
||||
puts("Continuation failed with unexpected error");
|
||||
puts(e.message().data());
|
||||
#endif
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
void check_aborted_operation(cti::exception_t ex) {
|
||||
if (bool(ex)) {
|
||||
unexpected_error(ex);
|
||||
} else {
|
||||
puts("Continuation failed due to aborted async operation, as expected.");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto through_post(T& postable) {
|
||||
return [&postable](auto&& work) mutable {
|
||||
asio::post(postable, std::forward<decltype(work)>(work));
|
||||
};
|
||||
}
|
||||
|
||||
void using_strand() {
|
||||
asio::io_context ioc(1);
|
||||
asio::io_context::strand strand(ioc);
|
||||
|
||||
asio::post(strand, cti::use_continuable).then([]() {
|
||||
puts("Dispatched through initiation token");
|
||||
});
|
||||
|
||||
cti::async_on(
|
||||
[]() mutable {
|
||||
puts("Dispatched through executor");
|
||||
},
|
||||
through_post(strand));
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -34,12 +34,30 @@
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
#include <exception>
|
||||
#endif
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include <continuable/continuable.hpp>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
inline auto error_code_remapper() {
|
||||
return [](auto&& promise, asio::error_code e, auto&&... args) {
|
||||
if (e) {
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
promise.set_exception(std::make_exception_ptr(e));
|
||||
#else
|
||||
promise.set_exception(cti::exception_t(e.value(), e.category()));
|
||||
#endif
|
||||
} else {
|
||||
promise.set_value(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
struct functional_io_service {
|
||||
asio::io_context service_;
|
||||
asio::ip::udp::resolver resolver_;
|
||||
@ -49,7 +67,11 @@ struct functional_io_service {
|
||||
|
||||
auto trough_post() noexcept {
|
||||
return [&](auto&& work) mutable {
|
||||
asio::post(service_, std::forward<decltype(work)>(work));
|
||||
asio::post(service_,
|
||||
[work = std::forward<decltype(work)>(work)]() mutable {
|
||||
std::move(work)();
|
||||
// .. or: work.set_value();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -61,7 +83,8 @@ struct functional_io_service {
|
||||
}
|
||||
|
||||
auto async_resolve(std::string host, std::string service) {
|
||||
return cti::promisify<asio::ip::udp::resolver::iterator>::from_asio(
|
||||
return cti::promisify<asio::ip::udp::resolver::iterator>::with(
|
||||
error_code_remapper(),
|
||||
[&](auto&&... args) {
|
||||
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
@ -89,7 +112,7 @@ int main(int, char**) {
|
||||
// auto socket = std::make_shared<udp::socket>(service);
|
||||
// socket->async_send_to()
|
||||
})
|
||||
.fail([](cti::error_type /*error*/) {
|
||||
.fail([](cti::exception_t /*error*/) {
|
||||
// ...
|
||||
});
|
||||
|
||||
|
||||
5
examples/example-slideshow/CMakeLists.txt
Normal file
5
examples/example-slideshow/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
add_executable(example-slideshow
|
||||
${CMAKE_CURRENT_LIST_DIR}/example-slideshow.cpp)
|
||||
target_link_libraries(example-slideshow
|
||||
PRIVATE
|
||||
continuable)
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -25,43 +25,32 @@
|
||||
#include "continuable/continuable.hpp"
|
||||
|
||||
cti::continuable<std::string> http_request(std::string /*url*/) {
|
||||
return [](auto&& callback) {
|
||||
// ...
|
||||
callback.set_value("<html>...</html>");
|
||||
};
|
||||
return cti::make_ready_continuable<std::string>("<html>...</html>");
|
||||
}
|
||||
|
||||
struct ResultSet {};
|
||||
struct Buffer {};
|
||||
|
||||
cti::continuable<ResultSet> mysql_query(std::string /*url*/) {
|
||||
return [](auto&& callback) {
|
||||
// ...
|
||||
callback.set_value(ResultSet{});
|
||||
};
|
||||
return cti::make_ready_continuable(ResultSet{});
|
||||
}
|
||||
|
||||
cti::continuable<Buffer> read_file(std::string /*url*/) {
|
||||
return [](auto&& callback) {
|
||||
// ...
|
||||
callback.set_value(Buffer{});
|
||||
};
|
||||
return cti::make_ready_continuable(Buffer{});
|
||||
}
|
||||
|
||||
struct a {
|
||||
struct functional_executor {
|
||||
auto post() const {
|
||||
return [](auto&&) {};
|
||||
}
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
a e;
|
||||
functional_executor e;
|
||||
auto executor = &e;
|
||||
|
||||
// clang-format off
|
||||
|
||||
|
||||
// ----------
|
||||
|
||||
(http_request("github.com") && http_request("atom.io"))
|
||||
.then([] (std::string /*github*/, std::string /*atom*/) {
|
||||
// ...
|
||||
@ -71,23 +60,15 @@ int main(int, char**) {
|
||||
// ...
|
||||
}, executor->post());
|
||||
|
||||
// ----------
|
||||
// clang-format on
|
||||
|
||||
auto c1 = http_request("github.com") && http_request("atom.io") ;
|
||||
http_request("github.com") && http_request("atom.io");
|
||||
|
||||
http_request("github.com") || http_request("atom.io");
|
||||
|
||||
http_request("github.com") >> http_request("atom.io");
|
||||
|
||||
auto c2 = http_request("github.com") || http_request("atom.io") ;
|
||||
|
||||
|
||||
|
||||
auto c3 = http_request("github.com") >> http_request("atom.io") ;
|
||||
|
||||
(void)c1;
|
||||
(void)c2;
|
||||
(void)c3;
|
||||
|
||||
// ----------
|
||||
// clang-format off
|
||||
|
||||
read_file("entries.csv")
|
||||
.then([] (Buffer /*buffer*/) {
|
||||
@ -98,8 +79,7 @@ int main(int, char**) {
|
||||
// ...
|
||||
});
|
||||
|
||||
// ----------
|
||||
|
||||
// clang-format on
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -32,60 +32,35 @@
|
||||
#define CONTINUABLE_BASE_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/continuable-result.hpp>
|
||||
#include <continuable/detail/connection/connection-all.hpp>
|
||||
#include <continuable/detail/connection/connection-any.hpp>
|
||||
#include <continuable/detail/connection/connection-seq.hpp>
|
||||
#include <continuable/detail/connection/connection.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
#include <continuable/detail/awaiting.hpp>
|
||||
#include <continuable/detail/base.hpp>
|
||||
#include <continuable/detail/composition-all.hpp>
|
||||
#include <continuable/detail/composition-any.hpp>
|
||||
#include <continuable/detail/composition-seq.hpp>
|
||||
#include <continuable/detail/composition.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
#if defined(CONTINUABLE_HAS_COROUTINE)
|
||||
# include <continuable/detail/other/coroutines.hpp>
|
||||
#endif // defined(CONTINUABLE_HAS_COROUTINE)
|
||||
|
||||
namespace cti {
|
||||
/// Represents a tag which can be placed first in a signature
|
||||
/// in order to overload callables with the asynchronous result
|
||||
/// as well as an error.
|
||||
///
|
||||
/// See the example below:
|
||||
/// ```cpp
|
||||
/// struct my_callable {
|
||||
/// void operator() (std::string result) {
|
||||
/// // ...
|
||||
/// }
|
||||
/// void operator() (cti::dispatch_error_tag, cti::error_type) {
|
||||
/// // ...
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// // Will receive errors and results
|
||||
/// continuable.next(my_callable{});
|
||||
/// ```
|
||||
///
|
||||
/// \note see continuable::next for details.
|
||||
///
|
||||
/// \since 2.0.0
|
||||
using detail::types::dispatch_error_tag;
|
||||
|
||||
/// Represents the type that is used as error type
|
||||
///
|
||||
/// By default this type deduces to `std::exception_ptr`.
|
||||
/// If `CONTINUABLE_WITH_NO_EXCEPTIONS` is defined the type
|
||||
/// will be a `std::error_condition`.
|
||||
/// A custom error type may be set through
|
||||
/// defining `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`.
|
||||
///
|
||||
/// \since 2.0.0
|
||||
using detail::types::error_type;
|
||||
/// \defgroup Base Base
|
||||
/// provides classes and functions to create continuable_base objects.
|
||||
/// \{
|
||||
|
||||
/// Deduces to a true_type if the given type is a continuable_base.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
using detail::base::is_continuable;
|
||||
template <typename T>
|
||||
using is_continuable = detail::base::is_continuable<T>;
|
||||
|
||||
/// The main class of the continuable library, it provides the functionality
|
||||
/// for chaining callbacks and continuations together to a unified hierarchy.
|
||||
@ -105,8 +80,7 @@ using detail::base::is_continuable;
|
||||
/// \note Nearly all methods of the cti::continuable_base are required to be
|
||||
/// called as r-value. This is required because the continuable carries
|
||||
/// variables which are consumed when the object is transformed as part
|
||||
/// of a method call. You may copy a continuable which underlying
|
||||
/// storages are copyable to split the call hierarchy into multiple parts.
|
||||
/// of a method call.
|
||||
///
|
||||
/// \attention The continuable_base objects aren't intended to be stored.
|
||||
/// If you want to store a continuble_base you should always
|
||||
@ -118,6 +92,10 @@ template <typename Data, typename Annotation>
|
||||
class continuable_base {
|
||||
|
||||
/// \cond false
|
||||
using ownership = detail::util::ownership;
|
||||
|
||||
using annotation_trait = detail::annotation_trait<Annotation>;
|
||||
|
||||
template <typename, typename>
|
||||
friend class continuable_base;
|
||||
friend struct detail::base::attorney;
|
||||
@ -125,25 +103,41 @@ class continuable_base {
|
||||
// The continuation type or intermediate result
|
||||
Data data_;
|
||||
// The transferable state which represents the validity of the object
|
||||
detail::util::ownership ownership_;
|
||||
ownership ownership_;
|
||||
/// \endcond
|
||||
|
||||
/// Constructor accepting the data object while erasing the annotation
|
||||
explicit continuable_base(Data data, detail::util::ownership ownership)
|
||||
: data_(std::move(data)), ownership_(std::move(ownership)) {
|
||||
}
|
||||
explicit continuable_base(Data data, ownership ownership)
|
||||
: data_(std::move(data))
|
||||
, ownership_(std::move(ownership)) {}
|
||||
|
||||
public:
|
||||
/// Constructor accepting the data object while erasing the annotation
|
||||
explicit continuable_base(Data data) : data_(std::move(data)) {
|
||||
}
|
||||
explicit continuable_base(Data data)
|
||||
: data_(std::move(data)) {}
|
||||
|
||||
/// Constructor accepting any object convertible to the data object,
|
||||
/// while erasing the annotation
|
||||
template <typename OData, std::enable_if_t<std::is_convertible<
|
||||
std::decay_t<OData>, Data>::value>* = nullptr>
|
||||
continuable_base(OData&& data) : data_(std::forward<OData>(data)) {
|
||||
}
|
||||
template <typename OtherData,
|
||||
std::enable_if_t<detail::base::can_accept_continuation<
|
||||
Data, Annotation,
|
||||
detail::traits::unrefcv_t<OtherData>>::value>* = nullptr>
|
||||
/* implicit */ continuable_base(OtherData&& data)
|
||||
: data_(
|
||||
detail::base::proxy_continuable<Annotation,
|
||||
detail::traits::unrefcv_t<OtherData>>(
|
||||
std::forward<OtherData>(data))) {}
|
||||
|
||||
/// Constructor taking the data of other continuable_base objects
|
||||
/// while erasing the hint.
|
||||
///
|
||||
/// This constructor makes it possible to replace the internal data object of
|
||||
/// the continuable by any object which is useful for type-erasure.
|
||||
template <typename OData,
|
||||
std::enable_if_t<std::is_convertible<
|
||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
||||
/* implicit */ continuable_base(continuable_base<OData, Annotation>&& other)
|
||||
: data_(std::move(other).consume()) {}
|
||||
|
||||
/// Constructor taking the data of other continuable_base objects
|
||||
/// while erasing the hint.
|
||||
@ -151,16 +145,15 @@ public:
|
||||
/// This constructor makes it possible to replace the internal data object of
|
||||
/// the continuable by any object which is useful for type-erasure.
|
||||
template <typename OData, typename OAnnotation>
|
||||
continuable_base(continuable_base<OData, OAnnotation>&& other)
|
||||
: continuable_base(std::move(other).materialize().consume_data()) {
|
||||
}
|
||||
/* implicit */ continuable_base(continuable_base<OData, OAnnotation>&& other)
|
||||
: continuable_base(std::move(other).finish().consume()) {}
|
||||
|
||||
/// \cond false
|
||||
continuable_base(continuable_base&&) = default;
|
||||
continuable_base(continuable_base const&) = default;
|
||||
continuable_base(continuable_base const&) = delete;
|
||||
|
||||
continuable_base& operator=(continuable_base&&) = default;
|
||||
continuable_base& operator=(continuable_base const&) = default;
|
||||
continuable_base& operator=(continuable_base const&) = delete;
|
||||
/// \endcond
|
||||
|
||||
/// The destructor automatically invokes the continuable_base
|
||||
@ -232,12 +225,17 @@ public:
|
||||
/// | `Arg` | `continuable_base with <Arg>` |
|
||||
/// | `std::pair<First, Second>` | `continuable_base with <First, Second>` |
|
||||
/// | `std::tuple<Args...>` | `continuable_base with <Args...>` |
|
||||
/// | `cti::result<Args...>` | `continuable_base with <Args...>` |
|
||||
/// | `continuable_base<Arg...>` | `continuable_base with <Args...>` |
|
||||
/// Which means the result type of the continuable_base is equal to
|
||||
/// the plain types the callback returns (`std::tuple` and
|
||||
/// `std::pair` arguments are unwrapped).
|
||||
/// A single continuable_base as argument is resolved and the result
|
||||
/// type is equal to the resolved continuable_base.
|
||||
/// A cti::result can be used to cancel the continuation or to
|
||||
/// transition to the exception handler.
|
||||
/// The special unwrapping of types can be disabled through wrapping
|
||||
/// such objects through a call to cti::make_plain.
|
||||
/// Consider the following examples:
|
||||
/// ```cpp
|
||||
/// http_request("github.com")
|
||||
@ -259,6 +257,17 @@ public:
|
||||
/// http_request("github.com")
|
||||
/// .then([](std::string github) { return http_request("atom.io"); })
|
||||
/// .then([](std::string atom) { }); // <std::string>
|
||||
///
|
||||
/// http_request("example.com")
|
||||
/// .then([](std::string content) -> result<std::string> {
|
||||
/// return rethrow(std::make_exception_ptr(std::exception{}));
|
||||
/// })
|
||||
/// .fail([] -> result<std::string> {
|
||||
/// return recover("Hello World!");
|
||||
/// })
|
||||
/// .then([](std::string content) -> result<std::string> {
|
||||
/// return cancel();
|
||||
/// })
|
||||
/// ```
|
||||
///
|
||||
/// \since 1.0.0
|
||||
@ -267,7 +276,7 @@ public:
|
||||
E&& executor = detail::types::this_thread_executor_tag{}) && {
|
||||
return detail::base::chain_continuation<detail::base::handle_results::yes,
|
||||
detail::base::handle_errors::no>(
|
||||
std::move(*this).materialize(), std::forward<T>(callback),
|
||||
std::move(*this).finish(), std::forward<T>(callback),
|
||||
std::forward<E>(executor));
|
||||
}
|
||||
|
||||
@ -293,7 +302,7 @@ public:
|
||||
template <typename OData, typename OAnnotation>
|
||||
auto then(continuable_base<OData, OAnnotation>&& continuation) && {
|
||||
return std::move(*this).then(
|
||||
detail::base::wrap_continuation(std::move(continuation).materialize()));
|
||||
detail::base::wrap_continuation(std::move(continuation).finish()));
|
||||
}
|
||||
|
||||
/// Main method of the continuable_base to catch exceptions and error codes
|
||||
@ -308,13 +317,19 @@ public:
|
||||
/// ```cpp
|
||||
/// http_request("github.com")
|
||||
/// .then([](std::string github) { })
|
||||
/// .fail([](std::exception_ptr ptr) {
|
||||
/// .fail([](std::exception_ptr ep) {
|
||||
/// // Check whether the exception_ptr is valid (not default constructed)
|
||||
/// // if bool(ep) == false this means that the operation was cancelled
|
||||
/// // by the user or application (promise.set_canceled() or
|
||||
/// // make_cancelling_continuable()).
|
||||
/// if (ep) {
|
||||
/// // Handle the error here
|
||||
/// try {
|
||||
/// std::rethrow_exception(ptr);
|
||||
/// std::rethrow_exception(ep);
|
||||
/// } catch (std::exception& e) {
|
||||
/// e.what(); // Handle the exception
|
||||
/// }
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
/// In case exceptions are disabled, `std::error_condition` is
|
||||
@ -333,14 +348,26 @@ public:
|
||||
/// \returns Returns a continuable_base with an asynchronous return type
|
||||
/// depending on the previous result type.
|
||||
///
|
||||
/// \attention The given exception type exception_t can be passed to the
|
||||
/// handler in a default constructed state <br>`bool(e) == false`.
|
||||
/// This always means that the operation was cancelled by the user,
|
||||
/// possibly through:
|
||||
/// - \ref promise_base::set_canceled
|
||||
/// - \ref make_cancelling_continuable
|
||||
/// - \ref result::set_canceled
|
||||
/// - \ref cancel<br>
|
||||
/// In that case the exception can be ignored safely (but it is
|
||||
/// recommended not to proceed, although it is possible to
|
||||
/// recover from the cancellation).
|
||||
///
|
||||
/// \since 2.0.0
|
||||
template <typename T, typename E = detail::types::this_thread_executor_tag>
|
||||
auto fail(T&& callback,
|
||||
E&& executor = detail::types::this_thread_executor_tag{}) && {
|
||||
return detail::base::chain_continuation<detail::base::handle_results::no,
|
||||
detail::base::handle_errors::plain>(
|
||||
std::move(*this).materialize(), std::forward<T>(callback),
|
||||
return detail::base::chain_continuation<
|
||||
detail::base::handle_results::no, detail::base::handle_errors::forward>(
|
||||
std::move(*this).finish(),
|
||||
detail::base::strip_exception_arg(std::forward<T>(callback)),
|
||||
std::forward<E>(executor));
|
||||
}
|
||||
|
||||
@ -362,9 +389,11 @@ public:
|
||||
/// \since 2.0.0
|
||||
template <typename OData, typename OAnnotation>
|
||||
auto fail(continuable_base<OData, OAnnotation>&& continuation) && {
|
||||
continuation.freeze();
|
||||
return std::move(*this).fail([continuation = std::move(continuation)](
|
||||
error_type) mutable { std::move(continuation).done(); });
|
||||
return std::move(*this) //
|
||||
.fail([continuation = std::move(continuation).freeze()] //
|
||||
(exception_t) mutable {
|
||||
std::move(continuation).done(); //
|
||||
});
|
||||
}
|
||||
|
||||
/// A method which allows to use an overloaded callable for the error
|
||||
@ -378,7 +407,7 @@ public:
|
||||
/// void operator() (std::string result) {
|
||||
/// // ...
|
||||
/// }
|
||||
/// void operator() (cti::dispatch_error_tag, cti::error_type) {
|
||||
/// void operator() (cti::exception_arg_t, cti::exception_t) {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
@ -399,22 +428,56 @@ public:
|
||||
E&& executor = detail::types::this_thread_executor_tag{}) && {
|
||||
return detail::base::chain_continuation<
|
||||
detail::base::handle_results::yes,
|
||||
detail::base::handle_errors::forward>(std::move(*this).materialize(),
|
||||
detail::base::handle_errors::forward>(std::move(*this).finish(),
|
||||
std::forward<T>(callback),
|
||||
std::forward<E>(executor));
|
||||
}
|
||||
|
||||
/// A method which allows to apply this continuable to the given callable.
|
||||
/// Returns a continuable_base which continues its invocation through the
|
||||
/// given executor.
|
||||
///
|
||||
/// \param transform A transform which shall accept this continuable
|
||||
/// \returns Returns a continuable_base of the same type.
|
||||
///
|
||||
/// \since 4.2.0
|
||||
template <typename E>
|
||||
auto via(E&& executor) && {
|
||||
return std::move(*this).next(
|
||||
[](auto&&... args) {
|
||||
return make_result(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
std::forward<E>(executor));
|
||||
}
|
||||
|
||||
/// Returns a continuable_base which will have its signature converted
|
||||
/// to the given Args.
|
||||
///
|
||||
/// A signature can only be converted if it can be partially applied
|
||||
/// from the previous one as shown below:
|
||||
/// ```cpp
|
||||
/// continuable<long> c = make_ready_continuable(0, 1, 2).as<long>();
|
||||
/// ```
|
||||
///
|
||||
/// \returns Returns a continuable_base with an asynchronous return type
|
||||
/// matching the given Args.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
template <typename... Args>
|
||||
auto as() && {
|
||||
return std::move(*this).then(detail::base::convert_to<Args...>{});
|
||||
}
|
||||
|
||||
/// A method which allows to apply a callable object to this continuable.
|
||||
///
|
||||
/// \param transform A callable objects that transforms a continuable
|
||||
/// to a different object.
|
||||
///
|
||||
/// \returns Returns the result of the given transform when this
|
||||
/// continuable is passed into it.
|
||||
///
|
||||
/// \since 2.0.0
|
||||
/// \since 4.0.0
|
||||
template <typename T>
|
||||
auto apply(T&& transform) && {
|
||||
return std::forward<T>(transform)(std::move(*this).materialize());
|
||||
return std::forward<T>(transform)(std::move(*this).finish());
|
||||
}
|
||||
|
||||
/// The pipe operator | is an alias for the continuable::then method.
|
||||
@ -430,22 +493,6 @@ public:
|
||||
return std::move(*this).then(std::forward<T>(right));
|
||||
}
|
||||
|
||||
/// The pipe operator | is an alias for the continuable::apply method.
|
||||
///
|
||||
/// \param transform The transformer which is applied.
|
||||
///
|
||||
/// \returns See the corresponding continuable_base::apply method for the
|
||||
/// explanation of the return type.
|
||||
///
|
||||
/// \note You may create your own transformation through
|
||||
/// calling make_transformation.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename T>
|
||||
auto operator|(detail::types::transform<T> transform) && {
|
||||
return std::move(*this).apply(std::move(transform));
|
||||
}
|
||||
|
||||
/// Invokes both continuable_base objects parallel and calls the
|
||||
/// callback with the result of both continuable_base objects.
|
||||
///
|
||||
@ -473,8 +520,8 @@ public:
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \note The continuable_base objects are invoked parallel on the
|
||||
/// current thread, because the `all` strategy tries to resolve
|
||||
/// \note The continuable_base objects are invoked all at onve,
|
||||
/// because the `all` strategy tries to resolve
|
||||
/// the continuations as fast as possible.
|
||||
/// Sequential invocation is also supported through the
|
||||
/// continuable_base::operator>> method.
|
||||
@ -482,8 +529,8 @@ public:
|
||||
/// \since 1.0.0
|
||||
template <typename OData, typename OAnnotation>
|
||||
auto operator&&(continuable_base<OData, OAnnotation>&& right) && {
|
||||
return detail::composition::connect(
|
||||
detail::composition::composition_strategy_all_tag{}, std::move(*this),
|
||||
return detail::connection::connect(
|
||||
detail::connection::connection_strategy_all_tag{}, std::move(*this),
|
||||
std::move(right));
|
||||
}
|
||||
|
||||
@ -513,15 +560,15 @@ public:
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \note The continuable_base objects are invoked parallel on the
|
||||
/// current thread, however, the callback is only called once with
|
||||
/// the first result which becomes available.
|
||||
/// \note The continuable_base objects are invoked all at once,
|
||||
/// however, the callback is only called once with
|
||||
/// the first result or exception which becomes available.
|
||||
///
|
||||
/// \since 1.0.0
|
||||
template <typename OData, typename OAnnotation>
|
||||
auto operator||(continuable_base<OData, OAnnotation>&& right) && {
|
||||
return detail::composition::connect(
|
||||
detail::composition::composition_strategy_any_tag{}, std::move(*this),
|
||||
return detail::connection::connect(
|
||||
detail::connection::connection_strategy_any_tag{}, std::move(*this),
|
||||
std::move(right));
|
||||
}
|
||||
|
||||
@ -542,14 +589,14 @@ public:
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \note The continuable_base objects are invoked sequential on the
|
||||
/// current thread. Parallel invocation is also supported through the
|
||||
/// continuable_base::operator&& method.
|
||||
/// \note The continuable_base objects are invoked sequential one after
|
||||
/// the previous one was finished. Parallel invocation is also
|
||||
/// supported through the continuable_base::operator && method.
|
||||
///
|
||||
/// \since 1.0.0
|
||||
template <typename OData, typename OAnnotation>
|
||||
auto operator>>(continuable_base<OData, OAnnotation>&& right) && {
|
||||
return detail::composition::seq::sequential_connect(std::move(*this),
|
||||
return detail::connection::seq::sequential_connect(std::move(*this),
|
||||
std::move(right));
|
||||
}
|
||||
|
||||
@ -564,7 +611,64 @@ public:
|
||||
///
|
||||
/// \since 1.0.0
|
||||
void done() && {
|
||||
detail::base::finalize_continuation(std::move(*this));
|
||||
detail::base::finalize_continuation(std::move(*this).finish());
|
||||
}
|
||||
|
||||
/// Materializes the continuation expression template and finishes
|
||||
/// the current applied strategy such that the resulting continuable
|
||||
/// will always be a concrete type and Continuable::is_concrete holds.
|
||||
///
|
||||
/// This can be used in the case where we are chaining continuations lazily
|
||||
/// through a strategy, for instance when applying operators for
|
||||
/// expressing connections and then want to return a materialized
|
||||
/// continuable_base which uses the strategy respectively.
|
||||
/// ```cpp
|
||||
/// auto do_both() {
|
||||
/// return (wait(10s) || wait_key_pressed(KEY_SPACE)).finish();
|
||||
/// }
|
||||
///
|
||||
/// // Without a call to finish() this would lead to
|
||||
/// // an unintended evaluation strategy:
|
||||
/// do_both() || wait(5s);
|
||||
/// ```
|
||||
///
|
||||
/// \note When using a type erased continuable_base such as
|
||||
/// `continuable<...>` this method doesn't need to be called
|
||||
/// since the continuable_base is materialized automatically
|
||||
/// on conversion.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
auto finish() && {
|
||||
return annotation_trait::finish(std::move(*this));
|
||||
}
|
||||
|
||||
/// Returns true when the continuable can provide its result immediately,
|
||||
/// and its lazy invocation would be side-effect free.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
bool is_ready() const noexcept {
|
||||
return annotation_trait::is_ready(*this);
|
||||
}
|
||||
|
||||
/// Invalidates the continuable and returns its immediate invocation result.
|
||||
///
|
||||
/// This method can be used to specialize the asynchronous control flow
|
||||
/// based on whether the continuable_base is_ready at every time,
|
||||
/// which is true for a continuable created through the following functions:
|
||||
/// - make_ready_continuable
|
||||
/// - make_exceptional_continuable
|
||||
///
|
||||
/// \returns A result<Args...> where Args... represent the current
|
||||
/// asynchronous parameters or the currently stored exception.
|
||||
///
|
||||
/// \attention unpack requires that continuable_base::is_ready returned true
|
||||
/// in a previous check, otherwise its behaviour is unspecified.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
auto unpack() && {
|
||||
assert(ownership_.is_acquired());
|
||||
assert(is_ready());
|
||||
return detail::base::attorney::query(std::move(*this).finish());
|
||||
}
|
||||
|
||||
/// Predicate to check whether the cti::continuable_base is frozen or not.
|
||||
@ -609,23 +713,82 @@ public:
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
#ifdef CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
|
||||
/// \cond false
|
||||
#if defined(CONTINUABLE_HAS_COROUTINE)
|
||||
/// \endcond
|
||||
/// Implements the operator for awaiting on continuables using `co_await`.
|
||||
///
|
||||
/// The operator is only enabled if `CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE`
|
||||
/// is defined and the toolchain supports experimental coroutines.
|
||||
///
|
||||
/// The return type of the `co_await` expression is specified as following:
|
||||
/// | Continuation type | co_await returns |
|
||||
/// | : ------------------------------- | : -------------------------------- |
|
||||
/// | `continuable_base with <>` | `void` |
|
||||
/// | `continuable_base with <Arg>` | `Arg` |
|
||||
/// | `continuable_base with <Args...>` | `std::tuple<Args...>` |
|
||||
///
|
||||
/// When exceptions are used the usage is as intuitive as shown below:
|
||||
/// ```cpp
|
||||
/// // Handling the exception isn't required and
|
||||
/// // the try catch clause may be omitted.
|
||||
/// try {
|
||||
/// std::string response = co_await http_request("github.com");
|
||||
/// } (std::exception& e) {
|
||||
/// e.what();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// In case the library is configured to use error codes or a custom
|
||||
/// exception type the return type of the co_await expression is changed.
|
||||
/// The result is returned through a cti::result<...>.
|
||||
/// | Continuation type | co_await returns |
|
||||
/// | : ------------------------------- | : -------------------------------- |
|
||||
/// | `continuable_base with <>` | `result<void>` |
|
||||
/// | `continuable_base with <Arg>` | `result<Arg>` |
|
||||
/// | `continuable_base with <Args...>` | `result<Args...>` |
|
||||
///
|
||||
/// \note Using continuable_base as return type for coroutines
|
||||
/// is supported. The coroutine is initially stopped and
|
||||
/// resumed when the continuation is requested in order to
|
||||
/// keep the lazy evaluation semantics of the continuable_base.
|
||||
/// ```cpp
|
||||
/// cti::continuable<> resolve_async_void() {
|
||||
/// co_await http_request("github.com");
|
||||
/// // ...
|
||||
/// co_return;
|
||||
/// }
|
||||
///
|
||||
/// cti::continuable<int> resolve_async() {
|
||||
/// co_await http_request("github.com");
|
||||
/// // ...
|
||||
/// co_return 0;
|
||||
/// }
|
||||
/// ```
|
||||
/// It's possible to return multiple return values from coroutines
|
||||
/// by wrapping those in a tuple like type:
|
||||
/// ```cpp
|
||||
/// cti::continuable<int, int, int> resolve_async_multiple() {
|
||||
/// co_await http_request("github.com");
|
||||
/// // ...
|
||||
/// co_return std::make_tuple(0, 1, 2);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// \since 2.0.0
|
||||
auto operator co_await() && {
|
||||
return detail::awaiting::create_awaiter(std::move(*this).materialize());
|
||||
return detail::awaiting::create_awaiter(std::move(*this).finish());
|
||||
}
|
||||
#endif // CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
|
||||
/// \cond false
|
||||
#endif // defined(CONTINUABLE_HAS_COROUTINE)
|
||||
/// \endcond
|
||||
|
||||
private:
|
||||
void release() noexcept {
|
||||
ownership_.release();
|
||||
}
|
||||
|
||||
auto materialize() && {
|
||||
return detail::composition::materializer<continuable_base>::apply(
|
||||
std::move(*this));
|
||||
}
|
||||
|
||||
Data&& consume_data() && {
|
||||
Data&& consume() && {
|
||||
assert_acquired();
|
||||
release();
|
||||
return std::move(data_);
|
||||
@ -636,20 +799,20 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/// Creates a continuable_base from a callback taking function.
|
||||
/// Creates a continuable_base from a promise/callback taking function.
|
||||
///
|
||||
/// \tparam Args The types (signature hint) the given callback is called with.
|
||||
/// * **Some arguments** indicate the types the callback will be invoked with.
|
||||
/// \tparam Args The types (signature hint) the given promise is resolved with.
|
||||
/// * **Some arguments** indicate the types the promise will be invoked with.
|
||||
/// ```cpp
|
||||
/// auto ct = cti::make_continuable<int, std::string>([](auto&& callback) {
|
||||
/// std::forward<decltype(callback)>(callback)(200, "<html>...</html>");
|
||||
/// auto ct = cti::make_continuable<int, std::string>([](auto&& promise) {
|
||||
/// promise.set_value(200, "<html>...</html>");
|
||||
/// });
|
||||
/// ```
|
||||
/// * **void as argument** indicates that the callback will be invoked
|
||||
/// * `void` **as argument** indicates that the promise will be invoked
|
||||
/// with no arguments:
|
||||
/// ```cpp
|
||||
/// auto ct = cti::make_continuable<void>([](auto&& callback) {
|
||||
/// std::forward<decltype(callback)>(callback)();
|
||||
/// auto ct = cti::make_continuable<void>([](auto&& promise) {
|
||||
/// promise.set_value();
|
||||
/// });
|
||||
/// ```
|
||||
/// * **No arguments** Since version 3.0.0 make_continuable always requires
|
||||
@ -660,13 +823,13 @@ private:
|
||||
/// the continuable right after creation.
|
||||
/// ```cpp
|
||||
/// // This won't work because the arguments are missing:
|
||||
/// auto ct = cti::make_continuable([](auto&& callback) {
|
||||
/// std::forward<decltype(callback)>(callback)(0.f, 'c');
|
||||
/// auto ct = cti::make_continuable([](auto&& promise) {
|
||||
/// promise.set_value(0.f, 'c');
|
||||
/// });
|
||||
///
|
||||
/// // However, you are allowed to do this:
|
||||
/// continuable<float, char> ct = [](auto&& callback) {
|
||||
/// std::forward<decltype(callback)>(callback)(0.f, 'c');
|
||||
/// cti::continuable<float, char> ct = [](auto&& promise) {
|
||||
/// promise.set_value(callback)(0.f, 'c');
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
@ -706,94 +869,273 @@ private:
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \returns A continuable_base with unknown template parameters which
|
||||
/// \returns A continuable_base with unspecified template parameters which
|
||||
/// wraps the given continuation.
|
||||
/// In order to convert the continuable_base to a known type
|
||||
/// you need to apply type erasure.
|
||||
/// you need to apply type erasure through the
|
||||
/// \link cti::continuable continuable\endlink or
|
||||
/// \link cti::promise promise\endlink facilities.
|
||||
///
|
||||
/// \note You should always turn the callback into a r-value if possible
|
||||
/// \note You should always turn the callback/promise into a r-value if possible
|
||||
/// (`std::move` or `std::forward`) for qualifier correct invokation.
|
||||
/// Additionally it's important to know that all continuable promises
|
||||
/// are callbacks and just expose their call operator nicely through
|
||||
/// \link cti::promise_base::set_value set_value \endlink and
|
||||
/// \link cti::promise_base::set_exception set_exception \endlink.
|
||||
///
|
||||
/// \since 1.0.0
|
||||
template <typename... Args, typename Continuation>
|
||||
auto make_continuable(Continuation&& continuation) {
|
||||
constexpr auto make_continuable(Continuation&& continuation) {
|
||||
static_assert(sizeof...(Args) > 0,
|
||||
"Since version 3.0.0 make_continuable requires an exact "
|
||||
"signature! If you did intend to create a void continuable "
|
||||
"use make_continuable<void>(...). Continuables with an exact "
|
||||
"signature may be created through make_continuable<Args...>.");
|
||||
|
||||
return detail::base::attorney::create(
|
||||
return detail::base::attorney::create_from(
|
||||
std::forward<Continuation>(continuation),
|
||||
detail::hints::extract(detail::traits::identity<Args...>{}),
|
||||
typename detail::hints::from_args<Args...>::type{},
|
||||
detail::util::ownership{});
|
||||
}
|
||||
|
||||
/// Returns a continuable with no result which instantly resolves
|
||||
/// Returns a continuable_base with no result which instantly resolves
|
||||
/// the promise with no values.
|
||||
///
|
||||
/// \attention Usually using this function isn't needed at all since
|
||||
/// the continuable library is capable of working with
|
||||
/// plain values in most cases.
|
||||
/// Try not to use it since it causes unneccessary recursive
|
||||
/// Try not to use it since it causes unnecessary recursive
|
||||
/// function calls.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename... Args>
|
||||
constexpr auto make_ready_continuable() {
|
||||
return make_continuable<void>([](auto&& promise) {
|
||||
std::forward<decltype(promise)>(promise).set_value();
|
||||
});
|
||||
auto make_ready_continuable(Args&&... args) {
|
||||
return detail::base::attorney::create_from_raw(
|
||||
detail::base::ready_continuation<detail::traits::unrefcv_t<Args>...>(
|
||||
result<detail::traits::unrefcv_t<Args>...>::from(
|
||||
std::forward<Args>(args)...)),
|
||||
detail::identity<detail::traits::unrefcv_t<Args>...>{},
|
||||
detail::util::ownership{});
|
||||
}
|
||||
|
||||
/// Returns a continuable with one result value which instantly resolves
|
||||
/// the promise with the given value.
|
||||
///
|
||||
/// \copydetails make_ready_continuable()
|
||||
template <typename Result>
|
||||
constexpr auto make_ready_continuable(Result&& result) {
|
||||
return make_continuable<std::decay_t<Result>>( // ...
|
||||
[result = std::forward<Result>(result)](auto&& promise) mutable {
|
||||
std::forward<decltype(promise)>(promise).set_value(std::move(result));
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a continuable with multiple result values which instantly resolves
|
||||
/// the promise with the given values.
|
||||
///
|
||||
/// \copydetails make_ready_continuable()
|
||||
template <typename FirstResult, typename SecondResult, typename... Rest>
|
||||
constexpr auto make_ready_continuable(FirstResult&& first_result,
|
||||
SecondResult&& second_result,
|
||||
Rest&&... rest) {
|
||||
return make_continuable<std::decay_t<FirstResult>, std::decay_t<SecondResult>,
|
||||
std::decay_t<Rest>...>( // ...
|
||||
[result = std::make_tuple(std::forward<FirstResult>(first_result),
|
||||
std::forward<SecondResult>(second_result),
|
||||
std::forward<Rest>(rest)...)](
|
||||
auto&& promise) mutable {
|
||||
detail::traits::unpack(result,
|
||||
std::forward<decltype(promise)>(promise));
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a continuable with the parameterized result which instantly
|
||||
/// Returns a continuable_base with the parameterized result which instantly
|
||||
/// resolves the promise with the given error type.
|
||||
///
|
||||
/// See an example below:
|
||||
/// ```cpp
|
||||
/// std::logic_error exception("Some issue!");
|
||||
/// auto ptr = std::make_exception_ptr(exception);
|
||||
/// auto ct = cti::make_exceptional_continuable<int>(ptr);
|
||||
/// ```
|
||||
///
|
||||
/// \tparam Args The fake signature of the returned continuable.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename... Args, typename Exception>
|
||||
constexpr auto make_exceptional_continuable(Exception&& exception) {
|
||||
static_assert(sizeof...(Args) > 0,
|
||||
"Requires at least one type for the fake signature!");
|
||||
|
||||
using hint_t = typename detail::hints::from_args<Args...>::type;
|
||||
using ready_continuation_t = typename detail::base::
|
||||
ready_continuation_from_hint<hint_t>::type;
|
||||
using result_t = typename detail::base::result_from_hint<hint_t>::type;
|
||||
return detail::base::attorney::create_from_raw(
|
||||
ready_continuation_t(result_t::from(exception_arg_t{},
|
||||
std::forward<Exception>(exception))),
|
||||
hint_t{}, detail::util::ownership{});
|
||||
}
|
||||
|
||||
/// Returns a continuable_base with the parameterized result which never
|
||||
/// resolves its promise and thus cancels the asynchronous continuation chain
|
||||
/// through throwing a default constructed exception_t.
|
||||
///
|
||||
/// This can be used to cancel an asynchronous continuation chain when
|
||||
/// returning a continuable_base from a handler where other paths could
|
||||
/// possibly continue the asynchronous chain. See an example below:
|
||||
/// ```cpp
|
||||
/// do_sth().then([weak = this->weak_from_this()]() -> continuable<> {
|
||||
/// if (auto me = weak.lock()) {
|
||||
/// return do_sth_more();
|
||||
/// } else {
|
||||
/// // Abort the asynchronous continuation chain since the
|
||||
/// // weakly referenced object expired previously.
|
||||
/// return make_cancelling_continuable<void>();
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
/// The default unhandled exception handler ignores exception types
|
||||
/// that don't evaluate to true when being converted to a bool.
|
||||
/// This saves expensive construction of std::exception_ptr or similar types,
|
||||
/// where only one exception type is used for signaling the cancellation.
|
||||
///
|
||||
/// \tparam Signature The fake signature of the returned continuable.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename... Signature, typename Exception>
|
||||
constexpr auto make_exceptional_continuable(Exception&& exception) {
|
||||
/// \since 4.0.0
|
||||
template <typename... Signature>
|
||||
auto make_cancelling_continuable() {
|
||||
static_assert(sizeof...(Signature) > 0,
|
||||
"Requires at least one type for the fake signature!");
|
||||
|
||||
return make_continuable<Signature...>( // ...
|
||||
[exception = std::forward<Exception>(exception)](auto&& promise) mutable {
|
||||
std::forward<decltype(promise)>(promise).set_exception(
|
||||
std::move(exception));
|
||||
});
|
||||
return make_exceptional_continuable<Signature...>(exception_t{});
|
||||
}
|
||||
|
||||
/// Can be used to disable the special meaning for a returned value in
|
||||
/// asynchronous handler functions.
|
||||
///
|
||||
/// Several types have a special meaning when being returned from a callable
|
||||
/// passed to asynchronous handler functions like:
|
||||
/// - continuable_base::then
|
||||
/// - continuable_base::fail
|
||||
/// - continuable_base::next
|
||||
///
|
||||
/// For instance such types are std::tuple, std::pair and cti::result.
|
||||
///
|
||||
/// Wrapping such an object through a call to make_plain disables the special
|
||||
/// meaning for such objects as shown below:
|
||||
/// ```cpp
|
||||
/// continuable<result<int, int> c = http_request("example.com")
|
||||
/// .then([](std::string content) {
|
||||
/// return make_plain(make_result(0, 1));
|
||||
/// })
|
||||
/// ```
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
template <typename T>
|
||||
auto make_plain(T&& value) {
|
||||
return plain_t<detail::traits::unrefcv_t<T>>(std::forward<T>(value));
|
||||
}
|
||||
|
||||
/// Can be used to recover to from a failure handler,
|
||||
/// the result handler which comes after will be called with the
|
||||
/// corresponding result.
|
||||
///
|
||||
/// The \ref exceptional_result returned by this function can be returned
|
||||
/// from any result or failure handler in order to rethrow the exception.
|
||||
/// ```cpp
|
||||
/// http_request("example.com")
|
||||
/// .then([](std::string content) {
|
||||
/// return recover(1, 2);
|
||||
/// })
|
||||
/// .fail([](cti::exception_t exception) {
|
||||
/// return recover(1, 2);
|
||||
/// })
|
||||
/// .then([](int a, int b) {
|
||||
/// // Recovered from the failure
|
||||
/// })
|
||||
/// ```
|
||||
/// A corresponding \ref result is returned by \ref recover
|
||||
/// ```cpp
|
||||
/// http_request("example.com")
|
||||
/// .then([](std::string content) -> cti::result<int, int> {
|
||||
/// return recover(1, 2);
|
||||
/// })
|
||||
/// .fail([](cti::exception_t exception) -> cti::result<int, int> {
|
||||
/// return recover(1, 2);
|
||||
/// })
|
||||
/// .then([](int a, int b) -> cti::result<int, int> {
|
||||
/// // Recovered from the failure
|
||||
/// })
|
||||
/// ```
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
template <typename... Args>
|
||||
result<detail::traits::unrefcv_t<Args>...> recover(Args&&... args) {
|
||||
return make_result(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Can be used to rethrow an exception to the asynchronous continuation chain,
|
||||
/// the failure handler which comes after will be called with the
|
||||
/// corresponding exception.
|
||||
///
|
||||
/// The \ref exceptional_result returned by this function can be returned
|
||||
/// from any result or failure handler in order to rethrow the exception.
|
||||
/// ```cpp
|
||||
/// http_request("example.com")
|
||||
/// .then([](std::string content) {
|
||||
/// return rethrow(std::make_exception_ptr(std::exception{}));
|
||||
/// })
|
||||
/// .fail([](cti::exception_t exception) {
|
||||
/// return rethrow(std::make_exception_ptr(std::exception{}));
|
||||
/// })
|
||||
/// .next([](auto&&...) {
|
||||
/// return rethrow(std::make_exception_ptr(std::exception{}));
|
||||
/// });
|
||||
/// ```
|
||||
/// The returned \ref exceptional_result is convertible to
|
||||
/// any \ref result as shown below:
|
||||
/// ```cpp
|
||||
/// http_request("example.com")
|
||||
/// .then([](std::string content) -> cti::result<> {
|
||||
/// return rethrow(std::make_exception_ptr(std::exception{}));
|
||||
/// })
|
||||
/// .fail([](cti::exception_t exception) -> cti::result<> {
|
||||
/// return rethrow(std::make_exception_ptr(std::exception{}));
|
||||
/// })
|
||||
/// .next([](auto&&...) -> cti::result<> {
|
||||
/// return rethrow(std::make_exception_ptr(std::exception{}));
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
// NOLINTNEXTLINE(performance-unnecessary-value-param)
|
||||
inline exceptional_result rethrow(exception_t exception) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
return exceptional_result{std::move(exception)};
|
||||
}
|
||||
|
||||
/// Can be used to cancel an asynchronous continuation chain,
|
||||
/// the next failure handler which comes after cancel will be called
|
||||
/// with a default constructed exception_t object.
|
||||
///
|
||||
/// The \ref cancellation_result returned by this function can be returned from
|
||||
/// any result or failure handler in order to cancel the chain.
|
||||
/// ```cpp
|
||||
/// http_request("example.com")
|
||||
/// .then([](std::string content) {
|
||||
/// return cancel();
|
||||
/// })
|
||||
/// .fail([](cti::exception_t exception) {
|
||||
/// return cancel();
|
||||
/// })
|
||||
/// .next([](auto&&...) {
|
||||
/// return cancel();
|
||||
/// });
|
||||
/// ```
|
||||
/// The returned \ref empty_result is convertible to
|
||||
/// any \ref result as shown below:
|
||||
/// ```cpp
|
||||
/// http_request("example.com")
|
||||
/// .then([](std::string content) -> cti::result<> {
|
||||
/// return cancel();
|
||||
/// })
|
||||
/// .fail([](cti::exception_t exception) -> cti::result<> {
|
||||
/// return cancel();
|
||||
/// })
|
||||
/// .next([](auto&&...) -> cti::result<> {
|
||||
/// return cancel();
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
inline cancellation_result cancel() {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Can be used to stop an asynchronous continuation chain,
|
||||
/// no handler which comes after stop was received won't be called.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
inline empty_result stop() {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_BASE_HPP_INCLUDED
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,25 +21,32 @@
|
||||
|
||||
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
|
||||
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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_COMPOSITIONS_HPP_INCLUDED
|
||||
#define CONTINUABLE_COMPOSITIONS_HPP_INCLUDED
|
||||
#ifndef CONTINUABLE_CONNECTIONS_HPP_INCLUDED
|
||||
#define CONTINUABLE_CONNECTIONS_HPP_INCLUDED
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/composition-all.hpp>
|
||||
#include <continuable/detail/composition-any.hpp>
|
||||
#include <continuable/detail/composition-seq.hpp>
|
||||
#include <continuable/detail/composition.hpp>
|
||||
#include <continuable/detail/range.hpp>
|
||||
#include <vector>
|
||||
#include <continuable/detail/connection/connection-all.hpp>
|
||||
#include <continuable/detail/connection/connection-any.hpp>
|
||||
#include <continuable/detail/connection/connection-seq.hpp>
|
||||
#include <continuable/detail/connection/connection.hpp>
|
||||
#include <continuable/detail/traversal/range.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \defgroup Connections Connections
|
||||
/// provides functions to connect \link continuable_base
|
||||
/// continuable_bases\endlink through various strategies.
|
||||
/// \{
|
||||
|
||||
/// Connects the given arguments with an all logic.
|
||||
/// All continuables contained inside the given nested pack are
|
||||
/// invoked at once. On completion the final handler is called
|
||||
@ -55,8 +62,8 @@ namespace cti {
|
||||
/// cti::when_all(
|
||||
/// cti::make_ready_continuable(0, 1),
|
||||
/// 2, //< See this plain value
|
||||
/// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3),
|
||||
/// cti::make_ready_continuable(4)},
|
||||
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
|
||||
/// cti::make_ready_continuable(4)), // sized container.
|
||||
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
|
||||
/// .then([](int r0, int r1, int r2, std::vector<int> r34,
|
||||
/// std::tuple<std::tuple<int>> r5) {
|
||||
@ -69,8 +76,8 @@ namespace cti {
|
||||
/// \since 1.1.0
|
||||
template <typename... Args>
|
||||
auto when_all(Args&&... args) {
|
||||
return detail::composition::apply_composition(
|
||||
detail::composition::composition_strategy_all_tag{},
|
||||
return detail::connection::apply_connection(
|
||||
detail::connection::connection_strategy_all_tag{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@ -79,8 +86,9 @@ auto when_all(Args&&... args) {
|
||||
/// to a temporary `std::vector` which is then passed to when_all.
|
||||
///
|
||||
/// ```cpp
|
||||
/// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0),
|
||||
/// cti::make_ready_continuable(1)};
|
||||
/// // cti::populate just creates a std::vector from the two continuables.
|
||||
/// auto v = cti::populate(cti::make_ready_continuable(0),
|
||||
/// cti::make_ready_continuable(1));
|
||||
///
|
||||
/// cti::when_all(v.begin(), v.end())
|
||||
/// .then([](std::vector<int> r01) {
|
||||
@ -123,8 +131,8 @@ auto when_all(Iterator begin, Iterator end) {
|
||||
/// cti::when_seq(
|
||||
/// cti::make_ready_continuable(0, 1),
|
||||
/// 2, //< See this plain value
|
||||
/// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3),
|
||||
/// cti::make_ready_continuable(4)},
|
||||
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
|
||||
/// cti::make_ready_continuable(4)), // sized container.
|
||||
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
|
||||
/// .then([](int r0, int r1, int r2, std::vector<int> r34,
|
||||
/// std::tuple<std::tuple<int>> r5) {
|
||||
@ -137,8 +145,8 @@ auto when_all(Iterator begin, Iterator end) {
|
||||
/// \since 1.1.0
|
||||
template <typename... Args>
|
||||
auto when_seq(Args&&... args) {
|
||||
return detail::composition::apply_composition(
|
||||
detail::composition::composition_strategy_seq_tag{},
|
||||
return detail::connection::apply_connection(
|
||||
detail::connection::connection_strategy_seq_tag{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@ -147,8 +155,9 @@ auto when_seq(Args&&... args) {
|
||||
/// to a temporary `std::vector` which is then passed to when_seq.
|
||||
///
|
||||
/// ```cpp
|
||||
/// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0),
|
||||
/// cti::make_ready_continuable(1)};
|
||||
/// // cti::populate just creates a std::vector from the two continuables.
|
||||
/// auto v = cti::populate(cti::make_ready_continuable(0),
|
||||
/// cti::make_ready_continuable(1));
|
||||
///
|
||||
/// cti::when_seq(v.begin(), v.end())
|
||||
/// .then([](std::vector<int> r01) {
|
||||
@ -191,8 +200,8 @@ auto when_seq(Iterator begin, Iterator end) {
|
||||
/// cti::when_any(
|
||||
/// cti::make_ready_continuable(0, 1),
|
||||
/// 2, //< See this plain value
|
||||
/// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3),
|
||||
/// cti::make_ready_continuable(4)},
|
||||
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
|
||||
/// cti::make_ready_continuable(4)), // sized container.
|
||||
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
|
||||
/// .then([](int r0) {
|
||||
/// // ...
|
||||
@ -204,8 +213,8 @@ auto when_seq(Iterator begin, Iterator end) {
|
||||
/// \since 1.1.0
|
||||
template <typename... Args>
|
||||
auto when_any(Args&&... args) {
|
||||
return detail::composition::apply_composition(
|
||||
detail::composition::composition_strategy_any_tag{},
|
||||
return detail::connection::apply_connection(
|
||||
detail::connection::connection_strategy_any_tag{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@ -214,8 +223,9 @@ auto when_any(Args&&... args) {
|
||||
/// to a temporary `std::vector` which is then passed to when_all.
|
||||
///
|
||||
/// ```cpp
|
||||
/// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0),
|
||||
/// cti::make_ready_continuable(1)};
|
||||
/// // cti::populate just creates a std::vector from the two continuables.
|
||||
/// auto v = cti::populate(cti::make_ready_continuable(0),
|
||||
/// cti::make_ready_continuable(1));
|
||||
///
|
||||
/// cti::when_any(v.begin(), v.end())
|
||||
/// .then([](int r01) {
|
||||
@ -242,6 +252,50 @@ template <
|
||||
auto when_any(Iterator begin, Iterator end) {
|
||||
return when_any(detail::range::persist_range(begin, end));
|
||||
}
|
||||
|
||||
/// Populates a homogeneous container from the given arguments.
|
||||
/// All arguments need to be convertible to the first one,
|
||||
/// by default `std::vector` is used as container type.
|
||||
///
|
||||
/// This method mainly helps to create a homogeneous container from
|
||||
/// a runtime known count of continuables which type isn't exactly known.
|
||||
/// All continuables which are passed to this function should be originating
|
||||
/// from the same source or a method called with the same types of arguments:
|
||||
/// ```cpp
|
||||
/// auto container = cti::populate(cti::make_ready_continuable(0),
|
||||
/// cti::make_ready_continuable(1)),
|
||||
///
|
||||
/// for (int i = 2; i < 5; ++i) {
|
||||
/// // You may add more continuables to the container afterwards
|
||||
/// container.emplace_back(cti::make_ready_continuable(i));
|
||||
/// }
|
||||
///
|
||||
/// cti::when_any(std::move(container))
|
||||
/// .then([](int) {
|
||||
/// // ...
|
||||
/// });
|
||||
/// ```
|
||||
/// Additionally it is possible to change the targeted container as below:
|
||||
/// ```cpp
|
||||
/// auto container = cti::populate<std::list>(cti::make_ready_continuable(0),
|
||||
/// cti::make_ready_continuable(1)),
|
||||
/// ```
|
||||
///
|
||||
/// \tparam C The container type which is used to store the arguments into.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <template <typename, typename> class C = std::vector, typename First,
|
||||
typename... Args>
|
||||
C<std::decay_t<First>, std::allocator<std::decay_t<First>>>
|
||||
populate(First&& first, Args&&... args) {
|
||||
C<std::decay_t<First>, std::allocator<std::decay_t<First>>> container;
|
||||
container.reserve(1 + sizeof...(Args));
|
||||
container.emplace_back(std::forward<First>(first));
|
||||
(void)std::initializer_list<int>{
|
||||
0, ((void)container.emplace_back(std::forward<Args>(args)), 0)...};
|
||||
return container; // RVO
|
||||
}
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_COMPOSITIONS_HPP_INCLUDED
|
||||
#endif // CONTINUABLE_CONNECTIONS_HPP_INCLUDED
|
||||
97
include/continuable/continuable-coroutine.hpp
Normal file
97
include/continuable/continuable-coroutine.hpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_COROUTINE_HPP_INCLUDED
|
||||
#define CONTINUABLE_COROUTINE_HPP_INCLUDED
|
||||
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/continuable-types.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
# include <exception>
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
#if defined(CONTINUABLE_HAS_COROUTINE)
|
||||
# include <continuable/detail/other/coroutines.hpp>
|
||||
|
||||
namespace cti {
|
||||
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
/// Is thrown from co_await expressions if the awaited continuable is canceled
|
||||
///
|
||||
/// Default constructed exception types that are returned by a cancelled
|
||||
/// continuable are converted automatically to await_canceled_exception when
|
||||
/// being returned by a co_await expression.
|
||||
///
|
||||
/// The await_canceled_exception gets converted again to a default constructed
|
||||
/// exception type if it becomes unhandled inside a coroutine which
|
||||
/// returns a continuable_base.
|
||||
/// ```cpp
|
||||
/// continuable<> cancelled_coroutine() {
|
||||
/// co_await make_cancelling_continuable<void>();
|
||||
///
|
||||
/// co_return;
|
||||
/// }
|
||||
///
|
||||
/// // ...
|
||||
///
|
||||
/// cancelled_coroutine().fail([](exception_t e) {
|
||||
/// assert(bool(e) == false);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \since 4.1.0
|
||||
using await_canceled_exception = detail::awaiting::await_canceled_exception;
|
||||
# endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
} // namespace cti
|
||||
|
||||
/// \cond false
|
||||
// As far as I know there is no other way to implement this specialization...
|
||||
// NOLINTNEXTLINE(cert-dcl58-cpp)
|
||||
namespace std {
|
||||
# if defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
|
||||
namespace experimental {
|
||||
# endif // defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
|
||||
template <typename Data, typename... Args, typename... FunctionArgs>
|
||||
struct coroutine_traits<
|
||||
cti::continuable_base<Data, cti::detail::identity<Args...>>,
|
||||
FunctionArgs...> {
|
||||
|
||||
using promise_type = cti::detail::awaiting::promise_type<
|
||||
cti::continuable<Args...>, cti::promise<Args...>, Args...>;
|
||||
};
|
||||
# if defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
|
||||
} // namespace experimental
|
||||
# endif // defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
|
||||
} // namespace std
|
||||
/// \endcond
|
||||
#endif // defined(CONTINUABLE_HAS_COROUTINE)
|
||||
|
||||
#endif // CONTINUABLE_COROUTINE_HPP_INCLUDED
|
||||
41
include/continuable/continuable-operations.hpp
Normal file
41
include/continuable/continuable-operations.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_OPERATIONS_HPP_INCLUDED
|
||||
#define CONTINUABLE_OPERATIONS_HPP_INCLUDED
|
||||
|
||||
/// \defgroup Operations Operations
|
||||
/// provides functions to work with asynchronous control flows.
|
||||
|
||||
#include <continuable/operations/async.hpp>
|
||||
#include <continuable/operations/loop.hpp>
|
||||
#include <continuable/operations/split.hpp>
|
||||
|
||||
#endif // CONTINUABLE_OPERATIONS_HPP_INCLUDED
|
||||
141
include/continuable/continuable-primitives.hpp
Normal file
141
include/continuable/continuable-primitives.hpp
Normal file
@ -0,0 +1,141 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_PRIMITIVES_HPP_INCLUDED
|
||||
#define CONTINUABLE_PRIMITIVES_HPP_INCLUDED
|
||||
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/utility/identity.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \defgroup Primitives Primitives
|
||||
/// provides basic tag types for creating a customized callbacks
|
||||
/// and continuations.
|
||||
///
|
||||
/// For the callback and the continuation `Args...` represents the
|
||||
/// asynchronous result:
|
||||
/// ```cpp
|
||||
/// template<typename... Args>
|
||||
/// struct continuation {
|
||||
/// void operator() (callback<Args...>);
|
||||
/// bool operator() (cti::is_ready_arg_t) const;
|
||||
/// result<Args...> operator() (cti::unpack_arg_t);
|
||||
/// };
|
||||
/// ```
|
||||
/// ```cpp
|
||||
/// template<typename... Args>
|
||||
/// struct callback {
|
||||
/// void operator() (Args...) &&;
|
||||
/// void operator() (cti::exception_arg_t, cti::exception_t) &&;
|
||||
/// };
|
||||
/// ```
|
||||
/// \{
|
||||
|
||||
/// Represents the tag type that is used to specify the signature hint
|
||||
/// of a continuable_base or promise_base.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
template <typename... Args>
|
||||
using signature_arg_t = detail::identity<Args...>;
|
||||
|
||||
/// Represents the tag type that is used to query the continuation
|
||||
/// for whether it resolves the callback instantly with its arguments
|
||||
/// without having side effects.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
struct is_ready_arg_t {};
|
||||
|
||||
/// Represents the tag type that is used to unpack the result of a continuation.
|
||||
///
|
||||
/// \attention It's required that the query of is_ready_arg_t returns true,
|
||||
/// otherwise the behaviour when unpacking is unspecified.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
struct unpack_arg_t {};
|
||||
|
||||
/// \copydoc unpack_arg_t
|
||||
///
|
||||
/// \deprecated The query_arg_t was deprecated because of
|
||||
/// its new naming unpack_arg_t.
|
||||
///
|
||||
[[deprecated("The dispatch_error_tag was replaced by unpack_arg_t and will "
|
||||
"be removed in a later major version!")]] //
|
||||
typedef unpack_arg_t query_arg_t;
|
||||
|
||||
/// Represents the tag type that is used to disambiguate the
|
||||
/// callback operator() in order to take the exception asynchronous chain.
|
||||
///
|
||||
/// \note see continuable::next for details.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
struct exception_arg_t {};
|
||||
|
||||
/// \copydoc exception_arg_t
|
||||
///
|
||||
/// \deprecated The dispatch_error_tag was deprecated in order to move closer
|
||||
/// to the types specified in the "A Unified Future" proposal
|
||||
/// especially regarding naming types similar.
|
||||
///
|
||||
[[deprecated("The dispatch_error_tag was replaced by exception_arg_t and will "
|
||||
"be removed in a later major version!")]] //
|
||||
typedef exception_arg_t dispatch_error_tag;
|
||||
|
||||
/// Represents the type that is used as exception type
|
||||
///
|
||||
/// By default this type deduces to `std::exception_ptr`.
|
||||
/// If `CONTINUABLE_WITH_NO_EXCEPTIONS` is defined the type
|
||||
/// will be a `std::error_condition`.
|
||||
/// A custom error type may be set through
|
||||
/// defining `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
using exception_t = detail::types::exception_t;
|
||||
|
||||
/// \copydoc exception_t
|
||||
///
|
||||
/// \deprecated The error_type was deprecated in order to move closer
|
||||
/// to the types specified in the "A Unified Future" proposal
|
||||
/// especially regarding naming types similar.
|
||||
///
|
||||
[[deprecated("The error_type was replaced by exception_t and will "
|
||||
"be removed in a later major version!")]] //
|
||||
typedef exception_t error_type;
|
||||
|
||||
/// Represents the type that is used to disable the special meaning of types
|
||||
/// which are returned by a asynchronous result handler.
|
||||
/// See cti::plain for details.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
template <typename T>
|
||||
using plain_t = detail::types::plain_tag<T>;
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_PRIMITIVES_HPP_INCLUDED
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -31,20 +31,32 @@
|
||||
#ifndef CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
||||
#define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/hints.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/core/annotation.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \defgroup Base Base
|
||||
/// provides classes and functions to create continuable_base objects.
|
||||
/// \{
|
||||
|
||||
/// The promise_base makes it possible to resolve an asynchronous
|
||||
/// continuable through it's result or through an error type.
|
||||
///
|
||||
/// Use the promise type defined in `continuable/continuable_types.hpp`,
|
||||
/// in order to use this class.
|
||||
///
|
||||
/// If we want to resolve the promise_base trough the call operator,
|
||||
/// and we want to resolve it through an exception, we must call it with a
|
||||
/// exception_arg_t as first and the exception as second argument.
|
||||
/// Additionally the promise is resolveable only through its call
|
||||
/// operator when invoked as an r-value.
|
||||
///
|
||||
/// \since 2.0.0
|
||||
// clang-format off
|
||||
template <typename Data, typename Hint>
|
||||
@ -52,7 +64,7 @@ class promise_base
|
||||
/// \cond false
|
||||
;
|
||||
template <typename Data, typename... Args>
|
||||
class promise_base<Data, detail::hints::signature_hint_tag<Args...>>
|
||||
class promise_base<Data, detail::identity<Args...>>
|
||||
: detail::util::non_copyable
|
||||
/// \endcond
|
||||
{ // clang-format on
|
||||
@ -63,53 +75,141 @@ class promise_base<Data, detail::hints::signature_hint_tag<Args...>>
|
||||
/// \endcond
|
||||
|
||||
public:
|
||||
/// Constructor for constructing an empty promise
|
||||
explicit promise_base() = default;
|
||||
/// Constructor accepting the data object
|
||||
explicit promise_base(Data data) : data_(std::move(data)) {
|
||||
}
|
||||
|
||||
/// \cond false
|
||||
promise_base(promise_base&&) = default;
|
||||
promise_base(promise_base const&) = delete;
|
||||
|
||||
promise_base& operator=(promise_base&&) = default;
|
||||
promise_base& operator=(promise_base const&) = delete;
|
||||
/// \endcond
|
||||
|
||||
/// Constructor accepting any object convertible to the data object
|
||||
template <typename OData, std::enable_if_t<std::is_convertible<
|
||||
std::decay_t<OData>, Data>::value>* = nullptr>
|
||||
promise_base(OData&& data) : data_(std::forward<OData>(data)) {
|
||||
template <typename OData,
|
||||
std::enable_if_t<std::is_convertible<
|
||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
||||
/* implicit */ promise_base(OData&& data) : data_(std::forward<OData>(data)) {
|
||||
}
|
||||
|
||||
/// Assignment operator accepting any object convertible to the data object
|
||||
template <typename OData,
|
||||
std::enable_if_t<std::is_convertible<
|
||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
||||
promise_base& operator=(OData&& data) {
|
||||
data_ = std::forward<OData>(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Resolves the continuation with the given values.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the promise is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the promise such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in promise_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid promise_base is undefined!
|
||||
///
|
||||
/// \since 2.0.0
|
||||
void operator()(Args... args) && noexcept {
|
||||
assert(data_);
|
||||
std::move(data_)(std::move(args)...);
|
||||
data_ = nullptr;
|
||||
}
|
||||
/// Resolves the continuation with the given exception.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the promise is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the promise such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in promise_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid promise_base is undefined!
|
||||
///
|
||||
/// \since 2.0.0
|
||||
void operator()(detail::types::dispatch_error_tag tag,
|
||||
detail::types::error_type exception) &&
|
||||
noexcept {
|
||||
void operator()(exception_arg_t tag, exception_t exception) && noexcept {
|
||||
assert(data_);
|
||||
std::move(data_)(tag, std::move(exception));
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
/// Resolves the continuation with the given values.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the promise is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the promise such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in promise_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid promise_base is undefined!
|
||||
///
|
||||
/// \since 2.0.0
|
||||
void set_value(Args... args) noexcept {
|
||||
// assert(data_);
|
||||
std::move(data_)(std::move(args)...);
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
/// Resolves the continuation with the given exception.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the promise is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the promise such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in promise_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid promise_base is undefined!
|
||||
///
|
||||
/// \since 2.0.0
|
||||
void set_exception(detail::types::error_type exception) noexcept {
|
||||
std::move(data_)(detail::types::dispatch_error_tag{}, std::move(exception));
|
||||
void set_exception(exception_t exception) noexcept {
|
||||
assert(data_);
|
||||
std::move(data_)(exception_arg_t{}, std::move(exception));
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
/// Resolves the continuation with the cancellation token which is represented
|
||||
/// by a default constructed exception_t.
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \attention This method may only be called once,
|
||||
/// when the promise is valid operator bool() returns true.
|
||||
/// Calling this method will invalidate the promise such that
|
||||
/// subsequent calls to operator bool() will return false.
|
||||
/// This behaviour is only consistent in promise_base and
|
||||
/// non type erased promises may behave differently.
|
||||
/// Invoking an invalid promise_base is undefined!
|
||||
///
|
||||
/// \since 4.0.0
|
||||
void set_canceled() noexcept {
|
||||
assert(data_);
|
||||
std::move(data_)(exception_arg_t{}, exception_t{});
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
/// Returns true if the continuation is valid (non empty).
|
||||
///
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
explicit operator bool() const noexcept {
|
||||
return bool(data_);
|
||||
}
|
||||
};
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -33,13 +33,17 @@
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/promisify.hpp>
|
||||
#include <continuable/detail/other/promisify.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \defgroup Promisify Promisify
|
||||
/// provides helper methods to convert various callback styles to
|
||||
/// \link continuable_base continuable_bases\endlink.
|
||||
/// \{
|
||||
|
||||
/// Helper class for converting callback taking callable types into a
|
||||
/// a continuable. Various styles are supported.
|
||||
/// - `from_asio`: Converts callback taking callable types into continuables
|
||||
/// - `from`: Converts callback taking callable types into continuables
|
||||
/// which pass an error code as first parameter and the rest of
|
||||
/// the result afterwards.
|
||||
///
|
||||
@ -59,7 +63,7 @@ public:
|
||||
/// See an example of how to promisify boost asio's async_resolve below:
|
||||
/// ```cpp
|
||||
/// auto async_resolve(std::string host, std::string service) {
|
||||
/// return cti::promisify<asio::ip::udp::resolver::iterator>::from_asio(
|
||||
/// return cti::promisify<asio::ip::udp::resolver::iterator>::from(
|
||||
/// [&](auto&&... args) {
|
||||
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
/// },
|
||||
@ -67,20 +71,50 @@ public:
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If the error code which is passed as first parameter is set there are
|
||||
/// two behaviours depending whether exceptions are enabled:
|
||||
/// - If exceptions are enabled the error type is passed via
|
||||
/// an exception_ptr to the failure handler.
|
||||
/// - If exceptions are disabled the error type is converted to a
|
||||
/// `std::error_conditon` and passed down to the error handler.
|
||||
/// A given error variable is converted to the used error type.
|
||||
/// If this isn't possible you need to create a custom resolver callable
|
||||
/// object \see with for details.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename Callable, typename... Args>
|
||||
static auto from_asio(Callable&& callable, Args&&... args) {
|
||||
return helper::template from<detail::convert::promisify_asio>(
|
||||
std::forward<Callable>(callable), std::forward<Args>(args)...);
|
||||
static auto from(Callable&& callable, Args&&... args) {
|
||||
return helper::template from(detail::convert::default_resolver(),
|
||||
std::forward<Callable>(callable),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// \copybrief from
|
||||
///
|
||||
/// This modification of \ref from additionally takes a resolver callable
|
||||
/// object which is used to resolve the promise from the given result.
|
||||
///
|
||||
/// See an example of how to promisify boost asio's async_resolve below:
|
||||
/// ```cpp
|
||||
/// auto async_resolve(std::string host, std::string service) {
|
||||
/// return cti::promisify<asio::ip::udp::resolver::iterator>::with(
|
||||
/// [](auto&& promise, auto&& e, auto&&... args) {
|
||||
/// if (e) {
|
||||
/// promise.set_exception(std::forward<decltype(e)>(e));
|
||||
/// } else {
|
||||
/// promise.set_value(std::forward<decltype(args)>(args)...);
|
||||
/// }
|
||||
/// },
|
||||
/// [&](auto&&... args) {
|
||||
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
/// },
|
||||
/// std::move(host), std::move(service));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// \since 4.0.0
|
||||
template <typename Resolver, typename Callable, typename... Args>
|
||||
static auto with(Resolver&& resolver, Callable&& callable, Args&&... args) {
|
||||
return helper::template from(std::forward<Resolver>(resolver),
|
||||
std::forward<Callable>(callable),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_PROMISIFY_HPP_INCLUDED
|
||||
|
||||
356
include/continuable/continuable-result.hpp
Normal file
356
include/continuable/continuable-result.hpp
Normal file
@ -0,0 +1,356 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_RESULT_HPP_INCLUDED
|
||||
#define CONTINUABLE_RESULT_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/utility/result-trait.hpp>
|
||||
#include <continuable/detail/utility/result-variant.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \defgroup Result Result
|
||||
/// provides the \ref result class and corresponding utility functions to work
|
||||
/// with the result of an asynchronous operation which can possibly yield:
|
||||
/// - *no result*: If the operation didn't finish
|
||||
/// - *a value*: If the operation finished successfully
|
||||
/// - *an exception*: If the operation finished with an exception
|
||||
/// or was cancelled.
|
||||
/// \{
|
||||
|
||||
/// A tag which represents present void values in result.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
using void_arg_t = detail::void_arg_t;
|
||||
|
||||
/// A class which is convertible to any \ref result and that definitely holds no
|
||||
/// value so the real result gets invalidated when this object is passed to it.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
struct empty_result {};
|
||||
|
||||
/// A class which is convertible to any \ref result and that definitely holds
|
||||
/// a default constructed exception which signals the cancellation of the
|
||||
/// asynchronous control flow.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
struct cancellation_result {};
|
||||
|
||||
/// A class which is convertible to any result and that holds
|
||||
/// an exception which is then passed to the converted result object.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
class exceptional_result {
|
||||
exception_t exception_;
|
||||
|
||||
public:
|
||||
exceptional_result() = delete;
|
||||
exceptional_result(exceptional_result const&) = default;
|
||||
exceptional_result(exceptional_result&&) = default;
|
||||
exceptional_result& operator=(exceptional_result const&) = default;
|
||||
exceptional_result& operator=(exceptional_result&&) = default;
|
||||
~exceptional_result() = default;
|
||||
|
||||
explicit exceptional_result(exception_t exception)
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
: exception_(std::move(exception)) {}
|
||||
|
||||
exceptional_result& operator=(exception_t exception) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
exception_ = std::move(exception);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Sets an exception
|
||||
void set_exception(exception_t exception) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
exception_ = std::move(exception);
|
||||
}
|
||||
|
||||
/// Returns the contained exception
|
||||
exception_t& get_exception() & noexcept {
|
||||
return exception_;
|
||||
}
|
||||
/// \copydoc get_exception
|
||||
exception_t const& get_exception() const& noexcept {
|
||||
return exception_;
|
||||
}
|
||||
/// \copydoc get_exception
|
||||
exception_t&& get_exception() && noexcept {
|
||||
return std::move(exception_);
|
||||
}
|
||||
};
|
||||
|
||||
/// The result class can carry the three kinds of results an asynchronous
|
||||
/// operation possibly can return, it's implemented in a variant like
|
||||
/// data structure which is also specialized to hold arbitrary arguments.
|
||||
///
|
||||
/// The result can be in the following three states:
|
||||
/// - *no result*: If the operation didn't finish
|
||||
/// - *a value*: If the operation finished successfully
|
||||
/// - *an exception*: If the operation finished with an exception
|
||||
/// or was cancelled.
|
||||
///
|
||||
/// The interface of the result object is similar to the one proposed in
|
||||
/// the `std::expected` proposal:
|
||||
/// ```cpp
|
||||
/// result<std::string> result = make_result("Hello World!");
|
||||
/// bool(result);
|
||||
/// result.is_value();
|
||||
/// result.is_exception();
|
||||
/// *result; // Same as result.get_value()
|
||||
/// result.get_value();
|
||||
/// result.get_exception();
|
||||
/// ```
|
||||
///
|
||||
/// \since 4.0.0
|
||||
///
|
||||
template <typename... T>
|
||||
class result {
|
||||
using trait_t = detail::result_trait<T...>;
|
||||
|
||||
template <typename... Args>
|
||||
explicit result(detail::init_result_arg_t arg, Args&&... values)
|
||||
: variant_(arg, trait_t::wrap(std::forward<Args>(values)...)) {}
|
||||
explicit result(detail::init_exception_arg_t arg, exception_t exception)
|
||||
: variant_(arg, std::move(exception)) {}
|
||||
|
||||
public:
|
||||
using value_t = typename trait_t::value_t;
|
||||
using value_placeholder_t = typename trait_t::surrogate_t;
|
||||
|
||||
template <typename FirstArg, typename... Args>
|
||||
explicit result(FirstArg&& first, Args&&... values)
|
||||
: variant_(detail::init_result_arg_t{},
|
||||
trait_t::wrap(std::forward<FirstArg>(first),
|
||||
std::forward<Args>(values)...)) {}
|
||||
|
||||
result() = default;
|
||||
result(result const&) = delete;
|
||||
result(result&&) = default;
|
||||
result& operator=(result const&) = delete;
|
||||
result& operator=(result&&) = default;
|
||||
~result() = default;
|
||||
|
||||
explicit result(exception_t exception)
|
||||
: variant_(detail::init_exception_arg_t{}, std::move(exception)) {}
|
||||
/* implicit */ result(empty_result) {}
|
||||
/* implicit */ result(exceptional_result exceptional_result)
|
||||
: variant_(detail::init_exception_arg_t{},
|
||||
std::move(exceptional_result.get_exception())) {}
|
||||
/* implicit */ result(cancellation_result)
|
||||
: variant_(detail::init_exception_arg_t{}, exception_t{}) {}
|
||||
|
||||
result& operator=(empty_result) {
|
||||
variant_.set_empty();
|
||||
return *this;
|
||||
}
|
||||
result& operator=(value_placeholder_t value) {
|
||||
variant_.set_value(std::move(value));
|
||||
return *this;
|
||||
}
|
||||
result& operator=(exceptional_result exception) {
|
||||
variant_.set_exception(std::move(exception.get_exception()));
|
||||
return *this;
|
||||
}
|
||||
result& operator=(cancellation_result) {
|
||||
variant_.set_exception({});
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set the result to an empty state
|
||||
void set_empty() {
|
||||
variant_.set_empty();
|
||||
}
|
||||
/// Set the result to a the state which holds the corresponding value
|
||||
void set_value(T... values) {
|
||||
variant_.set_value(trait_t::wrap(std::move(values)...));
|
||||
}
|
||||
/// Set the result into a state which holds the corresponding exception
|
||||
void set_exception(exception_t exception) {
|
||||
variant_.set_exception(std::move(exception));
|
||||
}
|
||||
/// Set the result into a state which holds the cancellation token
|
||||
void set_canceled() {
|
||||
variant_.set_exception(exception_t{});
|
||||
}
|
||||
|
||||
/// Returns true if the state of the result is empty
|
||||
bool is_empty() const noexcept {
|
||||
return variant_.is_empty();
|
||||
}
|
||||
/// Returns true if the state of the result holds the result
|
||||
bool is_value() const noexcept {
|
||||
return variant_.is_value();
|
||||
}
|
||||
/// Returns true if the state of the result holds a present exception
|
||||
bool is_exception() const noexcept {
|
||||
return variant_.is_exception();
|
||||
}
|
||||
|
||||
/// \copydoc is_value
|
||||
explicit constexpr operator bool() const noexcept {
|
||||
return is_value();
|
||||
}
|
||||
|
||||
/// Returns the values of the result, if the result doesn't hold the value
|
||||
/// the behaviour is undefined but will assert in debug mode.
|
||||
decltype(auto) get_value() & noexcept {
|
||||
return trait_t::unwrap(variant_.get_value());
|
||||
}
|
||||
///\copydoc get_value
|
||||
decltype(auto) get_value() const& noexcept {
|
||||
return trait_t::unwrap(variant_.get_value());
|
||||
}
|
||||
///\copydoc get_value
|
||||
decltype(auto) get_value() && noexcept {
|
||||
return trait_t::unwrap(std::move(variant_.get_value()));
|
||||
}
|
||||
|
||||
///\copydoc get_value
|
||||
decltype(auto) operator*() & noexcept {
|
||||
return get_value();
|
||||
}
|
||||
///\copydoc get_value
|
||||
decltype(auto) operator*() const& noexcept {
|
||||
return get_value();
|
||||
}
|
||||
///\copydoc get_value
|
||||
decltype(auto) operator*() && noexcept {
|
||||
return std::move(variant_.get_value());
|
||||
}
|
||||
|
||||
/// Returns the exception of the result, if the result doesn't hold an
|
||||
/// exception the behaviour is undefined but will assert in debug mode.
|
||||
exception_t& get_exception() & noexcept {
|
||||
return variant_.get_exception();
|
||||
}
|
||||
/// \copydoc get_exception
|
||||
exception_t const& get_exception() const& noexcept {
|
||||
return variant_.get_exception();
|
||||
}
|
||||
/// \copydoc get_exception
|
||||
exception_t&& get_exception() && noexcept {
|
||||
return std::move(variant_.get_exception());
|
||||
}
|
||||
|
||||
/// Creates a present result from the given values
|
||||
static result from(T... values) {
|
||||
return result{detail::init_result_arg_t{}, std::move(values)...};
|
||||
}
|
||||
/// Creates a present result from the given exception
|
||||
static result from(exception_arg_t, exception_t exception) {
|
||||
return result{detail::init_exception_arg_t{}, std::move(exception)};
|
||||
}
|
||||
|
||||
/// Creates an empty result
|
||||
static result empty() {
|
||||
return result{empty_result{}};
|
||||
}
|
||||
|
||||
private:
|
||||
detail::result_variant<value_placeholder_t> variant_;
|
||||
};
|
||||
|
||||
/// Returns the value at position I of the given result
|
||||
template <std::size_t I, typename... T>
|
||||
decltype(auto) get(result<T...>& result) {
|
||||
return detail::result_trait<T...>::template get<I>(result);
|
||||
}
|
||||
/// \copydoc get
|
||||
template <std::size_t I, typename... T>
|
||||
decltype(auto) get(result<T...> const& result) {
|
||||
return detail::result_trait<T...>::template get<I>(result);
|
||||
}
|
||||
/// \copydoc get
|
||||
template <std::size_t I, typename... T>
|
||||
decltype(auto) get(result<T...>&& result) {
|
||||
return detail::result_trait<T...>::template get<I>(std::move(result));
|
||||
}
|
||||
|
||||
/// Creates a present result from the given values.
|
||||
///
|
||||
/// This could be used to pass the result of the next handler to the same
|
||||
/// asynchronous path it came from as shown below:
|
||||
/// ```cpp
|
||||
/// make_ready_continuable().next([&](auto&&... args) {
|
||||
/// result<> captured = make_result(std::forward<decltype(args)>(args)...);
|
||||
/// return shutdown().then([captured = std::move(captured)]() mutable {
|
||||
/// return std::move(captured);
|
||||
/// });
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \since 4.0.0
|
||||
template <typename... T,
|
||||
typename Result = result<detail::traits::unrefcv_t<T>...>>
|
||||
Result make_result(T&&... values) {
|
||||
return Result::from(std::forward<T>(values)...);
|
||||
}
|
||||
|
||||
/// Creates an exceptional_result from the given exception.
|
||||
///
|
||||
/// \copydetails make_result
|
||||
///
|
||||
/// \since 4.0.0
|
||||
inline exceptional_result make_result(exception_arg_t, exception_t exception) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
|
||||
return exceptional_result{std::move(exception)};
|
||||
}
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
namespace std {
|
||||
// The GCC standard library defines tuple_size as class and struct which
|
||||
// triggers a warning here.
|
||||
#if defined(__clang__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
template <typename... Args>
|
||||
struct tuple_size<cti::result<Args...>>
|
||||
: std::integral_constant<size_t, sizeof...(Args)> {};
|
||||
|
||||
template <std::size_t I, typename... Args>
|
||||
struct tuple_element<I, cti::result<Args...>>
|
||||
: tuple_element<I, tuple<Args...>> {};
|
||||
#if defined(__clang__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
} // namespace std
|
||||
|
||||
#endif // CONTINUABLE_RESULT_HPP_INCLUDED
|
||||
@ -1,74 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_TRAIT_HPP_INCLUDED
|
||||
#define CONTINUABLE_TRAIT_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/continuable-promise-base.hpp>
|
||||
#include <continuable/detail/hints.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// Trait to retrieve a continuable_base type with a given type-erasure backend.
|
||||
///
|
||||
/// Every object may me used as type-erasure backend as long as the
|
||||
/// requirements of a `std::function` like wrapper are satisfied.
|
||||
///
|
||||
/// \tparam CallbackWrapper The type which is used to erase the callback.
|
||||
///
|
||||
/// \tparam ContinuationWrapper The type which is used to erase the
|
||||
/// continuation data.
|
||||
///
|
||||
/// \tparam Args The current signature of the continuable.
|
||||
template <template <std::size_t, typename...> class CallbackWrapper,
|
||||
template <std::size_t, typename...> class ContinuationWrapper,
|
||||
typename... Args>
|
||||
class continuable_trait {
|
||||
|
||||
using callback = CallbackWrapper<0U, void(Args...)&&,
|
||||
void(detail::types::dispatch_error_tag,
|
||||
detail::types::error_type) &&>;
|
||||
|
||||
public:
|
||||
/// The promise type which is used to resolve continuations
|
||||
using promise =
|
||||
promise_base<callback, detail::hints::signature_hint_tag<Args...>>;
|
||||
|
||||
/// The continuable type for the given parameters.
|
||||
using continuable =
|
||||
continuable_base<ContinuationWrapper<sizeof(callback), void(promise)>,
|
||||
detail::hints::signature_hint_tag<Args...>>;
|
||||
};
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_TRAIT_HPP_INCLUDED
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -31,72 +31,22 @@
|
||||
#ifndef CONTINUABLE_TRANSFORMS_HPP_INCLUDED
|
||||
#define CONTINUABLE_TRANSFORMS_HPP_INCLUDED
|
||||
|
||||
#include <continuable/detail/transforms.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/transforms/wait.hpp>
|
||||
#include <continuable/transforms/future.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// A callable tag object which marks a wrapped callable object
|
||||
/// as continuable transformation which enables some useful overloads.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
using detail::types::transform;
|
||||
|
||||
/// Wraps the given callable object into a transform class.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename T>
|
||||
auto make_transform(T&& callable) {
|
||||
return transform<std::decay_t<T>>(std::forward<T>(callable));
|
||||
}
|
||||
/// \defgroup Transforms Transforms
|
||||
/// provides utilities to convert
|
||||
/// \link continuable_base continuable_bases\endlink to other
|
||||
/// types such as (`std::future`).
|
||||
/// \{
|
||||
|
||||
/// The namespace transforms declares callable objects that transform
|
||||
/// any continuable_base to an object or to a continuable_base itself.
|
||||
///
|
||||
/// Transforms can be applied to continuables through using
|
||||
/// the cti::continuable_base::apply method accordingly.
|
||||
namespace transforms {
|
||||
/// Returns a transform that if applied to a continuable,
|
||||
/// it will start the continuation chain and returns the asynchronous
|
||||
/// result as `std::future<...>`.
|
||||
///
|
||||
/// \returns Returns a `std::future<...>` which becomes ready as soon
|
||||
/// as the the continuation chain has finished.
|
||||
/// The signature of the future depends on the result type:
|
||||
/// | Continuation type | Return type |
|
||||
/// | : ------------------------------- | : -------------------------------- |
|
||||
/// | `continuable_base with <>` | `std::future<void>` |
|
||||
/// | `continuable_base with <Arg>` | `std::future<Arg>` |
|
||||
/// | `continuable_base with <Args...>` | `std::future<std::tuple<Args...>>` |
|
||||
///
|
||||
/// \attention If exceptions are used, exceptions that are thrown, are forwarded
|
||||
/// to the returned future. If there are no exceptions supported,
|
||||
/// you shall not pass any errors to the end of the asynchronous
|
||||
/// call chain!
|
||||
/// Otherwise this will yield a trap that causes application exit.
|
||||
///
|
||||
/// \since 2.0.0
|
||||
inline auto futurize() {
|
||||
return make_transform([](auto&& continuable) {
|
||||
using detail::transforms::as_future;
|
||||
return as_future(std::forward<decltype(continuable)>(continuable));
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a transform that if applied to a continuable, it will ignores all
|
||||
/// error which ocured until the point the transform was applied.
|
||||
///
|
||||
/// \returns Returns a continuable with the same signature as applied to.
|
||||
///
|
||||
/// \attention This can be used to create a continuable which doesn't resolve
|
||||
/// the continuation on errors.
|
||||
///
|
||||
/// \since 2.0.0
|
||||
inline auto flatten() {
|
||||
return make_transform([](auto&& continuable) {
|
||||
return std::forward<decltype(continuable)>(continuable).fail([](auto&&) {});
|
||||
});
|
||||
}
|
||||
} // namespace transforms
|
||||
namespace transforms {}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_TRANSFORMS_HPP_INCLUDED
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -32,31 +32,37 @@
|
||||
#define CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/traverse-async.hpp>
|
||||
#include <continuable/detail/traversal/traverse-async.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \defgroup Traversal Traversal
|
||||
/// provides functions to traverse and remap nested packs.
|
||||
/// \{
|
||||
|
||||
/// A tag which is passed to the `operator()` of the visitor
|
||||
/// if an element is visited synchronously.
|
||||
/// if an element is visited synchronously through \ref traverse_pack_async.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
using detail::traversal::async_traverse_visit_tag;
|
||||
/// A tag which is passed to the `operator()` of the visitor
|
||||
/// if an element is visited after the traversal was detached.
|
||||
using async_traverse_visit_tag = detail::traversal::async_traverse_visit_tag;
|
||||
/// A tag which is passed to the `operator()` of the visitor if an element is
|
||||
/// visited after the traversal was detached through \ref traverse_pack_async.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
using detail::traversal::async_traverse_detach_tag;
|
||||
/// A tag which is passed to the `operator()` of the visitor
|
||||
/// if the asynchronous pack traversal was finished.
|
||||
using async_traverse_detach_tag = detail::traversal::async_traverse_detach_tag;
|
||||
/// A tag which is passed to the `operator()` of the visitor if the
|
||||
/// asynchronous pack traversal was finished through \ref traverse_pack_async.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
using detail::traversal::async_traverse_complete_tag;
|
||||
using async_traverse_complete_tag =
|
||||
detail::traversal::async_traverse_complete_tag;
|
||||
|
||||
/// A tag to identify that a mapper shall be constructed in-place
|
||||
/// from the first argument passed.
|
||||
/// from the first argument passed to \ref traverse_pack_async.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
using detail::traversal::async_traverse_in_place_tag;
|
||||
template <typename T>
|
||||
using async_traverse_in_place_tag =
|
||||
detail::traversal::async_traverse_in_place_tag<T>;
|
||||
|
||||
/// Traverses the pack with the given visitor in an asynchronous way.
|
||||
///
|
||||
@ -68,7 +74,7 @@ using detail::traversal::async_traverse_in_place_tag;
|
||||
/// ```cpp
|
||||
/// struct my_async_visitor {
|
||||
/// /// The synchronous overload is called for each object,
|
||||
/// /// it may return false to suspend the current control.
|
||||
/// /// it may return false to suspend the current control flow.
|
||||
/// /// In that case the overload below is called.
|
||||
/// template <typename T>
|
||||
/// bool operator()(async_traverse_visit_tag, T&& element) {
|
||||
@ -118,6 +124,7 @@ auto traverse_pack_async(Visitor&& visitor, T&&... pack) {
|
||||
return detail::traversal::apply_pack_transform_async(
|
||||
std::forward<Visitor>(visitor), std::forward<T>(pack)...);
|
||||
}
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -34,10 +34,13 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/traverse.hpp>
|
||||
#include <continuable/detail/traversal/traverse.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \defgroup Traversal Traversal
|
||||
/// provides functions to traverse and remap nested packs.
|
||||
/// \{
|
||||
|
||||
/// Maps the pack with the given mapper.
|
||||
///
|
||||
/// This function tries to visit all plain elements which may be wrapped in:
|
||||
@ -106,6 +109,7 @@ void traverse_pack(Mapper&& mapper, T&&... pack) {
|
||||
std::forward<Mapper>(mapper),
|
||||
std::forward<T>(pack)...);
|
||||
}
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_TRAVERSE_HPP_INCLUDED
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -31,78 +31,72 @@
|
||||
#ifndef CONTINUABLE_TYPES_HPP_INCLUDED
|
||||
#define CONTINUABLE_TYPES_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <function2/function2.hpp>
|
||||
|
||||
#include <continuable/continuable-trait.hpp>
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/continuable-promise-base.hpp>
|
||||
#include <continuable/detail/other/erasure.hpp>
|
||||
|
||||
namespace cti {
|
||||
// clang-format off
|
||||
namespace detail {
|
||||
/// A function which isn't size adjusted and copyable
|
||||
template<std::size_t Size, typename... Args>
|
||||
using function_adapter = fu2::function<Args...>;
|
||||
/// A function which isn't size adjusted and move only
|
||||
template<std::size_t, typename... Args>
|
||||
using unique_function_adapter = fu2::unique_function<Args...>;
|
||||
/// A function which is size adjusted and copyable
|
||||
template<std::size_t Size, typename... Args>
|
||||
using function_adjustable = fu2::function_base<true, true, Size,
|
||||
true, false, Args...>;
|
||||
/// A function which is size adjusted and move only
|
||||
template<std::size_t Size, typename... Args>
|
||||
using unique_function_adjustable = fu2::function_base<true, false, Size,
|
||||
true, false, Args...>;
|
||||
/// \defgroup Types Types
|
||||
/// provides the \link cti::continuable continuable\endlink and \link
|
||||
/// cti::promise promise\endlink facility for type erasure.
|
||||
/// \{
|
||||
|
||||
/// We adjust the internal capacity of the outer function wrapper so
|
||||
/// we don't have to allocate twice when using `continuable<...>`.
|
||||
template<typename... Args>
|
||||
using trait_of = continuable_trait<
|
||||
unique_function_adapter,
|
||||
function_adjustable,
|
||||
Args...
|
||||
>;
|
||||
|
||||
template<typename... Args>
|
||||
using unique_trait_of = continuable_trait<
|
||||
unique_function_adapter,
|
||||
unique_function_adjustable,
|
||||
Args...
|
||||
>;
|
||||
} // namespace detail
|
||||
|
||||
/// Defines a copyable continuation type which uses the
|
||||
/// function2 backend for type erasure.
|
||||
/// Deduces to the preferred continuation capacity for a possible
|
||||
/// small functor optimization. The given capacity size is always enough to
|
||||
/// to avoid any allocation when storing a ready continuable_base.
|
||||
///
|
||||
/// Usable like: `cti::continuable<int, float>`
|
||||
/// \since 4.0.0
|
||||
template <typename... Args>
|
||||
using continuable = typename detail::trait_of<
|
||||
Args...
|
||||
>::continuable;
|
||||
using continuation_capacity = detail::erasure::continuation_capacity<Args...>;
|
||||
|
||||
/// Defines a non-copyable continuation type which uses the
|
||||
/// function2 backend for type erasure.
|
||||
///
|
||||
/// Usable like: `unique_continuable<int, float>`
|
||||
/// Usable like: `continuable<int, float>`
|
||||
///
|
||||
/// \note You can always define your own continuable with a type erasure of
|
||||
/// choice, the type erasure wrapper just needs to accept a
|
||||
/// callable object with a continuation signature as specified
|
||||
/// in the Primitives section.
|
||||
///
|
||||
/// \since 1.0.0
|
||||
template <typename... Args>
|
||||
using unique_continuable = typename detail::unique_trait_of<
|
||||
Args...
|
||||
>::continuable;
|
||||
using continuable = continuable_base<detail::erasure::continuation<Args...>, //
|
||||
signature_arg_t<Args...>>;
|
||||
|
||||
/// Defines a non-copyable promise type which is using the
|
||||
/// function2 backend for type erasure.
|
||||
///
|
||||
/// Usable like: `promise<int, float>`
|
||||
///
|
||||
/// \note You can always define your own promise with a type erasure of
|
||||
/// choice, the type erasure wrapper just needs to accept a
|
||||
/// callable object with a callback signature as specified
|
||||
/// in the Primitives section.
|
||||
///
|
||||
/// \since 1.0.0
|
||||
template <typename... Args>
|
||||
using promise = typename detail::unique_trait_of<
|
||||
Args...
|
||||
>::promise;
|
||||
using promise = promise_base<detail::erasure::callback<Args...>, //
|
||||
signature_arg_t<Args...>>;
|
||||
|
||||
// TODO channel
|
||||
// TODO sink
|
||||
|
||||
// clang-format on
|
||||
/// Defines a non-copyable type erasure which is capable of carrying
|
||||
/// callable objects passed to executors.
|
||||
///
|
||||
/// The work behaves like a `promise<>` but the work type erasure uses extra
|
||||
/// stack space for small object optimization.
|
||||
/// Additionally the outstanding work can be resolved through an exception.
|
||||
///
|
||||
/// \note You can always define your own cancelable_work with a type erasure of
|
||||
/// choice, the type erasure wrapper just needs to accept a
|
||||
/// callable object which is callable with a `void()` and
|
||||
/// `void(exception_arg_t, exception_t)` signature.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
using work = promise_base<detail::erasure::work, //
|
||||
signature_arg_t<>>;
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_TYPES_HPP_INCLUDED
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -46,10 +46,13 @@
|
||||
namespace cti {}
|
||||
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/continuable-compositions.hpp>
|
||||
#include <continuable/continuable-connections.hpp>
|
||||
#include <continuable/continuable-coroutine.hpp>
|
||||
#include <continuable/continuable-operations.hpp>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/continuable-promise-base.hpp>
|
||||
#include <continuable/continuable-promisify.hpp>
|
||||
#include <continuable/continuable-trait.hpp>
|
||||
#include <continuable/continuable-result.hpp>
|
||||
#include <continuable/continuable-transforms.hpp>
|
||||
#include <continuable/continuable-traverse-async.hpp>
|
||||
#include <continuable/continuable-traverse.hpp>
|
||||
|
||||
@ -1,164 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_AWAITING_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_AWAITING_HPP_INCLUDED
|
||||
|
||||
// Exlude this header when coroutines are not available
|
||||
#ifdef CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
|
||||
|
||||
#include <cassert>
|
||||
#include <experimental/coroutine>
|
||||
|
||||
#include <continuable/detail/base.hpp>
|
||||
#include <continuable/detail/expected.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/hints.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
#include <exception>
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace awaiting {
|
||||
/// We import the coroutine handle in our namespace
|
||||
using std::experimental::coroutine_handle;
|
||||
|
||||
/// An object which provides the internal buffer and helper methods
|
||||
/// for waiting on a continuable in a stackless coroutine.
|
||||
template <typename Continuable>
|
||||
class awaitable {
|
||||
using trait_t = util::expected_result_trait_t<Continuable>;
|
||||
|
||||
/// The continuable which is invoked upon suspension
|
||||
Continuable continuable_;
|
||||
/// A cache which is used to pass the result of the continuation
|
||||
/// to the coroutine.
|
||||
typename trait_t::expected_type result_;
|
||||
|
||||
public:
|
||||
explicit constexpr awaitable(Continuable&& continuable)
|
||||
: continuable_(std::move(continuable)) {
|
||||
}
|
||||
|
||||
/// Since continuables are evaluated lazily we are not
|
||||
/// capable to say whether the resumption will be instantly.
|
||||
bool await_ready() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Suspend the current context
|
||||
// TODO Convert this to an r-value function once possible
|
||||
void await_suspend(coroutine_handle<> h) {
|
||||
// Forward every result to the current awaitable
|
||||
std::move(continuable_)
|
||||
.next([h, this](auto&&... args) mutable {
|
||||
resolve(std::forward<decltype(args)>(args)...);
|
||||
h.resume();
|
||||
})
|
||||
.done();
|
||||
}
|
||||
|
||||
/// Resume the coroutine represented by the handle
|
||||
auto await_resume() noexcept(false) {
|
||||
if (result_) {
|
||||
// When the result was resolved return it
|
||||
return trait_t::unwrap(std::move(result_));
|
||||
}
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
std::rethrow_exception(result_.get_exception());
|
||||
#else // CONTINUABLE_HAS_EXCEPTIONS
|
||||
// Returning error types in await isn't supported as of now
|
||||
util::trap();
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
}
|
||||
|
||||
private:
|
||||
/// Resolve the continuation through the result
|
||||
template <typename... Args>
|
||||
void resolve(Args&&... args) {
|
||||
result_.set_value(trait_t::wrap(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/// Resolve the continuation through an error
|
||||
void resolve(types::dispatch_error_tag, types::error_type error) {
|
||||
result_.set_exception(std::move(error));
|
||||
}
|
||||
};
|
||||
|
||||
/// Converts a continuable into an awaitable object as described by
|
||||
/// the C++ coroutine TS.
|
||||
template <typename T>
|
||||
constexpr auto create_awaiter(T&& continuable) {
|
||||
return awaitable<std::decay_t<T>>(std::forward<T>(continuable));
|
||||
}
|
||||
} // namespace awaiting
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
namespace std {
|
||||
namespace experimental {
|
||||
template <typename Data, typename... Args, typename... FunctionArgs>
|
||||
struct coroutine_traits<
|
||||
cti::continuable_base<Data,
|
||||
cti::detail::hints::signature_hint_tag<Args...>>,
|
||||
FunctionArgs...> /*{
|
||||
struct promise_type {
|
||||
// boost::promise<R> p;
|
||||
auto get_return_object() {
|
||||
// return p.get_future();
|
||||
}
|
||||
suspend_always initial_suspend() {
|
||||
return {};
|
||||
}
|
||||
suspend_never final_suspend() {
|
||||
return {};
|
||||
}
|
||||
void set_exception(std::exception_ptr e) {
|
||||
// p.set_exception(std::move(e));
|
||||
}
|
||||
void unhandled_exception() {
|
||||
// p.set_exception(std::current_exception());
|
||||
}
|
||||
template <typename U>
|
||||
void return_value(U&& u) {
|
||||
// p.set_value(std::forward<U>(u));
|
||||
}
|
||||
};
|
||||
}*/;
|
||||
} // namespace experimental
|
||||
} // namespace std
|
||||
|
||||
#endif // CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
|
||||
#endif // CONTINUABLE_DETAIL_UTIL_HPP_INCLUDED
|
||||
@ -1,590 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_BASE_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_BASE_HPP_INCLUDED
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/hints.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
#include <exception>
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
/// The namespace `base` provides the low level API for working
|
||||
/// with continuable types.
|
||||
///
|
||||
/// Important methods are:
|
||||
/// - Creating a continuation from a callback taking functional
|
||||
/// base::attorney::create(auto&& callback)
|
||||
/// -> base::continuation<auto>
|
||||
/// - Chaining a continuation together with a callback
|
||||
/// base::chain_continuation(base::continuation<auto> continuation,
|
||||
/// auto&& callback)
|
||||
/// -> base::continuation<auto>
|
||||
/// - Finally invoking the continuation chain
|
||||
/// base::finalize_continuation(base::continuation<auto> continuation)
|
||||
/// -> void
|
||||
namespace base {
|
||||
template <typename T>
|
||||
struct is_continuable : std::false_type {};
|
||||
template <typename Data, typename Annotation>
|
||||
struct is_continuable<continuable_base<Data, Annotation>> : std::true_type {};
|
||||
|
||||
/// Helper class to access private methods and members of
|
||||
/// the continuable_base class.
|
||||
struct attorney {
|
||||
/// Makes a continuation wrapper from the given argument
|
||||
template <typename T, typename A>
|
||||
static auto create(T&& continuation, A /*hint*/, util::ownership ownership_) {
|
||||
return continuable_base<std::decay_t<T>, std::decay_t<A>>(
|
||||
std::forward<T>(continuation), ownership_);
|
||||
}
|
||||
|
||||
/// Invokes a continuation object in a reference correct way
|
||||
template <typename Data, typename Annotation, typename Callback>
|
||||
static auto
|
||||
invoke_continuation(continuable_base<Data, Annotation>&& continuation,
|
||||
Callback&& callback) noexcept {
|
||||
auto materialized = std::move(continuation).materialize();
|
||||
materialized.release();
|
||||
return materialized.data_(std::forward<Callback>(callback));
|
||||
}
|
||||
|
||||
template <typename Data, typename Annotation>
|
||||
static auto materialize(continuable_base<Data, Annotation>&& continuation) {
|
||||
return std::move(continuation).materialize();
|
||||
}
|
||||
|
||||
template <typename Data, typename Annotation>
|
||||
static Data&&
|
||||
consume_data(continuable_base<Data, Annotation>&& continuation) {
|
||||
return std::move(continuation).consume_data();
|
||||
}
|
||||
|
||||
template <typename Continuable>
|
||||
static util::ownership ownership_of(Continuable&& continuation) noexcept {
|
||||
return continuation.ownership_;
|
||||
}
|
||||
};
|
||||
|
||||
// Returns the invoker of a callback, the next callback
|
||||
// and the arguments of the previous continuation.
|
||||
//
|
||||
// The return type of the invokerOf function matches a callable of:
|
||||
// void(auto&& callback, auto&& next_callback, auto&&... args)
|
||||
//
|
||||
// The invoker decorates the result type in the following way
|
||||
// - void -> next_callback()
|
||||
// - ? -> next_callback(?)
|
||||
// - std::pair<?, ?> -> next_callback(?, ?)
|
||||
// - std::tuple<?...> -> next_callback(?...)
|
||||
//
|
||||
// When the result is a continuation itself pass the callback to it
|
||||
// - continuation<?...> -> result(next_callback);
|
||||
namespace decoration {
|
||||
/// Helper class wrapping the underlaying unwrapping lambda
|
||||
/// in order to extend it with a hint method.
|
||||
template <typename T, typename Hint>
|
||||
class invoker : public T {
|
||||
public:
|
||||
constexpr explicit invoker(T invoke) : T(std::move(invoke)) {
|
||||
}
|
||||
|
||||
using T::operator();
|
||||
|
||||
/// Returns the underlaying signature hint
|
||||
static constexpr Hint hint() noexcept {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
#define CONTINUABLE_BLOCK_TRY_BEGIN try {
|
||||
#define CONTINUABLE_BLOCK_TRY_END \
|
||||
} \
|
||||
catch (...) { \
|
||||
std::forward<decltype(next_callback)>(next_callback)( \
|
||||
types::dispatch_error_tag{}, std::current_exception()); \
|
||||
}
|
||||
|
||||
#else // CONTINUABLE_HAS_EXCEPTIONS
|
||||
#define CONTINUABLE_BLOCK_TRY_BEGIN {
|
||||
#define CONTINUABLE_BLOCK_TRY_END }
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
/// Invokes the given callable object with the given arguments while
|
||||
/// marking the operation as non exceptional.
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto invoke_no_except(T&& callable, Args&&... args) noexcept {
|
||||
return std::forward<T>(callable)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag<Args...>) {
|
||||
return invoker<std::decay_t<T>, hints::signature_hint_tag<Args...>>(
|
||||
std::forward<T>(invoke));
|
||||
}
|
||||
|
||||
/// - continuable<?...> -> result(next_callback);
|
||||
template <typename Data, typename Annotation>
|
||||
constexpr auto
|
||||
invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
|
||||
/// Get the hint of the unwrapped returned continuable
|
||||
using Type = decltype(attorney::materialize(
|
||||
std::declval<continuable_base<Data, Annotation>>()));
|
||||
|
||||
auto constexpr const hint = hints::hint_of(traits::identify<Type>{});
|
||||
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
auto continuation_ =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
attorney::invoke_continuation(
|
||||
std::move(continuation_),
|
||||
std::forward<decltype(next_callback)>(next_callback));
|
||||
CONTINUABLE_BLOCK_TRY_END
|
||||
},
|
||||
hint);
|
||||
}
|
||||
|
||||
/// - ? -> next_callback(?)
|
||||
template <typename T>
|
||||
constexpr auto invoker_of(traits::identity<T>) {
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
auto result =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
|
||||
std::move(result));
|
||||
CONTINUABLE_BLOCK_TRY_END
|
||||
},
|
||||
traits::identify<T>{});
|
||||
}
|
||||
|
||||
/// - void -> next_callback()
|
||||
inline auto invoker_of(traits::identity<void>) {
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_no_except(
|
||||
std::forward<decltype(next_callback)>(next_callback));
|
||||
CONTINUABLE_BLOCK_TRY_END
|
||||
},
|
||||
traits::identity<>{});
|
||||
}
|
||||
|
||||
/// Returns a sequenced invoker which is able to invoke
|
||||
/// objects where std::get is applicable.
|
||||
inline auto sequenced_unpack_invoker() {
|
||||
return [](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
auto result =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Workaround for MSVC not capturing the reference correctly inside
|
||||
// the lambda.
|
||||
using Next = decltype(next_callback);
|
||||
|
||||
traits::unpack(std::move(result), [&](auto&&... types) {
|
||||
/// TODO Add inplace resolution here
|
||||
|
||||
invoke_no_except(std::forward<Next>(next_callback),
|
||||
std::forward<decltype(types)>(types)...);
|
||||
});
|
||||
CONTINUABLE_BLOCK_TRY_END
|
||||
};
|
||||
} // namespace decoration
|
||||
|
||||
// - std::pair<?, ?> -> next_callback(?, ?)
|
||||
template <typename First, typename Second>
|
||||
constexpr auto invoker_of(traits::identity<std::pair<First, Second>>) {
|
||||
return make_invoker(sequenced_unpack_invoker(),
|
||||
traits::identity<First, Second>{});
|
||||
}
|
||||
|
||||
// - std::tuple<?...> -> next_callback(?...)
|
||||
template <typename... Args>
|
||||
constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
|
||||
return make_invoker(sequenced_unpack_invoker(), traits::identity<Args...>{});
|
||||
}
|
||||
|
||||
#undef CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
#undef CONTINUABLE_BLOCK_TRY_END
|
||||
} // namespace decoration
|
||||
|
||||
/// Invoke the callback immediately
|
||||
template <typename Invoker, typename... Args>
|
||||
void packed_dispatch(types::this_thread_executor_tag, Invoker&& invoker,
|
||||
Args&&... args) {
|
||||
|
||||
// Invoke the callback with the decorated invoker immediately
|
||||
std::forward<Invoker>(invoker)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Invoke the callback through the given executor
|
||||
template <typename Executor, typename Invoker, typename... Args>
|
||||
void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) {
|
||||
|
||||
// Create a worker object which when invoked calls the callback with the
|
||||
// the returned arguments.
|
||||
auto work = [
|
||||
invoker = std::forward<Invoker>(invoker),
|
||||
args = std::make_tuple(std::forward<Args>(args)...)
|
||||
]() mutable {
|
||||
traits::unpack(std::move(args), [&](auto&&... captured_args) {
|
||||
// Just use the packed dispatch method which dispatches the work on
|
||||
// the current thread.
|
||||
packed_dispatch(types::this_thread_executor_tag{}, std::move(invoker),
|
||||
std::forward<decltype(captured_args)>(captured_args)...);
|
||||
});
|
||||
};
|
||||
|
||||
// Pass the work callable object to the executor
|
||||
std::forward<Executor>(executor)(std::move(work));
|
||||
}
|
||||
|
||||
/// Tells whether we potentially move the chain upwards and handle the result
|
||||
enum class handle_results {
|
||||
no, //< The result is forwarded to the next callable
|
||||
yes //< The result is handled by the current callable
|
||||
};
|
||||
|
||||
/// Tells whether we handle the error through the callback
|
||||
enum class handle_errors {
|
||||
no, //< The error is forwarded to the next callable
|
||||
plain, //< The error is the only argument accepted by the callable
|
||||
forward //< The error is forwarded to the callable while keeping its tag
|
||||
};
|
||||
|
||||
namespace callbacks {
|
||||
namespace proto {
|
||||
template <handle_results HandleResults, typename Base, typename Hint>
|
||||
struct result_handler_base;
|
||||
template <typename Base, typename... Args>
|
||||
struct result_handler_base<handle_results::no, Base,
|
||||
hints::signature_hint_tag<Args...>> {
|
||||
void operator()(Args... args) && {
|
||||
// Forward the arguments to the next callback
|
||||
std::move(static_cast<Base*>(this)->next_callback_)(std::move(args)...);
|
||||
}
|
||||
};
|
||||
template <typename Base, typename... Args>
|
||||
struct result_handler_base<handle_results::yes, Base,
|
||||
hints::signature_hint_tag<Args...>> {
|
||||
/// The operator which is called when the result was provided
|
||||
void operator()(Args... args) && {
|
||||
// In order to retrieve the correct decorator we must know what the
|
||||
// result type is.
|
||||
auto result = traits::identify<decltype(util::partial_invoke(
|
||||
std::move(static_cast<Base*>(this)->callback_), std::move(args)...))>{};
|
||||
|
||||
// Pick the correct invoker that handles decorating of the result
|
||||
auto invoker = decoration::invoker_of(result);
|
||||
|
||||
// Invoke the callback
|
||||
packed_dispatch(std::move(static_cast<Base*>(this)->executor_),
|
||||
std::move(invoker),
|
||||
std::move(static_cast<Base*>(this)->callback_),
|
||||
std::move(static_cast<Base*>(this)->next_callback_),
|
||||
std::move(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
inline auto make_error_invoker(
|
||||
std::integral_constant<handle_errors, handle_errors::plain>) noexcept {
|
||||
return [](auto&& callback, types::error_type&& error) {
|
||||
// Errors are not partial invoked
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg)
|
||||
std::forward<decltype(callback)>(callback)(std::move(error));
|
||||
};
|
||||
}
|
||||
inline auto make_error_invoker(
|
||||
std::integral_constant<handle_errors, handle_errors::forward>) noexcept {
|
||||
return [](auto&& callback, types::error_type&& error) {
|
||||
// Errors are not partial invoked
|
||||
std::forward<decltype(callback)>(callback)(
|
||||
types::dispatch_error_tag{},
|
||||
std::move(error)); // NOLINT(hicpp-move-const-arg)
|
||||
};
|
||||
}
|
||||
|
||||
template <handle_errors HandleErrors /* = plain or forward*/, typename Base>
|
||||
struct error_handler_base {
|
||||
void operator()(types::dispatch_error_tag, types::error_type error) && {
|
||||
// Just invoke the error handler, cancel the calling hierarchy after
|
||||
auto invoker = make_error_invoker(
|
||||
std::integral_constant<handle_errors, HandleErrors>{});
|
||||
|
||||
// Invoke the error handler
|
||||
packed_dispatch(
|
||||
std::move(static_cast<Base*>(this)->executor_), std::move(invoker),
|
||||
std::move(static_cast<Base*>(this)->callback_), std::move(error));
|
||||
}
|
||||
};
|
||||
template <typename Base>
|
||||
struct error_handler_base<handle_errors::no, Base> {
|
||||
/// The operator which is called when an error occurred
|
||||
void operator()(types::dispatch_error_tag tag, types::error_type error) && {
|
||||
// Forward the error to the next callback
|
||||
std::move(static_cast<Base*>(this)->next_callback_)(tag, std::move(error));
|
||||
}
|
||||
};
|
||||
} // namespace proto
|
||||
|
||||
template <typename Hint, handle_results HandleResults,
|
||||
handle_errors HandleErrors, typename Callback, typename Executor,
|
||||
typename NextCallback>
|
||||
struct callback_base;
|
||||
|
||||
template <typename... Args, handle_results HandleResults,
|
||||
handle_errors HandleErrors, typename Callback, typename Executor,
|
||||
typename NextCallback>
|
||||
struct callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
||||
HandleErrors, Callback, Executor, NextCallback>
|
||||
: proto::result_handler_base<
|
||||
HandleResults,
|
||||
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
||||
HandleErrors, Callback, Executor, NextCallback>,
|
||||
hints::signature_hint_tag<Args...>>,
|
||||
proto::error_handler_base<
|
||||
HandleErrors,
|
||||
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
||||
HandleErrors, Callback, Executor, NextCallback>>,
|
||||
util::non_copyable {
|
||||
|
||||
Callback callback_;
|
||||
Executor executor_;
|
||||
NextCallback next_callback_;
|
||||
|
||||
explicit callback_base(Callback callback, Executor executor,
|
||||
NextCallback next_callback)
|
||||
: callback_(std::move(callback)), executor_(std::move(executor)),
|
||||
next_callback_(std::move(next_callback)) {
|
||||
}
|
||||
|
||||
/// Pull the result handling operator() in
|
||||
using proto::result_handler_base<
|
||||
HandleResults,
|
||||
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
||||
HandleErrors, Callback, Executor, NextCallback>,
|
||||
hints::signature_hint_tag<Args...>>::operator();
|
||||
|
||||
/// Pull the error handling operator() in
|
||||
using proto::error_handler_base<
|
||||
HandleErrors,
|
||||
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
||||
HandleErrors, Callback, Executor, NextCallback>>::
|
||||
operator();
|
||||
|
||||
/// Resolves the continuation with the given values
|
||||
void set_value(Args... args) {
|
||||
std::move (*this)(std::move(args)...);
|
||||
}
|
||||
|
||||
/// Resolves the continuation with the given error variable.
|
||||
void set_exception(types::error_type error) {
|
||||
std::move (*this)(types::dispatch_error_tag{}, std::move(error));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Hint, handle_results HandleResults,
|
||||
handle_errors HandleErrors, typename Callback, typename Executor,
|
||||
typename NextCallback>
|
||||
auto make_callback(Callback&& callback, Executor&& executor,
|
||||
NextCallback&& next_callback) {
|
||||
return callback_base<Hint, HandleResults, HandleErrors,
|
||||
std::decay_t<Callback>, std::decay_t<Executor>,
|
||||
std::decay_t<NextCallback>>{
|
||||
std::forward<Callback>(callback), std::forward<Executor>(executor),
|
||||
std::forward<NextCallback>(next_callback)};
|
||||
}
|
||||
|
||||
/// Once this was a workaround for GCC bug:
|
||||
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
|
||||
struct final_callback : util::non_copyable {
|
||||
template <typename... Args>
|
||||
void operator()(Args... /*args*/) && {
|
||||
}
|
||||
|
||||
void operator()(types::dispatch_error_tag, types::error_type error) && {
|
||||
(void)error;
|
||||
#ifndef CONTINUABLE_WITH_UNHANDLED_ERRORS
|
||||
// There were unhandled errors inside the asynchronous call chain!
|
||||
// Define `CONTINUABLE_WITH_UNHANDLED_ERRORS` in order
|
||||
// to ignore unhandled errors!"
|
||||
util::trap();
|
||||
#endif // CONTINUABLE_WITH_UNHANDLED_ERRORS
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void set_value(Args... args) {
|
||||
std::move (*this)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void set_exception(types::error_type error) {
|
||||
// NOLINTNEXTLINE(hicpp-move-const-arg)
|
||||
std::move (*this)(types::dispatch_error_tag{}, std::move(error));
|
||||
}
|
||||
};
|
||||
} // namespace callbacks
|
||||
|
||||
/// Returns the next hint when the callback is invoked with the given hint
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto
|
||||
next_hint_of(std::integral_constant<handle_results, handle_results::yes>,
|
||||
traits::identity<T> /*callback*/,
|
||||
hints::signature_hint_tag<Args...> /*current*/) {
|
||||
// Partial Invoke the given callback
|
||||
using Result = decltype(
|
||||
util::partial_invoke(std::declval<T>(), std::declval<Args>()...));
|
||||
|
||||
// Return the hint of thr given invoker
|
||||
return decltype(decoration::invoker_of(traits::identify<Result>{}).hint()){};
|
||||
}
|
||||
/// Don't progress the hint when we don't continue
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto
|
||||
next_hint_of(std::integral_constant<handle_results, handle_results::no>,
|
||||
traits::identity<T> /*callback*/,
|
||||
hints::signature_hint_tag<Args...> current) {
|
||||
return current;
|
||||
}
|
||||
|
||||
/// Chains a callback together with a continuation and returns a continuation:
|
||||
///
|
||||
/// For example given:
|
||||
/// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
|
||||
/// - Callback: [](std::string) { }
|
||||
///
|
||||
/// This function returns a function accepting the next callback in the chain:
|
||||
/// - Result: continuation<[](auto&& callback) { /*...*/ }>
|
||||
///
|
||||
template <handle_results HandleResults, handle_errors HandleErrors,
|
||||
typename Continuation, typename Callback, typename Executor>
|
||||
auto chain_continuation(Continuation&& continuation, Callback&& callback,
|
||||
Executor&& executor) {
|
||||
static_assert(is_continuable<std::decay_t<Continuation>>{},
|
||||
"Expected a continuation!");
|
||||
|
||||
using Hint = decltype(hints::hint_of(traits::identify<Continuation>()));
|
||||
constexpr auto next_hint =
|
||||
next_hint_of(std::integral_constant<handle_results, HandleResults>{},
|
||||
traits::identify<decltype(callback)>{}, Hint{});
|
||||
|
||||
// TODO consume only the data here so the freeze isn't needed
|
||||
auto ownership_ = attorney::ownership_of(continuation);
|
||||
continuation.freeze();
|
||||
|
||||
return attorney::create(
|
||||
[
|
||||
continuation = std::forward<Continuation>(continuation),
|
||||
callback = std::forward<Callback>(callback),
|
||||
executor = std::forward<Executor>(executor)
|
||||
](auto&& next_callback) mutable {
|
||||
|
||||
// Invokes a continuation with a given callback.
|
||||
// Passes the next callback to the resulting continuable or
|
||||
// invokes the next callback directly if possible.
|
||||
//
|
||||
// For example given:
|
||||
// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
|
||||
// - Callback: [](std::string) { }
|
||||
// - NextCallback: []() { }
|
||||
auto proxy =
|
||||
callbacks::make_callback<Hint, HandleResults, HandleErrors>(
|
||||
std::move(callback), std::move(executor),
|
||||
std::forward<decltype(next_callback)>(next_callback));
|
||||
|
||||
// Invoke the continuation with a proxy callback.
|
||||
// The proxy callback is responsible for passing
|
||||
// the result to the callback as well as decorating it.
|
||||
attorney::invoke_continuation(std::move(continuation),
|
||||
std::move(proxy));
|
||||
},
|
||||
next_hint, ownership_);
|
||||
}
|
||||
|
||||
/// Final invokes the given continuation chain:
|
||||
///
|
||||
/// For example given:
|
||||
/// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
|
||||
template <typename Continuation>
|
||||
void finalize_continuation(Continuation&& continuation) {
|
||||
attorney::invoke_continuation(std::forward<Continuation>(continuation),
|
||||
callbacks::final_callback{});
|
||||
}
|
||||
|
||||
/// Workaround for GCC bug:
|
||||
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
|
||||
template <typename T>
|
||||
class supplier_callback {
|
||||
T data_;
|
||||
|
||||
public:
|
||||
explicit supplier_callback(T data) : data_(std::move(data)) {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args...) {
|
||||
return std::move(data_);
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns a continuable into a callable object returning the continuable
|
||||
template <typename Continuation>
|
||||
auto wrap_continuation(Continuation&& continuation) {
|
||||
continuation.freeze();
|
||||
return supplier_callback<std::decay_t<Continuation>>(
|
||||
std::forward<Continuation>(continuation));
|
||||
}
|
||||
} // namespace base
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_BASE_HPP_INCLUDED
|
||||
@ -1,233 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_COMPOSITION_ALL_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_COMPOSITION_ALL_HPP_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/base.hpp>
|
||||
#include <continuable/detail/composition-remapping.hpp>
|
||||
#include <continuable/detail/composition.hpp>
|
||||
#include <continuable/detail/hints.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace composition {
|
||||
namespace all {
|
||||
struct all_hint_deducer {
|
||||
static constexpr auto deduce(hints::signature_hint_tag<>) noexcept {
|
||||
return spread_this();
|
||||
}
|
||||
|
||||
template <typename First>
|
||||
static constexpr auto deduce(hints::signature_hint_tag<First>) {
|
||||
return First{};
|
||||
}
|
||||
|
||||
template <typename First, typename Second, typename... Args>
|
||||
static constexpr auto
|
||||
deduce(hints::signature_hint_tag<First, Second, Args...>) {
|
||||
return spread_this(First{}, Second{}, Args{}...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<base::is_continuable<std::decay_t<T>>::value>* = nullptr>
|
||||
auto operator()(T&& /*continuable*/) const {
|
||||
return deduce(hints::hint_of(traits::identify<T>{}));
|
||||
}
|
||||
};
|
||||
|
||||
constexpr auto deduce_from_pack(traits::identity<void>)
|
||||
-> hints::signature_hint_tag<>;
|
||||
template <typename... T>
|
||||
constexpr auto deduce_from_pack(traits::identity<std::tuple<T...>>)
|
||||
-> hints::signature_hint_tag<T...>;
|
||||
template <typename T>
|
||||
constexpr auto deduce_from_pack(traits::identity<T>)
|
||||
-> hints::signature_hint_tag<T>;
|
||||
|
||||
// We must guard the mapped type against to be void since this represents an
|
||||
// empty signature hint.
|
||||
template <typename Composition>
|
||||
constexpr auto deduce_hint(Composition&& /*composition*/) {
|
||||
// Don't change this way since it addresses a GCC compiler bug:
|
||||
// error: extra ';' [-Werror=pedantic]
|
||||
// std::declval<Composition>()))>{})){};
|
||||
using mapped_t =
|
||||
decltype(map_pack(all_hint_deducer{}, std::declval<Composition>()));
|
||||
using deduced_t = decltype(deduce_from_pack(traits::identity<mapped_t>{}));
|
||||
return deduced_t{};
|
||||
}
|
||||
|
||||
/// Caches the partial results and invokes the callback when all results
|
||||
/// are arrived. This class is thread safe.
|
||||
template <typename Callback, typename Result>
|
||||
class result_submitter
|
||||
: public std::enable_shared_from_this<result_submitter<Callback, Result>>,
|
||||
public util::non_movable {
|
||||
|
||||
Callback callback_;
|
||||
Result result_;
|
||||
|
||||
std::atomic<std::size_t> left_;
|
||||
std::once_flag flag_;
|
||||
|
||||
// Invokes the callback with the cached result
|
||||
void invoke() {
|
||||
assert((left_ == 0U) && "Expected that the submitter is finished!");
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
|
||||
// Call the final callback with the cleaned result
|
||||
std::call_once(flag_, [&] {
|
||||
remapping::finalize_data(std::move(callback_), std::move(result_));
|
||||
});
|
||||
}
|
||||
|
||||
// Completes one result
|
||||
void complete_one() {
|
||||
assert((left_ > 0U) && "Expected that the submitter isn't finished!");
|
||||
|
||||
auto const current = --left_;
|
||||
if (!current) {
|
||||
invoke();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Box>
|
||||
struct partial_all_callback {
|
||||
Box* box;
|
||||
std::shared_ptr<result_submitter> me;
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(Args&&... args) && {
|
||||
|
||||
// Assign the result to the target
|
||||
box->assign(std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Complete one result
|
||||
me->complete_one();
|
||||
}
|
||||
|
||||
template <typename... PartialArgs>
|
||||
void operator()(types::dispatch_error_tag tag, types::error_type error) && {
|
||||
// We never complete the composition, but we forward the first error
|
||||
// which was raised.
|
||||
std::call_once(me->flag_, std::move(me->callback_), tag,
|
||||
std::move(error));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
explicit result_submitter(Callback callback, Result&& result)
|
||||
: callback_(std::move(callback)), result_(std::move(result)), left_(1) {
|
||||
}
|
||||
|
||||
/// Creates a submitter which submits it's result into the storage
|
||||
template <typename Box>
|
||||
auto create_callback(Box* box) {
|
||||
left_.fetch_add(1, std::memory_order_seq_cst);
|
||||
return partial_all_callback<std::decay_t<Box>>{box,
|
||||
this->shared_from_this()};
|
||||
}
|
||||
|
||||
/// Initially the counter is created with an initial count of 1 in order
|
||||
/// to prevent that the composition is finished before all callbacks
|
||||
/// were registered.
|
||||
void accept() {
|
||||
complete_one();
|
||||
}
|
||||
|
||||
constexpr auto& head() noexcept {
|
||||
return result_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Submitter>
|
||||
struct continuable_dispatcher {
|
||||
std::shared_ptr<Submitter>& submitter;
|
||||
|
||||
template <typename Box, std::enable_if_t<remapping::is_continuable_box<
|
||||
std::decay_t<Box>>::value>* = nullptr>
|
||||
void operator()(Box&& box) const {
|
||||
// Retrieve a callback from the submitter and attach it to the continuable
|
||||
box.fetch().next(submitter->create_callback(std::addressof(box))).done();
|
||||
}
|
||||
};
|
||||
} // namespace all
|
||||
|
||||
/// Finalizes the all logic of a given composition
|
||||
template <>
|
||||
struct composition_finalizer<composition_strategy_all_tag> {
|
||||
template <typename Composition>
|
||||
static constexpr auto hint() {
|
||||
return decltype(all::deduce_hint(std::declval<Composition>())){};
|
||||
}
|
||||
|
||||
/// Finalizes the all logic of a given composition
|
||||
template <typename Composition>
|
||||
static auto finalize(Composition&& composition) {
|
||||
return [composition = std::forward<Composition>(composition)] // ...
|
||||
(auto&& callback) mutable {
|
||||
|
||||
// Create the target result from the composition
|
||||
auto result = remapping::box_continuables(std::move(composition));
|
||||
|
||||
using submitter_t =
|
||||
all::result_submitter<std::decay_t<decltype(callback)>,
|
||||
std::decay_t<decltype(result)>>;
|
||||
|
||||
// Create the shared state which holds the result and the final callback
|
||||
auto state = std::make_shared<submitter_t>(
|
||||
std::forward<decltype(callback)>(callback), std::move(result));
|
||||
|
||||
// Dispatch the continuables and store its partial result
|
||||
// in the whole result
|
||||
traverse_pack(all::continuable_dispatcher<submitter_t>{state},
|
||||
state->head());
|
||||
|
||||
// Finalize the composition if all results arrived in-place
|
||||
state->accept();
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace composition
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_COMPOSITION_ALL_HPP_INCLUDED
|
||||
@ -1,166 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_COMPOSITION_SEQ_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_COMPOSITION_SEQ_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/continuable-traverse-async.hpp>
|
||||
#include <continuable/detail/base.hpp>
|
||||
#include <continuable/detail/composition-all.hpp>
|
||||
#include <continuable/detail/composition-remapping.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace composition {
|
||||
namespace seq {
|
||||
/// Connects the left and the right continuable to a sequence
|
||||
///
|
||||
/// \note This is implemented in an eager way because we would not gain
|
||||
/// any profit from chaining sequences lazily.
|
||||
template <typename Left, typename Right>
|
||||
auto sequential_connect(Left&& left, Right&& right) {
|
||||
left.freeze(right.is_frozen());
|
||||
right.freeze();
|
||||
|
||||
return std::forward<Left>(left).then([right = std::forward<Right>(right)](
|
||||
auto&&... args) mutable {
|
||||
return std::move(right).then([previous = std::make_tuple(
|
||||
std::forward<decltype(args)>(args)...)](
|
||||
auto&&... args) mutable {
|
||||
return traits::merge(
|
||||
std::move(previous),
|
||||
std::make_tuple(std::forward<decltype(args)>(args)...));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Callback, typename Box>
|
||||
struct sequential_dispatch_data {
|
||||
Callback callback;
|
||||
Box box;
|
||||
};
|
||||
|
||||
template <typename Data>
|
||||
class sequential_dispatch_visitor
|
||||
: public std::enable_shared_from_this<sequential_dispatch_visitor<Data>>,
|
||||
public util::non_movable {
|
||||
|
||||
Data data_;
|
||||
|
||||
public:
|
||||
explicit sequential_dispatch_visitor(Data&& data) : data_(std::move(data)) {
|
||||
}
|
||||
|
||||
virtual ~sequential_dispatch_visitor() = default;
|
||||
|
||||
/// Returns the pack that should be traversed
|
||||
auto& head() {
|
||||
return data_.box;
|
||||
}
|
||||
|
||||
template <typename Box, std::enable_if_t<remapping::is_continuable_box<
|
||||
std::decay_t<Box>>::value>* = nullptr>
|
||||
bool operator()(async_traverse_visit_tag, Box&& /*box*/) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Box, typename N>
|
||||
void operator()(async_traverse_detach_tag, Box&& box, N&& next) {
|
||||
box.fetch()
|
||||
.then([ box = std::addressof(box),
|
||||
next = std::forward<N>(next) ](auto&&... args) mutable {
|
||||
|
||||
// Assign the result to the target
|
||||
box->assign(std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Continue the asynchronous sequential traversal
|
||||
next();
|
||||
})
|
||||
.fail([me = this->shared_from_this()](types::error_type exception) {
|
||||
// Abort the traversal when an error occurred
|
||||
std::move(me->data_.callback)(types::dispatch_error_tag{},
|
||||
std::move(exception));
|
||||
})
|
||||
.done();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void operator()(async_traverse_complete_tag, T&& /*pack*/) {
|
||||
return remapping::finalize_data(std::move(data_.callback),
|
||||
std::move(data_.box));
|
||||
}
|
||||
};
|
||||
} // namespace seq
|
||||
|
||||
/// Finalizes the seq logic of a given composition
|
||||
template <>
|
||||
struct composition_finalizer<composition_strategy_seq_tag> {
|
||||
template <typename Composition>
|
||||
static constexpr auto hint() {
|
||||
// The result is the same as in the all composition
|
||||
using all_finalizer = composition_finalizer<composition_strategy_all_tag>;
|
||||
return all_finalizer::hint<Composition>();
|
||||
}
|
||||
|
||||
/// Finalizes the all logic of a given composition
|
||||
template <typename Composition>
|
||||
static auto finalize(Composition&& composition) {
|
||||
return [composition = std::forward<Composition>(composition)] // ...
|
||||
(auto&& callback) mutable {
|
||||
|
||||
auto boxed = remapping::box_continuables(std::move(composition));
|
||||
|
||||
// The data from which the visitor is constructed in-place
|
||||
using data_t =
|
||||
seq::sequential_dispatch_data<std::decay_t<decltype(callback)>,
|
||||
std::decay_t<decltype(boxed)>>;
|
||||
|
||||
// The visitor type
|
||||
using visitor_t = seq::sequential_dispatch_visitor<data_t>;
|
||||
|
||||
traverse_pack_async(
|
||||
async_traverse_in_place_tag<visitor_t>{},
|
||||
data_t{std::forward<decltype(callback)>(callback), std::move(boxed)});
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace composition
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_COMPOSITION_SEQ_HPP_INCLUDED
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,77 +21,102 @@
|
||||
|
||||
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
|
||||
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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_COMPOSITION_REMAPPING_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_COMPOSITION_REMAPPING_HPP_INCLUDED
|
||||
#ifndef CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/continuable-result.hpp>
|
||||
#include <continuable/continuable-traverse.hpp>
|
||||
#include <continuable/detail/base.hpp>
|
||||
#include <continuable/detail/container-category.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace composition {
|
||||
namespace connection {
|
||||
/// This namespace provides utilities for performing compound
|
||||
/// connections between deeply nested continuables and values.
|
||||
///
|
||||
/// We create the result pack from the provides values and
|
||||
/// the async values if those are default constructible,
|
||||
/// otherwise use a lazy initialization wrapper and unwrap
|
||||
/// the whole pack when the composition is finished.
|
||||
/// the whole pack when the connection is finished.
|
||||
/// - value -> value
|
||||
/// - single async value -> single value
|
||||
/// - multiple async value -> tuple of async values.
|
||||
namespace remapping {
|
||||
namespace aggregated {
|
||||
|
||||
/// Guards a type to be default constructible,
|
||||
/// and wraps it into an optional type if it isn't default constructible.
|
||||
template <typename T>
|
||||
using lazy_value_t = std::conditional_t<std::is_default_constructible<T>::value,
|
||||
T, result<T>>;
|
||||
|
||||
template <typename T>
|
||||
decltype(auto) unpack_lazy(std::true_type /*is_default_constructible*/,
|
||||
T&& value) {
|
||||
return std::forward<T>(value);
|
||||
}
|
||||
template <typename T>
|
||||
T&& unpack_lazy(std::false_type /*is_default_constructible*/,
|
||||
result<T>&& value) {
|
||||
assert(value.is_value() &&
|
||||
"The connection was finalized before all values were present!");
|
||||
|
||||
return std::move(value).get_value();
|
||||
}
|
||||
|
||||
template <typename Continuable>
|
||||
class continuable_box;
|
||||
template <typename Data>
|
||||
class continuable_box<continuable_base<Data, hints::signature_hint_tag<>>> {
|
||||
class continuable_box<continuable_base<Data, identity<>>> {
|
||||
|
||||
continuable_base<Data, hints::signature_hint_tag<>> continuable_;
|
||||
continuable_base<Data, identity<>> continuable_;
|
||||
|
||||
public:
|
||||
explicit continuable_box(
|
||||
continuable_base<Data, hints::signature_hint_tag<>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {
|
||||
explicit continuable_box(continuable_base<Data, identity<>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
}
|
||||
|
||||
continuable_base<Data, hints::signature_hint_tag<>>&& fetch() {
|
||||
auto&& fetch() {
|
||||
return std::move(continuable_);
|
||||
}
|
||||
|
||||
void assign() {
|
||||
}
|
||||
void assign() {}
|
||||
|
||||
auto unbox() && {
|
||||
return spread_this();
|
||||
}
|
||||
};
|
||||
template <typename Data, typename First>
|
||||
class continuable_box<
|
||||
continuable_base<Data, hints::signature_hint_tag<First>>> {
|
||||
|
||||
continuable_base<Data, hints::signature_hint_tag<First>> continuable_;
|
||||
First first_;
|
||||
template <typename Data, typename First>
|
||||
class continuable_box<continuable_base<Data, identity<First>>> {
|
||||
|
||||
continuable_base<Data, identity<First>> continuable_;
|
||||
lazy_value_t<First> first_;
|
||||
|
||||
public:
|
||||
explicit continuable_box(
|
||||
continuable_base<Data, hints::signature_hint_tag<First>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {
|
||||
continuable_base<Data, identity<First>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
}
|
||||
|
||||
continuable_base<Data, hints::signature_hint_tag<First>>&& fetch() {
|
||||
auto&& fetch() {
|
||||
return std::move(continuable_);
|
||||
}
|
||||
|
||||
@ -100,27 +125,27 @@ public:
|
||||
}
|
||||
|
||||
auto unbox() && {
|
||||
return std::move(first_);
|
||||
return unpack_lazy(std::is_default_constructible<First>{},
|
||||
std::move(first_));
|
||||
}
|
||||
};
|
||||
template <typename Data, typename First, typename Second, typename... Rest>
|
||||
class continuable_box<
|
||||
continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>>> {
|
||||
continuable_base<Data, identity<First, Second, Rest...>>> {
|
||||
|
||||
continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>>
|
||||
continuable_;
|
||||
std::tuple<First, Second, Rest...> args_;
|
||||
continuable_base<Data, identity<First, Second, Rest...>> continuable_;
|
||||
lazy_value_t<std::tuple<First, Second, Rest...>> args_;
|
||||
|
||||
public:
|
||||
explicit continuable_box(
|
||||
continuable_base<Data,
|
||||
hints::signature_hint_tag<First, Second, Rest...>>&&
|
||||
continuable)
|
||||
: continuable_(std::move(continuable)) {
|
||||
continuable_base<Data, identity<First, Second, Rest...>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
}
|
||||
|
||||
continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>>&&
|
||||
fetch() {
|
||||
auto&& fetch() {
|
||||
return std::move(continuable_);
|
||||
}
|
||||
|
||||
@ -130,9 +155,13 @@ public:
|
||||
}
|
||||
|
||||
auto unbox() && {
|
||||
return traits::unpack(std::move(args_), [](auto&&... args) {
|
||||
return traits::unpack(
|
||||
[](auto&&... args) {
|
||||
return spread_this(std::forward<decltype(args)>(args)...);
|
||||
});
|
||||
},
|
||||
unpack_lazy(
|
||||
std::is_default_constructible<std::tuple<First, Second, Rest...>>{},
|
||||
std::move(args_)));
|
||||
}
|
||||
};
|
||||
|
||||
@ -181,29 +210,41 @@ constexpr auto unbox_continuables(Args&&... args) {
|
||||
|
||||
namespace detail {
|
||||
template <typename Callback, typename Data>
|
||||
void finalize_impl(traits::identity<void>, Callback&& callback, Data&&) {
|
||||
std::forward<Callback>(callback)();
|
||||
constexpr auto finalize_impl(identity<void>, Callback&& callback, Data&&) {
|
||||
return std::forward<Callback>(callback)();
|
||||
}
|
||||
template <typename... Args, typename Callback, typename Data>
|
||||
void finalize_impl(traits::identity<std::tuple<Args...>>, Callback&& callback,
|
||||
constexpr auto finalize_impl(identity<std::tuple<Args...>>, Callback&& callback,
|
||||
Data&& data) {
|
||||
// Call the final callback with the cleaned result
|
||||
traits::unpack(unbox_continuables(std::forward<Data>(data)),
|
||||
std::forward<Callback>(callback));
|
||||
return traits::unpack(std::forward<Callback>(callback),
|
||||
unbox_continuables(std::forward<Data>(data)));
|
||||
}
|
||||
|
||||
struct hint_mapper {
|
||||
template <typename... T>
|
||||
constexpr auto operator()(T...) -> identity<T...> {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Callback, typename Data>
|
||||
void finalize_data(Callback&& callback, Data&& data) {
|
||||
constexpr auto finalize_data(Callback&& callback, Data&& data) {
|
||||
using result_t = decltype(unbox_continuables(std::forward<Data>(data)));
|
||||
// Guard the final result against void
|
||||
return detail::finalize_impl(traits::identity<std::decay_t<result_t>>{},
|
||||
return detail::finalize_impl(identity<std::decay_t<result_t>>{},
|
||||
std::forward<Callback>(callback),
|
||||
std::forward<Data>(data));
|
||||
}
|
||||
} // namespace remapping
|
||||
} // namespace composition
|
||||
|
||||
template <typename Data>
|
||||
constexpr auto hint_of_data() {
|
||||
return decltype(finalize_data(detail::hint_mapper{}, std::declval<Data>())){};
|
||||
}
|
||||
} // namespace aggregated
|
||||
} // namespace connection
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_COMPOSITION_REMAPPING_HPP_INCLUDED
|
||||
#endif // CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED
|
||||
198
include/continuable/detail/connection/connection-all.hpp
Normal file
198
include/continuable/detail/connection/connection-all.hpp
Normal file
@ -0,0 +1,198 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_CONNECTION_ALL_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_CONNECTION_ALL_HPP_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/connection/connection-aggregated.hpp>
|
||||
#include <continuable/detail/connection/connection.hpp>
|
||||
#include <continuable/detail/core/annotation.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace connection {
|
||||
namespace all {
|
||||
/// Caches the partial results and invokes the callback when all results
|
||||
/// are arrived. This class is thread safe.
|
||||
template <typename Callback, typename Result>
|
||||
class result_submitter
|
||||
: public std::enable_shared_from_this<result_submitter<Callback, Result>>,
|
||||
public util::non_movable {
|
||||
|
||||
Callback callback_;
|
||||
Result result_;
|
||||
|
||||
std::atomic<std::size_t> left_;
|
||||
std::once_flag flag_;
|
||||
|
||||
// Invokes the callback with the cached result
|
||||
void invoke() {
|
||||
assert((left_ == 0U) && "Expected that the submitter is finished!");
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
|
||||
// Call the final callback with the cleaned result
|
||||
std::call_once(flag_, [&] {
|
||||
aggregated::finalize_data(std::move(callback_), std::move(result_));
|
||||
});
|
||||
}
|
||||
|
||||
// Completes one result
|
||||
void complete_one() {
|
||||
assert((left_ > 0U) && "Expected that the submitter isn't finished!");
|
||||
|
||||
auto const current = --left_;
|
||||
if (!current) {
|
||||
invoke();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Box>
|
||||
struct partial_all_callback {
|
||||
Box* box;
|
||||
std::shared_ptr<result_submitter> me;
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(Args&&... args) && {
|
||||
|
||||
// Assign the result to the target
|
||||
box->assign(std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Complete one result
|
||||
me->complete_one();
|
||||
}
|
||||
|
||||
template <typename... PartialArgs>
|
||||
void operator()(exception_arg_t tag, exception_t exception) && {
|
||||
// We never complete the connection, but we forward the first error
|
||||
// which was raised.
|
||||
std::call_once(me->flag_, std::move(me->callback_), tag,
|
||||
std::move(exception));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
explicit result_submitter(Callback callback, Result&& result)
|
||||
: callback_(std::move(callback)), result_(std::move(result)), left_(1) {
|
||||
}
|
||||
|
||||
/// Creates a submitter which submits it's result into the storage
|
||||
template <typename Box>
|
||||
auto create_callback(Box* box) {
|
||||
left_.fetch_add(1, std::memory_order_seq_cst);
|
||||
return partial_all_callback<std::decay_t<Box>>{box,
|
||||
this->shared_from_this()};
|
||||
}
|
||||
|
||||
/// Initially the counter is created with an initial count of 1 in order
|
||||
/// to prevent that the connection is finished before all callbacks
|
||||
/// were registered.
|
||||
void accept() {
|
||||
complete_one();
|
||||
}
|
||||
|
||||
constexpr auto& head() noexcept {
|
||||
return result_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Submitter>
|
||||
struct continuable_dispatcher {
|
||||
std::shared_ptr<Submitter>& submitter;
|
||||
|
||||
template <typename Box, std::enable_if_t<aggregated::is_continuable_box<
|
||||
std::decay_t<Box>>::value>* = nullptr>
|
||||
void operator()(Box&& box) const {
|
||||
// Retrieve a callback from the submitter and attach it to the continuable
|
||||
box.fetch().next(submitter->create_callback(std::addressof(box))).done();
|
||||
}
|
||||
};
|
||||
} // namespace all
|
||||
|
||||
struct connection_strategy_all_tag {};
|
||||
template <>
|
||||
struct is_connection_strategy<connection_strategy_all_tag> // ...
|
||||
: std::true_type {};
|
||||
|
||||
/// Finalizes the all logic of a given connection
|
||||
template <>
|
||||
struct connection_finalizer<connection_strategy_all_tag> {
|
||||
/// Finalizes the all logic of a given connection
|
||||
template <typename Connection>
|
||||
static auto finalize(Connection&& connection, util::ownership ownership) {
|
||||
// Create the target result from the connection
|
||||
auto res =
|
||||
aggregated::box_continuables(std::forward<Connection>(connection));
|
||||
|
||||
auto signature = aggregated::hint_of_data<decltype(res)>();
|
||||
|
||||
return base::attorney::create_from(
|
||||
[res = std::move(res)](auto&& callback) mutable {
|
||||
using submitter_t =
|
||||
all::result_submitter<std::decay_t<decltype(callback)>,
|
||||
std::decay_t<decltype(res)>>;
|
||||
|
||||
// Create the shared state which holds the result
|
||||
// and the final callback
|
||||
auto state = std::make_shared<submitter_t>(
|
||||
std::forward<decltype(callback)>(callback), std::move(res));
|
||||
|
||||
// Dispatch the continuables and store its partial result
|
||||
// in the whole result
|
||||
traverse_pack(all::continuable_dispatcher<submitter_t>{state},
|
||||
state->head());
|
||||
|
||||
// Finalize the connection if all results arrived in-place
|
||||
state->accept();
|
||||
},
|
||||
signature, std::move(ownership));
|
||||
}
|
||||
};
|
||||
} // namespace connection
|
||||
|
||||
/// Specialization for a connection annotation
|
||||
template <>
|
||||
struct annotation_trait<connection::connection_strategy_all_tag>
|
||||
: connection::connection_annotation_trait<
|
||||
connection::connection_strategy_all_tag> {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_CONNECTION_ALL_HPP_INCLUDED
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,15 +21,15 @@
|
||||
|
||||
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
|
||||
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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED
|
||||
#ifndef CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@ -37,18 +37,18 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/continuable-promise-base.hpp>
|
||||
#include <continuable/continuable-traverse.hpp>
|
||||
#include <continuable/detail/base.hpp>
|
||||
#include <continuable/detail/container-category.hpp>
|
||||
#include <continuable/detail/hints.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/detail/core/annotation.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/traversal/container-category.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace composition {
|
||||
namespace connection {
|
||||
namespace any {
|
||||
/// Invokes the callback with the first arriving result
|
||||
template <typename T>
|
||||
@ -88,30 +88,30 @@ private:
|
||||
|
||||
struct result_deducer {
|
||||
template <typename T>
|
||||
static auto deduce_one(std::false_type, traits::identity<T>) {
|
||||
static auto deduce_one(std::false_type, identity<T>) {
|
||||
static_assert(traits::fail<T>::value,
|
||||
"Non continuable types except tuple like and homogeneous "
|
||||
"containers aren't allowed inside an any expression!");
|
||||
}
|
||||
template <typename T>
|
||||
static auto deduce_one(std::true_type, traits::identity<T> id) {
|
||||
return hints::hint_of(id);
|
||||
static auto deduce_one(std::true_type, identity<T> id) {
|
||||
return base::annotation_of(id);
|
||||
}
|
||||
template <typename T>
|
||||
static auto deduce(traversal::container_category_tag<false, false>,
|
||||
traits::identity<T> id) {
|
||||
identity<T> id) {
|
||||
return deduce_one<T>(base::is_continuable<T>{}, id);
|
||||
}
|
||||
|
||||
/// Deduce a homogeneous container
|
||||
template <bool IsTupleLike, typename T>
|
||||
static auto deduce(traversal::container_category_tag<true, IsTupleLike>,
|
||||
traits::identity<T>) {
|
||||
identity<T>) {
|
||||
|
||||
// Deduce the containing type
|
||||
using element_t = std::decay_t<decltype(*std::declval<T>().begin())>;
|
||||
return deduce(traversal::container_category_of_t<element_t>{},
|
||||
traits::identity<element_t>{});
|
||||
identity<element_t>{});
|
||||
}
|
||||
|
||||
template <typename First, typename... T>
|
||||
@ -125,19 +125,18 @@ struct result_deducer {
|
||||
|
||||
template <std::size_t... I, typename T>
|
||||
static auto deduce_tuple_like(std::integer_sequence<std::size_t, I...>,
|
||||
traits::identity<T>) {
|
||||
identity<T>) {
|
||||
|
||||
return deduce_same_hints(deduce(
|
||||
traversal::container_category_of_t<
|
||||
std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{},
|
||||
traits::identity<
|
||||
std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{})...);
|
||||
identity<std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{})...);
|
||||
}
|
||||
|
||||
/// Traverse tuple like container
|
||||
template <typename T>
|
||||
static auto deduce(traversal::container_category_tag<false, true>,
|
||||
traits::identity<T> id) {
|
||||
identity<T> id) {
|
||||
|
||||
constexpr auto const size = std::tuple_size<T>::value;
|
||||
return deduce_tuple_like(std::make_index_sequence<size>{}, id);
|
||||
@ -151,7 +150,7 @@ struct continuable_dispatcher {
|
||||
template <typename Continuable,
|
||||
std::enable_if_t<base::is_continuable<
|
||||
std::decay_t<Continuable>>::value>* = nullptr>
|
||||
void operator()(Continuable&& continuable) const {
|
||||
void operator()(Continuable&& continuable) {
|
||||
// Retrieve a callback from the submitter and attach it to the continuable
|
||||
std::forward<Continuable>(continuable)
|
||||
.next(submitter->create_callback())
|
||||
@ -160,21 +159,23 @@ struct continuable_dispatcher {
|
||||
};
|
||||
} // namespace any
|
||||
|
||||
/// Finalizes the any logic of a given composition
|
||||
struct connection_strategy_any_tag {};
|
||||
template <>
|
||||
struct composition_finalizer<composition_strategy_any_tag> {
|
||||
template <typename Composition>
|
||||
static constexpr auto hint() {
|
||||
return decltype(any::result_deducer::deduce(
|
||||
traversal::container_category_of_t<std::decay_t<Composition>>{},
|
||||
traits::identity<std::decay_t<Composition>>{})){};
|
||||
}
|
||||
struct is_connection_strategy<connection_strategy_any_tag> // ...
|
||||
: std::true_type {};
|
||||
|
||||
template <typename Composition>
|
||||
static auto finalize(Composition&& composition) {
|
||||
return [composition = std::forward<Composition>(composition)](
|
||||
auto&& callback) mutable {
|
||||
/// Finalizes the any logic of a given connection
|
||||
template <>
|
||||
struct connection_finalizer<connection_strategy_any_tag> {
|
||||
template <typename Connection>
|
||||
static auto finalize(Connection&& connection, util::ownership ownership) {
|
||||
constexpr auto const signature = decltype(any::result_deducer::deduce(
|
||||
traversal::container_category_of_t<std::decay_t<Connection>>{},
|
||||
identity<std::decay_t<Connection>>{})){};
|
||||
|
||||
return base::attorney::create_from(
|
||||
[connection =
|
||||
std::forward<Connection>(connection)](auto&& callback) mutable {
|
||||
using submitter_t =
|
||||
any::any_result_submitter<std::decay_t<decltype(callback)>>;
|
||||
|
||||
@ -184,12 +185,20 @@ struct composition_finalizer<composition_strategy_any_tag> {
|
||||
std::forward<decltype(callback)>(callback));
|
||||
|
||||
traverse_pack(any::continuable_dispatcher<submitter_t>{submitter},
|
||||
std::move(composition));
|
||||
};
|
||||
std::move(connection));
|
||||
},
|
||||
signature, std::move(ownership));
|
||||
}
|
||||
};
|
||||
} // namespace composition
|
||||
} // namespace connection
|
||||
|
||||
/// Specialization for a connection annotation
|
||||
template <>
|
||||
struct annotation_trait<connection::connection_strategy_any_tag>
|
||||
: connection::connection_annotation_trait<
|
||||
connection::connection_strategy_any_tag> {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED
|
||||
#endif // CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED
|
||||
183
include/continuable/detail/connection/connection-seq.hpp
Normal file
183
include/continuable/detail/connection/connection-seq.hpp
Normal file
@ -0,0 +1,183 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_CONNECTION_SEQ_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_CONNECTION_SEQ_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/continuable-traverse-async.hpp>
|
||||
#include <continuable/detail/connection/connection-aggregated.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace connection {
|
||||
namespace seq {
|
||||
/// Connects the left and the right continuable to a sequence
|
||||
///
|
||||
/// \note This is implemented in an eager way because we would not gain
|
||||
/// any profit from chaining sequences lazily.
|
||||
template <typename Left, typename Right>
|
||||
auto sequential_connect(Left&& left, Right&& right) {
|
||||
left.freeze(right.is_frozen());
|
||||
right.freeze();
|
||||
|
||||
return std::forward<Left>(left).then(
|
||||
[right = std::forward<Right>(right)](auto&&... args) mutable {
|
||||
return std::move(right).then(
|
||||
[previous = std::make_tuple(std::forward<decltype(args)>(args)...)](
|
||||
auto&&... args) mutable {
|
||||
return std::tuple_cat(
|
||||
std::move(previous),
|
||||
std::make_tuple(std::forward<decltype(args)>(args)...));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Callback, typename Box>
|
||||
struct sequential_dispatch_data {
|
||||
Callback callback;
|
||||
Box box;
|
||||
};
|
||||
|
||||
template <typename Data>
|
||||
class sequential_dispatch_visitor
|
||||
: public std::enable_shared_from_this<sequential_dispatch_visitor<Data>>,
|
||||
public util::non_movable {
|
||||
|
||||
Data data_;
|
||||
|
||||
public:
|
||||
explicit sequential_dispatch_visitor(Data&& data) : data_(std::move(data)) {
|
||||
}
|
||||
|
||||
virtual ~sequential_dispatch_visitor() = default;
|
||||
|
||||
/// Returns the pack that should be traversed
|
||||
auto& head() {
|
||||
return data_.box;
|
||||
}
|
||||
|
||||
template <typename Box, std::enable_if_t<aggregated::is_continuable_box<
|
||||
std::decay_t<Box>>::value>* = nullptr>
|
||||
bool operator()(async_traverse_visit_tag, Box&& box) {
|
||||
if (base::attorney::is_ready(box.peek())) {
|
||||
// The result can be resolved directly
|
||||
traits::unpack(
|
||||
[&](auto&&... args) mutable {
|
||||
box.assign(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
base::attorney::query(box.fetch()));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Box, typename N>
|
||||
void operator()(async_traverse_detach_tag, Box&& box, N&& next) {
|
||||
box.fetch()
|
||||
.then([box = std::addressof(box),
|
||||
next = std::forward<N>(next)](auto&&... args) mutable {
|
||||
// Assign the result to the target
|
||||
box->assign(std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Continue the asynchronous sequential traversal
|
||||
next();
|
||||
})
|
||||
.fail([me = this->shared_from_this()](exception_t exception) {
|
||||
// Abort the traversal when an error occurred
|
||||
std::move(me->data_.callback)(exception_arg_t{},
|
||||
std::move(exception));
|
||||
})
|
||||
.done();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void operator()(async_traverse_complete_tag, T&& /*pack*/) {
|
||||
return aggregated::finalize_data(std::move(data_.callback),
|
||||
std::move(data_.box));
|
||||
}
|
||||
};
|
||||
} // namespace seq
|
||||
|
||||
struct connection_strategy_seq_tag {};
|
||||
template <>
|
||||
struct is_connection_strategy<connection_strategy_seq_tag> // ...
|
||||
: std::true_type {};
|
||||
|
||||
/// Finalizes the seq logic of a given connection
|
||||
template <>
|
||||
struct connection_finalizer<connection_strategy_seq_tag> {
|
||||
/// Finalizes the all logic of a given connection
|
||||
template <typename Connection>
|
||||
static auto finalize(Connection&& connection, util::ownership ownership) {
|
||||
|
||||
auto res =
|
||||
aggregated::box_continuables(std::forward<Connection>(connection));
|
||||
|
||||
auto signature = aggregated::hint_of_data<decltype(res)>();
|
||||
|
||||
return base::attorney::create_from(
|
||||
[res = std::move(res)](auto&& callback) mutable {
|
||||
// The data from which the visitor is constructed in-place
|
||||
using data_t =
|
||||
seq::sequential_dispatch_data<std::decay_t<decltype(callback)>,
|
||||
std::decay_t<decltype(res)>>;
|
||||
|
||||
// The visitor type
|
||||
using visitor_t = seq::sequential_dispatch_visitor<data_t>;
|
||||
|
||||
traverse_pack_async(async_traverse_in_place_tag<visitor_t>{},
|
||||
data_t{std::forward<decltype(callback)>(callback),
|
||||
std::move(res)});
|
||||
},
|
||||
signature, std::move(ownership));
|
||||
}
|
||||
};
|
||||
} // namespace connection
|
||||
|
||||
/// Specialization for a connection annotation
|
||||
template <>
|
||||
struct annotation_trait<connection::connection_strategy_seq_tag>
|
||||
: connection::connection_annotation_trait<
|
||||
connection::connection_strategy_seq_tag> {};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_CONNECTION_SEQ_HPP_INCLUDED
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,55 +21,41 @@
|
||||
|
||||
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
|
||||
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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED
|
||||
#ifndef CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/continuable-traverse.hpp>
|
||||
#include <continuable/detail/base.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
/// The namespace `composition` offers methods to chain continuations together
|
||||
/// The namespace `connection` offers methods to chain continuations together
|
||||
/// with `all`, `any` or `seq` logic.
|
||||
namespace composition {
|
||||
struct composition_strategy_all_tag {};
|
||||
struct composition_strategy_any_tag {};
|
||||
struct composition_strategy_seq_tag {};
|
||||
|
||||
namespace connection {
|
||||
template <typename T>
|
||||
struct is_composition_strategy // ...
|
||||
struct is_connection_strategy // ...
|
||||
: std::false_type {};
|
||||
template <>
|
||||
struct is_composition_strategy<composition_strategy_all_tag> // ...
|
||||
: std::true_type {};
|
||||
template <>
|
||||
struct is_composition_strategy<composition_strategy_any_tag> // ...
|
||||
: std::true_type {};
|
||||
template <>
|
||||
struct is_composition_strategy<composition_strategy_seq_tag> // ...
|
||||
: std::true_type {};
|
||||
|
||||
/// Adds the given continuation tuple to the left composition
|
||||
/// Adds the given continuation tuple to the left connection
|
||||
template <typename... LeftArgs, typename... RightArgs>
|
||||
auto chain_composition(std::tuple<LeftArgs...> leftPack,
|
||||
auto chain_connection(std::tuple<LeftArgs...> leftPack,
|
||||
std::tuple<RightArgs...> rightPack) {
|
||||
|
||||
return traits::merge(std::move(leftPack), std::move(rightPack));
|
||||
return std::tuple_cat(std::move(leftPack), std::move(rightPack));
|
||||
}
|
||||
|
||||
/// Normalizes a continuation to a tuple holding an arbitrary count of
|
||||
@ -80,7 +66,7 @@ auto chain_composition(std::tuple<LeftArgs...> leftPack,
|
||||
/// -> make a tuple containing the continuable as only element
|
||||
template <
|
||||
typename Strategy, typename Data, typename Annotation,
|
||||
std::enable_if_t<!is_composition_strategy<Annotation>::value>* = nullptr>
|
||||
std::enable_if_t<!is_connection_strategy<Annotation>::value>* = nullptr>
|
||||
auto normalize(Strategy /*strategy*/,
|
||||
continuable_base<Data, Annotation>&& continuation) {
|
||||
|
||||
@ -91,13 +77,13 @@ auto normalize(Strategy /*strategy*/,
|
||||
/// -> materialize it
|
||||
template <
|
||||
typename Strategy, typename Data, typename Annotation,
|
||||
std::enable_if_t<is_composition_strategy<Annotation>::value>* = nullptr>
|
||||
std::enable_if_t<is_connection_strategy<Annotation>::value>* = nullptr>
|
||||
auto normalize(Strategy /*strategy*/,
|
||||
continuable_base<Data, Annotation>&& continuation) {
|
||||
|
||||
// If the right continuation is a different strategy materialize it
|
||||
// in order to keep the precedence in cases where: `c1 && (c2 || c3)`.
|
||||
return std::make_tuple(base::attorney::materialize(std::move(continuation)));
|
||||
return std::make_tuple(std::move(continuation).finish());
|
||||
}
|
||||
/// - The continuable is inside the current strategy state:
|
||||
/// -> return the data of the tuple
|
||||
@ -106,7 +92,7 @@ auto normalize(Strategy /*strategy*/,
|
||||
continuable_base<Data, Strategy>&& continuation) {
|
||||
|
||||
// If we are in the given strategy we can just use the data of the continuable
|
||||
return base::attorney::consume_data(std::move(continuation));
|
||||
return base::attorney::consume(std::move(continuation));
|
||||
}
|
||||
|
||||
/// Entry function for connecting two continuables with a given strategy.
|
||||
@ -123,53 +109,39 @@ auto connect(Strategy strategy, continuable_base<LData, LAnnotation>&& left,
|
||||
|
||||
// Make the new data which consists of a tuple containing
|
||||
// all connected continuables.
|
||||
auto data = chain_composition(normalize(strategy, std::move(left)),
|
||||
auto data = chain_connection(normalize(strategy, std::move(left)),
|
||||
normalize(strategy, std::move(right)));
|
||||
|
||||
// Return a new continuable containing the tuple and holding
|
||||
// the current strategy as annotation.
|
||||
return base::attorney::create(std::move(data), strategy, ownership_);
|
||||
return base::attorney::create_from_raw(std::move(data), strategy, ownership_);
|
||||
}
|
||||
|
||||
/// All strategies should specialize this class in order to provide:
|
||||
/// - A finalize static method that creates the callable object which
|
||||
/// is invoked with the callback to call when the composition is finished.
|
||||
/// is invoked with the callback to call when the connection is finished.
|
||||
/// - A static method hint that returns the new signature hint.
|
||||
template <typename Strategy>
|
||||
struct composition_finalizer;
|
||||
struct connection_finalizer;
|
||||
|
||||
/// Finalizes the connection logic of a given composition
|
||||
template <typename Data, typename Strategy>
|
||||
auto finalize_composition(continuable_base<Data, Strategy>&& continuation) {
|
||||
using finalizer = composition_finalizer<Strategy>;
|
||||
template <typename Strategy>
|
||||
struct connection_annotation_trait {
|
||||
/// Finalizes the connection logic of a given connection
|
||||
template <typename Continuable>
|
||||
static auto finish(Continuable&& continuable) {
|
||||
using finalizer = connection_finalizer<Strategy>;
|
||||
|
||||
util::ownership ownership = base::attorney::ownership_of(continuation);
|
||||
auto composition = base::attorney::consume_data(std::move(continuation));
|
||||
|
||||
// Retrieve the new signature hint
|
||||
constexpr auto const signature =
|
||||
finalizer::template hint<decltype(composition)>();
|
||||
util::ownership ownership = base::attorney::ownership_of(continuable);
|
||||
auto connection =
|
||||
base::attorney::consume(std::forward<Continuable>(continuable));
|
||||
|
||||
// Return a new continuable which
|
||||
return base::attorney::create(finalizer::finalize(std::move(composition)),
|
||||
signature, std::move(ownership));
|
||||
}
|
||||
|
||||
/// A base class from which the continuable may inherit in order to
|
||||
/// provide a materializer method which will finalize an oustanding strategy.
|
||||
template <typename Continuable, typename = void>
|
||||
struct materializer {
|
||||
static constexpr auto&& apply(Continuable&& continuable) {
|
||||
return std::move(continuable);
|
||||
return finalizer::finalize(std::move(connection), std::move(ownership));
|
||||
}
|
||||
};
|
||||
template <typename Data, typename Strategy>
|
||||
struct materializer<
|
||||
continuable_base<Data, Strategy>,
|
||||
std::enable_if_t<is_composition_strategy<Strategy>::value>> {
|
||||
|
||||
static constexpr auto apply(continuable_base<Data, Strategy>&& continuable) {
|
||||
return finalize_composition(std::move(continuable));
|
||||
template <typename Continuable>
|
||||
static bool is_ready(Continuable const& /*continuable*/) noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@ -199,13 +171,13 @@ public:
|
||||
|
||||
// Materialize every continuable
|
||||
// TODO Actually we would just need to consume the data here
|
||||
return base::attorney::materialize(std::forward<Continuable>(continuable));
|
||||
return std::forward<Continuable>(continuable).finish();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Strategy, typename... Args>
|
||||
auto apply_composition(Strategy, Args&&... args) {
|
||||
using finalizer = composition_finalizer<Strategy>;
|
||||
auto apply_connection(Strategy, Args&&... args) {
|
||||
using finalizer = connection_finalizer<Strategy>;
|
||||
|
||||
// Freeze every continuable inside the given arguments,
|
||||
// and freeze the ownership if one of the continuables
|
||||
@ -213,19 +185,13 @@ auto apply_composition(Strategy, Args&&... args) {
|
||||
// Additionally test whether every continuable is acquired.
|
||||
// Also materialize every continuable.
|
||||
util::ownership ownership;
|
||||
auto composition = map_pack(prepare_continuables{ownership},
|
||||
auto connection = map_pack(prepare_continuables{ownership},
|
||||
std::make_tuple(std::forward<Args>(args)...));
|
||||
|
||||
// Retrieve the new signature hint
|
||||
constexpr auto const signature =
|
||||
finalizer::template hint<decltype(composition)>();
|
||||
|
||||
// Return a new continuable which
|
||||
return base::attorney::create(finalizer::finalize(std::move(composition)),
|
||||
signature, std::move(ownership));
|
||||
return finalizer::finalize(std::move(connection), std::move(ownership));
|
||||
}
|
||||
} // namespace composition
|
||||
} // namespace connection
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED
|
||||
#endif // CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,51 +21,33 @@
|
||||
|
||||
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
|
||||
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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED
|
||||
#ifndef CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace hints {
|
||||
/// Represents a present signature hint
|
||||
template <typename... Args>
|
||||
using signature_hint_tag = traits::identity<Args...>;
|
||||
|
||||
/// Returns the signature hint of the given continuable
|
||||
template <typename Data, typename... Args>
|
||||
constexpr auto
|
||||
hint_of(traits::identity<continuable_base<Data, signature_hint_tag<Args...>>>) {
|
||||
return hints::signature_hint_tag<Args...>{};
|
||||
}
|
||||
|
||||
/// Extracts the signature we pass to the internal continuable
|
||||
/// from an argument pack as specified by make_continuable.
|
||||
///
|
||||
/// This is the overload taking an arbitrary amount of args
|
||||
template <typename... HintArgs>
|
||||
constexpr auto extract(traits::identity<HintArgs...> hint) {
|
||||
return hint;
|
||||
}
|
||||
/// \copybrief extract
|
||||
///
|
||||
/// This is the overload taking a void arg.
|
||||
constexpr auto extract(traits::identity<void> /*hint*/) {
|
||||
return traits::identity<>{};
|
||||
}
|
||||
struct from_args : std::common_type<signature_arg_t<HintArgs...>> {};
|
||||
template <>
|
||||
struct from_args<void> : std::common_type<signature_arg_t<>> {};
|
||||
} // namespace hints
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED
|
||||
#endif // CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED
|
||||
1093
include/continuable/detail/core/base.hpp
Normal file
1093
include/continuable/detail/core/base.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -31,9 +31,10 @@
|
||||
#ifndef CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/utility/identity.hpp>
|
||||
|
||||
#ifndef CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
|
||||
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
@ -51,32 +52,37 @@ namespace detail {
|
||||
/// Contains types used globally across the library
|
||||
namespace types {
|
||||
#ifdef CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
|
||||
using error_type = CONTINUABLE_WITH_CUSTOM_ERROR_TYPE;
|
||||
using exception_t = CONTINUABLE_WITH_CUSTOM_ERROR_TYPE;
|
||||
#else // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
|
||||
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
/// Represents the error type when exceptions are enabled
|
||||
using error_type = std::exception_ptr;
|
||||
/// Represents the exception type when exceptions are enabled
|
||||
using exception_t = std::exception_ptr;
|
||||
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
/// Represents the error type when exceptions are disabled
|
||||
using error_type = std::error_condition;
|
||||
using exception_t = std::error_condition;
|
||||
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
#endif // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
|
||||
|
||||
/// A tag which is used to execute the continuation inside the current thread
|
||||
struct this_thread_executor_tag {};
|
||||
/// A tag which is used to continue with an error
|
||||
struct dispatch_error_tag {};
|
||||
|
||||
/// Marks a given callable object as transformation
|
||||
template <typename T>
|
||||
class transform : T {
|
||||
class plain_tag {
|
||||
T value_;
|
||||
|
||||
public:
|
||||
explicit transform(T callable) : T(std::move(callable)) {
|
||||
template <typename O, std::enable_if_t<std::is_constructible<
|
||||
T, std::decay_t<O>>::value>* = nullptr>
|
||||
/* implicit */ plain_tag(O&& value) : value_(std::forward<O>(value)) {
|
||||
}
|
||||
explicit plain_tag(T value) : value_(std::move(value)) {
|
||||
}
|
||||
|
||||
using T::operator();
|
||||
T&& consume() && {
|
||||
return std::move(value_);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
@ -1,394 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/hints.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace util {
|
||||
namespace detail {
|
||||
enum class slot_t { empty, value, error };
|
||||
|
||||
template <typename T>
|
||||
using storage_of_t = //
|
||||
std::aligned_storage_t<
|
||||
(sizeof(types::error_type) > sizeof(T) ? sizeof(types::error_type)
|
||||
: sizeof(T)),
|
||||
(alignof(types::error_type) > alignof(T) ? alignof(types::error_type)
|
||||
: alignof(T))>;
|
||||
|
||||
template <typename T>
|
||||
struct expected_base {
|
||||
storage_of_t<T> storage_;
|
||||
slot_t slot_;
|
||||
|
||||
constexpr expected_base() : slot_(slot_t::empty) {
|
||||
}
|
||||
|
||||
expected_base(expected_base const&) noexcept {
|
||||
}
|
||||
expected_base(expected_base&&) noexcept {
|
||||
}
|
||||
expected_base& operator=(expected_base const&) {
|
||||
return *this;
|
||||
}
|
||||
expected_base& operator=(expected_base&&) {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
struct expected_move_base {
|
||||
constexpr expected_move_base() = default;
|
||||
|
||||
expected_move_base(expected_move_base const&) = default;
|
||||
explicit expected_move_base(expected_move_base&& right) {
|
||||
Base& me = *static_cast<Base*>(this);
|
||||
Base& other = *static_cast<Base*>(&right);
|
||||
assert(!other.is_empty());
|
||||
|
||||
#ifndef _NDEBUG
|
||||
me.set(slot_t::empty);
|
||||
#endif
|
||||
|
||||
other.visit([&](auto&& value) {
|
||||
// ...
|
||||
me.init(std::move(value));
|
||||
});
|
||||
me.set(other.get());
|
||||
other.destroy();
|
||||
}
|
||||
expected_move_base& operator=(expected_move_base const&) = default;
|
||||
expected_move_base& operator=(expected_move_base&& right) {
|
||||
Base& me = *static_cast<Base*>(this);
|
||||
Base& other = *static_cast<Base*>(&right);
|
||||
assert(!other.is_empty());
|
||||
|
||||
me.weak_destroy();
|
||||
|
||||
other.visit([&](auto&& value) {
|
||||
// ...
|
||||
me.init(std::move(value));
|
||||
});
|
||||
me.set(other.get());
|
||||
other.destroy();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
template <typename Base, bool IsCopyable /*= true*/>
|
||||
struct expected_copy_base : expected_move_base<Base> {
|
||||
constexpr expected_copy_base() = default;
|
||||
|
||||
expected_copy_base(expected_copy_base&&) = default;
|
||||
explicit expected_copy_base(expected_copy_base const& right)
|
||||
: expected_move_base<Base>()
|
||||
// TODO noexcept(Base::is_nothrow_move_constructible)
|
||||
{
|
||||
Base& me = *static_cast<Base*>(this);
|
||||
Base const& other = *static_cast<Base const*>(&right);
|
||||
assert(!other.is_empty());
|
||||
|
||||
#ifndef _NDEBUG
|
||||
me.set(slot_t::empty);
|
||||
#endif
|
||||
|
||||
other.visit([&](auto&& value) {
|
||||
// ...
|
||||
me.init(std::move(value));
|
||||
});
|
||||
me.set(other.get());
|
||||
}
|
||||
expected_copy_base& operator=(expected_copy_base&&) = default;
|
||||
expected_copy_base& operator=(expected_copy_base const& right)
|
||||
// TODO noexcept(Base::is_nothrow_move_constructible)
|
||||
{
|
||||
Base& me = *static_cast<Base*>(this);
|
||||
Base const& other = *static_cast<Base const*>(&right);
|
||||
assert(!other.is_empty());
|
||||
|
||||
me.weak_destroy();
|
||||
|
||||
other.visit([&](auto&& value) {
|
||||
// ...
|
||||
me.init(std::move(value));
|
||||
});
|
||||
me.set(other.get());
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
template <typename Base /*, bool IsCopyable = false*/>
|
||||
struct expected_copy_base<Base, false> : expected_move_base<Base> {
|
||||
constexpr expected_copy_base() = default;
|
||||
|
||||
expected_copy_base(expected_copy_base const&) = delete;
|
||||
explicit expected_copy_base(expected_copy_base&& right) = default;
|
||||
expected_copy_base& operator=(expected_copy_base const&) = delete;
|
||||
expected_copy_base& operator=(expected_copy_base&& right) = default;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// A class similar to the one in the expected proposal,
|
||||
/// however it is capable of carrying an exception_ptr if
|
||||
/// exceptions are used.
|
||||
template <typename T>
|
||||
class expected
|
||||
: detail::expected_copy_base<
|
||||
expected<T>, std::is_copy_constructible<types::error_type>::value &&
|
||||
std::is_copy_constructible<T>::value>,
|
||||
detail::expected_base<T> {
|
||||
|
||||
template <typename>
|
||||
friend class expected;
|
||||
template <typename>
|
||||
friend struct detail::expected_move_base;
|
||||
template <typename, bool>
|
||||
friend struct detail::expected_copy_base;
|
||||
|
||||
template <typename V>
|
||||
expected(V&& value, detail::slot_t const slot) {
|
||||
using type = std::decay_t<decltype(value)>;
|
||||
new (&this->storage_) type(std::forward<V>(value));
|
||||
set(slot);
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr expected() = default;
|
||||
expected(expected const&) = default;
|
||||
expected(expected&&) = default;
|
||||
expected& operator=(expected const&) = default;
|
||||
expected& operator=(expected&&) = default;
|
||||
|
||||
~expected() noexcept(
|
||||
std::is_nothrow_destructible<T>::value&&
|
||||
std::is_nothrow_destructible<types::error_type>::value) {
|
||||
weak_destroy();
|
||||
}
|
||||
|
||||
explicit expected(T value) //
|
||||
: expected(std::move(value), detail::slot_t::value) {
|
||||
}
|
||||
explicit expected(types::error_type error) //
|
||||
: expected(std::move(error), detail::slot_t::error) {
|
||||
}
|
||||
|
||||
expected& operator=(T value) {
|
||||
set_value(std::move(value));
|
||||
return *this;
|
||||
}
|
||||
expected& operator=(types::error_type error) {
|
||||
set_exception(std::move(error));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_value() const noexcept {
|
||||
assert(!is_empty());
|
||||
return is(detail::slot_t::value);
|
||||
}
|
||||
bool is_exception() const noexcept {
|
||||
assert(!is_empty());
|
||||
return is(detail::slot_t::error);
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const noexcept {
|
||||
return is_value();
|
||||
}
|
||||
|
||||
void set_value(T value) {
|
||||
weak_destroy();
|
||||
init(std::move(value));
|
||||
set(detail::slot_t::value);
|
||||
}
|
||||
void set_exception(types::error_type error) {
|
||||
weak_destroy();
|
||||
init(std::move(error));
|
||||
set(detail::slot_t::error);
|
||||
}
|
||||
|
||||
T& get_value() noexcept {
|
||||
assert(is_value());
|
||||
return cast<T>();
|
||||
}
|
||||
T const& get_value() const noexcept {
|
||||
assert(is_value());
|
||||
return cast<T>();
|
||||
}
|
||||
types::error_type& get_exception() noexcept {
|
||||
assert(is_exception());
|
||||
return cast<types::error_type>();
|
||||
}
|
||||
types::error_type const& get_exception() const noexcept {
|
||||
assert(is_exception());
|
||||
return cast<types::error_type>();
|
||||
}
|
||||
|
||||
T& operator*() noexcept {
|
||||
return get_value();
|
||||
}
|
||||
T const& operator*() const noexcept {
|
||||
return get_value();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename V>
|
||||
void visit(V&& visitor) {
|
||||
switch (this->slot_) {
|
||||
case detail::slot_t::value:
|
||||
return std::forward<V>(visitor)(cast<T>());
|
||||
case detail::slot_t::error:
|
||||
return std::forward<V>(visitor)(cast<types::error_type>());
|
||||
default:
|
||||
// We don't visit when there is no value
|
||||
break;
|
||||
}
|
||||
}
|
||||
template <typename V>
|
||||
void visit(V&& visitor) const {
|
||||
switch (this->slot_) {
|
||||
case detail::slot_t::value:
|
||||
return std::forward<V>(visitor)(cast<T>());
|
||||
case detail::slot_t::error:
|
||||
return std::forward<V>(visitor)(cast<types::error_type>());
|
||||
default:
|
||||
// We don't visit when there is no value
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_empty() const noexcept {
|
||||
return is(detail::slot_t::empty);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
V& cast() noexcept {
|
||||
assert(!is_empty());
|
||||
return *reinterpret_cast<V*>(&this->storage_);
|
||||
}
|
||||
template <typename V>
|
||||
V const& cast() const noexcept {
|
||||
assert(!is_empty());
|
||||
return *reinterpret_cast<V const*>(&this->storage_);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void init(V&& value) {
|
||||
assert(is_empty());
|
||||
using type = std::decay_t<decltype(value)>;
|
||||
new (&this->storage_) type(std::forward<V>(value));
|
||||
}
|
||||
void destroy() {
|
||||
weak_destroy();
|
||||
|
||||
#ifdef NDEBUG
|
||||
set(detail::slot_t::empty);
|
||||
#endif
|
||||
}
|
||||
void weak_destroy() {
|
||||
visit([&](auto&& value) {
|
||||
using type = std::decay_t<decltype(value)>;
|
||||
value.~type();
|
||||
});
|
||||
|
||||
#ifndef NDEBUG
|
||||
set(detail::slot_t::empty);
|
||||
#endif
|
||||
}
|
||||
detail::slot_t get() const noexcept {
|
||||
return this->slot_;
|
||||
}
|
||||
bool is(detail::slot_t const slot) const noexcept {
|
||||
return get() == slot;
|
||||
}
|
||||
void set(detail::slot_t const slot) {
|
||||
this->slot_ = slot;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct void_guard_tag {};
|
||||
|
||||
template <typename T>
|
||||
struct expected_result_trait;
|
||||
template <>
|
||||
struct expected_result_trait<traits::identity<>> {
|
||||
using expected_type = expected<void_guard_tag>;
|
||||
|
||||
static constexpr void_guard_tag wrap() noexcept {
|
||||
return {};
|
||||
}
|
||||
static void unwrap(expected_type&& e) {
|
||||
assert(e.is_value());
|
||||
(void)e;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct expected_result_trait<traits::identity<T>> {
|
||||
using expected_type = expected<T>;
|
||||
|
||||
static auto wrap(T arg) {
|
||||
return std::move(arg);
|
||||
}
|
||||
static auto unwrap(expected_type&& e) {
|
||||
assert(e.is_value());
|
||||
return std::move(e.get_value());
|
||||
}
|
||||
};
|
||||
template <typename First, typename Second, typename... Rest>
|
||||
struct expected_result_trait<traits::identity<First, Second, Rest...>> {
|
||||
using expected_type = expected<std::tuple<First, Second, Rest...>>;
|
||||
|
||||
static auto wrap(First first, Second second, Rest... rest) {
|
||||
return std::make_tuple(std::move(first), std::move(second),
|
||||
std::move(rest)...);
|
||||
}
|
||||
static auto unwrap(expected_type&& e) {
|
||||
assert(e.is_value());
|
||||
return std::move(e.get_value());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Continuable>
|
||||
using expected_result_trait_t = detail::expected_result_trait<decltype(
|
||||
hints::hint_of(traits::identify<Continuable>{}))>;
|
||||
} // namespace util
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED
|
||||
242
include/continuable/detail/external/asio.hpp
vendored
Normal file
242
include/continuable/detail/external/asio.hpp
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
|
||||
#if defined(ASIO_STANDALONE)
|
||||
# include <asio/async_result.hpp>
|
||||
# include <asio/error.hpp>
|
||||
# include <asio/error_code.hpp>
|
||||
# include <asio/version.hpp>
|
||||
|
||||
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
# include <asio/system_error.hpp>
|
||||
# endif
|
||||
|
||||
# if (ASIO_VERSION < 101300) // 1.13.0
|
||||
# define CTI_DETAIL_ASIO_HAS_NO_INTEGRATION
|
||||
# elif (ASIO_VERSION < 101600) // 1.16.0 (boost 1.72 baseline)
|
||||
# define CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION
|
||||
# endif
|
||||
|
||||
# define CTI_DETAIL_ASIO_NAMESPACE_BEGIN namespace asio {
|
||||
# define CTI_DETAIL_ASIO_NAMESPACE_END }
|
||||
#else
|
||||
# include <boost/asio/async_result.hpp>
|
||||
# include <boost/asio/error.hpp>
|
||||
# include <boost/system/error_code.hpp>
|
||||
# include <boost/version.hpp>
|
||||
|
||||
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
# include <boost/system/system_error.hpp>
|
||||
# endif
|
||||
|
||||
# if (BOOST_VERSION < 107000) // 1.70
|
||||
# define CTI_DETAIL_ASIO_HAS_NO_INTEGRATION
|
||||
# elif (BOOST_VERSION < 107200) // 1.72
|
||||
# define CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION
|
||||
# endif
|
||||
|
||||
# define CTI_DETAIL_ASIO_NAMESPACE_BEGIN \
|
||||
namespace boost { \
|
||||
namespace asio {
|
||||
# define CTI_DETAIL_ASIO_NAMESPACE_END \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CTI_DETAIL_ASIO_HAS_NO_INTEGRATION)
|
||||
# error "First-class ASIO support for continuable requires the form of "\
|
||||
"`async_result` with an `initiate` static member function, which was added " \
|
||||
"in standalone ASIO 1.13.0 and Boost ASIO 1.70. Older versions can be " \
|
||||
"integrated manually with `cti::promisify`."
|
||||
#endif
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
# include <exception>
|
||||
#endif
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace asio {
|
||||
|
||||
#if defined(ASIO_STANDALONE)
|
||||
using error_code_t = ::asio::error_code;
|
||||
using basic_errors_t = ::asio::error::basic_errors;
|
||||
|
||||
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
using system_error_t = ::asio::system_error;
|
||||
# endif
|
||||
#else
|
||||
using error_code_t = ::boost::system::error_code;
|
||||
using basic_errors_t = ::boost::asio::error::basic_errors;
|
||||
|
||||
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
using system_error_t = ::boost::system::system_error;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
template <typename Promise, typename Token>
|
||||
class promise_resolver {
|
||||
public:
|
||||
explicit promise_resolver(Promise promise, Token token)
|
||||
: promise_(std::move(promise))
|
||||
, token_(std::move(token)) {}
|
||||
|
||||
template <typename... T>
|
||||
void operator()(T&&... args) noexcept {
|
||||
promise_.set_value(std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
void operator()(error_code_t e, T&&... args) noexcept {
|
||||
if (e) {
|
||||
if (!token_.is_ignored(e)) {
|
||||
if (token_.is_cancellation(e)) {
|
||||
promise_.set_canceled();
|
||||
return;
|
||||
} else {
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
promise_.set_exception(
|
||||
std::make_exception_ptr(system_error_t(std::move(e))));
|
||||
#else
|
||||
promise_.set_exception(exception_t(e.value(), e.category()));
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
promise_.set_value(std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Promise promise_;
|
||||
Token token_;
|
||||
};
|
||||
|
||||
// Binds `promise` to the first argument of a continuable resolver, giving it
|
||||
// the signature of an ASIO handler.
|
||||
template <typename Promise, typename Token>
|
||||
auto promise_resolver_handler(Promise&& promise, Token&& token) noexcept {
|
||||
return promise_resolver<std::decay_t<Promise>, std::decay_t<Token>>(
|
||||
std::forward<Promise>(promise), std::forward<Token>(token));
|
||||
}
|
||||
|
||||
// Helper struct wrapping a call to `cti::make_continuable` and, if needed,
|
||||
// providing an erased, explicit `return_type` for `async_result`.
|
||||
template <typename Signature>
|
||||
struct initiate_make_continuable;
|
||||
|
||||
template <typename... Args>
|
||||
struct initiate_make_continuable<void(Args...)> {
|
||||
#if defined(CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION)
|
||||
using erased_return_type = continuable<Args...>;
|
||||
#endif
|
||||
|
||||
template <typename Continuation>
|
||||
auto operator()(Continuation&& continuation) {
|
||||
return base::attorney::create_from(std::forward<Continuation>(continuation),
|
||||
identity<Args...>{}, util::ownership{});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
struct initiate_make_continuable<void(error_code_t, Args...)> {
|
||||
#if defined(CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION)
|
||||
using erased_return_type = continuable<Args...>;
|
||||
#endif
|
||||
|
||||
template <typename Continuation>
|
||||
auto operator()(Continuation&& continuation) {
|
||||
return base::attorney::create_from(std::forward<Continuation>(continuation),
|
||||
identity<Args...>{}, util::ownership{});
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
struct initiate_make_continuable<void(error_code_t const&, Args...)>
|
||||
: initiate_make_continuable<void(error_code_t, Args...)> {};
|
||||
|
||||
struct map_default {
|
||||
constexpr map_default() noexcept {}
|
||||
|
||||
bool is_cancellation(error_code_t const& ec) const noexcept {
|
||||
// Continuable uses a default constructed exception type to signal
|
||||
// cancellation to the followed asynchronous control flow.
|
||||
return ec == basic_errors_t::operation_aborted;
|
||||
}
|
||||
bool is_ignored(error_code_t const& /*ec*/) const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct map_none {
|
||||
constexpr map_none() noexcept {}
|
||||
|
||||
bool is_cancellation(error_code_t const& /*ec*/) const noexcept {
|
||||
return false;
|
||||
}
|
||||
bool is_ignored(error_code_t const& /*ec*/) const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t Size>
|
||||
class map_ignore {
|
||||
public:
|
||||
map_ignore(std::array<basic_errors_t, Size> ignored) noexcept
|
||||
: ignored_(ignored) {}
|
||||
|
||||
bool is_cancellation(error_code_t const& ec) const noexcept {
|
||||
return ec == basic_errors_t::operation_aborted;
|
||||
}
|
||||
bool is_ignored(error_code_t const& ec) const noexcept {
|
||||
for (basic_errors_t ignored : ignored_) {
|
||||
if (ec == ignored) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<basic_errors_t, Size> ignored_;
|
||||
};
|
||||
} // namespace asio
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
||||
@ -1,12 +1,13 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -20,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -32,19 +33,19 @@
|
||||
|
||||
// Defines CONTINUABLE_WITH_NO_EXCEPTIONS when exception support is disabled
|
||||
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
#if defined(_MSC_VER)
|
||||
#if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0)
|
||||
#define CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
#endif
|
||||
#elif defined(__clang__)
|
||||
#if !(__EXCEPTIONS && __has_feature(cxx_exceptions))
|
||||
#define CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
#if !__EXCEPTIONS
|
||||
#define CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
#endif
|
||||
#endif
|
||||
# if defined(_MSC_VER)
|
||||
# if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0)
|
||||
# define CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
# endif
|
||||
# elif defined(__clang__)
|
||||
# if !(__EXCEPTIONS && __has_feature(cxx_exceptions))
|
||||
# define CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
# endif
|
||||
# elif defined(__GNUC__)
|
||||
# if !__EXCEPTIONS
|
||||
# define CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
# endif
|
||||
# endif
|
||||
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
|
||||
// clang-format off
|
||||
@ -54,8 +55,11 @@
|
||||
#define CONTINUABLE_HAS_CXX17_CONSTEXPR_IF
|
||||
#define CONTINUABLE_HAS_CXX17_DISJUNCTION
|
||||
#define CONTINUABLE_HAS_CXX17_CONJUNCTION
|
||||
#define CONTINUABLE_HAS_CXX17_VOID_T
|
||||
#else
|
||||
// Generic feature detection based on __has_feature
|
||||
// and other preprocessor definitions based on:
|
||||
// http://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
|
||||
#if defined(__has_feature)
|
||||
#if !defined(CONTINUABLE_HAS_CXX17_CONSTEXPR_IF) && \
|
||||
__has_feature(cxx_if_constexpr)
|
||||
@ -74,12 +78,49 @@
|
||||
(__cpp_lib_experimental_logical_traits >= 201511)
|
||||
#define CONTINUABLE_HAS_CXX17_CONJUNCTION
|
||||
#endif
|
||||
|
||||
#if !defined(CONTINUABLE_HAS_CXX17_VOID_T) && \
|
||||
defined(__cpp_lib_void_t) && \
|
||||
(__cpp_lib_void_t >= 201411)
|
||||
#define CONTINUABLE_HAS_CXX17_VOID_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Usually this is enabled by the CMake project
|
||||
#if !defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE) && \
|
||||
defined(__cpp_coroutines) && (__cpp_coroutines >= 201707)
|
||||
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
|
||||
// Automatically detects support for coroutines.
|
||||
// Parts of this detection mechanism were adapted from boost::asio,
|
||||
// with support added for experimental coroutines.
|
||||
#if !defined(CONTINUABLE_HAS_DISABLED_COROUTINE) \
|
||||
&& !defined(CONTINUABLE_HAS_COROUTINE)
|
||||
/// Define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE when
|
||||
/// CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE is defined.
|
||||
#if defined(CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE)
|
||||
#define CONTINUABLE_HAS_COROUTINE 1
|
||||
#elif defined(CONTINUABLE_WITH_COROUTINE)
|
||||
#define CONTINUABLE_HAS_COROUTINE 1
|
||||
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
|
||||
#elif defined(_MSC_VER) // Visual Studio
|
||||
#if (_MSC_VER >= 1928) && (_MSVC_LANG >= 201705)
|
||||
#define CONTINUABLE_HAS_COROUTINE 1
|
||||
#elif _MSC_FULL_VER >= 190023506
|
||||
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
|
||||
#define CONTINUABLE_HAS_COROUTINE 1
|
||||
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
|
||||
#endif // defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
|
||||
#endif // _MSC_FULL_VER >= 190023506
|
||||
#elif defined(__clang__) // Clang
|
||||
#if defined(__cpp_coroutines) && (__cpp_coroutines >= 201703L)
|
||||
#define CONTINUABLE_HAS_COROUTINE 1
|
||||
#if defined(_LIBCPP_EXPERIMENTAL_COROUTINE)
|
||||
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
|
||||
#endif
|
||||
#endif // defined(__cpp_coroutines) && (__cpp_coroutines >= 201703L)
|
||||
#elif defined(__GNUC__) // GCC
|
||||
#if (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
|
||||
#if __has_include(<coroutine>)
|
||||
#define CONTINUABLE_HAS_COROUTINE 1
|
||||
#endif // __has_include(<coroutine>)
|
||||
#endif // (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/// Define CONTINUABLE_HAS_EXCEPTIONS when exceptions are used
|
||||
@ -89,6 +130,18 @@
|
||||
#else
|
||||
#undef CONTINUABLE_HAS_EXCEPTIONS
|
||||
#endif
|
||||
|
||||
/// Define CONTINUABLE_HAS_IMMEDIATE_TYPES when either
|
||||
/// - CONTINUABLE_WITH_IMMEDIATE_TYPES is defined
|
||||
/// - Building in release mode (NDEBUG is defined)
|
||||
///
|
||||
/// Build error messages will become more readable in debug mode while
|
||||
/// we don't suffer any runtime penalty in release.
|
||||
#if defined(CONTINUABLE_WITH_IMMEDIATE_TYPES) || defined(NDEBUG)
|
||||
#define CONTINUABLE_HAS_IMMEDIATE_TYPES 1
|
||||
#else
|
||||
#undef CONTINUABLE_HAS_IMMEDIATE_TYPES
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED
|
||||
|
||||
80
include/continuable/detail/operations/async.hpp
Normal file
80
include/continuable/detail/operations/async.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_OPERATIONS_ASYNC_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_OPERATIONS_ASYNC_HPP_INCLUDED
|
||||
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/detail/core/annotation.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/utility/identity.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace operations {
|
||||
template <typename Callable, typename Executor, typename... Args>
|
||||
auto async(Callable&& callable, Executor&& executor, Args&&... args) {
|
||||
using result_t =
|
||||
decltype(util::invoke(std::forward<decltype(callable)>(callable),
|
||||
std::forward<decltype(args)>(args)...));
|
||||
|
||||
constexpr auto hint =
|
||||
decltype(base::decoration::invoker_of(identity<result_t>{}))::hint();
|
||||
|
||||
auto continuation = [callable = std::forward<decltype(callable)>(callable),
|
||||
executor = std::forward<decltype(executor)>(executor),
|
||||
args = std::make_tuple(std::forward<decltype(args)>(
|
||||
args)...)](auto&& promise) mutable {
|
||||
auto invoker = base::decoration::invoker_of(identity<result_t>{});
|
||||
|
||||
using promise_t = decltype(promise);
|
||||
|
||||
// Invoke the callback
|
||||
traits::unpack(
|
||||
[&](auto&&... args) mutable {
|
||||
// Invoke the promise through the dedicated invoker
|
||||
// and through the given executor
|
||||
base::on_executor(std::move(executor), std::move(invoker),
|
||||
std::move(callable),
|
||||
std::forward<promise_t>(promise),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
std::move(args));
|
||||
};
|
||||
|
||||
return base::attorney::create_from(std::move(continuation), //
|
||||
hint, util::ownership{});
|
||||
}
|
||||
} // namespace operations
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_OPERATIONS_ASYNC_HPP_INCLUDED
|
||||
180
include/continuable/detail/operations/loop.hpp
Normal file
180
include/continuable/detail/operations/loop.hpp
Normal file
@ -0,0 +1,180 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_OPERATIONS_LOOP_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_OPERATIONS_LOOP_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/continuable-result.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
#include <exception>
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct loop_trait {
|
||||
static_assert(!std::is_same<T, T>::value,
|
||||
"The callable passed to cti::loop must always return a "
|
||||
"cti::continuable_base which resolves to a cti::result.");
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
struct loop_trait<identity<result<Args...>>> {
|
||||
template <typename Callable>
|
||||
static auto make(Callable&& callable) {
|
||||
return make_continuable<Args...>(std::forward<Callable>(callable));
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct loop_trait<identity<result<>>> {
|
||||
template <typename Callable>
|
||||
static auto make(Callable&& callable) {
|
||||
return make_continuable<void>(std::forward<Callable>(callable));
|
||||
}
|
||||
};
|
||||
|
||||
namespace operations {
|
||||
template <typename Promise, typename Callable, typename ArgsTuple>
|
||||
class loop_frame : public std::enable_shared_from_this<
|
||||
loop_frame<Promise, Callable, ArgsTuple>> {
|
||||
Promise promise_;
|
||||
Callable callable_;
|
||||
ArgsTuple args_;
|
||||
|
||||
public:
|
||||
explicit loop_frame(Promise promise, Callable callable, ArgsTuple args)
|
||||
: promise_(std::move(promise)), callable_(std::move(callable)),
|
||||
args_(std::move(args)) {
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// MSVC can't evaluate this inside the lambda capture
|
||||
auto me = this->shared_from_this();
|
||||
|
||||
traits::unpack(
|
||||
[&](auto&&... args) mutable {
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
try {
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
util::invoke(callable_, std::forward<decltype(args)>(args)...)
|
||||
.next([me = std::move(me)](auto&&... args) {
|
||||
me->resolve(std::forward<decltype(args)>(args)...);
|
||||
});
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
} catch (...) {
|
||||
me->resolve(exception_arg_t{}, std::current_exception());
|
||||
}
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
},
|
||||
args_);
|
||||
}
|
||||
|
||||
template <typename Result>
|
||||
void resolve(Result&& result) {
|
||||
if (result.is_empty()) {
|
||||
loop();
|
||||
} else if (result.is_value()) {
|
||||
traits::unpack(std::move(promise_), std::forward<Result>(result));
|
||||
} else {
|
||||
assert(result.is_exception());
|
||||
std::move(promise_).set_exception(
|
||||
std::forward<Result>(result).get_exception());
|
||||
}
|
||||
}
|
||||
|
||||
void resolve(exception_arg_t, exception_t exception) {
|
||||
promise_.set_exception(std::move(exception));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Promise, typename Callable, typename ArgsTuple>
|
||||
auto make_loop_frame(Promise&& promise, Callable&& callable,
|
||||
ArgsTuple&& args_tuple) {
|
||||
using frame_t =
|
||||
loop_frame<traits::unrefcv_t<Promise>, traits::unrefcv_t<Callable>,
|
||||
traits::unrefcv_t<ArgsTuple>>;
|
||||
|
||||
return std::make_shared<frame_t>(std::forward<Promise>(promise),
|
||||
std::forward<Callable>(callable),
|
||||
std::forward<ArgsTuple>(args_tuple));
|
||||
}
|
||||
|
||||
template <typename Callable, typename... Args>
|
||||
auto loop(Callable&& callable, Args&&... args) {
|
||||
using invocation_result_t =
|
||||
decltype(util::invoke(callable, args...).finish());
|
||||
|
||||
auto constexpr hint = base::annotation_of(identify<invocation_result_t>{});
|
||||
|
||||
using trait_t = loop_trait<std::remove_const_t<decltype(hint)>>;
|
||||
|
||||
return trait_t::make([callable = std::forward<decltype(callable)>(callable),
|
||||
args = std::make_tuple(std::forward<decltype(args)>(
|
||||
args)...)](auto&& promise) mutable {
|
||||
// Do the actual looping
|
||||
auto frame = make_loop_frame(std::forward<decltype(promise)>(promise),
|
||||
std::move(callable), std::move(args));
|
||||
frame->loop();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Callable, typename Begin, typename End>
|
||||
auto make_range_looper(Callable&& callable, Begin&& begin, End&& end) {
|
||||
return [callable = std::forward<Callable>(callable),
|
||||
begin = std::forward<Begin>(begin),
|
||||
end = std::forward<End>(end)]() mutable {
|
||||
return util::invoke(callable, begin)
|
||||
.then([&begin, &end]() mutable -> plain_t<result<>> {
|
||||
// begin and end stays valid over the `then` here
|
||||
if (++begin != end) {
|
||||
return make_plain(result<>::empty());
|
||||
} else {
|
||||
return make_plain(make_result());
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
} // namespace operations
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_OPERATIONS_LOOP_HPP_INCLUDED
|
||||
118
include/continuable/detail/operations/split.hpp
Normal file
118
include/continuable/detail/operations/split.hpp
Normal file
@ -0,0 +1,118 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_OPERATIONS_SPLIT_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_OPERATIONS_SPLIT_HPP_INCLUDED
|
||||
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/continuable-traverse.hpp>
|
||||
#include <continuable/continuable-types.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace operations {
|
||||
template <typename T, bool Else, typename = void>
|
||||
struct operator_bool_or {
|
||||
template <typename O>
|
||||
static bool get(O&& /*obj*/) noexcept {
|
||||
return Else;
|
||||
}
|
||||
};
|
||||
template <typename T, bool Else>
|
||||
struct operator_bool_or<T, Else,
|
||||
traits::void_t<decltype(bool(std::declval<T&>()))>> {
|
||||
template <typename O>
|
||||
static bool get(O&& obj) noexcept {
|
||||
return bool(obj);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename First, typename... Promises>
|
||||
class split_promise {
|
||||
First first_;
|
||||
std::tuple<Promises...> promises_;
|
||||
|
||||
public:
|
||||
explicit split_promise(First first, Promises... promises)
|
||||
: first_(std::move(first)), promises_(std::move(promises)...) {
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(Args&&... args) && {
|
||||
traverse_pack(
|
||||
[&](auto&& promise) mutable -> void {
|
||||
using accessor =
|
||||
operator_bool_or<traits::unrefcv_t<decltype(promise)>, true>;
|
||||
if (accessor::get(promise)) {
|
||||
std::forward<decltype(promise)>(promise)(args...);
|
||||
}
|
||||
},
|
||||
std::move(promises_));
|
||||
|
||||
if (operator_bool_or<First, true>::get(first_)) {
|
||||
std::move(first_)(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void set_value(Args... args) noexcept {
|
||||
std::move (*this)(std::move(args)...);
|
||||
}
|
||||
|
||||
void set_exception(exception_t error) noexcept {
|
||||
std::move (*this)(exception_arg_t{}, std::move(error));
|
||||
}
|
||||
|
||||
void set_canceled() noexcept {
|
||||
std::move (*this)(exception_arg_t{}, exception_t{});
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
bool is_valid = operator_bool_or<First, true>::get(first_);
|
||||
traverse_pack(
|
||||
[&](auto&& promise) mutable -> void {
|
||||
using accessor =
|
||||
operator_bool_or<traits::unrefcv_t<decltype(promise)>, true>;
|
||||
if (!is_valid && accessor::get(promise)) {
|
||||
is_valid = true;
|
||||
}
|
||||
},
|
||||
promises_);
|
||||
return is_valid;
|
||||
}
|
||||
};
|
||||
} // namespace operations
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_OPERATIONS_SPLIT_HPP_INCLUDED
|
||||
273
include/continuable/detail/other/coroutines.hpp
Normal file
273
include/continuable/detail/other/coroutines.hpp
Normal file
@ -0,0 +1,273 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
// Exclude this header when coroutines are not available
|
||||
#ifndef CONTINUABLE_DETAIL_AWAITING_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_AWAITING_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/continuable-result.hpp>
|
||||
#include <continuable/detail/core/annotation.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
# include <exception>
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
|
||||
# include <experimental/coroutine>
|
||||
#elif defined(CONTINUABLE_HAS_COROUTINE)
|
||||
# include <coroutine>
|
||||
#endif
|
||||
|
||||
#if defined(CONTINUABLE_HAS_COROUTINE)
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace awaiting {
|
||||
/// We import the coroutine handle in our namespace
|
||||
# if defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
|
||||
using std::experimental::coroutine_handle;
|
||||
using std::experimental::suspend_never;
|
||||
# else
|
||||
using std::coroutine_handle;
|
||||
using std::suspend_never;
|
||||
# endif
|
||||
|
||||
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
class await_canceled_exception : public std::exception {
|
||||
public:
|
||||
await_canceled_exception() noexcept = default;
|
||||
|
||||
char const* what() const noexcept override {
|
||||
return "co_await canceled due to cancellation of the continuation";
|
||||
}
|
||||
};
|
||||
# endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
template <typename T>
|
||||
struct result_from_identity;
|
||||
template <typename... T>
|
||||
struct result_from_identity<identity<T...>> {
|
||||
using result_t = result<T...>;
|
||||
};
|
||||
|
||||
/// An object which provides the internal buffer and helper methods
|
||||
/// for waiting on a continuable in a stackless coroutine.
|
||||
template <typename Continuable>
|
||||
class awaitable {
|
||||
using hint_t = decltype(base::annotation_of(identify<Continuable>{}));
|
||||
using result_t = typename result_from_identity<hint_t>::result_t;
|
||||
|
||||
/// The continuable which is invoked upon suspension
|
||||
Continuable continuable_;
|
||||
/// A cache which is used to pass the result of the continuation
|
||||
/// to the coroutine.
|
||||
result_t result_;
|
||||
/// Enumeration that represents the suspension state of the awaitable.
|
||||
enum class state : std::uint8_t {
|
||||
suspended,
|
||||
pending,
|
||||
resolved,
|
||||
};
|
||||
/// An atomic that specifies whether the awaitable has suspended or not.
|
||||
/// Allows to perform symmetric transfer on continuables that are
|
||||
/// immediately resolved.
|
||||
std::atomic<state> state_{state::pending};
|
||||
|
||||
public:
|
||||
explicit constexpr awaitable(Continuable&& continuable)
|
||||
: continuable_(std::move(continuable)) {
|
||||
|
||||
// If the continuable is ready resolve the result from the
|
||||
// continuable immediately.
|
||||
if (base::attorney::is_ready(continuable_)) {
|
||||
assert(result_.is_empty());
|
||||
result_ = base::attorney::query(std::move(continuable_));
|
||||
}
|
||||
}
|
||||
|
||||
/// Return whether the continuable can provide its result instantly,
|
||||
/// which also means its execution is side-effect free.
|
||||
bool await_ready() const noexcept {
|
||||
return !result_.is_empty();
|
||||
}
|
||||
|
||||
/// Suspend the current context
|
||||
// TODO Convert this to an r-value function once possible
|
||||
bool await_suspend(coroutine_handle<> h) {
|
||||
assert(result_.is_empty());
|
||||
// Forward every result to the current awaitable
|
||||
std::move(continuable_)
|
||||
.next([h, this](auto&&... args) mutable {
|
||||
assert(result_.is_empty());
|
||||
result_ = result_t::from(std::forward<decltype(args)>(args)...);
|
||||
|
||||
// If true, it means that the promise was suspended (i.e., the
|
||||
// awaitable await_suspend method has already returned). That
|
||||
// means we must call the resume coroutine from the continuation
|
||||
// chain.
|
||||
if (state_.exchange(state::resolved, std::memory_order_acq_rel) ==
|
||||
state::suspended) {
|
||||
return h.resume();
|
||||
}
|
||||
})
|
||||
.done();
|
||||
|
||||
return state_.exchange(state::suspended, std::memory_order_acq_rel) !=
|
||||
state::resolved;
|
||||
}
|
||||
|
||||
/// Resume the coroutine represented by the handle
|
||||
typename result_t::value_t await_resume() noexcept(false) {
|
||||
if (result_.is_value()) {
|
||||
// When the result was resolved return it
|
||||
return std::move(result_).get_value();
|
||||
}
|
||||
|
||||
assert(result_.is_exception());
|
||||
|
||||
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
if (exception_t e = result_.get_exception()) {
|
||||
std::rethrow_exception(std::move(e));
|
||||
} else {
|
||||
throw await_canceled_exception();
|
||||
}
|
||||
# else // CONTINUABLE_HAS_EXCEPTIONS
|
||||
// Returning error types from co_await isn't supported!
|
||||
CTI_DETAIL_TRAP();
|
||||
# endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
}
|
||||
};
|
||||
|
||||
/// Converts a continuable into an awaitable object as described by
|
||||
/// the C++ coroutine TS.
|
||||
template <typename T>
|
||||
constexpr auto create_awaiter(T&& continuable) {
|
||||
return awaitable<std::decay_t<T>>(std::forward<T>(continuable));
|
||||
}
|
||||
|
||||
/// This makes it possible to take the coroutine_handle over on suspension
|
||||
struct handle_takeover {
|
||||
coroutine_handle<>& handle_;
|
||||
|
||||
bool await_ready() noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
void await_suspend(coroutine_handle<> handle) noexcept {
|
||||
handle_ = handle;
|
||||
}
|
||||
|
||||
void await_resume() noexcept {}
|
||||
};
|
||||
|
||||
/// The type which is passed to the compiler that describes the properties
|
||||
/// of a continuable_base used as coroutine promise type.
|
||||
template <typename Continuable, typename Promise, typename... Args>
|
||||
struct promise_type;
|
||||
|
||||
/// Implements the resolving method return_void and return_value accordingly
|
||||
template <typename Base>
|
||||
struct promise_resolver_base;
|
||||
|
||||
template <typename Continuable, typename Promise>
|
||||
struct promise_resolver_base<promise_type<Continuable, Promise>> {
|
||||
void return_void() {
|
||||
auto me = static_cast<promise_type<Continuable, Promise>*>(this);
|
||||
me->promise_.set_value();
|
||||
}
|
||||
};
|
||||
template <typename Continuable, typename Promise, typename T>
|
||||
struct promise_resolver_base<promise_type<Continuable, Promise, T>> {
|
||||
void return_value(T value) {
|
||||
auto me = static_cast<promise_type<Continuable, Promise, T>*>(this);
|
||||
me->promise_.set_value(std::move(value));
|
||||
}
|
||||
};
|
||||
template <typename Continuable, typename Promise, typename... Args>
|
||||
struct promise_resolver_base<promise_type<Continuable, Promise, Args...>> {
|
||||
template <typename T>
|
||||
void return_value(T&& tuple_like) {
|
||||
auto me = static_cast<promise_type<Continuable, Promise, Args...>*>(this);
|
||||
traits::unpack(std::move(me->promise_), std::forward<T>(tuple_like));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Continuable, typename Promise, typename... Args>
|
||||
struct promise_type
|
||||
: promise_resolver_base<promise_type<Continuable, Promise, Args...>> {
|
||||
|
||||
coroutine_handle<> handle_;
|
||||
Promise promise_;
|
||||
|
||||
explicit promise_type() = default;
|
||||
|
||||
Continuable get_return_object() {
|
||||
return [this](auto&& promise) {
|
||||
promise_ = std::forward<decltype(promise)>(promise);
|
||||
handle_.resume();
|
||||
};
|
||||
}
|
||||
|
||||
handle_takeover initial_suspend() {
|
||||
return {handle_};
|
||||
}
|
||||
|
||||
suspend_never final_suspend() noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
void unhandled_exception() noexcept {
|
||||
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
try {
|
||||
std::rethrow_exception(std::current_exception());
|
||||
} catch (await_canceled_exception const&) {
|
||||
promise_.set_canceled();
|
||||
} catch (...) {
|
||||
promise_.set_exception(std::current_exception());
|
||||
}
|
||||
# else // CONTINUABLE_HAS_EXCEPTIONS
|
||||
// Returning exception types from a coroutine isn't supported
|
||||
CTI_DETAIL_TRAP();
|
||||
# endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
}
|
||||
};
|
||||
} // namespace awaiting
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
#endif // defined(CONTINUABLE_HAS_COROUTINE)
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_UTIL_HPP_INCLUDED
|
||||
241
include/continuable/detail/other/erasure.hpp
Normal file
241
include/continuable/detail/other/erasure.hpp
Normal file
@ -0,0 +1,241 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_ERASURE_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_ERASURE_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <function2/function2.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace erasure {
|
||||
template <typename... Args>
|
||||
using callback_erasure_t =
|
||||
fu2::function_base<true, false, fu2::capacity_none, true, false,
|
||||
void(Args...)&&, void(exception_arg_t, exception_t) &&>;
|
||||
|
||||
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
|
||||
template <typename... Args>
|
||||
using callback = callback_erasure_t<Args...>;
|
||||
#else
|
||||
template <typename... Args>
|
||||
class callback;
|
||||
|
||||
template <typename T>
|
||||
struct is_callback : std::false_type {};
|
||||
template <typename... Args>
|
||||
struct is_callback<callback<Args...>> : std::true_type {};
|
||||
|
||||
template <typename... Args>
|
||||
class callback : public callback_erasure_t<Args...> {
|
||||
public:
|
||||
using erasure_t = callback_erasure_t<Args...>;
|
||||
erasure_t erasure_;
|
||||
|
||||
callback() = default;
|
||||
~callback() = default;
|
||||
callback(callback const&) = delete;
|
||||
callback(callback&&) = default;
|
||||
callback& operator=(callback const&) = delete;
|
||||
callback& operator=(callback&&) = default;
|
||||
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_convertible<T, erasure_t>::value>* = nullptr,
|
||||
std::enable_if_t<!is_callback<traits::unrefcv_t<T>>::value>* = nullptr>
|
||||
/* implicit */ callback(T&& callable) : erasure_(std::forward<T>(callable)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_assignable<erasure_t, T>::value>* = nullptr,
|
||||
std::enable_if_t<!is_callback<traits::unrefcv_t<T>>::value>* = nullptr>
|
||||
callback& operator=(T&& callable) {
|
||||
erasure_ = std::forward<T>(callable);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator()(Args... args) && noexcept {
|
||||
std::move(erasure_)(std::move(args)...);
|
||||
}
|
||||
|
||||
void operator()(exception_arg_t exception_arg, exception_t exception) &&
|
||||
noexcept {
|
||||
std::move(erasure_)(exception_arg, std::move(exception));
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return bool(erasure_);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
using work_erasure_t =
|
||||
fu2::function_base<true, false, fu2::capacity_fixed<32UL>, true, false,
|
||||
void()&&, void(exception_arg_t, exception_t) &&>;
|
||||
|
||||
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
|
||||
using work = work_erasure_t;
|
||||
#else
|
||||
class work;
|
||||
|
||||
template <typename T>
|
||||
struct is_work : std::false_type {};
|
||||
template <>
|
||||
struct is_work<work> : std::true_type {};
|
||||
|
||||
class work {
|
||||
using erasure_t = work_erasure_t;
|
||||
erasure_t erasure_;
|
||||
|
||||
public:
|
||||
work() = default;
|
||||
~work() = default;
|
||||
work(work const&) = delete;
|
||||
work(work&&) = default;
|
||||
work& operator=(work const&) = delete;
|
||||
work& operator=(work&&) = default;
|
||||
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_convertible<T, erasure_t>::value>* = nullptr,
|
||||
std::enable_if_t<!is_work<traits::unrefcv_t<T>>::value>* = nullptr>
|
||||
/* implicit */ work(T&& callable) : erasure_(std::forward<T>(callable)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_assignable<erasure_t, T>::value>* = nullptr,
|
||||
std::enable_if_t<!is_work<traits::unrefcv_t<T>>::value>* = nullptr>
|
||||
work& operator=(T&& callable) {
|
||||
erasure_ = std::forward<T>(callable);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator()() && noexcept {
|
||||
std::move(erasure_)();
|
||||
}
|
||||
|
||||
void operator()(exception_arg_t, exception_t exception) && noexcept {
|
||||
std::move(erasure_)(exception_arg_t{}, std::move(exception));
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return bool(erasure_);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename... Args>
|
||||
struct continuation_capacity {
|
||||
using type = union {
|
||||
void* pointer_;
|
||||
base::ready_continuation<Args...> continuation_;
|
||||
};
|
||||
|
||||
static constexpr std::size_t capacity = sizeof(type);
|
||||
static constexpr std::size_t alignment = alignof(type);
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
using continuation_erasure_t = fu2::function_base<
|
||||
true, false, continuation_capacity<Args...>, true, false,
|
||||
void(promise_base<callback<Args...>, signature_arg_t<Args...>>),
|
||||
bool(is_ready_arg_t) const, result<Args...>(unpack_arg_t)>;
|
||||
|
||||
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
|
||||
template <typename... Args>
|
||||
using continuation = continuation_erasure_t<Args...>;
|
||||
#else
|
||||
template <typename... Args>
|
||||
class continuation;
|
||||
|
||||
template <typename T>
|
||||
struct is_continuation : std::false_type {};
|
||||
template <typename... Args>
|
||||
struct is_continuation<continuation<Args...>> : std::true_type {};
|
||||
|
||||
template <typename... Args>
|
||||
class continuation {
|
||||
using erasure_t = continuation_erasure_t<Args...>;
|
||||
erasure_t erasure_;
|
||||
|
||||
public:
|
||||
continuation() = default;
|
||||
~continuation() = default;
|
||||
continuation(continuation const&) = delete;
|
||||
continuation(continuation&&) = default;
|
||||
continuation& operator=(continuation const&) = delete;
|
||||
continuation& operator=(continuation&&) = default;
|
||||
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_convertible<T, erasure_t>::value>* = nullptr,
|
||||
std::enable_if_t<!is_continuation<traits::unrefcv_t<T>>::value>* =
|
||||
nullptr>
|
||||
/* implicit */ continuation(T&& callable)
|
||||
: erasure_(std::forward<T>(callable)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<std::is_assignable<erasure_t, T>::value>* = nullptr,
|
||||
std::enable_if_t<!is_continuation<traits::unrefcv_t<T>>::value>* =
|
||||
nullptr>
|
||||
continuation& operator=(T&& callable) {
|
||||
erasure_ = std::forward<T>(callable);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void operator()(promise_base<callback<Args...>, //
|
||||
signature_arg_t<Args...>>
|
||||
promise) {
|
||||
erasure_(std::move(promise));
|
||||
}
|
||||
|
||||
bool operator()(is_ready_arg_t is_ready_arg) const {
|
||||
return erasure_(is_ready_arg);
|
||||
}
|
||||
|
||||
result<Args...> operator()(unpack_arg_t query_arg) {
|
||||
return erasure_(query_arg);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
} // namespace erasure
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_ERASURE_HPP_INCLUDED
|
||||
97
include/continuable/detail/other/promisify.hpp
Normal file
97
include/continuable/detail/other/promisify.hpp
Normal file
@ -0,0 +1,97 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
#include <exception>
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace convert {
|
||||
/// A resolver for promisifying asio and js style callbacks.
|
||||
inline auto default_resolver() {
|
||||
return [](auto&& promise, auto&& e, auto&&... args) {
|
||||
static_assert(
|
||||
std::is_convertible<std::decay_t<decltype(e)>, exception_t>::value,
|
||||
"The given error type must be convertible to the error type used! "
|
||||
"Specify a custom resolver in order to apply a conversion to the "
|
||||
"used error type.");
|
||||
|
||||
if (e) {
|
||||
promise.set_exception(std::forward<decltype(e)>(e));
|
||||
} else {
|
||||
promise.set_value(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... Result>
|
||||
struct promisify_helper {
|
||||
template <typename Resolver, typename Callable, typename... Args>
|
||||
static auto from(Resolver&& resolver, Callable&& callable, Args&&... args) {
|
||||
return make_continuable<Result...>(
|
||||
[resolver = std::forward<Resolver>(resolver),
|
||||
args = traits::make_flat_tuple(std::forward<Callable>(callable),
|
||||
std::forward<Args>(args)...)](
|
||||
auto&& promise) mutable {
|
||||
traits::unpack(
|
||||
[promise = std::forward<decltype(promise)>(promise),
|
||||
&resolver](auto&&... args) mutable {
|
||||
// Call the resolver from with the promise and result
|
||||
auto callback =
|
||||
[resolver = std::move(resolver),
|
||||
promise = std::move(promise)](auto&&... args) mutable {
|
||||
resolver(std::move(promise),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
};
|
||||
|
||||
// Invoke the callback taking function
|
||||
util::invoke(std::forward<decltype(args)>(args)...,
|
||||
std::move(callback));
|
||||
},
|
||||
std::move(args));
|
||||
});
|
||||
}
|
||||
};
|
||||
} // namespace convert
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
|
||||
@ -5,9 +5,9 @@
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
v4.2.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
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
|
||||
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
|
||||
@ -33,13 +33,12 @@
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <continuable/continuable-primitives.hpp>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/types.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
@ -55,7 +54,7 @@ void assert_async_completion(C&& continuable) {
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
})
|
||||
.fail([](cti::error_type /*error*/) {
|
||||
.fail([](cti::exception_t /*error*/) {
|
||||
// ...
|
||||
FAIL();
|
||||
});
|
||||
@ -74,7 +73,28 @@ void assert_async_exception_completion(C&& continuable) {
|
||||
// ...
|
||||
FAIL();
|
||||
})
|
||||
.fail([called](cti::error_type /*error*/) {
|
||||
.fail([called](cti::exception_t error) {
|
||||
ASSERT_TRUE(bool(error));
|
||||
ASSERT_FALSE(*called);
|
||||
*called = true;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(*called);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
void assert_async_cancellation(C&& continuable) {
|
||||
auto called = std::make_shared<bool>(false);
|
||||
std::forward<C>(continuable)
|
||||
.then([](auto&&... args) {
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
|
||||
// ...
|
||||
FAIL();
|
||||
})
|
||||
.fail([called](cti::exception_t error) {
|
||||
ASSERT_FALSE(bool(error));
|
||||
ASSERT_FALSE(*called);
|
||||
*called = true;
|
||||
});
|
||||
@ -91,7 +111,7 @@ void assert_async_never_completed(C&& continuable) {
|
||||
|
||||
FAIL();
|
||||
})
|
||||
.fail([](cti::error_type /*error*/) {
|
||||
.fail([](cti::exception_t) {
|
||||
// ...
|
||||
FAIL();
|
||||
});
|
||||
@ -101,9 +121,8 @@ template <typename C, typename V>
|
||||
void assert_async_validation(C&& continuable, V&& validator) {
|
||||
assert_async_completion(
|
||||
std::forward<C>(continuable)
|
||||
.then([validator =
|
||||
std::forward<V>(validator)](auto&&... args) mutable {
|
||||
|
||||
.then(
|
||||
[validator = std::forward<V>(validator)](auto&&... args) mutable {
|
||||
validator(std::forward<decltype(args)>(args)...);
|
||||
}));
|
||||
}
|
||||
@ -115,11 +134,10 @@ void assert_async_binary_validation(V&& validator, C&& continuable,
|
||||
|
||||
using size = std::integral_constant<std::size_t, sizeof...(expected)>;
|
||||
|
||||
assert_async_validation(std::forward<C>(continuable), [
|
||||
expected_pack = std::make_tuple(std::forward<Args>(expected)...),
|
||||
validator = std::forward<V>(validator)
|
||||
](auto&&... args) mutable {
|
||||
|
||||
assert_async_validation(
|
||||
std::forward<C>(continuable),
|
||||
[expected_pack = std::make_tuple(std::forward<Args>(expected)...),
|
||||
validator = std::forward<V>(validator)](auto&&... args) mutable {
|
||||
static_assert(size::value == sizeof...(args),
|
||||
"Async completion handler called with a different count "
|
||||
"of arguments!");
|
||||
@ -139,20 +157,19 @@ void assert_async_binary_exception_validation(V&& validator, C&& continuable,
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
|
||||
// ...
|
||||
// The exception was not thrown!
|
||||
FAIL();
|
||||
})
|
||||
.fail([
|
||||
called, validator = std::forward<decltype(validator)>(validator),
|
||||
expected = std::forward<decltype(expected)>(expected)
|
||||
](types::error_type error) {
|
||||
.fail([called, validator = std::forward<decltype(validator)>(validator),
|
||||
expected = std::forward<decltype(expected)>(expected)](
|
||||
exception_t error) {
|
||||
ASSERT_FALSE(*called);
|
||||
*called = true;
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
try {
|
||||
std::rethrow_exception(error);
|
||||
} catch (std::decay_t<decltype(expected)>& exception) {
|
||||
} catch (std::decay_t<decltype(expected)> const& exception) {
|
||||
validator(exception, expected);
|
||||
} catch (...) {
|
||||
FAIL();
|
||||
@ -183,15 +200,14 @@ template <typename... Expected>
|
||||
struct assert_async_types_validator {
|
||||
template <typename... Actual>
|
||||
void operator()(Actual...) {
|
||||
static_assert(std::is_same<traits::identity<Actual...>,
|
||||
traits::identity<Expected...>>::value,
|
||||
static_assert(
|
||||
std::is_same<identity<Actual...>, identity<Expected...>>::value,
|
||||
"The called arguments don't match with the expected ones!");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename C, typename... Args>
|
||||
void assert_async_types(C&& continuable,
|
||||
traits::identity<Args...> /*expected*/) {
|
||||
void assert_async_types(C&& continuable, identity<Args...> /*expected*/) {
|
||||
assert_async_validation(std::forward<C>(continuable),
|
||||
assert_async_types_validator<Args...>{});
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
#include <exception>
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace convert {
|
||||
/// A helper class for promisifying asio style callback taking functions
|
||||
/// into a continuable.
|
||||
template <typename P>
|
||||
struct promisify_asio {
|
||||
P promise;
|
||||
|
||||
template <typename E, typename... T>
|
||||
void operator()(E&& error, T&&... result) {
|
||||
if (error) {
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
promise.set_exception(std::make_exception_ptr(std::forward<E>(error)));
|
||||
#else
|
||||
promise.set_exception(
|
||||
std::error_condition(error.value(), error.category()));
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
} else {
|
||||
promise.set_value(std::forward<T>(result)...);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Result>
|
||||
struct promisify_helper {
|
||||
template <template <class T> class Evaluator, typename Callable,
|
||||
typename... Args>
|
||||
static auto from(Callable&& callable, Args&&... args) {
|
||||
return make_continuable<Result...>([args = std::make_tuple(
|
||||
std::forward<Callable>(callable),
|
||||
std::forward<Args>(args)...)](
|
||||
auto&& promise) mutable {
|
||||
|
||||
traits::unpack(
|
||||
std::move(args), [promise = std::forward<decltype(promise)>(promise)](
|
||||
auto&&... args) mutable {
|
||||
Evaluator<std::decay_t<decltype(promise)>> evaluator{
|
||||
std::move(promise)};
|
||||
|
||||
util::invoke(std::forward<decltype(args)>(args)...,
|
||||
std::move(evaluator));
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
} // namespace convert
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
|
||||
@ -1,342 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.0.0
|
||||
|
||||
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
||||
|
||||
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 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.
|
||||
**/
|
||||
|
||||
#ifndef CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/features.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace traits {
|
||||
/// Evaluates to the element at position I.
|
||||
template <std::size_t I, typename... Args>
|
||||
using at_t = decltype(std::get<I>(std::declval<std::tuple<Args...>>()));
|
||||
|
||||
/// A tagging type for wrapping other types
|
||||
template <typename... T>
|
||||
struct identity {};
|
||||
template <typename T>
|
||||
struct identity<T> : std::common_type<T> {};
|
||||
|
||||
template <typename>
|
||||
struct is_identity : std::false_type {};
|
||||
template <typename... Args>
|
||||
struct is_identity<identity<Args...>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
constexpr identity<std::decay_t<T>> identity_of(T const& /*type*/) noexcept {
|
||||
return {};
|
||||
}
|
||||
template <typename... Args>
|
||||
constexpr identity<Args...> identity_of(identity<Args...> /*type*/) noexcept {
|
||||
return {};
|
||||
}
|
||||
template <typename T>
|
||||
using identify = std::conditional_t<is_identity<std::decay_t<T>>::value, T,
|
||||
identity<std::decay_t<T>>>;
|
||||
|
||||
template <std::size_t I, typename... T>
|
||||
constexpr auto get(identity<T...>) noexcept {
|
||||
return identify<at_t<I, T...>>{};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
// Equivalent to C++17's std::void_t which targets a bug in GCC,
|
||||
// that prevents correct SFINAE behavior.
|
||||
// See http://stackoverflow.com/questions/35753920 for details.
|
||||
template <typename...>
|
||||
struct deduce_to_void : std::common_type<void> {};
|
||||
} // namespace detail
|
||||
|
||||
/// C++17 like void_t type
|
||||
template <typename... T>
|
||||
using void_t = typename detail::deduce_to_void<T...>::type;
|
||||
|
||||
namespace detail {
|
||||
template <typename Type, typename TrueCallback>
|
||||
constexpr void static_if_impl(std::true_type, Type&& type,
|
||||
TrueCallback&& trueCallback) {
|
||||
std::forward<TrueCallback>(trueCallback)(std::forward<Type>(type));
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback>
|
||||
constexpr void static_if_impl(std::false_type, Type&& /*type*/,
|
||||
TrueCallback&& /*trueCallback*/) {
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback, typename FalseCallback>
|
||||
constexpr auto static_if_impl(std::true_type, Type&& type,
|
||||
TrueCallback&& trueCallback,
|
||||
FalseCallback&& /*falseCallback*/) {
|
||||
return std::forward<TrueCallback>(trueCallback)(std::forward<Type>(type));
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback, typename FalseCallback>
|
||||
constexpr auto static_if_impl(std::false_type, Type&& type,
|
||||
TrueCallback&& /*trueCallback*/,
|
||||
FalseCallback&& falseCallback) {
|
||||
return std::forward<FalseCallback>(falseCallback)(std::forward<Type>(type));
|
||||
}
|
||||
|
||||
/// Evaluates to the size of the given tuple like type,
|
||||
// / if the type has no static size it will be one.
|
||||
template <typename T, typename Enable = void>
|
||||
struct tuple_like_size : std::integral_constant<std::size_t, 1U> {};
|
||||
template <typename T>
|
||||
struct tuple_like_size<T, void_t<decltype(std::tuple_size<T>::value)>>
|
||||
: std::tuple_size<T> {};
|
||||
} // namespace detail
|
||||
|
||||
/// Returns the pack size of the given empty pack
|
||||
constexpr std::size_t pack_size_of(identity<>) noexcept {
|
||||
return 0U;
|
||||
}
|
||||
/// Returns the pack size of the given type
|
||||
template <typename T>
|
||||
constexpr std::size_t pack_size_of(identity<T>) noexcept {
|
||||
return detail::tuple_like_size<T>::value;
|
||||
}
|
||||
/// Returns the pack size of the given type
|
||||
template <typename First, typename Second, typename... Args>
|
||||
constexpr std::size_t pack_size_of(identity<First, Second, Args...>) noexcept {
|
||||
return 2U + sizeof...(Args);
|
||||
}
|
||||
|
||||
/// Returns an index sequence of the given type
|
||||
template <typename T>
|
||||
constexpr auto sequence_of(identity<T>) noexcept {
|
||||
constexpr auto const size = pack_size_of(identity<T>{});
|
||||
return std::make_index_sequence<size>();
|
||||
}
|
||||
|
||||
/// Invokes the callback only if the given type matches the check
|
||||
template <typename Type, typename Check, typename TrueCallback>
|
||||
constexpr void static_if(Type&& type, Check&& check,
|
||||
TrueCallback&& trueCallback) {
|
||||
detail::static_if_impl(std::forward<Check>(check)(type),
|
||||
std::forward<Type>(type),
|
||||
std::forward<TrueCallback>(trueCallback));
|
||||
}
|
||||
|
||||
/// Invokes the callback only if the given type matches the check
|
||||
template <typename Type, typename Check, typename TrueCallback,
|
||||
typename FalseCallback>
|
||||
constexpr auto static_if(Type&& type, Check&& check,
|
||||
TrueCallback&& trueCallback,
|
||||
FalseCallback&& falseCallback) {
|
||||
return detail::static_if_impl(std::forward<Check>(check)(type),
|
||||
std::forward<Type>(type),
|
||||
std::forward<TrueCallback>(trueCallback),
|
||||
std::forward<FalseCallback>(falseCallback));
|
||||
}
|
||||
|
||||
/// Calls the given unpacker with the content of the given sequence
|
||||
template <typename U, std::size_t... I>
|
||||
constexpr decltype(auto) unpack(std::integer_sequence<std::size_t, I...>,
|
||||
U&& unpacker) {
|
||||
return std::forward<U>(unpacker)(std::integral_constant<std::size_t, I>{}...);
|
||||
}
|
||||
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename U, std::size_t... I>
|
||||
constexpr auto unpack(F&& first_sequenceable, U&& unpacker,
|
||||
std::integer_sequence<std::size_t, I...>)
|
||||
-> decltype(std::forward<U>(unpacker)(
|
||||
get<I>(std::forward<F>(first_sequenceable))...)) {
|
||||
(void)first_sequenceable;
|
||||
return std::forward<U>(unpacker)(
|
||||
get<I>(std::forward<F>(first_sequenceable))...);
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename S, typename U, std::size_t... If,
|
||||
std::size_t... Is>
|
||||
constexpr auto unpack(F&& first_sequenceable, S&& second_sequenceable,
|
||||
U&& unpacker, std::integer_sequence<std::size_t, If...>,
|
||||
std::integer_sequence<std::size_t, Is...>)
|
||||
-> decltype(std::forward<U>(unpacker)(
|
||||
get<If>(std::forward<F>(first_sequenceable))...,
|
||||
get<Is>(std::forward<S>(second_sequenceable))...)) {
|
||||
(void)first_sequenceable;
|
||||
(void)second_sequenceable;
|
||||
return std::forward<U>(unpacker)(
|
||||
get<If>(std::forward<F>(first_sequenceable))...,
|
||||
get<Is>(std::forward<S>(second_sequenceable))...);
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename U>
|
||||
constexpr auto unpack(F&& first_sequenceable, U&& unpacker)
|
||||
-> decltype(unpack(std::forward<F>(first_sequenceable),
|
||||
std::forward<U>(unpacker),
|
||||
sequence_of(identify<decltype(first_sequenceable)>{}))) {
|
||||
return unpack(std::forward<F>(first_sequenceable), std::forward<U>(unpacker),
|
||||
sequence_of(identify<decltype(first_sequenceable)>{}));
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceables
|
||||
template <typename F, typename S, typename U>
|
||||
constexpr auto unpack(F&& first_sequenceable, S&& second_sequenceable,
|
||||
U&& unpacker)
|
||||
-> decltype(unpack(std::forward<F>(first_sequenceable),
|
||||
std::forward<S>(second_sequenceable),
|
||||
std::forward<U>(unpacker),
|
||||
sequence_of(identity_of(first_sequenceable)),
|
||||
sequence_of(identity_of(second_sequenceable)))) {
|
||||
return unpack(std::forward<F>(first_sequenceable),
|
||||
std::forward<S>(second_sequenceable), std::forward<U>(unpacker),
|
||||
sequence_of(identity_of(first_sequenceable)),
|
||||
sequence_of(identity_of(second_sequenceable)));
|
||||
}
|
||||
|
||||
/// Adds the given type at the back of the left sequenceable
|
||||
template <typename Left, typename Element>
|
||||
constexpr auto push(Left&& left, Element&& element) {
|
||||
return unpack(std::forward<Left>(left), [&](auto&&... args) {
|
||||
return std::make_tuple(std::forward<decltype(args)>(args)...,
|
||||
std::forward<Element>(element));
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds the element to the back of the identity
|
||||
template <typename... Args, typename Element>
|
||||
constexpr auto push(identity<Args...>, identity<Element>) noexcept {
|
||||
return identity<Args..., Element>{};
|
||||
}
|
||||
|
||||
/// Removes the first element from the identity
|
||||
template <typename First, typename... Rest>
|
||||
constexpr auto pop_first(identity<First, Rest...>) noexcept {
|
||||
return identity<Rest...>{};
|
||||
}
|
||||
|
||||
/// Returns the merged sequence
|
||||
template <typename Left>
|
||||
constexpr auto merge(Left&& left) {
|
||||
return std::forward<Left>(left);
|
||||
}
|
||||
/// Merges the left sequenceable with the right ones
|
||||
template <typename Left, typename Right, typename... Rest>
|
||||
constexpr auto merge(Left&& left, Right&& right, Rest&&... rest) {
|
||||
// Merge the left with the right sequenceable and
|
||||
// merge the result with the rest.
|
||||
return merge(unpack(std::forward<Left>(left), std::forward<Right>(right),
|
||||
[&](auto&&... args) {
|
||||
// Maybe use: template <template<typename...> class T,
|
||||
// typename... Args>
|
||||
return std::make_tuple(
|
||||
std::forward<decltype(args)>(args)...);
|
||||
}),
|
||||
std::forward<Rest>(rest)...);
|
||||
}
|
||||
/// Merges the left identity with the right ones
|
||||
template <typename... LeftArgs, typename... RightArgs, typename... Rest>
|
||||
constexpr auto merge(identity<LeftArgs...> /*left*/,
|
||||
identity<RightArgs...> /*right*/, Rest&&... rest) {
|
||||
return merge(identity<LeftArgs..., RightArgs...>{},
|
||||
std::forward<Rest>(rest)...);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename T, typename Args, typename = traits::void_t<>>
|
||||
struct is_invokable_impl : std::common_type<std::false_type> {};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct is_invokable_impl<
|
||||
T, std::tuple<Args...>,
|
||||
void_t<decltype(std::declval<T>()(std::declval<Args>()...))>>
|
||||
: std::common_type<std::true_type> {};
|
||||
} // namespace detail
|
||||
|
||||
/// Deduces to a std::true_type if the given type is callable with the arguments
|
||||
/// inside the given tuple.
|
||||
/// The main reason for implementing it with the detection idiom instead of
|
||||
/// hana like detection is that MSVC has issues with capturing raw template
|
||||
/// arguments inside lambda closures.
|
||||
///
|
||||
/// ```cpp
|
||||
/// traits::is_invokable<object, std::tuple<Args...>>
|
||||
/// ```
|
||||
template <typename T, typename Args>
|
||||
using is_invokable_from_tuple =
|
||||
typename detail::is_invokable_impl<T, Args>::type;
|
||||
|
||||
// Checks whether the given callable object is invocable with the given
|
||||
// arguments. This doesn't take member functions into account!
|
||||
template <typename T, typename... Args>
|
||||
using is_invocable = is_invokable_from_tuple<T, std::tuple<Args...>>;
|
||||
|
||||
/// Deduces to a std::false_type
|
||||
template <typename T>
|
||||
using fail = std::integral_constant<bool, !std::is_same<T, T>::value>;
|
||||
|
||||
#ifdef CONTINUABLE_HAS_CXX17_DISJUNCTION
|
||||
using std::disjunction;
|
||||
#else
|
||||
namespace detail {
|
||||
/// Declares a C++14 polyfill for C++17 std::disjunction.
|
||||
template <typename Args, typename = void_t<>>
|
||||
struct disjunction_impl : std::common_type<std::true_type> {};
|
||||
template <typename... Args>
|
||||
struct disjunction_impl<identity<Args...>,
|
||||
void_t<std::enable_if_t<!bool(Args::value)>...>>
|
||||
: std::common_type<std::false_type> {};
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Args>
|
||||
using disjunction = typename detail::disjunction_impl<identity<Args...>>::type;
|
||||
#endif // CONTINUABLE_HAS_CXX17_DISJUNCTION
|
||||
|
||||
#ifdef CONTINUABLE_HAS_CXX17_CONJUNCTION
|
||||
using std::conjunction;
|
||||
#else
|
||||
namespace detail {
|
||||
/// Declares a C++14 polyfill for C++17 std::conjunction.
|
||||
template <typename Args, typename = void_t<>>
|
||||
struct conjunction_impl : std::common_type<std::false_type> {};
|
||||
template <typename... Args>
|
||||
struct conjunction_impl<identity<Args...>,
|
||||
void_t<std::enable_if_t<bool(Args::value)>...>>
|
||||
: std::common_type<std::true_type> {};
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Args>
|
||||
using conjunction = typename detail::conjunction_impl<identity<Args...>>::type;
|
||||
#endif // CONTINUABLE_HAS_CXX17_CONJUNCTION
|
||||
|
||||
} // namespace traits
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED
|
||||
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