mirror of
https://github.com/Naios/continuable.git
synced 2025-12-07 01:06:44 +08:00
Compare commits
No commits in common. "master" and "v0.8.0" have entirely different histories.
@ -1,38 +1,4 @@
|
||||
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
|
||||
|
||||
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
|
||||
IndentCaseLabels: true
|
||||
|
||||
19
.clang-tidy
19
.clang-tidy
@ -1,19 +0,0 @@
|
||||
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
|
||||
- key: readability-identifier-naming.EnumCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.FunctionCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.MemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.ParameterCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.UnionCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.VariableCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.Macro
|
||||
value: UPPER_CASE
|
||||
HeaderFilterRegex: 'include/continuable/.(hpp)$'
|
||||
@ -1,11 +0,0 @@
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 80
|
||||
|
||||
[*.{c,h,cpp,hpp,inl}]
|
||||
charset = latin1
|
||||
36
.gitattributes
vendored
36
.gitattributes
vendored
@ -1,36 +0,0 @@
|
||||
#sources
|
||||
*.c text
|
||||
*.cc text
|
||||
*.cxx text
|
||||
*.cpp text
|
||||
*.c++ text
|
||||
*.hpp text
|
||||
*.h text
|
||||
*.h++ text
|
||||
*.hh text
|
||||
|
||||
# Compiled Object files
|
||||
*.slo binary
|
||||
*.lo binary
|
||||
*.o binary
|
||||
*.obj binary
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch binary
|
||||
*.pch binary
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so binary
|
||||
*.dylib binary
|
||||
*.dll binary
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai binary
|
||||
*.la binary
|
||||
*.a binary
|
||||
*.lib binary
|
||||
|
||||
# Executables
|
||||
*.exe binary
|
||||
*.out binary
|
||||
*.app binary
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -1 +0,0 @@
|
||||
* @Naios
|
||||
11
.github/CONTRIBUTING.md
vendored
11
.github/CONTRIBUTING.md
vendored
@ -1,11 +0,0 @@
|
||||
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
30
.github/ISSUE_TEMPLATE.md
vendored
@ -1,30 +0,0 @@
|
||||
@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
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,22 +0,0 @@
|
||||
@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
109
.github/workflows/build_and_install.yml
vendored
@ -1,109 +0,0 @@
|
||||
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
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -48,8 +48,9 @@ bld/
|
||||
# Visual Studo 2015 cache/options directory
|
||||
.vs/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
doc/doxygen/
|
||||
|
||||
# TMP files generated from clang-format
|
||||
*.TMP
|
||||
format.sh
|
||||
push.sh
|
||||
commit.sh
|
||||
pull.sh
|
||||
|
||||
10
.gitmodules
vendored
10
.gitmodules
vendored
@ -1,16 +1,6 @@
|
||||
[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
|
||||
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
|
||||
|
||||
106
.travis.yml
Normal file
106
.travis.yml
Normal file
@ -0,0 +1,106 @@
|
||||
sudo: true
|
||||
dist: trusty
|
||||
language: cpp
|
||||
|
||||
git:
|
||||
depth: 1
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- valgrind
|
||||
- g++-5
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
env:
|
||||
- COMPILER=g++-5
|
||||
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- valgrind
|
||||
- g++-6
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
env:
|
||||
- COMPILER=g++-6
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.7 main"
|
||||
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
|
||||
packages:
|
||||
- g++-6
|
||||
- clang-3.7
|
||||
env:
|
||||
- COMPILER=clang++-3.7
|
||||
|
||||
- os: linux
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
|
||||
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
|
||||
packages:
|
||||
- g++-6
|
||||
- clang-3.9
|
||||
env:
|
||||
- COMPILER=clang++-3.9
|
||||
|
||||
install:
|
||||
- export CXX=$COMPILER
|
||||
- $CXX --version
|
||||
|
||||
# Function for creating a new 'build' directory
|
||||
- |
|
||||
function renew_build {
|
||||
echo "Renew build directory..."
|
||||
cd $TRAVIS_BUILD_DIR
|
||||
|
||||
# Remove any existing build directory
|
||||
[ -e build ] && rm -r -f build
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
# Configure the project and build it
|
||||
cmake -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS -Werror" -DCMAKE_BUILD_TYPE=Debug ..
|
||||
}
|
||||
|
||||
script:
|
||||
- |
|
||||
if [[ $COMPILER == *"clang"* ]]; then
|
||||
# Build the test suite with various sanitizers:
|
||||
# - ASan (LSan):
|
||||
echo "Building with address sanitizer..."
|
||||
CMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer"
|
||||
renew_build
|
||||
make -j2
|
||||
ctest --verbose
|
||||
|
||||
# - UBSan:
|
||||
echo "Building with undefined behaviour sanitizer..."
|
||||
CMAKE_CXX_FLAGS="-fsanitize=undefined -fno-omit-frame-pointer"
|
||||
renew_build
|
||||
make -j2
|
||||
ctest --verbose
|
||||
else
|
||||
# Build an run the tests suite with valgrind
|
||||
renew_build
|
||||
make -j2
|
||||
valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes ctest --verbose
|
||||
fi
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
229
CMakeLists.txt
229
CMakeLists.txt
@ -1,115 +1,24 @@
|
||||
# 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.
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(continuable C CXX)
|
||||
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
project(
|
||||
continuable
|
||||
VERSION 4.0.0
|
||||
LANGUAGES C CXX)
|
||||
|
||||
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()
|
||||
|
||||
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
|
||||
message(
|
||||
STATUS
|
||||
"Building with ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER_VERSION})")
|
||||
endif()
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_INSTALL "Add the continuable install targets"
|
||||
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_TESTS "Build the continuable unit tests"
|
||||
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_EXAMPLES "Build the continuable examples"
|
||||
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
|
||||
|
||||
option(CTI_CONTINUABLE_WITH_BENCHMARKS "Build the continuable benchmarks" OFF)
|
||||
|
||||
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()
|
||||
# Dependencies
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# continuable-base
|
||||
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
|
||||
add_library(continuable-base INTERFACE)
|
||||
else()
|
||||
add_library(continuable-base INTERFACE IMPORTED GLOBAL)
|
||||
endif()
|
||||
add_library(continuable-base INTERFACE)
|
||||
|
||||
add_library(continuable::continuable-base ALIAS continuable-base)
|
||||
target_include_directories(continuable-base
|
||||
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
|
||||
@ -123,111 +32,27 @@ target_compile_features(
|
||||
cxx_trailing_return_types
|
||||
cxx_return_type_deduction)
|
||||
|
||||
if(CTI_CONTINUABLE_WITH_CPP_LATEST)
|
||||
target_compile_features(continuable-base INTERFACE cxx_std_20)
|
||||
endif()
|
||||
add_library(continuable INTERFACE)
|
||||
|
||||
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(
|
||||
target_link_libraries(continuable
|
||||
INTERFACE
|
||||
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()
|
||||
function2)
|
||||
|
||||
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::continuable ALIAS continuable)
|
||||
|
||||
target_link_libraries(continuable INTERFACE continuable::continuable-base
|
||||
function2::function2)
|
||||
|
||||
if(CTI_CONTINUABLE_WITH_INSTALL)
|
||||
include(ExternalProject)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# 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 .)
|
||||
|
||||
# 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}")
|
||||
|
||||
# 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}")
|
||||
|
||||
# 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}")
|
||||
|
||||
# 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)
|
||||
# Testing
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
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")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
if(CTI_CONTINUABLE_WITH_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
include(cmake/CMakeLists.txt)
|
||||
|
||||
if(CTI_CONTINUABLE_WITH_EXAMPLES)
|
||||
add_subdirectory(dep)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
endif()
|
||||
add_subdirectory(test)
|
||||
endif ()
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
# 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 - 2019 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright (c) 2015-2017 Denis Blank
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
481
Readme.md
481
Readme.md
@ -1,98 +1,107 @@
|
||||
# continuable->then(make_things_simple());
|
||||
|
||||
<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>
|
||||
[](https://travis-ci.org/Naios/continuable) [](https://ci.appveyor.com/project/Naios/continuable)  [](http://melpon.org/wandbox/permlink/gRWxSNHtARvRcmSY)
|
||||
|
||||
<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>
|
||||
> Async C++14 platform independent continuation chainer providing light and allocation aware futures
|
||||
|
||||
------
|
||||
This library provides full feature support of:
|
||||
|
||||
#### Continuable is a C++14 library that provides full support for:
|
||||
|
||||
* 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.
|
||||
|
||||
------
|
||||
|
||||
#### 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)
|
||||
* async continuation chaining using **callbacks** (*then*).
|
||||
* **no enforced type-erasure** which means we need **extremely fewer heap allocations** .
|
||||
* support for **finite logical connections** between continuables through an **all, any or sequence** strategy.
|
||||
* **syntactic sugar** for attaching callbacks to a continuation like partial invocation.
|
||||
|
||||
|
||||
#### Issues and contributions
|
||||
|
||||
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
|
||||
## The library design
|
||||
|
||||
- **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();
|
||||
});
|
||||
}
|
||||
The continuable library was designed in order to provide you as much as flexibility as possible:
|
||||
|
||||
auto mysql_query(std::string query) {
|
||||
return cti::make_continuable<result_set, bool>([url = std::move(url)](auto&& promise) {
|
||||
// ^^^^^^^^^^^^^^ multiple result types
|
||||
});
|
||||
}
|
||||
- There is no enforced type erasure which means there is less memory allocation and thus the callback chains are heavily optimizable by the compiler. That's why the library is well usable in the embedded or gaming field. **Don't pay for what you don't use!**
|
||||
- The library provides support for **dispatching callbacks on a given executor**, however, it doesn't provide it's own one. You probably will use your own executor like [asio](https://github.com/chriskohlhoff/asio), [libuv](https://github.com/libuv/libuv) or a corresponding [lock-free concurrentqueue](https://github.com/cameron314/concurrentqueue) anyway. In most cases, the executor will do the type erasure for you, so there is no reason to do it twice.
|
||||
- The library provides as much as **syntactic sugar** as it's possible, in order to make continuation chaining expressive and simple. For instance, it allows you to logical connect continuables through the well-known operators `&&` and `||`.
|
||||
- The library is header only and has **fewer dependencies**:
|
||||
- The `continuable-base.hpp` header only depends on the standard library and provides the basic continuation logic.
|
||||
- The `continuable-test.hpp` header also depends on `gtest` because it adds various test macros for asserting on the result of asynchronous continuations.
|
||||
- The `continuable.hpp`header depends on my header-only [function2](https://github.com/Naios/function2) library for providing efficient type erasure - non-copyable objects are natively supported without any workaround.
|
||||
|
||||
auto do_sth() {
|
||||
return cti::make_continuable<void>([](auto&& promise) {
|
||||
// ^^^^ no result at all
|
||||
});
|
||||
}
|
||||
|
||||
auto run_it() {
|
||||
return async([] {
|
||||
// Directly start with a handler
|
||||
});
|
||||
}
|
||||
## Installation
|
||||
|
||||
continuable<> run_it() { // With type erasure
|
||||
return async([] {
|
||||
### Inclusion
|
||||
|
||||
});
|
||||
}
|
||||
```
|
||||
As mentioned earlier the library is header-only. There is a cmake project provided for simple setup:
|
||||
|
||||
- **Attach your continuations through `then`, supports multiple results and partial handlers:**
|
||||
```cpp
|
||||
mysql_query("SELECT `id`, `name` FROM `users`")
|
||||
.then([](result_set users) {
|
||||
```sh
|
||||
# Shell:
|
||||
git submodule add https://github.com/Naios/continuable.git
|
||||
```
|
||||
|
||||
```cma
|
||||
# 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 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.
|
||||
|
||||
### 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
|
||||
|
||||
Currently, the library is in the incubation state, it provides a stable functionality as the CI unit tests indicate.
|
||||
|
||||
The API isn't fixed right now and will be eventually changed into the future.
|
||||
|
||||
Also, the unit-test is observed with the Clang sanitizers (asan, ubsan and lsan - when compiling with Clang) or Valgrind (when compiling with GCC).
|
||||
|
||||
## Quick reference
|
||||
|
||||
### Creating Continuables
|
||||
|
||||
Create a continuable from a callback taking function:
|
||||
|
||||
```c++
|
||||
#include "continuable/continuable-base.hpp"
|
||||
// ...
|
||||
|
||||
auto void_continuable = cti::make_continuable<void>([](auto&& callback) {
|
||||
// ^^^^
|
||||
|
||||
// Call the callback later when you have finished your work
|
||||
callback();
|
||||
});
|
||||
|
||||
auto str_continuable = cti::make_continuable<std::string>([](auto&& callback) {
|
||||
// ^^^^^^^^^^^
|
||||
callback("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) {
|
||||
// Return the next continuable to process ...
|
||||
return mysql_query("SELECT `id` name FROM `sessions`");
|
||||
})
|
||||
.then([](result_set 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([](result_set sessions, bool is_ok) {
|
||||
.then([](ResultSet sessions, bool is_ok) {
|
||||
// ... or pass a single value to the next callback ...
|
||||
return 10;
|
||||
})
|
||||
@ -101,133 +110,257 @@ in improving the implementation or if you have any troubles in using the library
|
||||
})
|
||||
// ... you may even pass continuables to the `then` method directly:
|
||||
.then(mysql_query("SELECT * `statistics`"))
|
||||
.then([](result_set result) {
|
||||
// ...
|
||||
return "Hi";
|
||||
})
|
||||
.then([] /*(std::string result) */ { // Handlers can accept a partial set of arguments{
|
||||
.then([](ResultSet result) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
```
|
||||
|
||||
- **Handle failures through `fail` or `next`:**
|
||||
```cpp
|
||||
http_request("example.com")
|
||||
.then([] {
|
||||
throw std::exception("Some error");
|
||||
})
|
||||
.fail([] (std::exception_ptr ptr) {
|
||||
if (ptr) {
|
||||
try {
|
||||
std::rethrow_exception(ptr);
|
||||
} catch(std::exception const& e) {
|
||||
// Handle the exception or error code here
|
||||
}
|
||||
}
|
||||
|
||||
### 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&& 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));
|
||||
});
|
||||
```
|
||||
}
|
||||
|
||||
- **Dispatch continuations through a specific executor** (possibly on a different thread or later)
|
||||
// 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");
|
||||
|
||||
```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) {
|
||||
// Or using chaining to handle the result which is covered in the documentation.
|
||||
mysql_query("SELECT `id`, `name` FROM users")
|
||||
.then([](ResultSet result) {
|
||||
// ...
|
||||
}, executor);
|
||||
// ^^^^^^^^
|
||||
```
|
||||
});
|
||||
```
|
||||
|
||||
- **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.
|
||||
### Connecting Continuables {all or any}
|
||||
|
||||
Continuables provide the operators **&&** and **||** for logical connection:
|
||||
|
||||
* **&&** invokes the final callback with the compound result of all connected continuables.
|
||||
* **||** invokes the final callback once with the first result available.
|
||||
|
||||
```C++
|
||||
auto http_request(std::string url) {
|
||||
return cti::make_continuable<std::string>([](auto&& callback) {
|
||||
callback("<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("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.
|
||||
// `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("example.com") >> http_request("wikipedia.org"))
|
||||
.then([](std::string github, std::string example, std::string wikipedia) {
|
||||
// The requests are invoked sequentially
|
||||
});
|
||||
|
||||
// Mixed logical connections:
|
||||
(http_request("github.com") && (http_request("example.com") || http_request("wikipedia.org")))
|
||||
.then([](std::string github, std::string example_or_wikipedia) {
|
||||
// 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 example or wikipedia.
|
||||
// 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("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"));
|
||||
```
|
||||
// There are helper functions for connecting continuables:
|
||||
auto all = cti::all_of(http_request("github.com"), http_request("travis-ci.org"));
|
||||
auto any = cti::any_of(http_request("github.com"), http_request("travis-ci.org"));
|
||||
```
|
||||
|
||||
- **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();
|
||||
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).
|
||||
|
||||
### Type erasure
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
```c++
|
||||
#include "continuable/continuable.hpp"
|
||||
// ...
|
||||
|
||||
cti::unique_continuable<int, std::string> unique =
|
||||
cti::make_continuable([value = std::make_unique<int>(0)](auto&& callback) {
|
||||
|
||||
// The use of non copyable objects is possible by design if
|
||||
// the type erasure backend supports it.
|
||||
callback(*value, "Hello, World!");
|
||||
});
|
||||
|
||||
std::move(unique).then([](int i) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
However you may still define your own continuation wrapper with the backend of your choice, but keep in mind that the capabilities of your wrapper determine the possible objects, the continuation is capable of carrying. This limits continuations using *std::function* as a backend to copyable types:
|
||||
|
||||
```c++
|
||||
template <typename... Args>
|
||||
using mycontinuation = cti::continuable_of_t<
|
||||
cti::continuable_erasure_of_t<std::function, std::function, Args...>,
|
||||
Args...>;
|
||||
|
||||
// ...
|
||||
|
||||
mycontinuation<int> myc = cti::make_continuable([](auto&& callback) {
|
||||
// ^^^^^
|
||||
// Signatures may be omitted for continuables which are type erased
|
||||
callback(0);
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### 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");
|
||||
})
|
||||
.then([] () -> result<> {
|
||||
// We recovered from the failure and proceeding normally
|
||||
.futurize();
|
||||
// ^^^^^^^^
|
||||
|
||||
// Will yield a default constructed exception type to signal cancellation
|
||||
return cancel();
|
||||
});
|
||||
```
|
||||
std::future<std::tuple<std::string, std::string>> future =
|
||||
(http_request("travis-ci.org") && http_request("atom.io")).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));
|
||||
}
|
||||
Continuables returning nothing will evaluate to: `std::future<void>`.
|
||||
|
||||
// (boost) asio completion token integration
|
||||
asio::io_context io_context;
|
||||
asio::steady_timer steady_timer(io_context);
|
||||
Continuables returning only one value will evaluate the corresponding future: `std::future<type>`.
|
||||
|
||||
steady_timer.expires_after(std::chrono::seconds(5));
|
||||
steady_timer.async_wait(cti::use_continuable)
|
||||
.then([] {
|
||||
// Is called after 5s
|
||||
});
|
||||
```
|
||||
Continuables returning more then one value will evaluate to a future providing a tuple carrying the values : `std::future<std::tuple<...>>`.
|
||||
|
||||
- **C++20 Coroutine support:**
|
||||
### In Progress (ToDo-List)
|
||||
|
||||
(`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:
|
||||
Although the library has progressed very far there are still some candies missing:
|
||||
|
||||
```cpp
|
||||
int i = co_await cti::make_continuable<int>([](auto&& promise) {
|
||||
promise.set_value(0);
|
||||
- [x] **Partial application**:
|
||||
|
||||
We could allow callbacks to be invoked with fewer arguments than expected:
|
||||
|
||||
```C++
|
||||
http_request("github.com")
|
||||
.then([]() { // ERROR: Because we expect an object accepting a std::string
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
#### Appearances:
|
||||
- [x] The **sequential operator** which invokes continuables sequentially and calls the callback with all results:
|
||||
|
||||
| [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)] |
|
||||
```c++
|
||||
(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 done sequentially and the callback is called
|
||||
// with the response of github, travis and atom as soon as atom has responded.
|
||||
// The responses of github and travis are stored meanwhile.
|
||||
});
|
||||
|
||||
.
|
||||
auto seq = cti::seq_of(http_request("github.com"), http_request("travis-ci.org"));
|
||||
```
|
||||
|
||||
This differs from the `all` connection in the way that the continuables are invoked sequentially instead of parallel.
|
||||
|
||||
|
||||
- [ ] **Inplace resolution** (makes it possible to keep the nesting level flat):
|
||||
|
||||
```c++
|
||||
http_request("github.com")
|
||||
.then([](std::string response) {
|
||||
// Do something with the response
|
||||
int response_code = get_code(response);
|
||||
|
||||
return std::make_tuple(http_request("atom.io"), response_code);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
})
|
||||
.then([](std::string atom, int response_code) {
|
||||
// - `std::string atom` was resolved from `http_request("atom.io")`
|
||||
// - `response_code` was passed through the tuple directly
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] Library support of **infinite logical connections**:
|
||||
|
||||
```c++
|
||||
std::vector<cti::continuable<int, int>> some;
|
||||
|
||||
cti::all(std::move(some))
|
||||
.then([](std::vector<int, int> result) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
This library mainly aims to support un-erased continuations, however, sometimes it's required to work with a compile-time unknown amount of continuables.
|
||||
|
||||
- [ ] Maybe **Un-erasured fail/rejection handlers** and (possible exception support):
|
||||
|
||||
```c++
|
||||
http_request("github.com")
|
||||
.rejected([](std::error_code) {
|
||||
// Is called when the request fails
|
||||
|
||||
// Potential difficult to implement with less type erasure
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Compatibility
|
||||
|
||||
Tested & compatible with:
|
||||
|
||||
- Visual Studio 2015+ Update 3
|
||||
- Clang 3.6+
|
||||
- GCC 5.0+
|
||||
|
||||
Every compiler with modern C++14 support should work.
|
||||
|
||||
The library only depends on the standard library when using the `continuable/continuable-base.hpp` header only which provides the full un-erasured support.
|
||||
|
||||
|
||||
|
||||
On Posix: don't forget to **link a corresponding thread library** into your application otherwise `std::future's` won't work `(-pthread)`.
|
||||
|
||||
## Similar implementations and alternatives
|
||||
|
||||
You already thought it, the idea isn't new and thus it is well known in the JavaScript world already.
|
||||
|
||||
There are some existing solutions with similar design thoughts already, which I don't want to hold back from you - you should choose the library fitting your needs best:
|
||||
|
||||
#### **[q (C++)](https://github.com/grantila/q)**
|
||||
|
||||
Is designed in a similar way, however, it orientates itself more on the corresponding JavaScript libraries which leaves some benefits behind we could reach with modern C++ meta-programming. Like previous approaches, the library uses type erasure excessively (and thus isn't memory allocation aware). What differentiates **q** from the continuable library is that it supports infinite logical connections and ships with built-in exception support as well as it's own executors (thread pools) - thus the library isn't header-only anymore (but the library is still proclaimed to work with other executors). My personal opinion is that a continuation library shouldn't include any other stuff then the continuation logic itself.
|
||||
|
||||
### [cpprestsdk](https://github.com/Microsoft/cpprestsdk)
|
||||
|
||||
Basically, the same arguments as explained in the q section apply to the cpprestsdk as well, it's major drawbacks is the overwhelming use of type-erasure. Probably you will benefit a lot from the library if you intend to use it's provided asynchronously *http*, *websocket* and *filesystem* functionalities. The *continuable* library was designed with different thoughts in mind - it basically provides the continuation logic without any support methods so you can embed it into your application without depending on a heavy framework. This makes it possible to apply continuation chaning to esoteric domains such as C++ AI scripts with fast or immediately response times. Who knows - maybe someone will provide *continuable* wrappers for open-source libraries like *asio*, libuv or *uWebSockets* in the future too.
|
||||
|
||||
### Others
|
||||
|
||||
If I forget to mention a library here let me know, so we can add the alternatives.
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The continuable library is licensed under the MIT License
|
||||
43
appveyor.yml
43
appveyor.yml
@ -1,43 +0,0 @@
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
- Visual Studio 2019
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- WITH_NO_EXCEPTIONS: OFF
|
||||
WITH_CPP_LATEST: OFF
|
||||
- WITH_NO_EXCEPTIONS: ON
|
||||
WITH_CPP_LATEST: OFF
|
||||
- WITH_NO_EXCEPTIONS: OFF
|
||||
WITH_CPP_LATEST: ON
|
||||
- WITH_NO_EXCEPTIONS: ON
|
||||
WITH_CPP_LATEST: ON
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
clone_script:
|
||||
- cmd: git clone -q --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER%
|
||||
- cmd: cd %APPVEYOR_BUILD_FOLDER%
|
||||
- cmd: git checkout -qf %APPVEYOR_REPO_COMMIT%
|
||||
- cmd: git submodule update --init --recursive
|
||||
|
||||
before_build:
|
||||
- cmd: >
|
||||
cmake -H. -Bbuild -A%PLATFORM%
|
||||
-DCTI_CONTINUABLE_WITH_NO_EXCEPTIONS=%WITH_NO_EXCEPTIONS%
|
||||
-DCTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE=ON
|
||||
-DCTI_CONTINUABLE_WITH_CPP_LATEST=%WITH_CPP_LATEST%
|
||||
|
||||
build_script:
|
||||
- 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 Debug -V .
|
||||
- cmd: ctest -C Release -V .
|
||||
|
||||
artifacts:
|
||||
- path: 'build/*.zip'
|
||||
@ -1,27 +1 @@
|
||||
|
||||
# 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.
|
||||
|
||||
add_library(continuable-features-flags INTERFACE)
|
||||
add_library(continuable-features-warnings INTERFACE)
|
||||
add_library(continuable-features-noexcept INTERFACE)
|
||||
|
||||
include(cmake/configure_compiler.cmake)
|
||||
include(cmake/configure_macros.cmake)
|
||||
|
||||
@ -1,35 +1,8 @@
|
||||
|
||||
# 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.
|
||||
|
||||
# Enable full warnings
|
||||
target_compile_options(continuable-features-warnings
|
||||
INTERFACE
|
||||
-Wall
|
||||
-pedantic
|
||||
-Wextra)
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
|
||||
target_compile_options(continuable-features-noexcept
|
||||
INTERFACE
|
||||
-fno-exceptions)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra")
|
||||
|
||||
if (TESTS_NO_EXCEPTIONS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
message(STATUS "Clang: Disabled exceptions")
|
||||
endif()
|
||||
|
||||
|
||||
@ -1,35 +1,7 @@
|
||||
|
||||
# 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.
|
||||
|
||||
# Enable full warnings
|
||||
target_compile_options(continuable-features-warnings
|
||||
INTERFACE
|
||||
-Wall
|
||||
-pedantic
|
||||
-Wextra)
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
|
||||
target_compile_options(continuable-features-noexcept
|
||||
INTERFACE
|
||||
-fno-exceptions)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra")
|
||||
|
||||
if (TESTS_NO_EXCEPTIONS)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
message(STATUS "GCC: Disabled exceptions")
|
||||
endif()
|
||||
|
||||
@ -1,23 +1,8 @@
|
||||
|
||||
# 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.
|
||||
if (${MSVC_VERSION} LESS 1900)
|
||||
message(FATAL_ERROR "You are using an unsupported version of Visual Studio "
|
||||
"which doesn't support all required C++11 features. "
|
||||
"(Visual Studio 2015 (version >= 1900) is required!)")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SIZEOF_VOID_P MATCHES 8)
|
||||
set(PLATFORM 64)
|
||||
@ -26,41 +11,13 @@ else()
|
||||
endif()
|
||||
|
||||
if (PLATFORM EQUAL 64)
|
||||
target_compile_definitions(continuable-features-flags
|
||||
INTERFACE
|
||||
-D_WIN64)
|
||||
add_definitions("-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
|
||||
/bigobj
|
||||
/permissive-)
|
||||
|
||||
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
|
||||
-D_HAS_EXCEPTIONS=0)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP /bigobj")
|
||||
|
||||
if (TESTS_NO_EXCEPTIONS)
|
||||
add_definitions(-D_HAS_EXCEPTIONS=0)
|
||||
string(REGEX REPLACE "/GX" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
||||
message(STATUS "MSVC: Disabled exceptions")
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
|
||||
check_required_components(@PROJECT_NAME@)
|
||||
@ -1,32 +1,11 @@
|
||||
|
||||
# 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.
|
||||
|
||||
# Select the compiler specific cmake file
|
||||
set(MSVC_ID "MSVC")
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "(Apple)?Clang")
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/compiler/clang.cmake)
|
||||
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/compiler/clang.cmake)
|
||||
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/compiler/gcc.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/compiler/gcc.cmake)
|
||||
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL ${MSVC_ID})
|
||||
include(${PROJECT_SOURCE_DIR}/cmake/compiler/msvc.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/compiler/msvc.cmake)
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown compiler!")
|
||||
endif()
|
||||
|
||||
@ -1,22 +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(${PROJECT_SOURCE_DIR}/cmake/macros/group_sources.cmake)
|
||||
@ -1,60 +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.
|
||||
|
||||
set(WITH_SOURCE_TREE "hierarchical")
|
||||
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} ${dir}/*)
|
||||
|
||||
foreach(element ${elements})
|
||||
# Extract filename and directory
|
||||
get_filename_component(element_name ${element} NAME)
|
||||
get_filename_component(element_dir ${element} DIRECTORY)
|
||||
|
||||
if (NOT ${element_dir} STREQUAL "")
|
||||
# If the file is in a subdirectory use it as source group.
|
||||
if (${WITH_SOURCE_TREE} STREQUAL "flat")
|
||||
# Build flat structure by using only the first subdirectory.
|
||||
string(FIND ${element_dir} "/" delemiter_pos)
|
||||
if (NOT ${delemiter_pos} EQUAL -1)
|
||||
string(SUBSTRING ${element_dir} 0 ${delemiter_pos} group_name)
|
||||
source_group("${group_name}" FILES ${dir}/${element})
|
||||
else()
|
||||
# Build hierarchical structure.
|
||||
# File is in root directory.
|
||||
source_group("${element_dir}" FILES ${dir}/${element})
|
||||
endif()
|
||||
else()
|
||||
# Use the full hierarchical structure to build source_groups.
|
||||
string(REPLACE "/" "\\" group_name ${element_dir})
|
||||
source_group("${group_name}" FILES ${dir}/${element})
|
||||
endif()
|
||||
else()
|
||||
# If the file is in the root directory, place it in the root source_group.
|
||||
source_group("\\" FILES ${dir}/${element})
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endif()
|
||||
endmacro()
|
||||
37
conanfile.py
37
conanfile.py
@ -1,37 +0,0 @@
|
||||
#!/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,25 +1,7 @@
|
||||
if(NOT TARGET function2::function2)
|
||||
if(NOT TARGET gtest)
|
||||
add_subdirectory(googletest)
|
||||
endif()
|
||||
|
||||
if(NOT TARGET function2)
|
||||
add_subdirectory(function2)
|
||||
endif()
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_BENCHMARKS)
|
||||
if(NOT TARGET gtest)
|
||||
add_subdirectory(googletest)
|
||||
endif()
|
||||
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,40 +0,0 @@
|
||||
add_library(asio STATIC
|
||||
${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}/include)
|
||||
|
||||
target_compile_definitions(asio
|
||||
PUBLIC
|
||||
-DASIO_STANDALONE=1
|
||||
-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
|
||||
-D_WIN32_WINNT=0x0501)
|
||||
endif()
|
||||
|
||||
target_compile_features(asio
|
||||
PUBLIC
|
||||
cxx_alias_templates
|
||||
cxx_auto_type
|
||||
cxx_decltype
|
||||
cxx_final
|
||||
cxx_lambdas
|
||||
cxx_variadic_templates
|
||||
cxx_defaulted_functions
|
||||
cxx_nullptr)
|
||||
@ -1 +0,0 @@
|
||||
Subproject commit 6c054e98f3f53352d12b6cd46d63b6d404cc044b
|
||||
@ -1,47 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,9 +0,0 @@
|
||||
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 +0,0 @@
|
||||
Subproject commit b874e72208b6e21b62287942e5e3b11f6630107f
|
||||
@ -1,43 +0,0 @@
|
||||
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 +1 @@
|
||||
Subproject commit 3a0746bf5f601dfed05330aefcb6854354fce07d
|
||||
Subproject commit dfb91a53cbac35ac4800aac058e0905f0384a431
|
||||
@ -1,4 +1,3 @@
|
||||
if(ON)
|
||||
add_library(gtest STATIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc)
|
||||
|
||||
@ -43,10 +42,3 @@ 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 f2fb48c3b3d79a75a88a99fba6576b25d42ec528
|
||||
Subproject commit 51143d5b62521f71020ada4ba1b6b44f3a6749bb
|
||||
1
doc/.gitignore
vendored
1
doc/.gitignore
vendored
@ -1 +0,0 @@
|
||||
doxygen/
|
||||
92
doc/Doxyfile
92
doc/Doxyfile
@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.14
|
||||
# Doxyfile 1.8.13
|
||||
|
||||
# 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
|
||||
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
|
||||
# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
|
||||
# for the list of possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# title of most generated pages and in a few other places.
|
||||
# The default value is: My Project.
|
||||
|
||||
PROJECT_NAME = Continuable
|
||||
PROJECT_NAME = continuable
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 4.1.0
|
||||
PROJECT_NUMBER =
|
||||
|
||||
# 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 = "C++14 asynchronous allocation aware futures"
|
||||
PROJECT_BRIEF = "Async C++14 platform independent continuation chainer providing light and 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
|
||||
@ -337,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:
|
||||
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
|
||||
# http://www.riverbankcomputing.co.uk/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.
|
||||
@ -623,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 = NO
|
||||
GENERATE_TODOLIST = YES
|
||||
|
||||
# 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 = NO
|
||||
GENERATE_TESTLIST = YES
|
||||
|
||||
# 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 = NO
|
||||
GENERATE_BUGLIST = YES
|
||||
|
||||
# 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
|
||||
@ -680,7 +680,7 @@ SHOW_FILES = YES
|
||||
# Folder Tree View (if specified).
|
||||
# The default value is: YES.
|
||||
|
||||
SHOW_NAMESPACES = YES
|
||||
SHOW_NAMESPACES = NO
|
||||
|
||||
# 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
|
||||
@ -708,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 https://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# to be installed. See also http://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.
|
||||
@ -791,12 +791,12 @@ WARN_LOGFILE =
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../include \
|
||||
../doc
|
||||
Index.md
|
||||
|
||||
# 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: https://www.gnu.org/software/libiconv/) for the list of
|
||||
# documentation (see: http://www.gnu.org/software/libiconv) for the list of
|
||||
# possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
@ -858,7 +858,6 @@ FILE_PATTERNS = *.c \
|
||||
*.ucf \
|
||||
*.qsf \
|
||||
*.as \
|
||||
*.dox \
|
||||
*.js
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
@ -985,7 +984,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 =
|
||||
USE_MDFILE_AS_MAINPAGE = Index.md
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
@ -1046,7 +1045,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 https://www.gnu.org/software/global/global.html). You will need version
|
||||
# (see http://www.gnu.org/software/global/global.html). You will need version
|
||||
# 4.8.6 or higher.
|
||||
#
|
||||
# To use it do the following:
|
||||
@ -1092,17 +1091,6 @@ 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
|
||||
#---------------------------------------------------------------------------
|
||||
@ -1221,7 +1209,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
|
||||
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
|
||||
# http://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.
|
||||
@ -1257,17 +1245,6 @@ 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.
|
||||
@ -1291,12 +1268,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: https://developer.apple.com/tools/xcode/), introduced with
|
||||
# environment (see: http://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 https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
|
||||
# startup. See http://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.
|
||||
@ -1412,7 +1389,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://doc.qt.io/qt-4.8/qthelpproject.html#namespace).
|
||||
# (see: http://qt-project.org/doc/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.
|
||||
|
||||
@ -1420,7 +1397,8 @@ 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://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders).
|
||||
# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
|
||||
# folders).
|
||||
# The default value is: doc.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@ -1428,21 +1406,23 @@ 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://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# Filters (see: http://qt-project.org/doc/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://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# Filters (see: http://qt-project.org/doc/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://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
@ -1535,7 +1515,7 @@ EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||
# Use the FORMULA_TRANPARENT 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.
|
||||
#
|
||||
@ -1547,7 +1527,7 @@ FORMULA_FONTSIZE = 10
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# https://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# http://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
|
||||
@ -1574,7 +1554,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 https://www.mathjax.org before deployment.
|
||||
# MathJax from http://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.
|
||||
|
||||
@ -1636,7 +1616,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: https://xapian.org/).
|
||||
# Xapian (see: http://xapian.org/).
|
||||
#
|
||||
# See the section "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
@ -1649,7 +1629,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: https://xapian.org/). See the section "External Indexing and
|
||||
# Xapian (see: http://xapian.org/). See the section "External Indexing and
|
||||
# Searching" for details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
@ -1836,7 +1816,7 @@ 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
|
||||
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# http://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.
|
||||
|
||||
@ -2019,9 +1999,9 @@ DOCBOOK_PROGRAMLISTING = NO
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||
# 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.
|
||||
# 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.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
|
||||
20
doc/Index.md
Normal file
20
doc/Index.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Documentation of continuable
|
||||
|
||||
This documentation covers the continuable library in detail
|
||||
|
||||
## Content
|
||||
|
||||
- Class cti::continuable_base - main class for continuation chaining.
|
||||
- \link cti::continuable_base::then then\endlink - adds a callback or cti::continuable_base to the invocation chain.
|
||||
- \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::release release\endlink - releases the cti::continuable_base and prevents the automatic invocation on destruction.
|
||||
- Helper functions
|
||||
- \link cti::make_continuable make_continuable\endlink - creates a cti::continuable_base from a callback tanking function.
|
||||
- \link cti::all_of all_of\endlink - connects all given cti::continuable_base objects with an *all* logic.
|
||||
- \link cti::any_of any_of\endlink - connects all given cti::continuable_base objects with an *any* logic.
|
||||
- GTest macros:
|
||||
- \link EXPECT_ASYNC_RESULT EXPECT_ASYNC_RESULT\endlink - Expects that the given continuable is finished with the given result.
|
||||
- \link ASSERT_ASYNC_RESULT ASSERT_ASYNC_RESULT\endlink - Asserts that the given continuable is finished with the given result.
|
||||
- \link ASSERT_ASYNC_TYPES ASSERT_ASYNC_TYPES\endlink - Asserts that the given continuable is finished with the given types without validating it against equality.
|
||||
|
||||
@ -1,303 +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.
|
||||
*/
|
||||
|
||||
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,41 +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.
|
||||
*/
|
||||
|
||||
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. |
|
||||
|
||||
*/
|
||||
}
|
||||
101
doc/index.dox
101
doc/index.dox
@ -1,101 +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.
|
||||
*/
|
||||
|
||||
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.
|
||||
>
|
||||
|
||||
*/
|
||||
}
|
||||
@ -1,157 +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.
|
||||
*/
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB |
@ -1,129 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@ -1,215 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@ -1,212 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
|
||||
*/
|
||||
}
|
||||
@ -1,142 +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.
|
||||
*/
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
}
|
||||
@ -1,99 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
*/
|
||||
}
|
||||
@ -1,84 +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.
|
||||
*/
|
||||
|
||||
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
|
||||
*/
|
||||
}
|
||||
@ -1,41 +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.
|
||||
*/
|
||||
|
||||
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,3 +1 @@
|
||||
add_subdirectory(example-asio)
|
||||
add_subdirectory(example-ai)
|
||||
add_subdirectory(example-slideshow)
|
||||
add_subdirectory(documentation)
|
||||
|
||||
5
examples/documentation/CMakeLists.txt
Normal file
5
examples/documentation/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
add_executable(example-documentation
|
||||
${CMAKE_CURRENT_LIST_DIR}/example-documentation.cpp)
|
||||
target_link_libraries(example-documentation
|
||||
PRIVATE
|
||||
continuable)
|
||||
145
examples/documentation/example-documentation.cpp
Normal file
145
examples/documentation/example-documentation.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
/**
|
||||
Copyright(c) 2015 - 2017 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 "continuable/continuable.hpp"
|
||||
|
||||
using cti::detail::util::unused;
|
||||
|
||||
void creating_continuables() {
|
||||
auto void_continuable = cti::make_continuable<void>([](auto&& callback) {
|
||||
// ^^^^
|
||||
|
||||
// Call the callback later when you have finished your work
|
||||
callback();
|
||||
});
|
||||
|
||||
auto str_continuable =
|
||||
cti::make_continuable<std::string>([](auto&& callback) {
|
||||
// ^^^^^^^^^^^
|
||||
callback("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("<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::all_of(http_request("github.com"), http_request("travis-ci.org"));
|
||||
auto any =
|
||||
cti::any_of(http_request("github.com"), http_request("travis-ci.org"));
|
||||
}
|
||||
|
||||
int main() {
|
||||
creating_continuables();
|
||||
|
||||
providing_helper_functions();
|
||||
|
||||
chaining_continuables();
|
||||
|
||||
connecting_continuables();
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.ClassCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.EnumCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.FunctionCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.MemberCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.UnionCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.VariableCase
|
||||
value: lower_case
|
||||
@ -1,14 +0,0 @@
|
||||
add_executable(example-ai
|
||||
${CMAKE_CURRENT_LIST_DIR}/example-ai.cpp)
|
||||
|
||||
target_include_directories(example-ai
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_link_libraries(example-ai
|
||||
PRIVATE
|
||||
continuable)
|
||||
|
||||
target_compile_definitions(example-asio
|
||||
PUBLIC
|
||||
-DCONTINUABLE_WITH_NO_EXCEPTIONS)
|
||||
@ -1,77 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v3.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 <chrono>
|
||||
#include <string>
|
||||
|
||||
#include "continuable/continuable.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
struct Position {
|
||||
float x, y, z, o;
|
||||
};
|
||||
|
||||
struct Unit {
|
||||
virtual ~Unit() = default;
|
||||
};
|
||||
|
||||
struct UnitAI {
|
||||
virtual ~UnitAI() = default;
|
||||
|
||||
virtual void OnEnterCombat() {
|
||||
}
|
||||
};
|
||||
|
||||
struct CreatureAI : UnitAI {
|
||||
virtual cti::continuable<> MoveTo(Position pos) = 0;
|
||||
|
||||
virtual cti::continuable<> CastSpell(unsigned id, Unit* target) = 0;
|
||||
|
||||
virtual cti::continuable<> Say(std::string text,
|
||||
std::chrono::milliseconds duration) = 0;
|
||||
};
|
||||
|
||||
struct MyCreatureAI : CreatureAI {
|
||||
void OnEnterCombat() override {
|
||||
CastSpell(3736, nullptr)
|
||||
.then(MoveTo({0, 0, 0, 0}) && Say("Walking...!", 5s))
|
||||
.then(Say("Done!", 5s))
|
||||
.fail(Say("Interrupted!", 5s));
|
||||
}
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
|
||||
// CreatureAI* ai = nullptr;
|
||||
// ai->OnEnterCombat();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
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_link_libraries(example-asio
|
||||
PRIVATE
|
||||
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)
|
||||
@ -1,176 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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();
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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 <array>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#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_;
|
||||
|
||||
functional_io_service() : resolver_(service_) {
|
||||
}
|
||||
|
||||
auto trough_post() noexcept {
|
||||
return [&](auto&& work) mutable {
|
||||
asio::post(service_,
|
||||
[work = std::forward<decltype(work)>(work)]() mutable {
|
||||
std::move(work)();
|
||||
// .. or: work.set_value();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
auto wait_async(std::chrono::milliseconds /*time*/) {
|
||||
return cti::make_continuable<void>([](auto&& promise) {
|
||||
// ...
|
||||
promise.set_value();
|
||||
});
|
||||
}
|
||||
|
||||
auto async_resolve(std::string host, std::string service) {
|
||||
return cti::promisify<asio::ip::udp::resolver::iterator>::with(
|
||||
error_code_remapper(),
|
||||
[&](auto&&... args) {
|
||||
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
std::move(host), std::move(service));
|
||||
}
|
||||
|
||||
void run() {
|
||||
service_.run();
|
||||
}
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
using asio::ip::udp;
|
||||
|
||||
functional_io_service service;
|
||||
|
||||
service.async_resolve("127.0.0.1", "daytime")
|
||||
.then(
|
||||
[](udp::resolver::iterator iterator) {
|
||||
// ...
|
||||
return *iterator;
|
||||
},
|
||||
service.trough_post())
|
||||
.then([](udp::endpoint /*endpoint*/) {
|
||||
// auto socket = std::make_shared<udp::socket>(service);
|
||||
// socket->async_send_to()
|
||||
})
|
||||
.fail([](cti::exception_t /*error*/) {
|
||||
// ...
|
||||
});
|
||||
|
||||
service.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
add_executable(example-slideshow
|
||||
${CMAKE_CURRENT_LIST_DIR}/example-slideshow.cpp)
|
||||
target_link_libraries(example-slideshow
|
||||
PRIVATE
|
||||
continuable)
|
||||
@ -1,85 +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 <string>
|
||||
|
||||
#include "continuable/continuable.hpp"
|
||||
|
||||
cti::continuable<std::string> http_request(std::string /*url*/) {
|
||||
return cti::make_ready_continuable<std::string>("<html>...</html>");
|
||||
}
|
||||
|
||||
struct ResultSet {};
|
||||
struct Buffer {};
|
||||
|
||||
cti::continuable<ResultSet> mysql_query(std::string /*url*/) {
|
||||
return cti::make_ready_continuable(ResultSet{});
|
||||
}
|
||||
|
||||
cti::continuable<Buffer> read_file(std::string /*url*/) {
|
||||
return cti::make_ready_continuable(Buffer{});
|
||||
}
|
||||
|
||||
struct functional_executor {
|
||||
auto post() const {
|
||||
return [](auto&&) {};
|
||||
}
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
functional_executor e;
|
||||
auto executor = &e;
|
||||
|
||||
// clang-format off
|
||||
|
||||
(http_request("github.com") && http_request("atom.io"))
|
||||
.then([] (std::string /*github*/, std::string /*atom*/) {
|
||||
// ...
|
||||
return mysql_query("select * from `users`");
|
||||
})
|
||||
.then([] (ResultSet /*result*/) {
|
||||
// ...
|
||||
}, executor->post());
|
||||
|
||||
// clang-format on
|
||||
|
||||
http_request("github.com") && http_request("atom.io");
|
||||
|
||||
http_request("github.com") || http_request("atom.io");
|
||||
|
||||
http_request("github.com") >> http_request("atom.io");
|
||||
|
||||
// clang-format off
|
||||
|
||||
read_file("entries.csv")
|
||||
.then([] (Buffer /*buffer*/) {
|
||||
// ...
|
||||
return std::make_tuple("hey", true, 0);
|
||||
})
|
||||
.then([] (std::string /*msg*/) {
|
||||
// ...
|
||||
});
|
||||
|
||||
// clang-format on
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,301 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_CONNECTIONS_HPP_INCLUDED
|
||||
#define CONTINUABLE_CONNECTIONS_HPP_INCLUDED
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#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
|
||||
/// with the aggregated result of all continuables.
|
||||
///
|
||||
/// \param args Arbitrary arguments which are connected.
|
||||
/// Every type is allowed as arguments, continuables may be
|
||||
/// contained inside tuple like types (`std::tuple`)
|
||||
/// or in homogeneous containers such as `std::vector`.
|
||||
/// Non continuable arguments are preserved and passed
|
||||
/// to the final result as shown below:
|
||||
/// ```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) {
|
||||
/// // ...
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \see continuable_base::operator&& for details.
|
||||
///
|
||||
/// \since 1.1.0
|
||||
template <typename... Args>
|
||||
auto when_all(Args&&... args) {
|
||||
return detail::connection::apply_connection(
|
||||
detail::connection::connection_strategy_all_tag{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Connects the given arguments with an all logic.
|
||||
/// The content of the iterator is moved out and converted
|
||||
/// to a temporary `std::vector` which is then passed to when_all.
|
||||
///
|
||||
/// ```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));
|
||||
///
|
||||
/// cti::when_all(v.begin(), v.end())
|
||||
/// .then([](std::vector<int> r01) {
|
||||
/// // ...
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \param begin The begin iterator to the range which will be moved out
|
||||
/// and used as the arguments to the all connection
|
||||
///
|
||||
/// \param end The end iterator to the range which will be moved out
|
||||
/// and used as the arguments to the all connection
|
||||
///
|
||||
/// \see when_all for details.
|
||||
///
|
||||
/// \attention Prefer to invoke when_all with the whole container the
|
||||
/// iterators were taken from, since this saves us
|
||||
/// the creation of a temporary storage.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <
|
||||
typename Iterator,
|
||||
std::enable_if_t<detail::range::is_iterator<Iterator>::value>* = nullptr>
|
||||
auto when_all(Iterator begin, Iterator end) {
|
||||
return when_all(detail::range::persist_range(begin, end));
|
||||
}
|
||||
|
||||
/// Connects the given arguments with a sequential logic.
|
||||
/// All continuables contained inside the given nested pack are
|
||||
/// invoked one after one. On completion the final handler is called
|
||||
/// with the aggregated result of all continuables.
|
||||
///
|
||||
/// \param args Arbitrary arguments which are connected.
|
||||
/// Every type is allowed as arguments, continuables may be
|
||||
/// contained inside tuple like types (`std::tuple`)
|
||||
/// or in homogeneous containers such as `std::vector`.
|
||||
/// Non continuable arguments are preserved and passed
|
||||
/// to the final result as shown below:
|
||||
/// ```cpp
|
||||
/// cti::when_seq(
|
||||
/// 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) {
|
||||
/// // ...
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \see continuable_base::operator>> for details.
|
||||
///
|
||||
/// \since 1.1.0
|
||||
template <typename... Args>
|
||||
auto when_seq(Args&&... args) {
|
||||
return detail::connection::apply_connection(
|
||||
detail::connection::connection_strategy_seq_tag{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Connects the given arguments with a sequential logic.
|
||||
/// The content of the iterator is moved out and converted
|
||||
/// to a temporary `std::vector` which is then passed to when_seq.
|
||||
///
|
||||
/// ```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));
|
||||
///
|
||||
/// cti::when_seq(v.begin(), v.end())
|
||||
/// .then([](std::vector<int> r01) {
|
||||
/// // ...
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \param begin The begin iterator to the range which will be moved out
|
||||
/// and used as the arguments to the sequential connection
|
||||
///
|
||||
/// \param end The end iterator to the range which will be moved out
|
||||
/// and used as the arguments to the sequential connection
|
||||
///
|
||||
/// \see when_seq for details.
|
||||
///
|
||||
/// \attention Prefer to invoke when_seq with the whole container the
|
||||
/// iterators were taken from, since this saves us
|
||||
/// the creation of a temporary storage.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <
|
||||
typename Iterator,
|
||||
std::enable_if_t<detail::range::is_iterator<Iterator>::value>* = nullptr>
|
||||
auto when_seq(Iterator begin, Iterator end) {
|
||||
return when_seq(detail::range::persist_range(begin, end));
|
||||
}
|
||||
|
||||
/// Connects the given arguments with an any logic.
|
||||
/// All continuables contained inside the given nested pack are
|
||||
/// invoked at once. On completion of one continuable the final handler
|
||||
/// is called with the result of the resolved continuable.
|
||||
///
|
||||
/// \param args Arbitrary arguments which are connected.
|
||||
/// Every type is allowed as arguments, continuables may be
|
||||
/// contained inside tuple like types (`std::tuple`)
|
||||
/// or in homogeneous containers such as `std::vector`.
|
||||
/// Non continuable arguments are preserved and passed
|
||||
/// to the final result as shown below:
|
||||
/// ```cpp
|
||||
/// cti::when_any(
|
||||
/// 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) {
|
||||
/// // ...
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \see continuable_base::operator|| for details.
|
||||
///
|
||||
/// \since 1.1.0
|
||||
template <typename... Args>
|
||||
auto when_any(Args&&... args) {
|
||||
return detail::connection::apply_connection(
|
||||
detail::connection::connection_strategy_any_tag{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Connects the given arguments with an any logic.
|
||||
/// The content of the iterator is moved out and converted
|
||||
/// to a temporary `std::vector` which is then passed to when_all.
|
||||
///
|
||||
/// ```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));
|
||||
///
|
||||
/// cti::when_any(v.begin(), v.end())
|
||||
/// .then([](int r01) {
|
||||
/// // ...
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \param begin The begin iterator to the range which will be moved out
|
||||
/// and used as the arguments to the all connection
|
||||
///
|
||||
/// \param end The end iterator to the range which will be moved out
|
||||
/// and used as the arguments to the all connection
|
||||
///
|
||||
/// \see when_any for details.
|
||||
///
|
||||
/// \attention Prefer to invoke when_any with the whole container the
|
||||
/// iterators were taken from, since this saves us
|
||||
/// the creation of a temporary storage.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <
|
||||
typename Iterator,
|
||||
std::enable_if_t<detail::range::is_iterator<Iterator>::value>* = nullptr>
|
||||
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_CONNECTIONS_HPP_INCLUDED
|
||||
@ -1,97 +0,0 @@
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,41 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,141 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,215 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_PROMISE_BASE_HPP_INCLUDED
|
||||
#define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#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>
|
||||
class promise_base
|
||||
/// \cond false
|
||||
;
|
||||
template <typename Data, typename... Args>
|
||||
class promise_base<Data, detail::identity<Args...>>
|
||||
: detail::util::non_copyable
|
||||
/// \endcond
|
||||
{ // clang-format on
|
||||
|
||||
/// \cond false
|
||||
// The callback type
|
||||
Data data_;
|
||||
/// \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<
|
||||
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()(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(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
|
||||
@ -1,120 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_PROMISIFY_HPP_INCLUDED
|
||||
#define CONTINUABLE_PROMISIFY_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#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`: Converts callback taking callable types into continuables
|
||||
/// which pass an error code as first parameter and the rest of
|
||||
/// the result afterwards.
|
||||
///
|
||||
/// \tparam Result The result of the converted continuable, this should align
|
||||
/// with the arguments that are passed to the callback.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename... Result>
|
||||
class promisify {
|
||||
using helper = detail::convert::promisify_helper<Result...>;
|
||||
|
||||
public:
|
||||
/// Converts callback taking callable types into a continuable.
|
||||
/// This applies to calls which pass an error code as first parameter
|
||||
/// and the rest of the asynchronous result afterwards.
|
||||
///
|
||||
/// 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(
|
||||
/// [&](auto&&... args) {
|
||||
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
|
||||
/// },
|
||||
/// std::move(host), std::move(service));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// 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(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
|
||||
@ -1,356 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
228
include/continuable/continuable-testing.hpp
Normal file
228
include/continuable/continuable-testing.hpp
Normal file
@ -0,0 +1,228 @@
|
||||
|
||||
/**
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v0.8.0
|
||||
|
||||
Copyright(c) 2015 - 2017 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_TESTING_HPP_INCLUDED__
|
||||
#define CONTINUABLE_TESTING_HPP_INCLUDED__
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "continuable/continuable-base.hpp"
|
||||
|
||||
namespace cti {
|
||||
/// \cond false
|
||||
inline namespace abi_v1 {
|
||||
/// \endcond
|
||||
namespace detail {
|
||||
namespace testing {
|
||||
template <typename C> void expect_async_completion(C&& continuable) {
|
||||
auto called = std::make_shared<bool>(false);
|
||||
std::forward<C>(continuable).then([called](auto&&... args) {
|
||||
ASSERT_FALSE(*called);
|
||||
*called = true;
|
||||
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
});
|
||||
EXPECT_TRUE(called);
|
||||
}
|
||||
|
||||
template <typename C> void expect_async_incomplete(C&& continuable) {
|
||||
std::forward<C>(continuable).then([](auto&&... args) {
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
|
||||
FAIL();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename C, typename V>
|
||||
void expect_async_validation(C&& continuable, V&& validator) {
|
||||
expect_async_completion(
|
||||
std::forward<C>(continuable)
|
||||
.then([validator =
|
||||
std::forward<V>(validator)](auto&&... args) mutable {
|
||||
|
||||
validator(std::forward<decltype(args)>(args)...);
|
||||
}));
|
||||
}
|
||||
|
||||
/// Expects that the continuable is finished with the given arguments
|
||||
template <typename V, typename C, typename... Args>
|
||||
void expect_async_binary_validation(V&& validator, C&& continuable,
|
||||
Args&&... expected) {
|
||||
expect_async_validation(std::forward<C>(continuable), [
|
||||
expected_pack = std::make_tuple(std::forward<Args>(expected)...),
|
||||
validator = std::forward<V>(validator)
|
||||
](auto&&... args) mutable {
|
||||
|
||||
auto actual_pack = std::make_tuple(std::forward<decltype(args)>(args)...);
|
||||
|
||||
auto size = util::pack_size_of(util::identity_of(expected_pack));
|
||||
|
||||
static_assert(size.value == std::tuple_size<decltype(actual_pack)>::value,
|
||||
"Async completion handler called with a different count "
|
||||
"of arguments!");
|
||||
|
||||
util::static_for_each_in(
|
||||
std::make_index_sequence<size.value>{}, [&](auto current) mutable {
|
||||
auto expected = std::get<current.value>(std::move(expected_pack));
|
||||
auto actual = std::get<current.value>(std::move(actual_pack));
|
||||
(void)current;
|
||||
|
||||
validator(expected, actual);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
inline auto expecting_eq_check() {
|
||||
return [](auto expected, auto actual) { EXPECT_EQ(expected, actual); };
|
||||
}
|
||||
|
||||
inline auto asserting_eq_check() {
|
||||
return [](auto expected, auto actual) { ASSERT_EQ(expected, actual); };
|
||||
}
|
||||
|
||||
template <typename C, typename... Args>
|
||||
void assert_async_types(C&& continuable, util::identity<Args...> expected) {
|
||||
expect_async_validation(
|
||||
std::forward<C>(continuable), [&](auto... actualPack) {
|
||||
auto actual = util::identity<decltype(actualPack)...>{};
|
||||
util::unused(expected, actual,
|
||||
std::forward<decltype(actualPack)>(actualPack)...);
|
||||
|
||||
static_assert(
|
||||
std::is_same<std::decay_t<decltype(expected)>,
|
||||
std::decay_t<decltype(actual)>>::value,
|
||||
"The called arguments don't match with the expected ones!");
|
||||
});
|
||||
}
|
||||
} // end namespace testing
|
||||
} // end namespace detail
|
||||
/// \cond false
|
||||
} // end inline namespace abi_...
|
||||
/// \endcond
|
||||
} // end namespace cti
|
||||
|
||||
/// Expects the final callback of the given continuable to be called
|
||||
/// with any result.
|
||||
///
|
||||
/// \since version 1.0.0
|
||||
#define EXPECT_ASYNC_COMPLETION(CONTINUABLE) \
|
||||
cti::detail::testing::expect_async_completion(CONTINUABLE);
|
||||
|
||||
/// Expects the final callback of the given continuable is never called
|
||||
/// with any result.
|
||||
///
|
||||
/// \since version 1.0.0
|
||||
#define EXPECT_ASYNC_INCOMPLETE(CONTINUABLE) \
|
||||
cti::detail::testing::expect_async_incomplete(CONTINUABLE);
|
||||
|
||||
/// Expects the continuation to be called and forwards it's arguments to
|
||||
/// the given validator which can then do assertions on the result.
|
||||
#define EXPECT_ASYNC_VALIDATION(CONTINUABLE, VALIDATOR) \
|
||||
cti::detail::testing::expect_async_validation(CONTINUABLE, VALIDATOR);
|
||||
|
||||
/// Expects the continuation to be called and forwards it's arguments to
|
||||
/// the given validator which can then do assertions on the result.
|
||||
///
|
||||
/// A validator consists of a binary consumer with a signature as in
|
||||
/// in the example shown below:
|
||||
/// ```cpp
|
||||
/// auto validator = [](auto expected, auto actual) {
|
||||
/// EXPECT_EQ(expected, actual);
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// The macro is usable as shown in the following example:
|
||||
/// ```cpp
|
||||
/// continuable<string> async_get(std::string);
|
||||
/// // ...
|
||||
/// auto validator = [](auto expected, auto actual) {
|
||||
/// EXPECT_EQ(expected, actual);
|
||||
/// };
|
||||
///
|
||||
/// EXPECT_ASYNC_BINARY_VALIDATION(validator, async_get("hello"), "hello")
|
||||
/// ```
|
||||
///
|
||||
/// The validator is called for every expecting and actual result.
|
||||
///
|
||||
/// \note This macro is mainly present for building other assertions
|
||||
/// relying on custom validation logic.
|
||||
///
|
||||
/// \since version 1.0.0
|
||||
#define EXPECT_ASYNC_BINARY_VALIDATION(VALIDATOR, ...) \
|
||||
cti::detail::testing::expect_async_binary_validation(VALIDATOR, __VA_ARGS__);
|
||||
|
||||
/// Expects that the continuable is finished with the given result
|
||||
///
|
||||
/// ```cpp
|
||||
/// continuable<string> async_get(std::string);
|
||||
/// // ...
|
||||
///
|
||||
/// EXPECT_ASYNC_RESULT(async_get("hello"), "hello");
|
||||
/// ```
|
||||
///
|
||||
/// \since version 1.0.0
|
||||
#define EXPECT_ASYNC_RESULT(...) \
|
||||
EXPECT_ASYNC_BINARY_VALIDATION(cti::detail::testing::expecting_eq_check(), \
|
||||
__VA_ARGS__)
|
||||
|
||||
/// Asserts that the continuable is finished with the given result
|
||||
///
|
||||
/// ```cpp
|
||||
/// continuable<string> async_get(std::string);
|
||||
/// // ...
|
||||
///
|
||||
/// ASSERT_ASYNC_RESULT(async_get("hello"), "hello");
|
||||
/// ```
|
||||
///
|
||||
/// \since version 1.0.0
|
||||
#define ASSERT_ASYNC_RESULT(...) \
|
||||
EXPECT_ASYNC_BINARY_VALIDATION(cti::detail::testing::asserting_eq_check(), \
|
||||
__VA_ARGS__)
|
||||
|
||||
/// Asserts that the continuable is finished with the given type of arguments
|
||||
/// without validating it against equality.
|
||||
///
|
||||
/// ```cpp
|
||||
/// continuable<string> async_get(std::string);
|
||||
/// // ...
|
||||
///
|
||||
/// ASSERT_ASYNC_TYPES(async_get("hello"), std::string);
|
||||
/// ```
|
||||
///
|
||||
/// \note This is a compile-time assertion.
|
||||
///
|
||||
/// \since version 1.0.0
|
||||
#define ASSERT_ASYNC_TYPES(CONTINUABLE, ...) \
|
||||
cti::detail::testing::assert_async_types( \
|
||||
CONTINUABLE, cti::detail::util::identity<__VA_ARGS__>{})
|
||||
|
||||
#endif // CONTINUABLE_TESTING_HPP_INCLUDED__
|
||||
@ -1,52 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TRANSFORMS_HPP_INCLUDED
|
||||
#define CONTINUABLE_TRANSFORMS_HPP_INCLUDED
|
||||
|
||||
#include <continuable/transforms/wait.hpp>
|
||||
#include <continuable/transforms/future.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// \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 {}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_TRANSFORMS_HPP_INCLUDED
|
||||
@ -1,130 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TRAVERSE_ASYNC_HPP_INCLUDED
|
||||
#define CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED
|
||||
|
||||
#include <utility>
|
||||
#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 through \ref traverse_pack_async.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
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 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 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 to \ref traverse_pack_async.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
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.
|
||||
///
|
||||
/// This function works in the same way as `traverse_pack`,
|
||||
/// however, we are able to suspend and continue the traversal at
|
||||
/// later time.
|
||||
/// Thus we require a visitor callable object which provides three
|
||||
/// `operator()` overloads as depicted by the code sample below:
|
||||
/// ```cpp
|
||||
/// struct my_async_visitor {
|
||||
/// /// The synchronous overload is called for each object,
|
||||
/// /// 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) {
|
||||
/// return true;
|
||||
/// }
|
||||
///
|
||||
/// /// The asynchronous overload this is called when the
|
||||
/// /// synchronous overload returned false.
|
||||
/// /// In addition to the current visited element the overload is
|
||||
/// /// called with a contnuation callable object which resumes the
|
||||
/// /// traversal when it's called later.
|
||||
/// /// The continuation next may be stored and called later or
|
||||
/// /// dropped completely to abort the traversal early.
|
||||
/// template <typename T, typename N>
|
||||
/// void operator()(async_traverse_detach_tag, T&& element, N&& next) {
|
||||
/// }
|
||||
///
|
||||
/// /// The overload is called when the traversal was finished.
|
||||
/// /// As argument the whole pack is passed over which we
|
||||
/// /// traversed asynchrnously.
|
||||
/// template <typename T>
|
||||
/// void operator()(async_traverse_complete_tag, T&& pack) {
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// \param visitor A visitor object which provides the three `operator()`
|
||||
/// overloads that were described above.
|
||||
/// Additionally the visitor must be compatible
|
||||
/// for referencing it from a `boost::intrusive_ptr`.
|
||||
/// The visitor should must have a virtual destructor!
|
||||
///
|
||||
/// \param pack The arbitrary parameter pack which is traversed
|
||||
/// asynchronously. Nested objects inside containers and
|
||||
/// tuple like types are traversed recursively.
|
||||
///
|
||||
/// \returns A std::shared_ptr that references an instance of
|
||||
/// the given visitor object.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
///
|
||||
/// See `traverse_pack` for a detailed description about the
|
||||
/// traversal behaviour and capabilities.
|
||||
///
|
||||
template <typename Visitor, typename... T>
|
||||
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
|
||||
@ -1,115 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TRAVERSE_HPP_INCLUDED
|
||||
#define CONTINUABLE_TRAVERSE_HPP_INCLUDED
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#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:
|
||||
/// - homogeneous containers (`std::vector`, `std::list`)
|
||||
/// - heterogenous containers `(std::tuple`, `std::pair`, `std::array`)
|
||||
/// and re-assembles the pack with the result of the mapper.
|
||||
/// Mapping from one type to a different one is supported.
|
||||
///
|
||||
/// Elements that aren't accepted by the mapper are routed through
|
||||
/// and preserved through the hierarchy.
|
||||
///
|
||||
/// ```cpp
|
||||
/// // Maps all integers to floats
|
||||
/// map_pack([](int value) {
|
||||
/// return float(value);
|
||||
/// },
|
||||
/// 1, std::make_tuple(2, std::vector<int>{3, 4}), 5);
|
||||
/// ```
|
||||
///
|
||||
/// \throws std::exception like objects which are thrown by an
|
||||
/// invocation to the mapper.
|
||||
///
|
||||
/// \param mapper A callable object, which accept an arbitrary type
|
||||
/// and maps it to another type or the same one.
|
||||
///
|
||||
/// \param pack An arbitrary variadic pack which may contain any type.
|
||||
///
|
||||
/// \returns The mapped element or in case the pack contains
|
||||
/// multiple elements, the pack is wrapped into
|
||||
/// a `std::tuple`.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
///
|
||||
template <typename Mapper, typename... T>
|
||||
/*keep this inline*/ inline decltype(auto) map_pack(Mapper&& mapper,
|
||||
T&&... pack) {
|
||||
return detail::traversal::transform(detail::traversal::strategy_remap_tag{},
|
||||
std::forward<Mapper>(mapper),
|
||||
std::forward<T>(pack)...);
|
||||
}
|
||||
|
||||
/// Indicate that the result shall be spread across the parent container
|
||||
/// if possible. This can be used to create a mapper function used
|
||||
/// in map_pack that maps one element to an arbitrary count (1:n).
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename... T>
|
||||
constexpr detail::traversal::spreading::spread_box<std::decay_t<T>...>
|
||||
spread_this(T&&... args) noexcept(
|
||||
noexcept(std::make_tuple(std::forward<T>(args)...))) {
|
||||
using type = detail::traversal::spreading::spread_box<std::decay_t<T>...>;
|
||||
return type(std::make_tuple(std::forward<T>(args)...));
|
||||
}
|
||||
|
||||
/// Traverses the pack with the given visitor.
|
||||
///
|
||||
/// This function works in the same way as `map_pack`,
|
||||
/// however, the result of the mapper isn't preserved.
|
||||
///
|
||||
/// See `map_pack` for a detailed description.
|
||||
///
|
||||
/// \since 3.0.0
|
||||
template <typename Mapper, typename... T>
|
||||
void traverse_pack(Mapper&& mapper, T&&... pack) {
|
||||
detail::traversal::transform(detail::traversal::strategy_traverse_tag{},
|
||||
std::forward<Mapper>(mapper),
|
||||
std::forward<T>(pack)...);
|
||||
}
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_TRAVERSE_HPP_INCLUDED
|
||||
@ -1,102 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TYPES_HPP_INCLUDED
|
||||
#define CONTINUABLE_TYPES_HPP_INCLUDED
|
||||
|
||||
#include <function2/function2.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 {
|
||||
/// \defgroup Types Types
|
||||
/// provides the \link cti::continuable continuable\endlink and \link
|
||||
/// cti::promise promise\endlink facility 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.
|
||||
///
|
||||
/// \since 4.0.0
|
||||
template <typename... Args>
|
||||
using continuation_capacity = detail::erasure::continuation_capacity<Args...>;
|
||||
|
||||
/// Defines a non-copyable continuation type which uses the
|
||||
/// function2 backend for type erasure.
|
||||
///
|
||||
/// 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 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 = promise_base<detail::erasure::callback<Args...>, //
|
||||
signature_arg_t<Args...>>;
|
||||
|
||||
/// 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
|
||||
@ -1,13 +1,13 @@
|
||||
|
||||
/*
|
||||
/**
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
https://github.com/Naios/continuable
|
||||
v4.2.0
|
||||
v0.8.0
|
||||
|
||||
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
|
||||
Copyright(c) 2015 - 2017 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,41 +21,79 @@
|
||||
|
||||
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_HPP_INCLUDED
|
||||
#define CONTINUABLE_HPP_INCLUDED
|
||||
#ifndef CONTINUABLE_HPP_INCLUDED__
|
||||
#define CONTINUABLE_HPP_INCLUDED__
|
||||
|
||||
/// Declares the continuable library namespace.
|
||||
///
|
||||
/// The most important class is cti::continuable_base, that provides the
|
||||
/// whole functionality for continuation chaining.
|
||||
///
|
||||
/// The class cti::continuable_base is created through the
|
||||
/// cti::make_continuable() function which accepts a callback taking function.
|
||||
///
|
||||
/// Also there are following support functions available:
|
||||
/// - cti::when_all() - connects cti::continuable_base's to an `all` connection.
|
||||
/// - cti::when_any() - connects cti::continuable_base's to an `any` connection.
|
||||
/// - cti::when_seq() - connects cti::continuable_base's to a sequence.
|
||||
namespace cti {}
|
||||
#include "continuable/continuable-base.hpp"
|
||||
#include "function2/function2.hpp"
|
||||
|
||||
#include <continuable/continuable-base.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-result.hpp>
|
||||
#include <continuable/continuable-transforms.hpp>
|
||||
#include <continuable/continuable-traverse-async.hpp>
|
||||
#include <continuable/continuable-traverse.hpp>
|
||||
#include <continuable/continuable-types.hpp>
|
||||
namespace cti {
|
||||
// clang-format off
|
||||
/// \cond false
|
||||
inline namespace abi_v1 {
|
||||
/// \endcond
|
||||
namespace detail {
|
||||
template<typename... Args>
|
||||
using trait_of = continuable_trait<
|
||||
fu2::function,
|
||||
fu2::function,
|
||||
Args...
|
||||
>;
|
||||
|
||||
#endif // CONTINUABLE_HPP_INCLUDED
|
||||
template<typename... Args>
|
||||
using unique_trait_of = continuable_trait<
|
||||
fu2::unique_function,
|
||||
fu2::unique_function,
|
||||
Args...
|
||||
>;
|
||||
} // end namespace detail
|
||||
|
||||
/// Defines a copyable continuation type which uses the
|
||||
/// function2 backend for type erasure.
|
||||
///
|
||||
/// Usable like: `cti::continuable<int, float>`
|
||||
template <typename... Args>
|
||||
using continuable = typename detail::trait_of<
|
||||
Args...
|
||||
>::continuable;
|
||||
|
||||
/// Defines a copyable callback type which uses the
|
||||
/// function2 backend for the continuable type erasure.
|
||||
///
|
||||
/// Usable like: `callback<int, float>`
|
||||
template <typename... Args>
|
||||
using callback = typename detail::trait_of<
|
||||
Args...
|
||||
>::callback;
|
||||
|
||||
/// Defines a non-copyable continuation type which uses the
|
||||
/// function2 backend for type erasure.
|
||||
///
|
||||
/// Usable like: `unique_continuable<int, float>`
|
||||
template <typename... Args>
|
||||
using unique_continuable = typename detail::unique_trait_of<
|
||||
Args...
|
||||
>::continuable;
|
||||
|
||||
/// Defines a non-copyable callback type which uses the
|
||||
/// function2 backend for the continuable type erasure.
|
||||
///
|
||||
/// Usable like: `unique_callback<int, float>`
|
||||
template <typename... Args>
|
||||
using unique_callback = typename detail::unique_trait_of<
|
||||
Args...
|
||||
>::callback;
|
||||
/// \cond false
|
||||
} // end inline namespace abi_...
|
||||
/// \endcond
|
||||
// clang-format on
|
||||
} // end namespace cti
|
||||
|
||||
#endif // CONTINUABLE_HPP_INCLUDED__
|
||||
|
||||
@ -1,250 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_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/core/base.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
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 connection is finished.
|
||||
/// - value -> value
|
||||
/// - single async value -> single value
|
||||
/// - multiple async value -> tuple of async values.
|
||||
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, identity<>>> {
|
||||
|
||||
continuable_base<Data, identity<>> continuable_;
|
||||
|
||||
public:
|
||||
explicit continuable_box(continuable_base<Data, identity<>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
}
|
||||
|
||||
auto&& fetch() {
|
||||
return std::move(continuable_);
|
||||
}
|
||||
|
||||
void assign() {}
|
||||
|
||||
auto unbox() && {
|
||||
return spread_this();
|
||||
}
|
||||
};
|
||||
|
||||
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, identity<First>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
}
|
||||
|
||||
auto&& fetch() {
|
||||
return std::move(continuable_);
|
||||
}
|
||||
|
||||
void assign(First first) {
|
||||
first_ = std::move(first);
|
||||
}
|
||||
|
||||
auto unbox() && {
|
||||
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, identity<First, Second, Rest...>>> {
|
||||
|
||||
continuable_base<Data, identity<First, Second, Rest...>> continuable_;
|
||||
lazy_value_t<std::tuple<First, Second, Rest...>> args_;
|
||||
|
||||
public:
|
||||
explicit continuable_box(
|
||||
continuable_base<Data, identity<First, Second, Rest...>>&& continuable)
|
||||
: continuable_(std::move(continuable)) {}
|
||||
|
||||
auto const& peek() const {
|
||||
return continuable_;
|
||||
}
|
||||
|
||||
auto&& fetch() {
|
||||
return std::move(continuable_);
|
||||
}
|
||||
|
||||
void assign(First first, Second second, Rest... rest) {
|
||||
args_ = std::make_tuple(std::move(first), std::move(second),
|
||||
std::move(rest)...);
|
||||
}
|
||||
|
||||
auto unbox() && {
|
||||
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_)));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_continuable_box : std::false_type {};
|
||||
template <typename Continuable>
|
||||
struct is_continuable_box<continuable_box<Continuable>> : std::true_type {};
|
||||
|
||||
namespace detail {
|
||||
/// Maps a deeply nested pack of continuables to a continuable_box
|
||||
struct continuable_box_packer {
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<base::is_continuable<std::decay_t<T>>::value>* = nullptr>
|
||||
auto operator()(T&& continuable) {
|
||||
return continuable_box<std::decay_t<T>>{std::forward<T>(continuable)};
|
||||
}
|
||||
};
|
||||
/// Maps a deeply nested pack of continuable_boxes to its result
|
||||
struct continuable_box_unpacker {
|
||||
template <
|
||||
typename T,
|
||||
std::enable_if_t<is_continuable_box<std::decay_t<T>>::value>* = nullptr>
|
||||
auto operator()(T&& box) {
|
||||
return std::forward<T>(box).unbox();
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// Returns the boxed pack of the given deeply nested pack.
|
||||
/// This transforms all continuables into a continuable_box which is
|
||||
/// capable of caching the result from the corresponding continuable.
|
||||
template <typename... Args>
|
||||
constexpr auto box_continuables(Args&&... args) {
|
||||
return cti::map_pack(detail::continuable_box_packer{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Returns the unboxed pack of the given deeply nested boxed pack.
|
||||
/// This transforms all continuable_boxes into its result.
|
||||
template <typename... Args>
|
||||
constexpr auto unbox_continuables(Args&&... args) {
|
||||
return cti::map_pack(detail::continuable_box_unpacker{},
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template <typename Callback, typename Data>
|
||||
constexpr auto finalize_impl(identity<void>, Callback&& callback, Data&&) {
|
||||
return std::forward<Callback>(callback)();
|
||||
}
|
||||
template <typename... Args, typename Callback, typename Data>
|
||||
constexpr auto finalize_impl(identity<std::tuple<Args...>>, Callback&& callback,
|
||||
Data&& data) {
|
||||
// Call the final callback with the cleaned result
|
||||
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>
|
||||
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(identity<std::decay_t<result_t>>{},
|
||||
std::forward<Callback>(callback),
|
||||
std::forward<Data>(data));
|
||||
}
|
||||
|
||||
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_CONNECTION_REMAPPING_HPP_INCLUDED
|
||||
@ -1,198 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,204 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_ANY_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#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/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 connection {
|
||||
namespace any {
|
||||
/// Invokes the callback with the first arriving result
|
||||
template <typename T>
|
||||
class any_result_submitter
|
||||
: public std::enable_shared_from_this<any_result_submitter<T>>,
|
||||
public util::non_movable {
|
||||
|
||||
T callback_;
|
||||
std::once_flag flag_;
|
||||
|
||||
struct any_callback {
|
||||
std::shared_ptr<any_result_submitter> me_;
|
||||
|
||||
template <typename... PartialArgs>
|
||||
void operator()(PartialArgs&&... args) && {
|
||||
me_->invoke(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
explicit any_result_submitter(T callback) : callback_(std::move(callback)) {
|
||||
}
|
||||
|
||||
/// Creates a submitter which submits it's result to the callback
|
||||
auto create_callback() {
|
||||
return any_callback{this->shared_from_this()};
|
||||
}
|
||||
|
||||
private:
|
||||
// Invokes the callback with the given arguments
|
||||
template <typename... ActualArgs>
|
||||
void invoke(ActualArgs&&... args) {
|
||||
std::call_once(flag_, std::move(callback_),
|
||||
std::forward<ActualArgs>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
struct result_deducer {
|
||||
template <typename 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, identity<T> id) {
|
||||
return base::annotation_of(id);
|
||||
}
|
||||
template <typename T>
|
||||
static auto deduce(traversal::container_category_tag<false, false>,
|
||||
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>,
|
||||
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>{},
|
||||
identity<element_t>{});
|
||||
}
|
||||
|
||||
template <typename First, typename... T>
|
||||
static auto deduce_same_hints(First first, T...) {
|
||||
static_assert(traits::conjunction<std::is_same<First, T>...>::value,
|
||||
"The continuables inside the given pack must have the "
|
||||
"same signature hint!");
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
template <std::size_t... I, typename T>
|
||||
static auto deduce_tuple_like(std::integer_sequence<std::size_t, I...>,
|
||||
identity<T>) {
|
||||
|
||||
return deduce_same_hints(deduce(
|
||||
traversal::container_category_of_t<
|
||||
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>,
|
||||
identity<T> id) {
|
||||
|
||||
constexpr auto const size = std::tuple_size<T>::value;
|
||||
return deduce_tuple_like(std::make_index_sequence<size>{}, id);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Submitter>
|
||||
struct continuable_dispatcher {
|
||||
std::shared_ptr<Submitter>& submitter;
|
||||
|
||||
template <typename Continuable,
|
||||
std::enable_if_t<base::is_continuable<
|
||||
std::decay_t<Continuable>>::value>* = nullptr>
|
||||
void operator()(Continuable&& continuable) {
|
||||
// Retrieve a callback from the submitter and attach it to the continuable
|
||||
std::forward<Continuable>(continuable)
|
||||
.next(submitter->create_callback())
|
||||
.done();
|
||||
}
|
||||
};
|
||||
} // namespace any
|
||||
|
||||
struct connection_strategy_any_tag {};
|
||||
template <>
|
||||
struct is_connection_strategy<connection_strategy_any_tag> // ...
|
||||
: std::true_type {};
|
||||
|
||||
/// 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)>>;
|
||||
|
||||
// Create the submitter which calls the given callback once at the
|
||||
// first callback invocation.
|
||||
auto submitter = std::make_shared<submitter_t>(
|
||||
std::forward<decltype(callback)>(callback));
|
||||
|
||||
traverse_pack(any::continuable_dispatcher<submitter_t>{submitter},
|
||||
std::move(connection));
|
||||
},
|
||||
signature, std::move(ownership));
|
||||
}
|
||||
};
|
||||
} // 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_CONNECTION_ANY_HPP_INCLUDED
|
||||
@ -1,183 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,197 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_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/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 `connection` offers methods to chain continuations together
|
||||
/// with `all`, `any` or `seq` logic.
|
||||
namespace connection {
|
||||
template <typename T>
|
||||
struct is_connection_strategy // ...
|
||||
: std::false_type {};
|
||||
|
||||
/// Adds the given continuation tuple to the left connection
|
||||
template <typename... LeftArgs, typename... RightArgs>
|
||||
auto chain_connection(std::tuple<LeftArgs...> leftPack,
|
||||
std::tuple<RightArgs...> rightPack) {
|
||||
|
||||
return std::tuple_cat(std::move(leftPack), std::move(rightPack));
|
||||
}
|
||||
|
||||
/// Normalizes a continuation to a tuple holding an arbitrary count of
|
||||
/// continuations matching the given strategy.
|
||||
///
|
||||
/// Basically we can encounter 3 cases:
|
||||
/// - The continuable isn't in any strategy:
|
||||
/// -> make a tuple containing the continuable as only element
|
||||
template <
|
||||
typename Strategy, typename Data, typename Annotation,
|
||||
std::enable_if_t<!is_connection_strategy<Annotation>::value>* = nullptr>
|
||||
auto normalize(Strategy /*strategy*/,
|
||||
continuable_base<Data, Annotation>&& continuation) {
|
||||
|
||||
// If the continuation isn't a strategy initialize the strategy
|
||||
return std::make_tuple(std::move(continuation));
|
||||
}
|
||||
/// - The continuable is in a different strategy then the current one:
|
||||
/// -> materialize it
|
||||
template <
|
||||
typename Strategy, typename Data, typename Annotation,
|
||||
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(std::move(continuation).finish());
|
||||
}
|
||||
/// - The continuable is inside the current strategy state:
|
||||
/// -> return the data of the tuple
|
||||
template <typename Strategy, typename Data>
|
||||
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(std::move(continuation));
|
||||
}
|
||||
|
||||
/// Entry function for connecting two continuables with a given strategy.
|
||||
template <typename Strategy, typename LData, typename LAnnotation,
|
||||
typename RData, typename RAnnotation>
|
||||
auto connect(Strategy strategy, continuable_base<LData, LAnnotation>&& left,
|
||||
continuable_base<RData, RAnnotation>&& right) {
|
||||
|
||||
auto ownership_ =
|
||||
base::attorney::ownership_of(left) | base::attorney::ownership_of(right);
|
||||
|
||||
left.freeze();
|
||||
right.freeze();
|
||||
|
||||
// Make the new data which consists of a tuple containing
|
||||
// all connected continuables.
|
||||
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_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 connection is finished.
|
||||
/// - A static method hint that returns the new signature hint.
|
||||
template <typename Strategy>
|
||||
struct connection_finalizer;
|
||||
|
||||
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(continuable);
|
||||
auto connection =
|
||||
base::attorney::consume(std::forward<Continuable>(continuable));
|
||||
|
||||
// Return a new continuable which
|
||||
return finalizer::finalize(std::move(connection), std::move(ownership));
|
||||
}
|
||||
|
||||
template <typename Continuable>
|
||||
static bool is_ready(Continuable const& /*continuable*/) noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class prepare_continuables {
|
||||
util::ownership& ownership_;
|
||||
|
||||
public:
|
||||
explicit constexpr prepare_continuables(util::ownership& ownership)
|
||||
: ownership_(ownership) {
|
||||
}
|
||||
|
||||
template <typename Continuable,
|
||||
std::enable_if_t<base::is_continuable<
|
||||
std::decay_t<Continuable>>::value>* = nullptr>
|
||||
auto operator()(Continuable&& continuable) noexcept {
|
||||
util::ownership current = base::attorney::ownership_of(continuable);
|
||||
assert(current.is_acquired() &&
|
||||
"Only valid continuables should be passed!");
|
||||
|
||||
// Propagate a frozen state to the new continuable
|
||||
if (!ownership_.is_frozen() && current.is_frozen()) {
|
||||
ownership_.freeze();
|
||||
}
|
||||
|
||||
// Freeze the continuable since it is stored for later usage
|
||||
continuable.freeze();
|
||||
|
||||
// Materialize every continuable
|
||||
// TODO Actually we would just need to consume the data here
|
||||
return std::forward<Continuable>(continuable).finish();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Strategy, typename... Args>
|
||||
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
|
||||
// is frozen already.
|
||||
// Additionally test whether every continuable is acquired.
|
||||
// Also materialize every continuable.
|
||||
util::ownership ownership;
|
||||
auto connection = map_pack(prepare_continuables{ownership},
|
||||
std::make_tuple(std::forward<Args>(args)...));
|
||||
|
||||
return finalizer::finalize(std::move(connection), std::move(ownership));
|
||||
}
|
||||
} // namespace connection
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED
|
||||
@ -1,53 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_ANNOTATION_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <continuable/detail/core/types.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace hints {
|
||||
/// 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>
|
||||
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_ANNOTATION_HPP_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,90 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_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
|
||||
#include <exception>
|
||||
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
#include <system_error>
|
||||
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
#endif // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
|
||||
|
||||
namespace cti {
|
||||
template <typename Data, typename Annotation>
|
||||
class continuable_base;
|
||||
|
||||
namespace detail {
|
||||
/// Contains types used globally across the library
|
||||
namespace types {
|
||||
#ifdef 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 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 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 {};
|
||||
|
||||
/// Marks a given callable object as transformation
|
||||
template <typename T>
|
||||
class plain_tag {
|
||||
T value_;
|
||||
|
||||
public:
|
||||
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)) {
|
||||
}
|
||||
|
||||
T&& consume() && {
|
||||
return std::move(value_);
|
||||
}
|
||||
};
|
||||
} // namespace types
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED
|
||||
242
include/continuable/detail/external/asio.hpp
vendored
242
include/continuable/detail/external/asio.hpp
vendored
@ -1,242 +0,0 @@
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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,147 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_FEATURES_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED
|
||||
|
||||
// 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
|
||||
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
|
||||
|
||||
// clang-format off
|
||||
// Detect if the whole standard is available
|
||||
#if (defined(_MSC_VER) && defined(_HAS_CXX17) && _HAS_CXX17) || \
|
||||
(__cplusplus >= 201703L)
|
||||
#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)
|
||||
#define CONTINUABLE_HAS_CXX17_CONSTEXPR_IF
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(CONTINUABLE_HAS_CXX17_DISJUNCTION) && \
|
||||
defined(__cpp_lib_experimental_logical_traits) && \
|
||||
(__cpp_lib_experimental_logical_traits >= 201511)
|
||||
#define CONTINUABLE_HAS_CXX17_DISJUNCTION
|
||||
#endif
|
||||
|
||||
#if !defined(CONTINUABLE_HAS_CXX17_CONJUNCTION) && \
|
||||
defined(__cpp_lib_experimental_logical_traits) && \
|
||||
(__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
|
||||
|
||||
// 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
|
||||
#if !defined(CONTINUABLE_WITH_CUSTOM_ERROR_TYPE) && \
|
||||
!defined(CONTINUABLE_WITH_NO_EXCEPTIONS)
|
||||
#define CONTINUABLE_HAS_EXCEPTIONS 1
|
||||
#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
|
||||
@ -1,80 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,180 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,118 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,273 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,241 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,97 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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
|
||||
@ -1,218 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TESTING_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_TESTING_HPP_INCLUDED
|
||||
|
||||
#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/utility/traits.hpp>
|
||||
#include <continuable/detail/utility/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace testing {
|
||||
template <typename C>
|
||||
void assert_async_completion(C&& continuable) {
|
||||
auto called = std::make_shared<bool>(false);
|
||||
std::forward<C>(continuable)
|
||||
.then([called](auto&&... args) {
|
||||
ASSERT_FALSE(*called);
|
||||
*called = true;
|
||||
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
})
|
||||
.fail([](cti::exception_t /*error*/) {
|
||||
// ...
|
||||
FAIL();
|
||||
});
|
||||
|
||||
ASSERT_TRUE(*called);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
void assert_async_exception_completion(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_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;
|
||||
});
|
||||
|
||||
ASSERT_TRUE(*called);
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
void assert_async_never_completed(C&& continuable) {
|
||||
std::forward<C>(continuable)
|
||||
.then([](auto&&... args) {
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
|
||||
FAIL();
|
||||
})
|
||||
.fail([](cti::exception_t) {
|
||||
// ...
|
||||
FAIL();
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
validator(std::forward<decltype(args)>(args)...);
|
||||
}));
|
||||
}
|
||||
|
||||
/// Expects that the continuable is finished with the given arguments
|
||||
template <typename V, typename C, typename... Args>
|
||||
void assert_async_binary_validation(V&& validator, C&& continuable,
|
||||
Args&&... expected) {
|
||||
|
||||
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 {
|
||||
static_assert(size::value == sizeof...(args),
|
||||
"Async completion handler called with a different count "
|
||||
"of arguments!");
|
||||
|
||||
validator(std::make_tuple(std::forward<decltype(args)>(args)...),
|
||||
expected_pack);
|
||||
});
|
||||
}
|
||||
|
||||
/// Expects that the continuable is finished with the given arguments
|
||||
template <typename V, typename C, typename Args>
|
||||
void assert_async_binary_exception_validation(V&& validator, C&& continuable,
|
||||
Args&& expected) {
|
||||
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)...);
|
||||
|
||||
// The exception was not thrown!
|
||||
FAIL();
|
||||
})
|
||||
.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)> const& exception) {
|
||||
validator(exception, expected);
|
||||
} catch (...) {
|
||||
FAIL();
|
||||
}
|
||||
#else
|
||||
validator(error, expected);
|
||||
#endif
|
||||
});
|
||||
|
||||
ASSERT_TRUE(*called);
|
||||
}
|
||||
|
||||
inline auto expecting_eq_check() {
|
||||
return [](auto&& expected, auto&& actual) {
|
||||
EXPECT_EQ(std::forward<decltype(expected)>(expected),
|
||||
std::forward<decltype(actual)>(actual));
|
||||
};
|
||||
}
|
||||
|
||||
inline auto asserting_eq_check() {
|
||||
return [](auto&& expected, auto&& actual) {
|
||||
ASSERT_EQ(std::forward<decltype(expected)>(expected),
|
||||
std::forward<decltype(actual)>(actual));
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... Expected>
|
||||
struct assert_async_types_validator {
|
||||
template <typename... Actual>
|
||||
void operator()(Actual...) {
|
||||
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, identity<Args...> /*expected*/) {
|
||||
assert_async_validation(std::forward<C>(continuable),
|
||||
assert_async_types_validator<Args...>{});
|
||||
}
|
||||
} // namespace testing
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_TESTING_HPP_INCLUDED
|
||||
@ -1,138 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TRANSFORMS_FUTURE_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_TRANSFORMS_FUTURE_HPP_INCLUDED
|
||||
|
||||
#include <future>
|
||||
#include <continuable/continuable-primitives.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/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
/// Provides helper functions to transform continuations to other types
|
||||
namespace transforms {
|
||||
/// Provides helper functions and typedefs for converting callback arguments
|
||||
/// to their types a promise can accept.
|
||||
template <typename... Args>
|
||||
struct future_trait {
|
||||
/// The promise type used to create the future
|
||||
using promise_t = std::promise<std::tuple<Args...>>;
|
||||
/// Boxes the argument pack into a tuple
|
||||
static void resolve(promise_t& promise, Args... args) {
|
||||
promise.set_value(std::make_tuple(std::move(args)...));
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct future_trait<> {
|
||||
/// The promise type used to create the future
|
||||
using promise_t = std::promise<void>;
|
||||
/// Boxes the argument pack into void
|
||||
static void resolve(promise_t& promise) {
|
||||
promise.set_value();
|
||||
}
|
||||
};
|
||||
template <typename First>
|
||||
struct future_trait<First> {
|
||||
/// The promise type used to create the future
|
||||
using promise_t = std::promise<First>;
|
||||
/// Boxes the argument pack into nothing
|
||||
static void resolve(promise_t& promise, First first) {
|
||||
promise.set_value(std::move(first));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Hint>
|
||||
class promise_callback;
|
||||
|
||||
template <typename... Args>
|
||||
class promise_callback<identity<Args...>> : public future_trait<Args...> {
|
||||
|
||||
typename future_trait<Args...>::promise_t promise_;
|
||||
|
||||
public:
|
||||
constexpr promise_callback() = default;
|
||||
promise_callback(promise_callback const&) = delete;
|
||||
constexpr promise_callback(promise_callback&&) = default;
|
||||
promise_callback& operator=(promise_callback const&) = delete;
|
||||
promise_callback& operator=(promise_callback&&) = delete;
|
||||
|
||||
/// Resolves the promise
|
||||
void operator()(Args... args) {
|
||||
this->resolve(promise_, std::move(args)...);
|
||||
}
|
||||
|
||||
/// Resolves the promise through the exception
|
||||
void operator()(exception_arg_t, exception_t error) {
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
promise_.set_exception(error);
|
||||
#else
|
||||
(void)error;
|
||||
|
||||
// Can't forward a std::error_condition or custom error type
|
||||
// to a std::promise. Handle the error first in order
|
||||
// to prevent this trap!
|
||||
CTI_DETAIL_TRAP();
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
}
|
||||
|
||||
/// Returns the future from the promise
|
||||
auto get_future() {
|
||||
return promise_.get_future();
|
||||
}
|
||||
};
|
||||
|
||||
/// Transforms the continuation to a future
|
||||
template <typename Data, typename Annotation>
|
||||
auto to_future(continuable_base<Data, Annotation>&& continuable) {
|
||||
// Create the promise which is able to supply the current arguments
|
||||
constexpr auto const hint =
|
||||
base::annotation_of(identify<decltype(continuable)>{});
|
||||
|
||||
promise_callback<std::decay_t<decltype(hint)>> callback;
|
||||
(void)hint;
|
||||
|
||||
// Receive the future
|
||||
auto future = callback.get_future();
|
||||
|
||||
// Dispatch the continuation with the promise resolving callback
|
||||
std::move(continuable).next(std::move(callback)).done();
|
||||
|
||||
return future;
|
||||
}
|
||||
} // namespace transforms
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_TRANSFORMS_FUTURE_HPP_INCLUDED
|
||||
@ -1,264 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TRANSFORMS_WAIT_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_TRANSFORMS_WAIT_HPP_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#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>
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
# include <exception>
|
||||
#endif
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace transforms {
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
class wait_transform_canceled_exception : public std::exception {
|
||||
public:
|
||||
wait_transform_canceled_exception() noexcept = default;
|
||||
|
||||
char const* what() const noexcept override {
|
||||
return "cti::transforms::wait canceled due to cancellation of the "
|
||||
"continuation";
|
||||
}
|
||||
};
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
template <typename Hint>
|
||||
struct sync_trait;
|
||||
template <typename... Args>
|
||||
struct sync_trait<identity<Args...>> {
|
||||
using result_t = result<Args...>;
|
||||
};
|
||||
|
||||
using lock_t = std::unique_lock<std::mutex>;
|
||||
using condition_variable_t = std::condition_variable;
|
||||
|
||||
template <typename Result>
|
||||
struct unsafe_unlocker {
|
||||
explicit unsafe_unlocker(std::atomic_bool* ready, condition_variable_t* cv,
|
||||
std::mutex* mutex, Result* result)
|
||||
: ready_(ready)
|
||||
, cv_(cv)
|
||||
, mutex_(mutex)
|
||||
, result_(result) {}
|
||||
|
||||
unsafe_unlocker(unsafe_unlocker const&) = delete;
|
||||
unsafe_unlocker(unsafe_unlocker&&) = default;
|
||||
unsafe_unlocker& operator=(unsafe_unlocker const&) = delete;
|
||||
unsafe_unlocker& operator=(unsafe_unlocker&&) = default;
|
||||
|
||||
~unsafe_unlocker() {
|
||||
unlock(Result::empty());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(Args&&... args) {
|
||||
unlock(Result::from(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void unlock(Result&& result) {
|
||||
if (!ownership_.is_acquired()) {
|
||||
return;
|
||||
}
|
||||
ownership_.release();
|
||||
|
||||
lock_t lock(*mutex_);
|
||||
|
||||
*result_ = std::move(result);
|
||||
|
||||
assert(!ready_->load(std::memory_order_acquire));
|
||||
ready_->store(true, std::memory_order_release);
|
||||
|
||||
cv_->notify_all();
|
||||
}
|
||||
|
||||
std::atomic_bool* ready_;
|
||||
condition_variable_t* cv_;
|
||||
std::mutex* mutex_;
|
||||
Result* result_;
|
||||
util::ownership ownership_;
|
||||
};
|
||||
|
||||
template <typename Data, typename Annotation,
|
||||
typename Result = typename sync_trait<Annotation>::result_t>
|
||||
Result wait_relaxed(continuable_base<Data, Annotation>&& continuable) {
|
||||
|
||||
// Do an immediate unpack if the continuable is ready
|
||||
if (continuable.is_ready()) {
|
||||
return std::move(continuable).unpack();
|
||||
}
|
||||
|
||||
condition_variable_t cv;
|
||||
std::mutex cv_mutex;
|
||||
|
||||
std::atomic_bool ready{false};
|
||||
Result sync_result;
|
||||
|
||||
std::move(continuable)
|
||||
.next(unsafe_unlocker<Result>{
|
||||
&ready,
|
||||
&cv,
|
||||
&cv_mutex,
|
||||
&sync_result,
|
||||
})
|
||||
.done();
|
||||
|
||||
lock_t lock(cv_mutex);
|
||||
if (!ready.load(std::memory_order_acquire)) {
|
||||
cv.wait(lock, [&] {
|
||||
return ready.load(std::memory_order_acquire);
|
||||
});
|
||||
}
|
||||
|
||||
return sync_result;
|
||||
}
|
||||
|
||||
/// Transforms the continuation to sync execution and unpacks the result the if
|
||||
/// possible
|
||||
template <typename Data, typename Annotation>
|
||||
auto wait_and_unpack(continuable_base<Data, Annotation>&& continuable) {
|
||||
|
||||
auto sync_result = wait_relaxed(std::move(continuable));
|
||||
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
if (sync_result.is_value()) {
|
||||
return std::move(sync_result).get_value();
|
||||
} else if (sync_result.is_exception()) {
|
||||
if (sync_result.is_exception()) {
|
||||
if (exception_t e = sync_result.get_exception()) {
|
||||
std::rethrow_exception(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw wait_transform_canceled_exception();
|
||||
#else
|
||||
return sync_result;
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
}
|
||||
|
||||
template <typename Result>
|
||||
struct wait_frame {
|
||||
std::mutex cv_mutex;
|
||||
std::mutex rw_mutex;
|
||||
condition_variable_t cv;
|
||||
std::atomic_bool ready{false};
|
||||
Result sync_result;
|
||||
};
|
||||
|
||||
template <typename Result>
|
||||
struct unlocker {
|
||||
unlocker(unlocker const&) = delete;
|
||||
unlocker(unlocker&&) = default;
|
||||
unlocker& operator=(unlocker const&) = delete;
|
||||
unlocker& operator=(unlocker&&) = default;
|
||||
|
||||
explicit unlocker(std::weak_ptr<wait_frame<Result>> frame)
|
||||
: frame_(std::move(frame)) {}
|
||||
|
||||
~unlocker() {
|
||||
unlock(Result::empty());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(Args&&... args) {
|
||||
unlock(Result::from(std::forward<decltype(args)>(args)...));
|
||||
}
|
||||
|
||||
void unlock(Result&& result) {
|
||||
if (!ownership_.is_acquired()) {
|
||||
return;
|
||||
}
|
||||
ownership_.release();
|
||||
|
||||
if (auto locked = frame_.lock()) {
|
||||
{
|
||||
std::lock_guard<std::mutex> rw_lock(locked->rw_mutex);
|
||||
assert(!locked->ready.load(std::memory_order_acquire));
|
||||
locked->sync_result = std::move(result);
|
||||
}
|
||||
|
||||
locked->ready.store(true, std::memory_order_release);
|
||||
locked->cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
std::weak_ptr<wait_frame<Result>> frame_;
|
||||
util::ownership ownership_;
|
||||
};
|
||||
|
||||
template <typename Data, typename Annotation, typename Waiter,
|
||||
typename Result = typename sync_trait<Annotation>::result_t>
|
||||
Result wait_unsafe(continuable_base<Data, Annotation>&& continuable,
|
||||
Waiter&& waiter) {
|
||||
|
||||
// Do an immediate unpack if the continuable is ready
|
||||
if (continuable.is_ready()) {
|
||||
return std::move(continuable).unpack();
|
||||
}
|
||||
|
||||
using frame_t = wait_frame<Result>;
|
||||
|
||||
auto frame = std::make_shared<frame_t>();
|
||||
|
||||
std::move(continuable)
|
||||
.next(unlocker<Result>{std::weak_ptr<frame_t>(frame)})
|
||||
.done();
|
||||
|
||||
if (!frame->ready.load(std::memory_order_acquire)) {
|
||||
lock_t lock(frame->cv_mutex);
|
||||
std::forward<Waiter>(waiter)(frame->cv, lock, [&] {
|
||||
return frame->ready.load(std::memory_order_acquire);
|
||||
});
|
||||
}
|
||||
|
||||
return ([&] {
|
||||
std::lock_guard<std::mutex> rw_lock(frame->rw_mutex);
|
||||
Result cached = std::move(frame->sync_result);
|
||||
return cached;
|
||||
})();
|
||||
}
|
||||
} // namespace transforms
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_TRANSFORMS_WAIT_HPP_INCLUDED
|
||||
@ -1,76 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_CONTAINER_CATEGORY_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_CONTAINER_CATEGORY_HPP_INCLUDED
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace traversal {
|
||||
/// Deduces to a true type if the given parameter T
|
||||
/// has a begin() and end() method.
|
||||
// TODO Find out whether we should use std::begin and std::end instead, which
|
||||
// could cause issues with plain arrays.
|
||||
template <typename T, typename = void>
|
||||
struct is_range : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_range<T, traits::void_t<decltype(std::declval<T>().begin() ==
|
||||
std::declval<T>().end())>>
|
||||
: std::true_type {};
|
||||
|
||||
/// Deduces to a true type if the given parameter T
|
||||
/// is accessible through std::tuple_size.
|
||||
template <typename T, typename = void>
|
||||
struct is_tuple_like : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_tuple_like<T, traits::void_t<decltype(std::tuple_size<T>::value)>>
|
||||
: std::true_type {};
|
||||
|
||||
/// A tag for dispatching based on the tuple like
|
||||
/// or container properties of a type.
|
||||
///
|
||||
/// This type deduces to a true_type if it has any category.
|
||||
template <bool IsContainer, bool IsTupleLike>
|
||||
struct container_category_tag
|
||||
: std::integral_constant<bool, IsContainer || IsTupleLike> {};
|
||||
|
||||
/// Deduces to the container_category_tag of the given type T.
|
||||
template <typename T>
|
||||
using container_category_of_t =
|
||||
container_category_tag<is_range<T>::value, is_tuple_like<T>::value>;
|
||||
} // namespace traversal
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_CONTAINER_CATEGORY_HPP_INCLUDED
|
||||
@ -1,65 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_RANGE_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_RANGE_HPP_INCLUDED
|
||||
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace range {
|
||||
/// Deduces to a true_type if the given type is an interator
|
||||
template <typename T, typename = void>
|
||||
struct is_iterator : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_iterator<T,
|
||||
traits::void_t<typename std::iterator_traits<T>::value_type>>
|
||||
: std::true_type {};
|
||||
|
||||
/// Moves the content of the given iterators to a persistent storage
|
||||
template <typename Iterator>
|
||||
auto persist_range(Iterator begin, Iterator end) {
|
||||
std::vector<typename std::iterator_traits<Iterator>::value_type> storage;
|
||||
// TODO Find out why the superior idiom below has issues with move only types:
|
||||
// storage.insert(storage.end(), std::make_move_iterator(begin),
|
||||
// std::make_move_iterator(end));
|
||||
std::move(begin, end, std::back_inserter(storage));
|
||||
return storage;
|
||||
}
|
||||
} // namespace range
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_RANGE_HPP_INCLUDED
|
||||
@ -1,580 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TRAVERSE_ASYNC_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_TRAVERSE_ASYNC_HPP_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/detail/traversal/container-category.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace traversal {
|
||||
/// A tag which is passed to the `operator()` of the visitor
|
||||
/// if an element is visited synchronously.
|
||||
struct 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.
|
||||
struct async_traverse_detach_tag {};
|
||||
|
||||
/// A tag which is passed to the `operator()` of the visitor
|
||||
/// if the asynchronous pack traversal was finished.
|
||||
struct async_traverse_complete_tag {};
|
||||
|
||||
/// A tag to identify that a mapper shall be constructed in-place
|
||||
/// from the first argument passed.
|
||||
template <typename T>
|
||||
struct async_traverse_in_place_tag {};
|
||||
|
||||
/// Relocates the given pack with the given offset
|
||||
template <std::size_t Offset, typename Pack>
|
||||
struct relocate_index_pack;
|
||||
template <std::size_t Offset, std::size_t... Sequence>
|
||||
struct relocate_index_pack<Offset,
|
||||
std::integer_sequence<std::size_t, Sequence...>>
|
||||
: std::common_type<
|
||||
std::integer_sequence<std::size_t, (Sequence + Offset)...>> {};
|
||||
|
||||
/// Creates a sequence from begin to end explicitly
|
||||
template <std::size_t Begin, std::size_t End>
|
||||
using explicit_range_sequence_of_t =
|
||||
typename relocate_index_pack<Begin,
|
||||
std::make_index_sequence<End - Begin>>::type;
|
||||
|
||||
/// Continues the traversal when the object is called
|
||||
template <typename Frame, typename State>
|
||||
class resume_traversal_callable {
|
||||
Frame frame_;
|
||||
State state_;
|
||||
|
||||
public:
|
||||
explicit resume_traversal_callable(Frame frame, State state)
|
||||
: frame_(std::move(frame)), state_(std::move(state)) {
|
||||
}
|
||||
|
||||
/// The callable operator for resuming
|
||||
/// the asynchronous pack traversal
|
||||
void operator()();
|
||||
};
|
||||
|
||||
/// Creates a resume_traversal_callable from the given frame and the
|
||||
/// given iterator tuple.
|
||||
template <typename Frame, typename State>
|
||||
auto make_resume_traversal_callable(Frame&& frame, State&& state)
|
||||
-> resume_traversal_callable<std::decay_t<Frame>, std::decay_t<State>> {
|
||||
return resume_traversal_callable<std::decay_t<Frame>, std::decay_t<State>>(
|
||||
std::forward<Frame>(frame), std::forward<State>(state));
|
||||
}
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct has_head : std::false_type {};
|
||||
template <typename T>
|
||||
struct has_head<T, traits::void_t<decltype(std::declval<T>().head())>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename Visitor, typename... Args>
|
||||
class async_traversal_frame_data : public Visitor {
|
||||
|
||||
std::tuple<Args...> args_;
|
||||
|
||||
public:
|
||||
explicit async_traversal_frame_data(Visitor visitor, Args... args)
|
||||
: Visitor(std::move(visitor)),
|
||||
args_(std::make_tuple(std::move(args)...)) {
|
||||
}
|
||||
template <typename MapperArg>
|
||||
explicit async_traversal_frame_data(async_traverse_in_place_tag<Visitor>,
|
||||
MapperArg&& mapper_arg, Args... args)
|
||||
: Visitor(std::forward<MapperArg>(mapper_arg)),
|
||||
args_(std::make_tuple(std::move(args)...)) {
|
||||
}
|
||||
|
||||
/// Returns the arguments of the frame
|
||||
std::tuple<Args...>& head() noexcept {
|
||||
return args_;
|
||||
}
|
||||
};
|
||||
template <typename Visitor>
|
||||
class async_traversal_frame_no_data : public Visitor {
|
||||
public:
|
||||
explicit async_traversal_frame_no_data(Visitor visitor)
|
||||
: Visitor(std::move(visitor)) {
|
||||
}
|
||||
template <typename MapperArg>
|
||||
explicit async_traversal_frame_no_data(async_traverse_in_place_tag<Visitor>,
|
||||
MapperArg&& mapper_arg)
|
||||
: Visitor(std::forward<MapperArg>(mapper_arg)) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Visitor, typename... Args>
|
||||
using data_layout_t =
|
||||
std::conditional_t<has_head<Visitor>::value,
|
||||
async_traversal_frame_no_data<Visitor>,
|
||||
async_traversal_frame_data<Visitor, Args...>>;
|
||||
|
||||
/// Stores the visitor and the arguments to traverse
|
||||
template <typename Visitor, typename... Args>
|
||||
class async_traversal_frame : public data_layout_t<Visitor, Args...> {
|
||||
#ifndef NDEBUG
|
||||
std::atomic<bool> finished_;
|
||||
#endif // NDEBUG
|
||||
|
||||
Visitor& visitor() noexcept {
|
||||
return *static_cast<Visitor*>(this);
|
||||
}
|
||||
|
||||
Visitor const& visitor() const noexcept {
|
||||
return *static_cast<Visitor const*>(this);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... T>
|
||||
explicit async_traversal_frame(T&&... args)
|
||||
: data_layout_t<Visitor, Args...>(std::forward<T>(args)...)
|
||||
#ifndef NDEBUG
|
||||
,
|
||||
finished_(false)
|
||||
#endif // NDEBUG
|
||||
{
|
||||
}
|
||||
|
||||
/// We require a virtual base
|
||||
virtual ~async_traversal_frame() override = default;
|
||||
|
||||
/// Calls the visitor with the given element
|
||||
template <typename T>
|
||||
auto traverse(T&& value) -> decltype(visitor()(async_traverse_visit_tag{},
|
||||
std::forward<T>(value))) {
|
||||
return visitor()(async_traverse_visit_tag{}, std::forward<T>(value));
|
||||
}
|
||||
|
||||
/// Calls the visitor with the given element and a continuation
|
||||
/// which is capable of continuing the asynchronous traversal
|
||||
/// when it's called later.
|
||||
template <typename T, typename Hierarchy>
|
||||
void async_continue(T&& value, Hierarchy&& hierarchy) {
|
||||
// Cast the frame up
|
||||
auto frame = std::static_pointer_cast<async_traversal_frame>(
|
||||
this->shared_from_this());
|
||||
|
||||
// Create a callable object which resumes the current
|
||||
// traversal when it's called.
|
||||
auto resumable = make_resume_traversal_callable(
|
||||
std::move(frame), std::forward<Hierarchy>(hierarchy));
|
||||
|
||||
// Invoke the visitor with the current value and the
|
||||
// callable object to resume the control flow.
|
||||
visitor()(async_traverse_detach_tag{}, std::forward<T>(value),
|
||||
std::move(resumable));
|
||||
}
|
||||
|
||||
/// Calls the visitor with no arguments to signalize that the
|
||||
/// asynchronous traversal was finished.
|
||||
void async_complete() {
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
bool expected = false;
|
||||
assert(finished_.compare_exchange_strong(expected, true));
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
visitor()(async_traverse_complete_tag{}, std::move(this->head()));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Target, std::size_t Begin, std::size_t End>
|
||||
struct static_async_range {
|
||||
Target* target_;
|
||||
|
||||
constexpr decltype(auto) operator*() const noexcept {
|
||||
return std::get<Begin>(*target_);
|
||||
}
|
||||
|
||||
template <std::size_t Position>
|
||||
constexpr auto relocate(std::integral_constant<std::size_t, Position>) const
|
||||
noexcept {
|
||||
return static_async_range<Target, Position, End>{target_};
|
||||
}
|
||||
|
||||
constexpr auto next() const noexcept {
|
||||
return static_async_range<Target, Begin + 1, End>{target_};
|
||||
}
|
||||
|
||||
constexpr bool is_finished() const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/// Specialization for the end marker which doesn't provide
|
||||
/// a particular element dereference
|
||||
template <typename Target, std::size_t Begin>
|
||||
struct static_async_range<Target, Begin, Begin> {
|
||||
explicit static_async_range(Target*) {
|
||||
}
|
||||
|
||||
constexpr bool is_finished() const noexcept {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns a static range for the given type
|
||||
template <typename T>
|
||||
auto make_static_range(T&& element) {
|
||||
using range_t = static_async_range<std::decay_t<T>, 0U,
|
||||
std::tuple_size<std::decay_t<T>>::value>;
|
||||
|
||||
return range_t{std::addressof(element)};
|
||||
}
|
||||
|
||||
template <typename Begin, typename Sentinel>
|
||||
struct dynamic_async_range {
|
||||
Begin begin_;
|
||||
Sentinel sentinel_;
|
||||
|
||||
dynamic_async_range& operator++() noexcept {
|
||||
++begin_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto operator*() const noexcept -> decltype(*std::declval<Begin const&>()) {
|
||||
return *begin_;
|
||||
}
|
||||
|
||||
dynamic_async_range next() const {
|
||||
dynamic_async_range other = *this;
|
||||
++other;
|
||||
return other;
|
||||
}
|
||||
|
||||
bool is_finished() const {
|
||||
return begin_ == sentinel_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using dynamic_async_range_of_t =
|
||||
dynamic_async_range<std::decay_t<decltype(std::begin(std::declval<T>()))>,
|
||||
std::decay_t<decltype(std::end(std::declval<T>()))>>;
|
||||
|
||||
/// Returns a dynamic range for the given type
|
||||
template <typename T>
|
||||
auto make_dynamic_async_range(T&& element) {
|
||||
using range_t = dynamic_async_range_of_t<T>;
|
||||
return range_t{std::begin(element), std::end(element)};
|
||||
}
|
||||
|
||||
/// Represents a particular point in a asynchronous traversal hierarchy
|
||||
template <typename Frame, typename... Hierarchy>
|
||||
class async_traversal_point {
|
||||
Frame frame_;
|
||||
std::tuple<Hierarchy...> hierarchy_;
|
||||
bool& detached_;
|
||||
|
||||
public:
|
||||
explicit async_traversal_point(Frame frame,
|
||||
std::tuple<Hierarchy...> hierarchy,
|
||||
bool& detached)
|
||||
: frame_(std::move(frame)), hierarchy_(std::move(hierarchy)),
|
||||
detached_(detached) {
|
||||
}
|
||||
|
||||
// Abort the current control flow
|
||||
void detach() noexcept {
|
||||
assert(!detached_);
|
||||
detached_ = true;
|
||||
}
|
||||
|
||||
/// Returns true when we should abort the current control flow
|
||||
bool is_detached() const noexcept {
|
||||
return detached_;
|
||||
}
|
||||
|
||||
/// Creates a new traversal point which
|
||||
template <typename Parent>
|
||||
auto push(Parent&& parent)
|
||||
-> async_traversal_point<Frame, std::decay_t<Parent>, Hierarchy...> {
|
||||
// Create a new hierarchy which contains the
|
||||
// the parent (the last traversed element).
|
||||
auto hierarchy = std::tuple_cat(
|
||||
std::make_tuple(std::forward<Parent>(parent)), hierarchy_);
|
||||
|
||||
return async_traversal_point<Frame, std::decay_t<Parent>, Hierarchy...>(
|
||||
frame_, std::move(hierarchy), detached_);
|
||||
}
|
||||
|
||||
/// Forks the current traversal point and continues the child
|
||||
/// of the given parent.
|
||||
template <typename Child, typename Parent>
|
||||
void fork(Child&& child, Parent&& parent) {
|
||||
// Push the parent on top of the hierarchy
|
||||
auto point = push(std::forward<Parent>(parent));
|
||||
|
||||
// Continue the traversal with the current element
|
||||
point.async_traverse(std::forward<Child>(child));
|
||||
}
|
||||
|
||||
/// Async traverse a single element, and do nothing.
|
||||
/// This function is matched last.
|
||||
template <typename Matcher, typename Current>
|
||||
void async_traverse_one_impl(Matcher, Current&& /*current*/) {
|
||||
// Do nothing if the visitor doesn't accept the type
|
||||
}
|
||||
|
||||
/// Async traverse a single element which isn't a container or
|
||||
/// tuple like type. This function is SFINAEd out if the element
|
||||
/// isn't accepted by the visitor.
|
||||
template <typename Current>
|
||||
auto async_traverse_one_impl(container_category_tag<false, false>,
|
||||
Current&& current)
|
||||
/// SFINAE this out if the visitor doesn't accept
|
||||
/// the given element
|
||||
-> traits::void_t<decltype(std::declval<Frame>()->traverse(*current))> {
|
||||
if (!frame_->traverse(*current)) {
|
||||
// Store the current call hierarchy into a tuple for
|
||||
// later re-entrance.
|
||||
auto hierarchy =
|
||||
std::tuple_cat(std::make_tuple(current.next()), hierarchy_);
|
||||
|
||||
// First detach the current execution context
|
||||
detach();
|
||||
|
||||
// If the traversal method returns false, we detach the
|
||||
// current execution context and call the visitor with the
|
||||
// element and a continue callable object again.
|
||||
frame_->async_continue(*current, std::move(hierarchy));
|
||||
}
|
||||
}
|
||||
|
||||
/// Async traverse a single element which is a container or
|
||||
/// tuple like type.
|
||||
template <bool IsTupleLike, typename Current>
|
||||
void async_traverse_one_impl(container_category_tag<true, IsTupleLike>,
|
||||
Current&& current) {
|
||||
auto range = make_dynamic_async_range(*current);
|
||||
fork(std::move(range), std::forward<Current>(current));
|
||||
}
|
||||
|
||||
/// Async traverse a single element which is a tuple like type only.
|
||||
template <typename Current>
|
||||
void async_traverse_one_impl(container_category_tag<false, true>,
|
||||
Current&& current) {
|
||||
auto range = make_static_range(*current);
|
||||
fork(std::move(range), std::forward<Current>(current));
|
||||
}
|
||||
|
||||
/// Async traverse the current iterator
|
||||
template <typename Current>
|
||||
void async_traverse_one(Current&& current) {
|
||||
using ElementType = std::decay_t<decltype(*current)>;
|
||||
return async_traverse_one_impl(container_category_of_t<ElementType>{},
|
||||
std::forward<Current>(current));
|
||||
}
|
||||
|
||||
/// Async traverse the current iterator but don't traverse
|
||||
/// if the control flow was detached.
|
||||
template <typename Current>
|
||||
void async_traverse_one_checked(Current&& current) {
|
||||
if (!is_detached()) {
|
||||
async_traverse_one(std::forward<Current>(current));
|
||||
}
|
||||
}
|
||||
|
||||
template <std::size_t... Sequence, typename Current>
|
||||
void async_traverse_static_async_range(
|
||||
std::integer_sequence<std::size_t, Sequence...>, Current&& current) {
|
||||
int dummy[] = {0, (async_traverse_one_checked(current.relocate(
|
||||
std::integral_constant<std::size_t, Sequence>{})),
|
||||
0)...};
|
||||
(void)dummy;
|
||||
(void)current;
|
||||
}
|
||||
|
||||
/// Traverse a static range
|
||||
template <typename Target, std::size_t Begin, std::size_t End>
|
||||
void async_traverse(static_async_range<Target, Begin, End> current) {
|
||||
async_traverse_static_async_range(
|
||||
explicit_range_sequence_of_t<Begin, End>{}, current);
|
||||
}
|
||||
|
||||
/// Traverse a dynamic range
|
||||
template <typename Begin, typename Sentinel>
|
||||
void async_traverse(dynamic_async_range<Begin, Sentinel> range) {
|
||||
if (!is_detached()) {
|
||||
for (/**/; !range.is_finished(); ++range) {
|
||||
async_traverse_one(range);
|
||||
if (is_detached()) // test before increment
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Deduces to the traversal point class of the
|
||||
/// given frame and hierarchy
|
||||
template <typename Frame, typename... Hierarchy>
|
||||
using traversal_point_of_t =
|
||||
async_traversal_point<std::decay_t<Frame>, std::decay_t<Hierarchy>...>;
|
||||
|
||||
/// A callable object which is capable of resuming an asynchronous
|
||||
/// pack traversal.
|
||||
struct resume_state_callable {
|
||||
/// Reenter an asynchronous iterator pack and continue
|
||||
/// its traversal.
|
||||
template <typename Frame, typename Current, typename... Hierarchy>
|
||||
void operator()(Frame&& frame, Current&& current,
|
||||
Hierarchy&&... hierarchy) const {
|
||||
bool detached = false;
|
||||
next(detached, std::forward<Frame>(frame), std::forward<Current>(current),
|
||||
std::forward<Hierarchy>(hierarchy)...);
|
||||
}
|
||||
|
||||
template <typename Frame, typename Current>
|
||||
void next(bool& detached, Frame&& frame, Current&& current) const {
|
||||
// Only process the next element if the current iterator
|
||||
// hasn't reached its end.
|
||||
if (!current.is_finished()) {
|
||||
traversal_point_of_t<Frame> point(frame, std::make_tuple(), detached);
|
||||
|
||||
point.async_traverse(std::forward<Current>(current));
|
||||
|
||||
// Don't continue the frame when the execution was detached
|
||||
if (detached) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
frame->async_complete();
|
||||
}
|
||||
|
||||
/// Reenter an asynchronous iterator pack and continue
|
||||
/// its traversal.
|
||||
template <typename Frame, typename Current, typename Parent,
|
||||
typename... Hierarchy>
|
||||
void next(bool& detached, Frame&& frame, Current&& current, Parent&& parent,
|
||||
Hierarchy&&... hierarchy) const {
|
||||
// Only process the element if the current iterator
|
||||
// hasn't reached its end.
|
||||
if (!current.is_finished()) {
|
||||
// Don't forward the arguments here, since we still need
|
||||
// the objects in a valid state later.
|
||||
traversal_point_of_t<Frame, Parent, Hierarchy...> point(
|
||||
frame, std::make_tuple(parent, hierarchy...), detached);
|
||||
|
||||
point.async_traverse(std::forward<Current>(current));
|
||||
|
||||
// Don't continue the frame when the execution was detached
|
||||
if (detached) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Pop the top element from the hierarchy, and shift the
|
||||
// parent element one to the right
|
||||
next(detached, std::forward<Frame>(frame),
|
||||
std::forward<Parent>(parent).next(),
|
||||
std::forward<Hierarchy>(hierarchy)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Frame, typename State>
|
||||
void resume_traversal_callable<Frame, State>::operator()() {
|
||||
auto hierarchy = std::tuple_cat(std::make_tuple(frame_), state_);
|
||||
traits::unpack(resume_state_callable{}, std::move(hierarchy));
|
||||
}
|
||||
|
||||
/// Gives access to types related to the traversal frame
|
||||
template <typename Visitor, typename... Args>
|
||||
struct async_traversal_types {
|
||||
/// Deduces to the async traversal frame type of the given
|
||||
/// traversal arguments and mapper
|
||||
using frame_t =
|
||||
async_traversal_frame<std::decay_t<Visitor>, std::decay_t<Args>...>;
|
||||
|
||||
/// The type of the demoted visitor type
|
||||
using visitor_t = Visitor;
|
||||
};
|
||||
|
||||
template <typename Visitor, typename VisitorArg, typename... Args>
|
||||
struct async_traversal_types<async_traverse_in_place_tag<Visitor>, VisitorArg,
|
||||
Args...>
|
||||
: async_traversal_types<Visitor, Args...> {};
|
||||
|
||||
/// Traverses the given pack with the given mapper
|
||||
template <typename Visitor, typename... Args>
|
||||
auto apply_pack_transform_async(Visitor&& visitor, Args&&... args) {
|
||||
|
||||
// Provide the frame and visitor type
|
||||
using types = async_traversal_types<Visitor, Args...>;
|
||||
using frame_t = typename types::frame_t;
|
||||
using visitor_t = typename types::visitor_t;
|
||||
|
||||
// Check whether the visitor inherits enable_shared_from_this
|
||||
static_assert(std::is_base_of<std::enable_shared_from_this<visitor_t>,
|
||||
visitor_t>::value,
|
||||
"The visitor must inherit std::enable_shared_from_this!");
|
||||
|
||||
// Check whether the visitor is virtual destructible
|
||||
static_assert(std::has_virtual_destructor<visitor_t>::value,
|
||||
"The visitor must have a virtual destructor!");
|
||||
|
||||
// Create the frame on the heap which stores the arguments
|
||||
// to traverse asynchronous. It persists until the
|
||||
// traversal frame isn't referenced anymore.
|
||||
auto frame = std::make_shared<frame_t>(std::forward<Visitor>(visitor),
|
||||
std::forward<Args>(args)...);
|
||||
|
||||
// Create a static range for the top level tuple
|
||||
auto range = std::make_tuple(make_static_range(frame->head()));
|
||||
|
||||
// Create a resumer to start the asynchronous traversal
|
||||
auto resumer = make_resume_traversal_callable(frame, std::move(range));
|
||||
|
||||
// Start the asynchronous traversal
|
||||
resumer();
|
||||
|
||||
// Cast the shared_ptr down to the given visitor type
|
||||
// for implementation invisibility
|
||||
return std::static_pointer_cast<visitor_t>(std::move(frame));
|
||||
}
|
||||
} // namespace traversal
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_TRAVERSE_ASYNC_HPP_INCLUDED
|
||||
@ -1,868 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_TRAVERSE_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_TRAVERSE_HPP_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <continuable/detail/traversal/container-category.hpp>
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
namespace traversal {
|
||||
/// Exposes useful facilities for dealing with 1:n mappings
|
||||
namespace spreading {
|
||||
/// \cond false
|
||||
/// A struct to mark a tuple to be unpacked into the parent context
|
||||
template <typename... T>
|
||||
class spread_box {
|
||||
std::tuple<T...> boxed_;
|
||||
|
||||
public:
|
||||
explicit constexpr spread_box(std::tuple<T...> boxed)
|
||||
: boxed_(std::move(boxed)) {
|
||||
}
|
||||
|
||||
std::tuple<T...> unbox() {
|
||||
return std::move(boxed_);
|
||||
}
|
||||
};
|
||||
template <>
|
||||
class spread_box<> {
|
||||
public:
|
||||
explicit constexpr spread_box() noexcept {
|
||||
}
|
||||
explicit constexpr spread_box(std::tuple<>) noexcept {
|
||||
}
|
||||
|
||||
constexpr std::tuple<> unbox() const noexcept {
|
||||
return std::tuple<>{};
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns an empty spread box which represents an empty
|
||||
/// mapped object.
|
||||
constexpr spread_box<> empty_spread() noexcept {
|
||||
return spread_box<>{};
|
||||
}
|
||||
|
||||
/// Deduces to a true_type if the given type is a spread marker
|
||||
template <typename T>
|
||||
struct is_spread : std::false_type {};
|
||||
template <typename... T>
|
||||
struct is_spread<spread_box<T...>> : std::true_type {};
|
||||
|
||||
/// Deduces to a true_type if the given type is an empty
|
||||
/// spread marker
|
||||
template <typename T>
|
||||
struct is_empty_spread : std::false_type {};
|
||||
template <>
|
||||
struct is_empty_spread<spread_box<>> : std::true_type {};
|
||||
|
||||
/// Converts types to the type and spread_box objects to its
|
||||
/// underlying tuple.
|
||||
template <typename T>
|
||||
constexpr T unpack(T&& type) {
|
||||
return std::forward<T>(type);
|
||||
}
|
||||
template <typename... T>
|
||||
constexpr auto unpack(spread_box<T...> type) -> decltype(type.unbox()) {
|
||||
return type.unbox();
|
||||
}
|
||||
|
||||
/// Deduces to the type unpack is returning when called with the
|
||||
/// the given type T.
|
||||
template <typename T>
|
||||
using unpacked_of_t = decltype(unpack(std::declval<T>()));
|
||||
|
||||
/// Converts types to the type and spread_box objects to its
|
||||
/// underlying tuple. If the type is mapped to zero elements,
|
||||
/// the return type will be void.
|
||||
template <typename T>
|
||||
constexpr auto unpack_or_void(T&& type)
|
||||
-> decltype(unpack(std::forward<T>(type))) {
|
||||
return unpack(std::forward<T>(type));
|
||||
}
|
||||
inline void unpack_or_void(spread_box<>) noexcept {
|
||||
}
|
||||
|
||||
/// Converts types to the a tuple carrying the single type and
|
||||
/// spread_box objects to its underlying tuple.
|
||||
template <typename T>
|
||||
constexpr std::tuple<T> undecorate(T&& type) {
|
||||
return std::tuple<T>{std::forward<T>(type)};
|
||||
}
|
||||
template <typename... T>
|
||||
constexpr auto undecorate(spread_box<T...> type) -> decltype(type.unbox()) {
|
||||
return type.unbox();
|
||||
}
|
||||
|
||||
/// A callable object which maps its content back to a
|
||||
/// tuple like type.
|
||||
template <typename EmptyType, template <typename...> class Type>
|
||||
struct tupelizer_base {
|
||||
// We overload with one argument here so Clang and GCC don't
|
||||
// have any issues with overloading against zero arguments.
|
||||
template <typename First, typename... T>
|
||||
constexpr Type<First, T...> operator()(First&& first, T&&... args) const {
|
||||
return Type<First, T...>{std::forward<First>(first),
|
||||
std::forward<T>(args)...};
|
||||
}
|
||||
|
||||
// Specifically return the empty object which can be different
|
||||
// from a tuple.
|
||||
constexpr EmptyType operator()() const noexcept(noexcept(EmptyType{})) {
|
||||
return EmptyType{};
|
||||
}
|
||||
};
|
||||
|
||||
/// A callable object which maps its content back to a tuple.
|
||||
template <template <typename...> class Type = std::tuple>
|
||||
using tupelizer_of_t = tupelizer_base<std::tuple<>, Type>;
|
||||
|
||||
/// A callable object which maps its content back to a tuple like
|
||||
/// type if it wasn't empty. For empty types arguments an empty
|
||||
/// spread box is returned instead. This is useful to propagate
|
||||
/// empty mappings back to the caller.
|
||||
template <template <typename...> class Type = std::tuple>
|
||||
using flat_tupelizer_of_t = tupelizer_base<spread_box<>, Type>;
|
||||
|
||||
/// A callable object which maps its content back to an
|
||||
/// array like type.
|
||||
/// This transform can only be used for (flat) mappings which
|
||||
/// return an empty mapping back to the caller.
|
||||
template <template <typename, std::size_t> class Type>
|
||||
struct flat_arraylizer {
|
||||
/// Deduces to the array type when the array is instantiated
|
||||
/// with the given arguments.
|
||||
template <typename First, typename... Rest>
|
||||
using array_type_of_t = Type<std::decay_t<First>, 1 + sizeof...(Rest)>;
|
||||
|
||||
// We overload with one argument here so Clang and GCC don't
|
||||
// have any issues with overloading against zero arguments.
|
||||
template <typename First, typename... T>
|
||||
constexpr auto operator()(First&& first, T&&... args) const
|
||||
-> array_type_of_t<First, T...> {
|
||||
return array_type_of_t<First, T...>{
|
||||
{std::forward<First>(first), std::forward<T>(args)...}};
|
||||
}
|
||||
|
||||
constexpr auto operator()() const noexcept -> decltype(empty_spread()) {
|
||||
return empty_spread();
|
||||
}
|
||||
};
|
||||
|
||||
/// Use the recursive instantiation for a variadic pack which
|
||||
/// may contain spread types
|
||||
template <typename C, typename... T>
|
||||
constexpr auto apply_spread_impl(std::true_type, C&& callable, T&&... args)
|
||||
-> decltype(
|
||||
traits::unpack(std::forward<C>(callable),
|
||||
std::tuple_cat(undecorate(std::forward<T>(args))...))) {
|
||||
return traits::unpack(std::forward<C>(callable),
|
||||
std::tuple_cat(undecorate(std::forward<T>(args))...));
|
||||
}
|
||||
|
||||
/// Use the linear instantiation for variadic packs which don't
|
||||
/// contain spread types.
|
||||
template <typename C, typename... T>
|
||||
constexpr auto apply_spread_impl(std::false_type, C&& callable, T&&... args)
|
||||
-> decltype(std::forward<C>(callable)(std::forward<T>(args)...)) {
|
||||
return std::forward<C>(callable)(std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
/// Deduces to a true_type if any of the given types marks
|
||||
/// the underlying type to be spread into the current context.
|
||||
template <typename... T>
|
||||
using is_any_spread_t = traits::disjunction<is_spread<T>...>;
|
||||
|
||||
template <typename C, typename... T>
|
||||
constexpr auto map_spread(C&& callable, T&&... args)
|
||||
-> decltype(apply_spread_impl(is_any_spread_t<T...>{},
|
||||
std::forward<C>(callable),
|
||||
std::forward<T>(args)...)) {
|
||||
// Check whether any of the args is a detail::flatted_tuple_t,
|
||||
// if not, use the linear called version for better
|
||||
// compilation speed.
|
||||
return apply_spread_impl(is_any_spread_t<T...>{}, std::forward<C>(callable),
|
||||
std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
/// Converts the given variadic arguments into a tuple in a way
|
||||
/// that spread return values are inserted into the current pack.
|
||||
template <typename... T>
|
||||
constexpr auto tupelize(T&&... args)
|
||||
-> decltype(map_spread(tupelizer_of_t<>{}, std::forward<T>(args)...)) {
|
||||
return map_spread(tupelizer_of_t<>{}, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
/// Converts the given variadic arguments into a tuple in a way
|
||||
/// that spread return values are inserted into the current pack.
|
||||
/// If the arguments were mapped to zero arguments, the empty
|
||||
/// mapping is propagated backwards to the caller.
|
||||
template <template <typename...> class Type, typename... T>
|
||||
constexpr auto flat_tupelize_to(T&&... args)
|
||||
-> decltype(map_spread(flat_tupelizer_of_t<Type>{},
|
||||
std::forward<T>(args)...)) {
|
||||
return map_spread(flat_tupelizer_of_t<Type>{}, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
/// Converts the given variadic arguments into an array in a way
|
||||
/// that spread return values are inserted into the current pack.
|
||||
/// Through this the size of the array like type might change.
|
||||
/// If the arguments were mapped to zero arguments, the empty
|
||||
/// mapping is propagated backwards to the caller.
|
||||
template <template <typename, std::size_t> class Type, typename... T>
|
||||
constexpr auto flat_arraylize_to(T&&... args)
|
||||
-> decltype(map_spread(flat_arraylizer<Type>{}, std::forward<T>(args)...)) {
|
||||
return map_spread(flat_arraylizer<Type>{}, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
/// Converts an empty tuple to void
|
||||
template <typename First, typename... Rest>
|
||||
constexpr std::tuple<First, Rest...>
|
||||
voidify_empty_tuple(std::tuple<First, Rest...> val) {
|
||||
return std::move(val);
|
||||
}
|
||||
inline void voidify_empty_tuple(std::tuple<>) noexcept {
|
||||
}
|
||||
|
||||
/// Converts the given variadic arguments into a tuple in a way
|
||||
/// that spread return values are inserted into the current pack.
|
||||
///
|
||||
/// If the returned tuple is empty, voidis returned instead.
|
||||
template <typename... T>
|
||||
constexpr decltype(auto) tupelize_or_void(T&&... args) {
|
||||
return voidify_empty_tuple(tupelize(std::forward<T>(args)...));
|
||||
}
|
||||
/// \endcond
|
||||
} // namespace spreading
|
||||
|
||||
/// Just traverses the pack with the given callable object,
|
||||
/// no result is returned or preserved.
|
||||
struct strategy_traverse_tag {};
|
||||
/// Remaps the variadic pack with the return values from the mapper.
|
||||
struct strategy_remap_tag {};
|
||||
|
||||
/// Deduces to a true type if the type leads to at least one effective
|
||||
/// call to the mapper.
|
||||
template <typename Mapper, typename T>
|
||||
using is_effective_t = traits::is_invocable<typename Mapper::traversor_type, T>;
|
||||
|
||||
// TODO find out whether the linear compile-time instantiation is faster:
|
||||
// template <typename Mapper, typename... T>
|
||||
// struct is_effective_any_of_t
|
||||
// : traits::disjunction<is_effective_t<Mapper, T>...> {};
|
||||
// template <typename Mapper>
|
||||
// struct is_effective_any_of_t<Mapper> : std::false_type {};
|
||||
|
||||
/// Deduces to a true type if any type leads to at least one effective
|
||||
/// call to the mapper.
|
||||
template <typename Mapper, typename... T>
|
||||
struct is_effective_any_of_t;
|
||||
template <typename Mapper, typename First, typename... Rest>
|
||||
struct is_effective_any_of_t<Mapper, First, Rest...>
|
||||
: std::conditional<is_effective_t<Mapper, First>::value, std::true_type,
|
||||
is_effective_any_of_t<Mapper, Rest...>>::type {};
|
||||
template <typename Mapper>
|
||||
struct is_effective_any_of_t<Mapper> : std::false_type {};
|
||||
|
||||
/// Provides utilities for remapping the whole content of a
|
||||
/// container like type to the same container holding different types.
|
||||
namespace container_remapping {
|
||||
/// Deduces to a true type if the given parameter T
|
||||
/// has a push_back method that accepts a type of E.
|
||||
template <typename T, typename E, typename = void>
|
||||
struct has_push_back : std::false_type {};
|
||||
template <typename T, typename E>
|
||||
struct has_push_back<
|
||||
T, E,
|
||||
traits::void_t<decltype(std::declval<T>().push_back(std::declval<E>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
/// Specialization for a container with a single type T
|
||||
template <typename NewType, template <class> class Base, typename OldType>
|
||||
auto rebind_container(Base<OldType> const & /*container*/) -> Base<NewType> {
|
||||
return Base<NewType>();
|
||||
}
|
||||
|
||||
/// Specialization for a container with a single type T and
|
||||
/// a particular allocator,
|
||||
/// which is preserved across the remap.
|
||||
/// -> We remap the allocator through std::allocator_traits.
|
||||
template <
|
||||
typename NewType, template <class, class> class Base, typename OldType,
|
||||
typename OldAllocator,
|
||||
// Check whether the second argument of the container was
|
||||
// the used allocator.
|
||||
typename std::enable_if<std::uses_allocator<
|
||||
Base<OldType, OldAllocator>, OldAllocator>::value>::type* = nullptr,
|
||||
typename NewAllocator = typename std::allocator_traits<
|
||||
OldAllocator>::template rebind_alloc<NewType>>
|
||||
auto rebind_container(Base<OldType, OldAllocator> const& container)
|
||||
-> Base<NewType, NewAllocator> {
|
||||
// Create a new version of the allocator, that is capable of
|
||||
// allocating the mapped type.
|
||||
return Base<NewType, NewAllocator>(NewAllocator(container.get_allocator()));
|
||||
}
|
||||
|
||||
/// Returns the default iterators of the container in case
|
||||
/// the container was passed as an l-value reference.
|
||||
/// Otherwise move iterators of the container are returned.
|
||||
template <typename C, typename = void>
|
||||
class container_accessor {
|
||||
static_assert(std::is_lvalue_reference<C>::value,
|
||||
"This should be a lvalue reference here!");
|
||||
|
||||
C container_;
|
||||
|
||||
public:
|
||||
container_accessor(C container) : container_(container) {
|
||||
}
|
||||
|
||||
auto begin() -> decltype(container_.begin()) {
|
||||
return container_.begin();
|
||||
}
|
||||
|
||||
auto end() -> decltype(container_.end()) {
|
||||
return container_.end();
|
||||
}
|
||||
};
|
||||
template <typename C>
|
||||
class container_accessor<
|
||||
C, typename std::enable_if<std::is_rvalue_reference<C&&>::value>::type> {
|
||||
C&& container_;
|
||||
|
||||
public:
|
||||
container_accessor(C&& container) : container_(std::move(container)) {
|
||||
}
|
||||
|
||||
auto begin() -> decltype(std::make_move_iterator(container_.begin())) {
|
||||
return std::make_move_iterator(container_.begin());
|
||||
}
|
||||
|
||||
auto end() -> decltype(std::make_move_iterator(container_.end())) {
|
||||
return std::make_move_iterator(container_.end());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
container_accessor<T> container_accessor_of(T&& container) {
|
||||
// Don't use any decay here
|
||||
return container_accessor<T>(std::forward<T>(container));
|
||||
}
|
||||
|
||||
/// Deduces to the type the homogeneous container is containing
|
||||
///
|
||||
/// This alias deduces to the same type on which
|
||||
/// container_accessor<T> is iterating.
|
||||
///
|
||||
/// The basic idea is that we deduce to the type the homogeneous
|
||||
/// container T is carrying as reference while preserving the
|
||||
/// original reference type of the container:
|
||||
/// - If the container was passed as l-value its containing
|
||||
/// values are referenced through l-values.
|
||||
/// - If the container was passed as r-value its containing
|
||||
/// values are referenced through r-values.
|
||||
template <typename Container>
|
||||
using element_of_t = typename std::conditional<
|
||||
std::is_rvalue_reference<Container&&>::value,
|
||||
decltype(std::move(*(std::declval<Container>().begin()))),
|
||||
decltype(*(std::declval<Container>().begin()))>::type;
|
||||
|
||||
/// Removes all qualifier and references from the given type
|
||||
/// if the type is a l-value or r-value reference.
|
||||
template <typename T>
|
||||
using dereferenced_of_t = typename std::conditional<std::is_reference<T>::value,
|
||||
std::decay_t<T>, T>::type;
|
||||
|
||||
/// Returns the type which is resulting if the mapping is applied to
|
||||
/// an element in the container.
|
||||
///
|
||||
/// Since standard containers don't allow to be instantiated with
|
||||
/// references we try to construct the container from a copied
|
||||
/// version.
|
||||
template <typename Container, typename Mapping>
|
||||
using mapped_type_from_t = dereferenced_of_t<spreading::unpacked_of_t<decltype(
|
||||
std::declval<Mapping>()(std::declval<element_of_t<Container>>()))>>;
|
||||
|
||||
/// Deduces to a true_type if the mapping maps to zero elements.
|
||||
template <typename T, typename M>
|
||||
using is_empty_mapped = spreading::is_empty_spread<
|
||||
std::decay_t<decltype(std::declval<M>()(std::declval<element_of_t<T>>()))>>;
|
||||
|
||||
/// We are allowed to reuse the container if we map to the same
|
||||
/// type we are accepting and when we have
|
||||
/// the full ownership of the container.
|
||||
template <typename T, typename M>
|
||||
using can_reuse = std::integral_constant<
|
||||
bool, std::is_same<element_of_t<T>, mapped_type_from_t<T, M>>::value &&
|
||||
std::is_rvalue_reference<T&&>::value>;
|
||||
|
||||
/// Categorizes a mapping of a homogeneous container
|
||||
///
|
||||
/// \tparam IsEmptyMapped Identifies whether the mapping maps to
|
||||
/// to zero arguments.
|
||||
/// \tparam CanReuse Identifies whether the container can be
|
||||
/// re-used through the mapping.
|
||||
template <bool IsEmptyMapped, bool CanReuse>
|
||||
struct container_mapping_tag {};
|
||||
|
||||
/// Categorizes the given container through a container_mapping_tag
|
||||
template <typename T, typename M>
|
||||
using container_mapping_tag_of_t =
|
||||
container_mapping_tag<is_empty_mapped<T, M>::value, can_reuse<T, M>::value>;
|
||||
|
||||
/// Deduces to a true type if the given parameter T supports a `reserve` method
|
||||
template <typename From, typename To, typename = void>
|
||||
struct is_reservable_from : std::false_type {};
|
||||
template <typename From, typename To>
|
||||
struct is_reservable_from<From, To,
|
||||
traits::void_t<decltype(std::declval<To>().reserve(
|
||||
std::declval<From>().size()))>> : std::true_type {
|
||||
};
|
||||
|
||||
template <typename Dest, typename Source>
|
||||
void reserve_if(std::true_type, Dest&& dest, Source&& source) {
|
||||
// Reserve the mapped size
|
||||
dest.reserve(source.size());
|
||||
}
|
||||
template <typename Dest, typename Source>
|
||||
void reserve_if(std::false_type, Dest&&, Source&&) noexcept {
|
||||
// We do nothing here, since the container doesn't support reserving
|
||||
}
|
||||
|
||||
/// We create a new container, which may hold the resulting type
|
||||
template <typename M, typename T>
|
||||
auto remap_container(container_mapping_tag<false, false>, M&& mapper,
|
||||
T&& container)
|
||||
-> decltype(rebind_container<mapped_type_from_t<T, M>>(container)) {
|
||||
static_assert(has_push_back<std::decay_t<T>, element_of_t<T>>::value,
|
||||
"Can only remap containers that provide a push_back "
|
||||
"method!");
|
||||
|
||||
// Create the new container, which is capable of holding
|
||||
// the remappped types.
|
||||
auto remapped = rebind_container<mapped_type_from_t<T, M>>(container);
|
||||
|
||||
// We try to reserve the original size from the source
|
||||
// container inside the destination container.
|
||||
reserve_if(
|
||||
is_reservable_from<std::decay_t<T>, std::decay_t<decltype(remapped)>>{},
|
||||
remapped, container);
|
||||
|
||||
// Perform the actual value remapping from the source to
|
||||
// the destination.
|
||||
// We could have used std::transform for this, however,
|
||||
// I didn't want to pull a whole header for it in.
|
||||
for (auto&& val : container_accessor_of(std::forward<T>(container))) {
|
||||
remapped.push_back(spreading::unpack(
|
||||
std::forward<M>(mapper)(std::forward<decltype(val)>(val))));
|
||||
}
|
||||
|
||||
return remapped; // RVO
|
||||
}
|
||||
|
||||
/// The remapper optimized for the case that we map to the same
|
||||
/// type we accepted such as int -> int.
|
||||
template <typename M, typename T>
|
||||
auto remap_container(container_mapping_tag<false, true>, M&& mapper,
|
||||
T&& container) -> std::decay_t<T> {
|
||||
for (auto&& val : container_accessor_of(std::forward<T>(container))) {
|
||||
val = spreading::unpack(
|
||||
std::forward<M>(mapper)(std::forward<decltype(val)>(val)));
|
||||
}
|
||||
return std::forward<T>(container);
|
||||
}
|
||||
|
||||
/// Remap the container to zero arguments
|
||||
template <typename M, typename T>
|
||||
auto remap_container(container_mapping_tag<true, false>, M&& mapper,
|
||||
T&& container) -> decltype(spreading::empty_spread()) {
|
||||
for (auto&& val : container_accessor_of(std::forward<T>(container))) {
|
||||
// Don't save the empty mapping for each invocation
|
||||
// of the mapper.
|
||||
std::forward<M>(mapper)(std::forward<decltype(val)>(val));
|
||||
}
|
||||
// Return one instance of an empty mapping for the container
|
||||
return spreading::empty_spread();
|
||||
}
|
||||
|
||||
/// \cond false
|
||||
/// Remaps the content of the given container with type T,
|
||||
/// to a container of the same type which may contain
|
||||
/// different types.
|
||||
template <typename T, typename M>
|
||||
auto remap(
|
||||
strategy_remap_tag, T&& container, M&& mapper,
|
||||
typename std::enable_if<is_effective_t<M, element_of_t<T>>::value>::type* =
|
||||
nullptr) -> decltype(remap_container(container_mapping_tag_of_t<T, M>{},
|
||||
std::forward<M>(mapper),
|
||||
std::forward<T>(container))) {
|
||||
return remap_container(container_mapping_tag_of_t<T, M>{},
|
||||
std::forward<M>(mapper), std::forward<T>(container));
|
||||
}
|
||||
/// \endcond
|
||||
|
||||
/// Just call the visitor with the content of the container
|
||||
template <typename T, typename M>
|
||||
void remap(
|
||||
strategy_traverse_tag, T&& container, M&& mapper,
|
||||
typename std::enable_if<is_effective_t<M, element_of_t<T>>::value>::type* =
|
||||
nullptr) {
|
||||
for (auto&& element : container_accessor_of(std::forward<T>(container))) {
|
||||
std::forward<M>(mapper)(std::forward<decltype(element)>(element));
|
||||
}
|
||||
}
|
||||
} // end namespace container_remapping
|
||||
|
||||
/// Provides utilities for remapping the whole content of a
|
||||
/// tuple like type to the same type holding different types.
|
||||
namespace tuple_like_remapping {
|
||||
template <typename Strategy, typename Mapper, typename T,
|
||||
typename Enable = void>
|
||||
struct tuple_like_remapper;
|
||||
|
||||
/// Specialization for std::tuple like types which contain
|
||||
/// an arbitrary amount of heterogenous arguments.
|
||||
template <typename M, template <typename...> class Base, typename... OldArgs>
|
||||
struct tuple_like_remapper<strategy_remap_tag, M, Base<OldArgs...>,
|
||||
// Support for skipping completely untouched types
|
||||
typename std::enable_if<is_effective_any_of_t<
|
||||
M, OldArgs...>::value>::type> {
|
||||
M mapper_;
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) -> decltype(spreading::flat_tupelize_to<Base>(
|
||||
std::declval<M>()(std::forward<Args>(args))...)) {
|
||||
return spreading::flat_tupelize_to<Base>(
|
||||
mapper_(std::forward<Args>(args))...);
|
||||
}
|
||||
};
|
||||
template <typename M, template <typename...> class Base, typename... OldArgs>
|
||||
struct tuple_like_remapper<strategy_traverse_tag, M, Base<OldArgs...>,
|
||||
// Support for skipping completely untouched types
|
||||
typename std::enable_if<is_effective_any_of_t<
|
||||
M, OldArgs...>::value>::type> {
|
||||
M mapper_;
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args) -> traits::void_t<
|
||||
decltype(std::declval<M>()(std::declval<OldArgs>()))...> {
|
||||
int dummy[] = {0, ((void)mapper_(std::forward<Args>(args)), 0)...};
|
||||
(void)dummy;
|
||||
}
|
||||
};
|
||||
|
||||
/// Specialization for std::array like types, which contains a
|
||||
/// compile-time known amount of homogeneous types.
|
||||
template <typename M, template <typename, std::size_t> class Base,
|
||||
typename OldArg, std::size_t Size>
|
||||
struct tuple_like_remapper<
|
||||
strategy_remap_tag, M, Base<OldArg, Size>,
|
||||
// Support for skipping completely untouched types
|
||||
typename std::enable_if<is_effective_t<M, OldArg>::value>::type> {
|
||||
M mapper_;
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args)
|
||||
-> decltype(spreading::flat_arraylize_to<Base>(
|
||||
mapper_(std::forward<Args>(args))...)) {
|
||||
return spreading::flat_arraylize_to<Base>(
|
||||
mapper_(std::forward<Args>(args))...);
|
||||
}
|
||||
};
|
||||
template <typename M, template <typename, std::size_t> class Base,
|
||||
typename OldArg, std::size_t Size>
|
||||
struct tuple_like_remapper<
|
||||
strategy_traverse_tag, M, Base<OldArg, Size>,
|
||||
// Support for skipping completely untouched types
|
||||
typename std::enable_if<is_effective_t<M, OldArg>::value>::type> {
|
||||
M mapper_;
|
||||
|
||||
template <typename... Args>
|
||||
auto operator()(Args&&... args)
|
||||
-> decltype((std::declval<M>()(std::declval<OldArg>()))()) {
|
||||
int dummy[] = {0, ((void)mapper_(std::forward<Args>(args)), 0)...};
|
||||
(void)dummy;
|
||||
}
|
||||
};
|
||||
|
||||
/// Remaps the content of the given tuple like type T,
|
||||
/// to a container of the same type which may contain
|
||||
/// different types.
|
||||
template <typename Strategy, typename T, typename M>
|
||||
auto remap(Strategy, T&& container, M&& mapper) -> decltype(traits::unpack(
|
||||
std::declval<
|
||||
tuple_like_remapper<Strategy, std::decay_t<M>, std::decay_t<T>>>(),
|
||||
std::forward<T>(container))) {
|
||||
return traits::unpack(
|
||||
tuple_like_remapper<Strategy, std::decay_t<M>, std::decay_t<T>>{
|
||||
std::forward<M>(mapper)},
|
||||
std::forward<T>(container));
|
||||
}
|
||||
} // end namespace tuple_like_remapping
|
||||
|
||||
/// Base class for making strategy dependent behaviour available
|
||||
/// to the mapping_helper class.
|
||||
template <typename Strategy>
|
||||
struct mapping_strategy_base {
|
||||
template <typename T>
|
||||
auto may_void(T&& element) const -> std::decay_t<T> {
|
||||
return std::forward<T>(element);
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct mapping_strategy_base<strategy_traverse_tag> {
|
||||
template <typename T>
|
||||
void may_void(T&& /*element*/) const noexcept {
|
||||
}
|
||||
};
|
||||
|
||||
/// A helper class which applies the mapping or
|
||||
/// routes the element through
|
||||
template <typename Strategy, typename M>
|
||||
class mapping_helper : protected mapping_strategy_base<Strategy> {
|
||||
M mapper_;
|
||||
|
||||
class traversal_callable_base {
|
||||
mapping_helper* helper_;
|
||||
|
||||
public:
|
||||
explicit traversal_callable_base(mapping_helper* helper) : helper_(helper) {
|
||||
}
|
||||
|
||||
protected:
|
||||
mapping_helper* get_helper() noexcept {
|
||||
return helper_;
|
||||
}
|
||||
};
|
||||
|
||||
/// A callable object which forwards its invocations
|
||||
/// to mapping_helper::traverse.
|
||||
class traversor : public traversal_callable_base {
|
||||
public:
|
||||
using traversal_callable_base::traversal_callable_base;
|
||||
|
||||
/// SFINAE helper
|
||||
template <typename T>
|
||||
auto operator()(T&& element)
|
||||
-> decltype(std::declval<traversor>().get_helper()->traverse(
|
||||
Strategy{}, std::forward<T>(element)));
|
||||
|
||||
/// An alias to this type
|
||||
using traversor_type = traversor;
|
||||
};
|
||||
|
||||
/// A callable object which forwards its invocations
|
||||
/// to mapping_helper::try_traverse.
|
||||
///
|
||||
/// This callable object will accept any input,
|
||||
/// since elements passed to it are passed through,
|
||||
/// if the provided mapper doesn't accept it.
|
||||
class try_traversor : public traversal_callable_base {
|
||||
public:
|
||||
using traversal_callable_base::traversal_callable_base;
|
||||
|
||||
template <typename T>
|
||||
auto operator()(T&& element)
|
||||
-> decltype(std::declval<try_traversor>().get_helper()->try_traverse(
|
||||
Strategy{}, std::forward<T>(element))) {
|
||||
return this->get_helper()->try_traverse(Strategy{},
|
||||
std::forward<T>(element));
|
||||
}
|
||||
|
||||
/// An alias to the traversor type
|
||||
using traversor_type = traversor;
|
||||
};
|
||||
|
||||
/// Invokes the real mapper with the given element
|
||||
template <typename T>
|
||||
auto invoke_mapper(T&& element) -> decltype(
|
||||
std::declval<mapping_helper>().mapper_(std::forward<T>(element))) {
|
||||
return mapper_(std::forward<T>(element));
|
||||
}
|
||||
|
||||
/// SFINAE helper for plain elements not satisfying the tuple like
|
||||
/// or container requirements.
|
||||
///
|
||||
/// We use the proxy function invoke_mapper here,
|
||||
/// because some compilers (MSVC) tend to instantiate the invocation
|
||||
/// before matching the tag, which leads to build failures.
|
||||
template <typename T>
|
||||
auto match(container_category_tag<false, false>, T&& element) -> decltype(
|
||||
std::declval<mapping_helper>().invoke_mapper(std::forward<T>(element)));
|
||||
|
||||
/// SFINAE helper for elements satisfying the container
|
||||
/// requirements, which are not tuple like.
|
||||
template <typename T>
|
||||
auto match(container_category_tag<true, false>, T&& container)
|
||||
-> decltype(container_remapping::remap(Strategy{},
|
||||
std::forward<T>(container),
|
||||
std::declval<traversor>()));
|
||||
|
||||
/// SFINAE helper for elements which are tuple like and
|
||||
/// that also may satisfy the container requirements
|
||||
template <bool IsContainer, typename T>
|
||||
auto match(container_category_tag<IsContainer, true>, T&& tuple_like)
|
||||
-> decltype(tuple_like_remapping::remap(Strategy{},
|
||||
std::forward<T>(tuple_like),
|
||||
std::declval<traversor>()));
|
||||
|
||||
/// This method implements the functionality for routing
|
||||
/// elements through, that aren't accepted by the mapper.
|
||||
/// Since the real matcher methods below are failing through SFINAE,
|
||||
/// the compiler will try to specialize this function last,
|
||||
/// since it's the least concrete one.
|
||||
/// This works recursively, so we only call the mapper
|
||||
/// with the minimal needed set of accepted arguments.
|
||||
template <typename MatcherTag, typename T>
|
||||
auto try_match(MatcherTag, T&& element) -> decltype(
|
||||
std::declval<mapping_helper>().may_void(std::forward<T>(element))) {
|
||||
return this->may_void(std::forward<T>(element));
|
||||
}
|
||||
|
||||
/// Match plain elements not satisfying the tuple like or
|
||||
/// container requirements.
|
||||
///
|
||||
/// We use the proxy function invoke_mapper here,
|
||||
/// because some compilers (MSVC) tend to instantiate the invocation
|
||||
/// before matching the tag, which leads to build failures.
|
||||
template <typename T>
|
||||
auto try_match(container_category_tag<false, false>, T&& element) -> decltype(
|
||||
std::declval<mapping_helper>().invoke_mapper(std::forward<T>(element))) {
|
||||
// T could be any non container or non tuple like type here,
|
||||
// take int or hpx::future<int> as an example.
|
||||
return invoke_mapper(std::forward<T>(element));
|
||||
}
|
||||
|
||||
/// Match elements satisfying the container requirements,
|
||||
/// which are not tuple like.
|
||||
template <typename T>
|
||||
auto try_match(container_category_tag<true, false>, T&& container)
|
||||
-> decltype(container_remapping::remap(Strategy{},
|
||||
std::forward<T>(container),
|
||||
std::declval<try_traversor>())) {
|
||||
return container_remapping::remap(Strategy{}, std::forward<T>(container),
|
||||
try_traversor{this});
|
||||
}
|
||||
|
||||
/// Match elements which are tuple like and that also may
|
||||
/// satisfy the container requirements
|
||||
/// -> We match tuple like types over container like ones
|
||||
template <bool IsContainer, typename T>
|
||||
auto try_match(container_category_tag<IsContainer, true>, T&& tuple_like)
|
||||
-> decltype(tuple_like_remapping::remap(Strategy{},
|
||||
std::forward<T>(tuple_like),
|
||||
std::declval<try_traversor>())) {
|
||||
return tuple_like_remapping::remap(Strategy{}, std::forward<T>(tuple_like),
|
||||
try_traversor{this});
|
||||
}
|
||||
|
||||
/// Traverses a single element.
|
||||
///
|
||||
/// SFINAE helper: Doesn't allow routing through elements,
|
||||
/// that aren't accepted by the mapper
|
||||
template <typename T>
|
||||
auto traverse(Strategy, T&& element)
|
||||
-> decltype(std::declval<mapping_helper>().match(
|
||||
std::declval<container_category_of_t<std::decay_t<T>>>(),
|
||||
std::declval<T>()));
|
||||
|
||||
/// \copybrief traverse
|
||||
template <typename T>
|
||||
auto try_traverse(Strategy, T&& element)
|
||||
-> decltype(std::declval<mapping_helper>().try_match(
|
||||
std::declval<container_category_of_t<std::decay_t<T>>>(),
|
||||
std::declval<T>())) {
|
||||
// We use tag dispatching here, to categorize the type T whether
|
||||
// it satisfies the container or tuple like requirements.
|
||||
// Then we can choose the underlying implementation accordingly.
|
||||
return try_match(container_category_of_t<std::decay_t<T>>{},
|
||||
std::forward<T>(element));
|
||||
}
|
||||
|
||||
public:
|
||||
explicit mapping_helper(M mapper) : mapper_(std::move(mapper)) {
|
||||
}
|
||||
|
||||
/// \copybrief try_traverse
|
||||
template <typename T>
|
||||
decltype(auto) init_traverse(strategy_remap_tag, T&& element) {
|
||||
return spreading::unpack_or_void(
|
||||
try_traverse(strategy_remap_tag{}, std::forward<T>(element)));
|
||||
}
|
||||
template <typename T>
|
||||
void init_traverse(strategy_traverse_tag, T&& element) {
|
||||
try_traverse(strategy_traverse_tag{}, std::forward<T>(element));
|
||||
}
|
||||
|
||||
/// Calls the traversal method for every element in the pack,
|
||||
/// and returns a tuple containing the remapped content.
|
||||
template <typename First, typename Second, typename... T>
|
||||
decltype(auto) init_traverse(strategy_remap_tag strategy, First&& first,
|
||||
Second&& second, T&&... rest) {
|
||||
return spreading::tupelize_or_void(
|
||||
try_traverse(strategy, std::forward<First>(first)),
|
||||
try_traverse(strategy, std::forward<Second>(second)),
|
||||
try_traverse(strategy, std::forward<T>(rest))...);
|
||||
}
|
||||
|
||||
/// Calls the traversal method for every element in the pack,
|
||||
/// without preserving the return values of the mapper.
|
||||
template <typename First, typename Second, typename... T>
|
||||
void init_traverse(strategy_traverse_tag strategy, First&& first,
|
||||
Second&& second, T&&... rest) {
|
||||
try_traverse(strategy, std::forward<First>(first));
|
||||
try_traverse(strategy, std::forward<Second>(second));
|
||||
int dummy[] = {0,
|
||||
((void)try_traverse(strategy, std::forward<T>(rest)), 0)...};
|
||||
(void)dummy;
|
||||
}
|
||||
};
|
||||
|
||||
/// Traverses the given pack with the given mapper and strategy
|
||||
template <typename Strategy, typename Mapper, typename... T>
|
||||
decltype(auto) transform(Strategy strategy, Mapper&& mapper, T&&... pack) {
|
||||
mapping_helper<Strategy, std::decay_t<Mapper>> helper(
|
||||
std::forward<Mapper>(mapper));
|
||||
return helper.init_traverse(strategy, std::forward<T>(pack)...);
|
||||
}
|
||||
} // namespace traversal
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_TRAVERSE_HPP_INCLUDED
|
||||
@ -1,54 +0,0 @@
|
||||
|
||||
/*
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
\_,(_)| | | || ||_|(_||_)|(/_
|
||||
|
||||
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_IDENTITY_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_IDENTITY_HPP_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
#include <continuable/detail/features.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
/// A tagging type for wrapping other types
|
||||
template <typename... T>
|
||||
struct identity {};
|
||||
|
||||
template <typename>
|
||||
struct is_identity : std::false_type {};
|
||||
template <typename... Args>
|
||||
struct is_identity<identity<Args...>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
using identify = std::conditional_t<is_identity<std::decay_t<T>>::value, T,
|
||||
identity<std::decay_t<T>>>;
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
#endif // CONTINUABLE_DETAIL_IDENTITY_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