Compare commits

..

No commits in common. "master" and "3.0.0-alpha-1" have entirely different histories.

165 changed files with 4416 additions and 11553 deletions

View File

@ -1,38 +1,12 @@
BasedOnStyle: LLVM 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 PointerAlignment: Left
IndentCaseLabels: true
AllowShortFunctionsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
FixNamespaceComments: true
# IndentPPDirectives: AfterHash
MacroBlockBegin: "^CONTINUABLE_BLOCK_.*_BEGIN$" MacroBlockBegin: "^CONTINUABLE_BLOCK_.*_BEGIN$"
MacroBlockEnd: "^CONTINUABLE_BLOCK_.*_END$" 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

View File

@ -1,4 +1,4 @@
Checks: '-*,cppcoreguidelines-*,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-macro-usage,bugprone-*,modernize-*,boost-*,llvm-*,misc-*,portability-*,readability-*' Checks: '-*,cppcoreguidelines-*,-cppcoreguidelines-pro-type-vararg,modernize--*,llvm-*,misc-*,readability-identifier-naming'
CheckOptions: CheckOptions:
- key: readability-identifier-naming.ClassCase - key: readability-identifier-naming.ClassCase
value: lower_case value: lower_case
@ -16,4 +16,4 @@ CheckOptions:
value: lower_case value: lower_case
- key: readability-identifier-naming.Macro - key: readability-identifier-naming.Macro
value: UPPER_CASE value: UPPER_CASE
HeaderFilterRegex: 'include/continuable/.(hpp)$' HeaderFilterRegex: 'include/.(hpp)$'

1
.github/CODEOWNERS vendored
View File

@ -1 +0,0 @@
* @Naios

View File

@ -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.

View File

@ -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}

View File

@ -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}

View File

@ -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

6
.gitignore vendored
View File

@ -47,9 +47,3 @@ bld/
# Visual Studo 2015 cache/options directory # Visual Studo 2015 cache/options directory
.vs/ .vs/
# VSCode
.vscode/
# TMP files generated from clang-format
*.TMP

22
.gitmodules vendored
View File

@ -1,16 +1,12 @@
[submodule "dep/googletest/googletest"] [submodule "dep/googletest/googletest"]
path = dep/googletest/googletest path = dep/googletest/googletest
url = https://github.com/google/googletest.git url = https://github.com/google/googletest.git
branch = master
[submodule "dep/function2/function2"] [submodule "dep/function2/function2"]
path = dep/function2/function2 path = dep/function2/function2
url = https://github.com/Naios/function2.git url = https://github.com/Naios/function2.git
branch = master [submodule "dep/cxx_function/cxx_function"]
path = dep/cxx_function/cxx_function
url = https://github.com/potswa/cxx_function.git
[submodule "dep/asio/asio"] [submodule "dep/asio/asio"]
path = dep/asio/asio path = dep/asio/asio
url = https://github.com/chriskohlhoff/asio.git url = https://github.com/chriskohlhoff/asio.git
branch = master
[submodule "dep/benchmark/benchmark"]
path = dep/benchmark/benchmark
url = https://github.com/google/benchmark.git
branch = master

71
.travis.yml Normal file
View File

@ -0,0 +1,71 @@
sudo: true
dist: trusty
language: cpp
cache: apt
git:
depth: 1
matrix:
include:
- os: linux
compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-6
- valgrind
- cmake
- cmake-data
- ninja-build
env:
- COMPILER=g++-6
- WITH_NO_EXCEPTIONS=OFF
- WITH_AWAIT=OFF
- os: linux
compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-5.0
packages:
- clang-5.0
- cmake
- cmake-data
- ninja-build
env:
- COMPILER=clang++-5.0
- WITH_NO_EXCEPTIONS=OFF
- WITH_AWAIT=OFF
- os: linux
compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-5.0
packages:
- clang-5.0
- cmake
- cmake-data
- ninja-build
env:
- COMPILER=clang++-5.0
- WITH_NO_EXCEPTIONS=ON
- WITH_AWAIT=ON
install:
- export CXX=$COMPILER
- $CXX --version
- chmod +x tools/travis-ci.sh
script:
- ./tools/travis-ci.sh
notifications:
email: false

View File

@ -1,4 +1,5 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal # of this software and associated documentation files(the "Software"), to deal
@ -7,215 +8,121 @@
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions : # furnished to do so, subject to the following conditions :
# #
# The above copyright notice and this permission notice shall be included in all # The above copyright notice and this permission notice shall be included in
# copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # 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 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
cmake_minimum_required(VERSION 3.11) cmake_minimum_required(VERSION 3.2)
project(continuable VERSION 2.0.0 LANGUAGES C CXX)
project( string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
continuable IS_TOP_LEVEL_PROJECT)
VERSION 4.0.0
LANGUAGES C CXX)
if(CTI_CONTINUABLE_IS_FIND_INCLUDED) option(CTI_CONTINUABLE_WITH_TESTS
set(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT OFF) "Build the continuable unit tests"
else() ${IS_TOP_LEVEL_PROJECT})
string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
endif()
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT) option(CTI_CONTINUABLE_WITH_EXAMPLES
message( "Build the continuable examples"
STATUS ${IS_TOP_LEVEL_PROJECT})
"Building with ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER_VERSION})")
endif()
option(CTI_CONTINUABLE_WITH_INSTALL "Add the continuable install targets" option(CTI_CONTINUABLE_WITH_NO_EXCEPTIONS
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT}) "Disable exception support"
OFF)
option(CTI_CONTINUABLE_WITH_TESTS "Build the continuable unit tests" option(CTI_CONTINUABLE_WITH_AWAIT
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT}) "Enable co_await support"
OFF)
option(CTI_CONTINUABLE_WITH_EXAMPLES "Build the continuable examples" include(cmake/CMakeLists.txt)
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
option(CTI_CONTINUABLE_WITH_BENCHMARKS "Build the continuable benchmarks" OFF) set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
option(CTI_CONTINUABLE_WITH_NO_EXCEPTIONS "Disable exception support" OFF) add_subdirectory(dep)
option(CTI_CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS
"Enable unhandled asynchronous exceptions" OFF)
option(CTI_CONTINUABLE_WITH_COROUTINE "Enable C++20 coroutines" OFF)
option(CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE
"Enable experimental coroutines" OFF)
option(CTI_CONTINUABLE_WITH_CPP_LATEST
"Enable the highest C++ standard available for testing polyfills" OFF)
option(CTI_CONTINUABLE_WITH_LIGHT_TESTS
"Disable some template heavy unit tests (for CI usage)" OFF)
# Top level project settings only
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
set(CTI_CONTINUABLE_WITH_CONCURRENT_JOBS
"0"
CACHE
STRING
"Set the number of concurrent compilation jobs (0 = unlimited, for CI usage)"
)
else()
set(CTI_CONTINUABLE_WITH_CONCURRENT_JOBS "0")
endif()
if(NOT TARGET Threads::Threads)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
endif()
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
include(cmake/CMakeLists.txt)
add_subdirectory(dep)
else()
if(NOT TARGET function2::function2)
find_package(function2 4 REQUIRED)
endif()
endif()
# continuable-base # continuable-base
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT) add_library(continuable-base INTERFACE)
add_library(continuable-base INTERFACE)
else()
add_library(continuable-base INTERFACE IMPORTED GLOBAL)
endif()
add_library(continuable::continuable-base ALIAS continuable-base) add_library(continuable::continuable-base ALIAS continuable-base)
target_include_directories( target_include_directories(continuable-base
continuable-base INTERFACE
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
$<INSTALL_INTERFACE:include>) $<INSTALL_INTERFACE:include>)
target_link_libraries(continuable-base INTERFACE Threads::Threads) target_link_libraries(continuable-base
INTERFACE
Threads::Threads)
target_compile_features( target_compile_features(continuable-base
continuable-base INTERFACE
INTERFACE cxx_alias_templates cxx_alias_templates
cxx_auto_type cxx_auto_type
cxx_constexpr cxx_constexpr
cxx_decltype cxx_decltype
cxx_decltype_auto cxx_decltype_auto
cxx_final cxx_final
cxx_lambdas cxx_lambdas
cxx_generic_lambdas cxx_generic_lambdas
cxx_variadic_templates cxx_variadic_templates
cxx_defaulted_functions cxx_defaulted_functions
cxx_nullptr cxx_nullptr
cxx_trailing_return_types cxx_trailing_return_types
cxx_return_type_deduction) cxx_return_type_deduction)
if(CTI_CONTINUABLE_WITH_CPP_LATEST) if (CTI_CONTINUABLE_WITH_AWAIT)
target_compile_features(continuable-base INTERFACE cxx_std_20) target_compile_options(continuable-base
endif() INTERFACE
$<$<CXX_COMPILER_ID:MSVC>:/await>
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>)
if(CTI_CONTINUABLE_WITH_COROUTINE)
if(NOT CTI_CONTINUABLE_WITH_CPP_LATEST)
message(FATAL_ERROR "CTI_CONTINUABLE_WITH_COROUTINE requires "
"CTI_CONTINUABLE_WITH_CPP_LATEST!")
endif()
target_compile_options(
continuable-base
INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/await:strict>
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>
$<$<CXX_COMPILER_ID:GNU>:-fcoroutines>)
elseif(CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE)
target_compile_options(
continuable-base INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/await>
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>)
endif()
if(CTI_CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS)
target_compile_definitions(continuable-base target_compile_definitions(continuable-base
INTERFACE CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS) INTERFACE
endif() -DCONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
add_library(continuable INTERFACE)
else()
add_library(continuable INTERFACE IMPORTED GLOBAL)
endif() endif()
add_library(continuable INTERFACE)
add_library(continuable::continuable ALIAS continuable) add_library(continuable::continuable ALIAS continuable)
target_link_libraries(continuable INTERFACE continuable::continuable-base target_link_libraries(continuable
function2::function2) INTERFACE
continuable-base
function2)
if(CTI_CONTINUABLE_WITH_INSTALL) # Create an install target
include(ExternalProject) install(TARGETS continuable-base continuable
include(GNUInstallDirs) EXPORT continuable-config
include(CMakePackageConfigHelpers) INCLUDES DESTINATION include)
# Create an install target: Headers and license files install(EXPORT continuable-config
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/continuable" FILE continuable-config.cmake
DESTINATION "include") NAMESPACE continuable::
install(FILES "LICENSE.txt" DESTINATION .) DESTINATION share/continuable/cmake)
install(FILES "Readme.md" DESTINATION .)
# Config.cmake install(DIRECTORY include/continuable
write_basic_package_version_file( DESTINATION include FILES_MATCHING PATTERN "*.hpp")
"${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 install(FILES LICENSE.txt DESTINATION . RENAME continuable-LICENSE.txt)
configure_package_config_file( install(FILES Readme.md DESTINATION . RENAME continuable-Readme.md)
"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 # Setup CPack for bundling
export( set(CPACK_GENERATOR "ZIP")
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-base set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
NAMESPACE ${PROJECT_NAME}:: set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake") set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
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 include(CPack)
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 # Testing and examples
if(CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES) if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
if(MSVC) if (MSVC)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 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} /W4")
@ -223,11 +130,13 @@ if(CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
enable_testing() enable_testing()
if(CTI_CONTINUABLE_WITH_TESTS) add_subdirectory(doc)
if (CTI_CONTINUABLE_WITH_TESTS)
add_subdirectory(test) add_subdirectory(test)
endif() endif()
if(CTI_CONTINUABLE_WITH_EXAMPLES) if (CTI_CONTINUABLE_WITH_EXAMPLES)
add_subdirectory(examples) add_subdirectory(examples)
endif() endif()
endif() endif ()

View File

@ -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")

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com> Copyright (c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

590
Readme.md
View File

@ -1,233 +1,443 @@
![](https://raw.githubusercontent.com/Naios/continuable/master/doc/slideshow.gif)
<p align="center"> [![](https://img.shields.io/badge/Version-2.0.0-0091EA.svg)](https://github.com/Naios/continuable/releases/tag/2.0.0) [![Build Status](https://travis-ci.org/Naios/continuable.svg?branch=master)](https://travis-ci.org/Naios/continuable) [![Build status](https://ci.appveyor.com/api/projects/status/328ta3r5x92f3byv/branch/master?svg=true)](https://ci.appveyor.com/project/Naios/continuable/branch/master) ![](https://img.shields.io/badge/License-MIT-00838F.svg) [![](https://img.shields.io/badge/Documentation-Doxygen-26A69A.svg)](https://naios.github.io/continuable/) [![](https://img.shields.io/badge/Try-online-4DB6AC.svg)](http://melpon.org/wandbox/permlink/xVM2szjDLEge3YLV)
<a href="https://naios.github.io/continuable/">
<img alt="Continuable" src="https://raw.githubusercontent.com/Naios/continuable/master/doc/slideshow.gif">
</a>
</p>
<p align="center">
<a href="https://naios.github.io/continuable/changelog.html#changelog-versions-4-0-0"><img alt="Current version" src="https://img.shields.io/badge/Version-4.0.0-0091EA.svg"></a>
<a href="https://ci.appveyor.com/project/Naios/continuable/branch/master"><img alt="AppVeyor CI status" src="https://ci.appveyor.com/api/projects/status/328ta3r5x92f3byv/branch/master?svg=true"></a>
<img alt="MIT Licensed" src="https://img.shields.io/badge/License-MIT-00838F.svg">
<a href="https://naios.github.io/continuable/"><img alt="Documentation" src="https://img.shields.io/badge/Documentation-Doxygen-26A69A.svg"></a>
<a href="https://wandbox.org/permlink/EDr7u2P5HXs2W6p1"><img alt="Try continuable online" src="https://img.shields.io/badge/Run-online-4DB6AC.svg"></a>
<a href="https://godbolt.org/g/iyE4Ww"><img alt="Compiler explorer" src="https://img.shields.io/badge/Compiler-explorer-58CEC2.svg"></a>
</p>
------ ------
#### Continuable is a C++14 library that provides full support for:
* lazy async continuation chaining based on callbacks (**then**) and expression templates, callbacks are wrapped nicely as **promises**. This library provides full feature support of:
* **no enforced type-erasure** which means we need **less heap allocations** than comparable libraries, strictly following the **"don't pay for what you don't use"** principle.
* support for *all*, *any* and *sequential* connections between continuables through expressive operator overloads **&&**, **||** and **>>** as well as free functions **when_all**, **when_any** and **when_seq**.
* asynchronous **error handling** through **exceptions**, **error codes** and **user defined types**.
* syntactic sugar for instance: **partial invocation**, **tuple unpacking**, `co_await` support and **executors**.
* **encapsuled from any runtime**, larger framework or executors makes it possible to use continuable even in smaller or esoteric usage scenarios.
------ * lazy async continuation chaining based on **callbacks** (*then*) and expression templates, callbacks are wrapped nicely as promises.
* **no enforced type-erasure** which means we need **less heap allocations**, strictly following the **"don't pay for what you don't use"** principle.
#### Getting started: * support for **connections** between continuables through an **all, any or sequential** strategy through expressive operator overloads **&&**, **||** and **>>**.
* **error handling** through exceptions or custom types.
The [documentation](https://naios.github.io/continuable/) offers everything you need: * **syntactic sugar** for instance: **partial invocation**, **tuple unpacking** and **executors**.
* [Installation guide](https://naios.github.io/continuable/installation.html)
* [Usage tutorial](https://naios.github.io/continuable/tutorial.html)
* [Configuration explanation](https://naios.github.io/continuable/configuration.html)
* [Changelog](https://naios.github.io/continuable/changelog.html)
#### Issues and contributions
Issue reports and questions are accepted through the Github issue tracker as well as pull requests. ## The basics
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
- **Create a continuable through `make_continuable` which returns a promise on invocation:** * Supply a continuable which is invoked lazily upon request:
```cpp ```cpp
auto http_request(std::string url) { auto http_request(std::string url) {
return cti::make_continuable<std::string>([url = std::move(url)](auto&& promise) { return cti::make_continuable<std::string>([url = std::move(url)](auto&& promise) {
// Perform the actual request through a different library, // Perform the actual request through a different library,
// resolve the promise upon completion of the task. // resolve the promise upon completion of the task.
promise.set_value("<html> ... </html>"); promise.set_value("<html> ... </html>");
// or: promise.set_exception(std::make_exception_ptr(std::exception("Some error")));
// or: promise.set_canceled();
});
}
auto mysql_query(std::string query) {
return cti::make_continuable<result_set, bool>([url = std::move(url)](auto&& promise) {
// ^^^^^^^^^^^^^^ multiple result types
});
}
auto do_sth() {
return cti::make_continuable<void>([](auto&& promise) {
// ^^^^ no result at all
});
}
auto run_it() {
return async([] {
// Directly start with a handler
});
}
continuable<> run_it() { // With type erasure
return async([] {
// Or promise.set_exception(...);
}); });
} }
``` ```
* Continue the continuation using `.then(...)` and `.fail(...)`, exceptions are passed to the first available handler:
- **Attach your continuations through `then`, supports multiple results and partial handlers:**
```cpp ```cpp
mysql_query("SELECT `id`, `name` FROM `users`") http_request("github.com")
.then([](result_set users) { .then([] (std::string result) {
// Return the next continuable to process ... // Do something...
return mysql_query("SELECT `id` name FROM `sessions`"); return http_request("travis-ci.org")
}) })
.then([](result_set sessions) { .then([] (std::string result) {
// ... or pass multiple values to the next callback using tuples or pairs ... // Handle the response from 'travis-ci.org'
return std::make_tuple(std::move(sessions), true);
})
.then([](result_set 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 * `statistics`"))
.then([](result_set result) {
// ...
return "Hi";
})
.then([] /*(std::string result) */ { // Handlers can accept a partial set of arguments{
// ...
});
```
- **Handle failures through `fail` or `next`:**
```cpp
http_request("example.com")
.then([] {
throw std::exception("Some error");
}) })
.fail([] (std::exception_ptr ptr) { .fail([] (std::exception_ptr ptr) {
if (ptr) { try {
try { std::rethrow_exception(ptr);
std::rethrow_exception(ptr); } catch(std::exception const& e) {
} catch(std::exception const& e) { // Handle the exception or error code here
// Handle the exception or error code here
}
} }
}); });
``` ```
* Create connections between the continuables and use its compound result:
- **Dispatch continuations through a specific executor** (possibly on a different thread or later)
```cpp ```cpp
auto executor = [](auto&& work) { (http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io")))
// Dispatch the work here, store it for later invocation or move it to another thread. .then([](std::string github, std::string travis_or_atom) {
std::forward<decltype(work)>(work)(); // The promise is called with the response of github and travis or atom.
};
read_file("entries.csv")
.then([](Buffer buffer) {
// ...
}, 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.
});
// `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.
});
// `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) {
// The callback is called with the response of github for sure
// and the second parameter represents the response of example or wikipedia.
});
// 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"));
```
- **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();
})
.then([] () -> result<> {
// We recovered from the failure and proceeding normally
// Will yield a default constructed exception type to signal cancellation
return cancel();
}); });
``` ```
- **`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));
}
// (boost) asio completion token integration
asio::io_context io_context;
asio::steady_timer steady_timer(io_context);
steady_timer.expires_after(std::chrono::seconds(5)); ## Table of contents
steady_timer.async_wait(cti::use_continuable)
.then([] {
// Is called after 5s
});
```
- **C++20 Coroutine support:** - [Installation](#installation)
- [How-to use](#how-to-use)
- [Building the unit-tests](#building-the-unit-tests)
- [Stability and version](#stability-and-version)
- [Quick reference](#quick-reference)
- [Creating Continuables](#creating-continuables)
- [Chaining Continuables](#chaining-continuables)
- [Providing helper functions](#providing-helper-functions)
- [Error handling](#error-handling)
- [Connecting Continuables {all, any or sequential}](#connecting-continuables-all-any-or-sequential)
- [Partial argument application](#partial-argument-application)
- [Dispatching callbacks through a specific executor](#dispatching-callbacks-through-a-specific-executor)
- [Type erasure](#type-erasure)
- [Coroutines](#coroutines)
- [Future conversion](#future-conversion)
- [Compatibility](#compatibility)
- [License](#license)
(`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:
```cpp ## Installation
int i = co_await cti::make_continuable<int>([](auto&& promise) {
promise.set_value(0); ### How-to use
As mentioned earlier the library is header-only. There is a cmake project provided for simple setup:
#### As git submodule
```sh
# Shell:
git submodule add https://github.com/Naios/continuable.git
```
```cmake
# CMake file:
add_subdirectory(continuable)
# continuable provides an interface target which makes it's
# headers available to all projects using the continuable library.
target_link_libraries(my_project continuable)
```
On POSIX platforms you are required to link your application against a corresponding thread library, otherwise `std::future's` won't work properly, this is done automatically by the provided CMake project.
#### As CMake library
Additionally the project exports a `continuable` target which is importable through CMake when installed:
```sh
mkdir build
cd build
cmake ..
cmake --build . --target INSTALL --config Release
```
`CMakeLists.txt`:
```cmake
find_package(continuable REQUIRED)
```
### Building the unit-tests
In order to build the unit tests clone the repository recursively with all submodules:
```sh
# Shell:
git clone --recursive https://github.com/Naios/continuable.git
```
## Stability and version
The library follows the rules of [semantic versioning](http://semver.org/), the API is kept stable across minor versions.
The CI driven unit-tests are observed through the Clang sanitizers (asan, ubsan and lsan - when compiling with Clang) or Valgrind (when compiling with GCC).
## Quick reference
This chapter only overflies the functionality of the continuable library, the full documentation is located at https://naios.github.io/continuable/.
### Creating Continuables
Create a continuable from a promise taking function:
```c++
#include <continuable/continuable-base.hpp>
// ...
auto void_continuable = cti::make_continuable<void>([](auto&& promise) {
// ^^^^
// Resolve the promise later when you have finished your work
promise.set_value();
});
auto str_continuable = cti::make_continuable<std::string>([](auto&& promise) {
// ^^^^^^^^^^^
promise.set_value("Hello, World!");
});
```
### Chaining Continuables
Chain continuables together in order to build up an asynchronous call hierarchy:
```c++
mysql_query("SELECT `id`, `name` FROM `users`")
.then([](ResultSet users) {
// 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 * `statistics`"))
.then([](ResultSet result) {
// ...
}); });
``` ```
> **Note:** The continuation chain is invoked when the object is destructed or the `done()` method is called.
#### Appearances: ### Providing helper functions
| [MeetingC++ 2018 Talk](https://naios.github.io/talks/2018-11-17-Meeting-C%2B%2B-Berlin/Continuable.pdf) | Returning continuables from helper functions makes chaining simple and expressive:
| :---: |
| [<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++
#include <continuable/continuable-base.hpp>
// ...
auto mysql_query(std::string query) {
return cti::make_continuable<ResultSet>([query = std::move(query)](auto&& promise) mutable {
// Pass the callback to the handler which calls the callback when finished.
// Every function accepting callbacks works with continuables.
mysql_handle_async_query(std::move(query),
std::forward<decltype(promise)>(promise));
});
}
// You may use the helper function like you would normally do,
// without using the support methods of the continuable.
mysql_query("DELETE FROM `users` WHERE `id` = 27361");
// Or using chaining to handle the result which is covered in the documentation.
mysql_query("SELECT `id`, `name` FROM users")
.then([](ResultSet result) {
// ...
});
```
### Error handling
Continuables support asynchronous error handling through exceptions or custom error types.
The error type will be **`std::exception_ptr`** except if one of the following definition is defined:
- **`CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`**: Define this to use a user defined error type.
- **`CONTINUABLE_WITH_NO_EXCEPTIONS`**: Define this to use **`std::error_condition`** as error type and to disable exception support. When exceptions are disabled this definition is set automatically.
Resolving a promise through an error will skip all following result handlers attached through **`then`**:
```cpp
auto get_bad_continuable(std::exception const& e) {
return cti::make_continuable<void>([=] (auto&& promise) {
try {
throw e;
} catch(...) {
promise.set_exception(std::current_exception());
}
});
}
```
You may handle the exception as following:
```cpp
get_bad_continuable()
.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
}
});
```
### Connecting Continuables {all, any or sequential}
Continuables provide the operators **&&** and **||** for logical connection:
* **&&** invokes the final callback with the compound result of all connected continuables, the continuables were invoked in parallel.
* **||** invokes the final callback once with the first result which becomes available.
* **>\>** invokes the final callback with the compound result of all connected continuables but the continuations were invokes sequentially.
```C++
auto http_request(std::string url) {
return cti::make_continuable<std::string>([](auto&& promise) {
promise.set_value("<html>...</html>");
});
}
// `all` of connections:
(http_request("github.com") && http_request("travis-ci.org") && http_request("atom.io"))
.then([](std::string github, std::string travis, std::string atom) {
// The callback is called with the response of github, travis and atom.
});
// `any` of connections:
(http_request("github.com") || http_request("travis-ci.org") || http_request("atom.io"))
.then([](std::string github_or_travis_or_atom) {
// The callback is called with the first response of either github, travis or atom.
});
// `sequence` of connections:
(http_request("github.com") >> http_request("travis-ci.org") >> http_request("atom.io"))
.then([](std::string github, std::string travis, std::string atom) {
// The requests are invoked sequentially
});
// mixed logical connections:
(http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io")))
.then([](std::string github, std::string travis_or_atom) {
// The callback is called with the response of github for sure
// and the second parameter represents the response of travis or atom.
});
// There are helper functions for connecting continuables:
auto all = cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
auto any = cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
auto seq = cti::when_seq(http_request("github.com"), http_request("travis-ci.org"));
```
> **Note:** Logical connections are ensured to be **thread-safe** and **wait-free** by library design (when assuming that *std::call_once* is wait-free - which depends on the toolchain).
### Partial argument application
The callback is called only with the arguments it's accepting:
```c++
(http_request("github.com") && read_file("entries.csv"))
.then([] {
// ^^^^^^ The original signature was <std::string, Buffer>,
// however, the callback is only invoked with the amount of
// arguments it's accepting.
});
```
### Dispatching callbacks through a specific executor
Dispatching a callback through a specific executor is supported through through the second argument of `then()`:
```c++
auto executor = [](auto&& work) {
// 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);
// ^^^^^^^^
```
### 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&& promise) {
// The use of non copyable objects is possible by design if
// the type erasure backend supports it.
promise.set_value(*value, "Hello, World!");
});
std::move(unique).then([](int i) {
// ...
});
```
We could also think about using `std::future` as backend but this is even worse then using `std::function` because usually there is, even more, type erasure and allocations involved.
Additionally `std::function` doesn't provide support for multiple function overloads
### Coroutines
Since version 2.0.0 coroutines (`co_await` and `co_return`) are supported by continuables when the underlying toolchain supports the TS. Currently this works in MSVC 2017 and Clang 5.0.
You have to enable this capability through the `CTI_CONTINUABLE_WITH_AWAIT` define in CMake.
```c++
int i = co_await cti::make_continuable<int>([](auto&& promise) {
promise.set_value(0);
});
```
### Future conversion
The library is capable of converting (*futurizing*) every continuable into a fitting **std::future** through the `continuable<...>::futurize()` method.:
```c++
std::future<std::string> future = http_request("github.com")
.then([](std::string response) {
// Do sth...
return http_request("travis-ci.org") || http_request("atom.io");
})
.apply(cti::transforms::futurize());
// ^^^^^^^^
std::future<std::tuple<std::string, std::string>> future =
(http_request("travis-ci.org") && http_request("atom.io"))
.apply(cti::transforms::futurize());
```
> **Note:** See the [doxygen documentation](https://naios.github.io/continuable/) for detailed information about the return type of `futurize()`.
## Compatibility
Tested & compatible with:
- Visual Studio 2017+ Update 2
- Clang 5.0+
- GCC 6.0+
Although the build is observed with the latest toolchains earlier ones might work.
The library only depends on the standard library when using the `continuable/continuable-base.hpp` header, which provides the basic continuation logic.
> **Note:** On Posix: don't forget to **link a corresponding thread library** into your application otherwise `std::future's` won't work `(-pthread)` when using future based transforms.
## License
The continuable library is licensed under the MIT License:
```
/**
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v2.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
```
.

View File

@ -1,17 +1,13 @@
image: image:
- Visual Studio 2017 - Visual Studio 2017
- Visual Studio 2019
environment: environment:
matrix: matrix:
- WITH_NO_EXCEPTIONS: OFF - WITH_NO_EXCEPTIONS: OFF
WITH_CPP_LATEST: OFF - WITH_NO_EXCEPTIONS: ON
- WITH_NO_EXCEPTIONS: ON
WITH_CPP_LATEST: OFF configuration:
- WITH_NO_EXCEPTIONS: OFF - Debug
WITH_CPP_LATEST: ON
- WITH_NO_EXCEPTIONS: ON
WITH_CPP_LATEST: ON
platform: platform:
- x64 - x64
@ -24,20 +20,17 @@ clone_script:
before_build: before_build:
- cmd: > - cmd: >
cmake -H. -Bbuild -A%PLATFORM% cmake -H. -Bbuild -A%PLATFORM%
-DCTI_CONTINUABLE_WITH_NO_EXCEPTIONS=%WITH_NO_EXCEPTIONS% -DCTI_CONTINUABLE_WITH_NO_EXCEPTIONS=%WITH_NO_EXCEPTIONS%
-DCTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE=ON -DCTI_CONTINUABLE_WITH_AWAIT=ON
-DCTI_CONTINUABLE_WITH_CPP_LATEST=%WITH_CPP_LATEST%
build_script: 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 %CONFIGURATION% --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 %CONFIGURATION% --target PACKAGE -- /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: test_script:
- cmd: cd build - cmd: cd build
- cmd: ctest -C Debug -V . - cmd: ctest -C %CONFIGURATION% -V .
- cmd: ctest -C Release -V .
artifacts: artifacts:
- path: 'build/*.zip' - path: 'build/*.zip'

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal # of this software and associated documentation files(the "Software"), to deal
@ -13,7 +13,7 @@
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # 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 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal # of this software and associated documentation files(the "Software"), to deal
@ -13,7 +13,7 @@
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # 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 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal # of this software and associated documentation files(the "Software"), to deal
@ -13,7 +13,7 @@
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # 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 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal # of this software and associated documentation files(the "Software"), to deal
@ -13,7 +13,7 @@
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # 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 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -31,18 +31,9 @@ if (PLATFORM EQUAL 64)
-D_WIN64) -D_WIN64)
endif() 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 target_compile_options(continuable-features-flags
INTERFACE INTERFACE
/MP
/bigobj /bigobj
/permissive-) /permissive-)
@ -50,12 +41,6 @@ target_compile_options(continuable-features-warnings
INTERFACE INTERFACE
/W4) /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) if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
target_compile_definitions(continuable-features-noexcept target_compile_definitions(continuable-features-noexcept
INTERFACE INTERFACE

View File

@ -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@)

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal # of this software and associated documentation files(the "Software"), to deal
@ -13,7 +13,7 @@
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # 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 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -21,12 +21,12 @@
# Select the compiler specific cmake file # Select the compiler specific cmake file
set(MSVC_ID "MSVC") set(MSVC_ID "MSVC")
if (${CMAKE_CXX_COMPILER_ID} MATCHES "(Apple)?Clang") if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
include(${PROJECT_SOURCE_DIR}/cmake/compiler/clang.cmake) include(${CMAKE_SOURCE_DIR}/cmake/compiler/clang.cmake)
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") 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}) 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() else()
message(FATAL_ERROR "Unknown compiler!") message(FATAL_ERROR "Unknown compiler!")
endif() endif()

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal # of this software and associated documentation files(the "Software"), to deal
@ -13,10 +13,10 @@
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # 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 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
include(${PROJECT_SOURCE_DIR}/cmake/macros/group_sources.cmake) include(${CMAKE_SOURCE_DIR}/cmake/macros/group_sources.cmake)

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal # of this software and associated documentation files(the "Software"), to deal
@ -13,48 +13,46 @@
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # 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 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.
set(WITH_SOURCE_TREE "hierarchical") set(WITH_SOURCE_TREE "hierarchical")
macro(group_sources) macro(group_sources dir)
# Skip this if WITH_SOURCE_TREE is not set (empty string). # Skip this if WITH_SOURCE_TREE is not set (empty string).
if (NOT ${WITH_SOURCE_TREE} STREQUAL "") if (NOT ${WITH_SOURCE_TREE} STREQUAL "")
foreach(dir ${ARGN}) # Include all header and c files
# Include all header and c files file(GLOB_RECURSE elements RELATIVE ${dir} *.h *.hpp *.inl *.inc *.c *.cpp *.cc)
file(GLOB_RECURSE elements RELATIVE ${dir} ${dir}/*)
foreach(element ${elements}) foreach(element ${elements})
# Extract filename and directory # Extract filename and directory
get_filename_component(element_name ${element} NAME) get_filename_component(element_name ${element} NAME)
get_filename_component(element_dir ${element} DIRECTORY) get_filename_component(element_dir ${element} DIRECTORY)
if (NOT ${element_dir} STREQUAL "") if (NOT ${element_dir} STREQUAL "")
# If the file is in a subdirectory use it as source group. # If the file is in a subdirectory use it as source group.
if (${WITH_SOURCE_TREE} STREQUAL "flat") if (${WITH_SOURCE_TREE} STREQUAL "flat")
# Build flat structure by using only the first subdirectory. # Build flat structure by using only the first subdirectory.
string(FIND ${element_dir} "/" delemiter_pos) string(FIND ${element_dir} "/" delemiter_pos)
if (NOT ${delemiter_pos} EQUAL -1) if (NOT ${delemiter_pos} EQUAL -1)
string(SUBSTRING ${element_dir} 0 ${delemiter_pos} group_name) 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}) source_group("${group_name}" FILES ${dir}/${element})
else()
# Build hierarchical structure.
# File is in root directory.
source_group("${element_dir}" FILES ${dir}/${element})
endif() endif()
else() else()
# If the file is in the root directory, place it in the root source_group. # Use the full hierarchical structure to build source_groups.
source_group("\\" FILES ${dir}/${element}) string(REPLACE "/" "\\" group_name ${element_dir})
source_group("${group_name}" FILES ${dir}/${element})
endif() endif()
endforeach() 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() endif()
endmacro() endmacro()

View File

@ -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")

View File

@ -1,25 +1,17 @@
if(NOT TARGET function2::function2) if(NOT TARGET function2)
add_subdirectory(function2) add_subdirectory(function2)
endif() endif()
if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_BENCHMARKS) if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
if(NOT TARGET gtest) if(NOT TARGET gtest)
add_subdirectory(googletest) add_subdirectory(googletest)
endif() endif()
endif()
if (CTI_CONTINUABLE_WITH_EXAMPLES) if(NOT TARGET cxx_function)
add_subdirectory(cxx_function)
endif()
if(NOT TARGET asio) if(NOT TARGET asio)
add_subdirectory(asio) add_subdirectory(asio)
endif() endif()
endif() endif()
if (CTI_CONTINUABLE_WITH_BENCHMARKS)
if(NOT TARGET benchmark)
add_subdirectory(benchmark)
endif()
if(NOT TARGET boost)
add_subdirectory(boost)
endif()
endif()

View File

@ -1,11 +1,9 @@
add_library(asio STATIC add_library(asio STATIC
${CMAKE_CURRENT_LIST_DIR}/asio/asio/src/asio.cpp ${CMAKE_CURRENT_LIST_DIR}/asio/asio/src/asio.cpp)
${CMAKE_CURRENT_LIST_DIR}/include/boost/throw_exception.hpp)
target_include_directories(asio target_include_directories(asio
PUBLIC PUBLIC
${CMAKE_CURRENT_LIST_DIR}/asio/asio/include ${CMAKE_CURRENT_LIST_DIR}/asio/asio/include)
${CMAKE_CURRENT_LIST_DIR}/include)
target_compile_definitions(asio target_compile_definitions(asio
PUBLIC PUBLIC
@ -13,15 +11,6 @@ target_compile_definitions(asio
-DASIO_SEPARATE_COMPILATION=1 -DASIO_SEPARATE_COMPILATION=1
-DASIO_NO_TYPEID=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) if(WIN32)
target_compile_definitions(asio target_compile_definitions(asio
PUBLIC PUBLIC

@ -1 +1 @@
Subproject commit 6c054e98f3f53352d12b6cd46d63b6d404cc044b Subproject commit 230c0d2ae035c5ce1292233fcab03cea0d341264

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -0,0 +1,5 @@
add_library(cxx_function INTERFACE)
target_include_directories(cxx_function
INTERFACE
"${CMAKE_CURRENT_LIST_DIR}")

@ -0,0 +1 @@
Subproject commit c12ed6e2da0c05ebc7bac30cdd260f152cb565a1

@ -1 +1 @@
Subproject commit 3a0746bf5f601dfed05330aefcb6854354fce07d Subproject commit 8611ae3b0e2414427bf0381bd94085ca3175479b

View File

@ -1,4 +1,3 @@
if(ON)
add_library(gtest STATIC add_library(gtest STATIC
${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc) ${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc)
@ -43,10 +42,3 @@ target_include_directories(gmock
${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock ${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock
PUBLIC PUBLIC
${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock/include) ${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 ac34e6c950925df7165e626becd3f9d64dcd584b

1
doc/CMakeLists.txt Normal file
View File

@ -0,0 +1 @@
add_subdirectory(code)

41
doc/Index.md Normal file
View File

@ -0,0 +1,41 @@
![](https://raw.githubusercontent.com/Naios/continuable/master/doc/slideshow.gif)
## Content
- **Class cti::continuable_base** - for building up a continuation chain.
- \link cti::continuable_base::then then\endlink - adds a callback or cti::continuable_base to the invocation chain.
- \link cti::continuable_base::fail fail\endlink - adds an error callback to the invocation chain.
- \link cti::continuable_base::next next\endlink - adds an error and result callback to the invocation chain.
- \link cti::continuable_base::operator | operator|\endlink - connects another object through \link cti::continuable_base::then then\endlink.
- \link cti::continuable_base::operator && operator&&\endlink - connects another cti::continuable_base with an *all* logic.
- \link cti::continuable_base::operator|| operator||\endlink - connects another cti::continuable_base with an *any* logic.
- \link cti::continuable_base::operator>> operator>>\endlink - connects another cti::continuable_base with a *sequential* logic.
- \link cti::continuable_base::done done\endlink - \copybrief cti::continuable_base::done
- \link cti::continuable_base::freeze freeze \endlink - prevents the automatic invocation on destruction of the cti::continuable_base.
- \link cti::continuable_base::is_frozen is_frozen\endlink - \copybrief cti::continuable_base::is_frozen
- **Class cti::promise_base** - for resolving a continuation chain through a result or error.
- \link cti::promise_base::set_value set_value\endlink - resolves the continuation chain through a result.
- \link cti::promise_base::set_exception set_exception\endlink - resolves the continuation chain through an error.
- **Helper functions**
- \link cti::make_continuable make_continuable\endlink - creates a cti::continuable_base from a callback tanking function.
- \link cti::when_all when_all\endlink - connects all given cti::continuable_base objects with an *all* logic.
- \link cti::when_any when_any\endlink - connects all given cti::continuable_base objects with an *any* logic.
- \link cti::when_seq when_seq\endlink - connects all given cti::continuable_base objects with a *sequence* logic.
- **Transforms** - Apply a transform to the continuable
- \link cti::transforms::futurize futurize\endlink - \copybrief cti::transforms::futurize
- \link cti::transforms::flatten flatten\endlink - \copybrief cti::transforms::flatten
- **Predefined (erased) types** - Predefined type erarasures for continuables and promises
- \link cti::promise promise\endlink - \copybrief cti::promise
- \link cti::continuable continuable\endlink - \copybrief cti::continuable
- \link cti::unique_continuable unique_continuable\endlink - \copybrief cti::unique_continuable
- **Class cti::continuable_trait** - A trait class for defining your own cti::continuable_base trait with the type-erasure backend of your choice.
- \link cti::continuable_trait::promise promise\endlink - \copybrief cti::continuable_trait::promise
- \link cti::continuable_trait::continuable continuable\endlink - \copybrief cti::continuable_trait::continuable
- **GTest macros:**
- \link ASSERT_ASYNC_COMPLETION ASSERT_ASYNC_COMPLETION \endlink - \copybrief ASSERT_ASYNC_COMPLETION
- \link ASSERT_ASYNC_INCOMPLETION ASSERT_ASYNC_INCOMPLETION \endlink - \copybrief ASSERT_ASYNC_INCOMPLETION
- \link ASSERT_ASYNC_VALIDATION ASSERT_ASYNC_VALIDATION \endlink - \copybrief ASSERT_ASYNC_VALIDATION
- \link ASSERT_ASYNC_BINARY_VALIDATION ASSERT_ASYNC_BINARY_VALIDATION \endlink - \copybrief ASSERT_ASYNC_BINARY_VALIDATION
- \link EXPECT_ASYNC_RESULT EXPECT_ASYNC_RESULT \endlink - \copybrief EXPECT_ASYNC_RESULT
- \link ASSERT_ASYNC_RESULT ASSERT_ASYNC_RESULT \endlink - \copybrief ASSERT_ASYNC_RESULT
- \link ASSERT_ASYNC_TYPES ASSERT_ASYNC_TYPES \endlink - \copybrief ASSERT_ASYNC_TYPES

View File

@ -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.
*/
}

2
doc/code/CMakeLists.txt Normal file
View File

@ -0,0 +1,2 @@
add_subdirectory(documentation)
add_subdirectory(slideshow)

View File

@ -0,0 +1,5 @@
add_executable(doc-documentation
${CMAKE_CURRENT_LIST_DIR}/doc-documentation.cpp)
target_link_libraries(doc-documentation
PRIVATE
continuable)

View File

@ -0,0 +1,152 @@
/*
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#include <string>
#include <continuable/continuable.hpp>
#include <continuable/detail/util.hpp>
using cti::detail::util::unused;
void creating_continuables() {
auto void_continuable = cti::make_continuable<void>([](auto&& callback) {
// ^^^^
// Call the promise later when you have finished your work
callback.set_value();
});
auto str_continuable =
cti::make_continuable<std::string>([](auto&& callback) {
// ^^^^^^^^^^^
callback.set_value("Hello, World!");
});
}
struct ResultSet {};
template <typename... Args>
void mysql_handle_async_query(Args&&...) {
}
auto mysql_query(std::string query) {
return cti::make_continuable<ResultSet>([query = std::move(query)](
auto&& callback) mutable {
// Pass the callback to the handler which calls the callback when finished.
// Every function accepting callbacks works with continuables.
mysql_handle_async_query(std::move(query),
std::forward<decltype(callback)>(callback));
});
}
void providing_helper_functions() {
// You may use the helper function like you would normally do,
// without using the support methods of the continuable.
mysql_query("DELETE FROM `users` WHERE `id` = 27361");
// Or using chaining to handle the result which is covered in the
// documentation.
mysql_query("SELECT `id`, `name` FROM users").then([](ResultSet result) {
// ...
unused(result);
});
}
void chaining_continuables() {
mysql_query("SELECT `id`, `name` FROM `users`")
.then([](ResultSet users) {
(void)users;
// Return the next continuable to process ...
return mysql_query("SELECT `id` name FROM `sessions`");
})
.then([](ResultSet sessions) {
// ... or pass multiple values to the next callback using tuples or
// pairs ...
return std::make_tuple(std::move(sessions), true);
})
.then([](ResultSet sessions, bool is_ok) {
(void)sessions;
(void)is_ok;
// ... or pass a single value to the next callback ...
return 10;
})
.then([](auto value) {
// ^^^^ Templated callbacks are possible too
(void)value;
})
// ... you may even pass continuables to the `then` method directly:
.then(mysql_query("SELECT * `statistics`"))
.then([](ResultSet result) {
// ...
(void)result;
});
}
auto http_request(std::string /*url*/) {
return cti::make_continuable<std::string>([](auto&& callback) {
// ...
callback.set_value("<html>...</html>");
});
}
void connecting_continuables() {
// `all` of connections:
(http_request("github.com") && http_request("travis-ci.org") &&
http_request("atom.io"))
.then([](std::string github, std::string travis, std::string atom) {
// The callback is called with the response of github, travis and atom.
unused(github, travis, atom);
});
// `any` of connections:
(http_request("github.com") || http_request("travis-ci.org") ||
http_request("atom.io"))
.then([](std::string github_or_travis_or_atom) {
// The callback is called with the first response of either github,
// travis or atom.
unused(github_or_travis_or_atom);
});
// mixed logical connections:
(http_request("github.com") &&
(http_request("travis-ci.org") || http_request("atom.io")))
.then([](std::string github, std::string travis_or_atom) {
// The callback is called with the response of github for sure
// and the second parameter represents the response of travis or atom.
unused(github, travis_or_atom);
});
// There are helper functions for connecting continuables:
auto all =
cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
auto any =
cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
}
int main() {
creating_continuables();
providing_helper_functions();
chaining_continuables();
connecting_continuables();
}

View File

@ -0,0 +1,5 @@
add_executable(doc-slideshow
${CMAKE_CURRENT_LIST_DIR}/doc-slideshow.cpp)
target_link_libraries(doc-slideshow
PRIVATE
continuable)

View File

@ -1,5 +1,5 @@
/* /*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -13,7 +13,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -25,32 +25,43 @@
#include "continuable/continuable.hpp" #include "continuable/continuable.hpp"
cti::continuable<std::string> http_request(std::string /*url*/) { cti::continuable<std::string> http_request(std::string /*url*/) {
return cti::make_ready_continuable<std::string>("<html>...</html>"); return [](auto&& callback) {
// ...
callback.set_value("<html>...</html>");
};
} }
struct ResultSet {}; struct ResultSet {};
struct Buffer {}; struct Buffer {};
cti::continuable<ResultSet> mysql_query(std::string /*url*/) { cti::continuable<ResultSet> mysql_query(std::string /*url*/) {
return cti::make_ready_continuable(ResultSet{}); return [](auto&& callback) {
// ...
callback.set_value(ResultSet{});
};
} }
cti::continuable<Buffer> read_file(std::string /*url*/) { cti::continuable<Buffer> read_file(std::string /*url*/) {
return cti::make_ready_continuable(Buffer{}); return [](auto&& callback) {
// ...
callback.set_value(Buffer{});
};
} }
struct functional_executor { struct a {
auto post() const { auto post() const {
return [](auto&&) {}; return [](auto&&) {};
} }
}; };
int main(int, char**) { int main(int, char**) {
functional_executor e; a e;
auto executor = &e; auto executor = &e;
// clang-format off // clang-format off
// ----------
(http_request("github.com") && http_request("atom.io")) (http_request("github.com") && http_request("atom.io"))
.then([] (std::string /*github*/, std::string /*atom*/) { .then([] (std::string /*github*/, std::string /*atom*/) {
// ... // ...
@ -60,15 +71,23 @@ int main(int, char**) {
// ... // ...
}, executor->post()); }, executor->post());
// clang-format on // ----------
http_request("github.com") && http_request("atom.io"); auto c1 = http_request("github.com") && http_request("atom.io") ;
http_request("github.com") || http_request("atom.io");
http_request("github.com") >> http_request("atom.io");
// clang-format off auto c2 = http_request("github.com") || http_request("atom.io") ;
auto c3 = http_request("github.com") >> http_request("atom.io") ;
(void)c1;
(void)c2;
(void)c3;
// ----------
read_file("entries.csv") read_file("entries.csv")
.then([] (Buffer /*buffer*/) { .then([] (Buffer /*buffer*/) {
@ -79,7 +98,8 @@ int main(int, char**) {
// ... // ...
}); });
// clang-format on // ----------
// clang-format on
return 0; return 0;
} }

View File

@ -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. |
*/
}

1
doc/doxygen/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
doxygen/

View File

@ -1,4 +1,4 @@
# Doxyfile 1.8.14 # Doxyfile 1.8.10
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project. # doxygen (www.doxygen.org) for a project.
@ -20,8 +20,8 @@
# This tag specifies the encoding used for all characters in the config file # 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 # 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 # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
# built into libc) for the transcoding. See # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # for the list of possible encodings.
# The default value is: UTF-8. # The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8 DOXYFILE_ENCODING = UTF-8
@ -38,13 +38,13 @@ PROJECT_NAME = Continuable
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 4.1.0 PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description # 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 # 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. # 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 # 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 # in the documentation. The maximum height of the logo should not exceed 55
@ -303,15 +303,6 @@ EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 0.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 0
# When enabled doxygen tries to link words that correspond to documented # When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can # classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or # be prevented in individual cases by putting a % sign in front of the word or
@ -337,7 +328,7 @@ BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # 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 # will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present. # of private inheritance when no explicit protection keyword is present.
# The default value is: NO. # The default value is: NO.
@ -623,19 +614,19 @@ STRICT_PROTO_MATCHING = NO
# list. This list is created by putting \todo commands in the documentation. # list. This list is created by putting \todo commands in the documentation.
# The default value is: YES. # 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 # 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. # list. This list is created by putting \test commands in the documentation.
# The default value is: YES. # 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 # 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. # list. This list is created by putting \bug commands in the documentation.
# The default value is: YES. # 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 GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
# the deprecated list. This list is created by putting \deprecated commands in # the deprecated list. This list is created by putting \deprecated commands in
@ -680,7 +671,7 @@ SHOW_FILES = YES
# Folder Tree View (if specified). # Folder Tree View (if specified).
# The default value is: YES. # 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 # 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 # doxygen should invoke to get the current version for each file (typically from
@ -708,7 +699,7 @@ LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing # 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 # the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool # 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 # 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 # 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. # search path. See also \cite for info how to create references.
@ -758,12 +749,6 @@ WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen # The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which # can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated # will be replaced by the file and line number from which the warning originated
@ -790,13 +775,13 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = ../include \ INPUT = ../../include \
../doc ../Index.md
# This tag can be used to specify the character encoding of the source files # 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 # 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 # 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. # possible encodings.
# The default value is: UTF-8. # The default value is: UTF-8.
@ -813,8 +798,8 @@ INPUT_ENCODING = UTF-8
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd,
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. # *.vhdl, *.ucf, *.qsf, *.as and *.js.
FILE_PATTERNS = *.c \ FILE_PATTERNS = *.c \
*.cc \ *.cc \
@ -858,7 +843,6 @@ FILE_PATTERNS = *.c \
*.ucf \ *.ucf \
*.qsf \ *.qsf \
*.as \ *.as \
*.dox \
*.js *.js
# The RECURSIVE tag can be used to specify whether or not subdirectories should # The RECURSIVE tag can be used to specify whether or not subdirectories should
@ -874,9 +858,9 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is # Note that relative paths are relative to the directory from which doxygen is
# run. # run.
EXCLUDE = ../dep \ EXCLUDE = ../../dep \
../test \ ../../test \
../examples ../../examples
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded # directories that are symbolic links (a Unix file system feature) are excluded
@ -945,10 +929,6 @@ IMAGE_PATH =
# Note that the filter must not add or remove lines; it is applied before the # Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added # code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly. # or removed, the anchors will not be placed correctly.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER = INPUT_FILTER =
@ -958,10 +938,6 @@ INPUT_FILTER =
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the # filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied. # patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
FILTER_PATTERNS = FILTER_PATTERNS =
@ -985,7 +961,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub # (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. # 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 # Configuration options related to source browsing
@ -1046,7 +1022,7 @@ SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will # 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 # 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 # 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. # 4.8.6 or higher.
# #
# To use it do the following: # To use it do the following:
@ -1079,7 +1055,7 @@ VERBATIM_HEADERS = YES
# rich C++ code for which doxygen's built-in parser lacks the necessary type # rich C++ code for which doxygen's built-in parser lacks the necessary type
# information. # information.
# Note: The availability of this option depends on whether or not doxygen was # Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse-libclang=ON option for CMake. # compiled with the --with-libclang option.
# The default value is: NO. # The default value is: NO.
CLANG_ASSISTED_PARSING = NO CLANG_ASSISTED_PARSING = NO
@ -1092,17 +1068,6 @@ CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS = 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 # Configuration options related to the alphabetical class index
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -1221,7 +1186,7 @@ HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # 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 # 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 # 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 # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again. # purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220. # Minimum value: 0, maximum value: 359, default value: 220.
@ -1257,17 +1222,6 @@ HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO 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 # 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 # documentation will contain sections that can be hidden and shown after the
# page has loaded. # page has loaded.
@ -1291,12 +1245,12 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be # 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 # 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 # 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 # Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install 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 # ~/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. # for more information.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
@ -1412,7 +1366,7 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace # 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. # The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
@ -1420,7 +1374,8 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # 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 # 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. # The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
@ -1428,21 +1383,23 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # 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 # 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. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME = QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # 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 # 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. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS = QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see: # 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. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS =
@ -1535,7 +1492,7 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10 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 # generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers. # supported properly for IE 6.0, but are supported on all modern browsers.
# #
@ -1547,7 +1504,7 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # 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 # 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 # 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 # enabled you may also need to install MathJax separately and configure the path
@ -1574,7 +1531,7 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing # 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. 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. # The default value is: http://cdn.mathjax.org/mathjax/latest.
# This tag requires that the tag USE_MATHJAX is set to YES. # This tag requires that the tag USE_MATHJAX is set to YES.
@ -1636,7 +1593,7 @@ SERVER_BASED_SEARCH = NO
# #
# Doxygen ships with an example indexer (doxyindexer) and search engine # Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library # (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. # See the section "External Indexing and Searching" for details.
# The default value is: NO. # The default value is: NO.
@ -1649,7 +1606,7 @@ EXTERNAL_SEARCH = NO
# #
# Doxygen ships with an example indexer (doxyindexer) and search engine # Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library # (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. # Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES. # This tag requires that the tag SEARCHENGINE is set to YES.
@ -1836,20 +1793,12 @@ LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the # The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See # 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. # The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the RTF output # Configuration options related to the RTF output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -2019,9 +1968,9 @@ DOCBOOK_PROGRAMLISTING = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures # AutoGen Definitions (see http://autogen.sf.net) file that captures the
# the structure of the code including all documentation. Note that this feature # structure of the code including all documentation. Note that this feature is
# is still experimental and incomplete at the moment. # still experimental and incomplete at the moment.
# The default value is: NO. # The default value is: NO.
GENERATE_AUTOGEN_DEF = NO GENERATE_AUTOGEN_DEF = NO
@ -2443,11 +2392,6 @@ DIAFILE_DIRS =
PLANTUML_JAR_PATH = PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by # When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block. # the !include statement in a plantuml block.

View File

@ -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.
>
*/
}

View File

@ -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.
*/
}

View File

@ -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
*/
}

View File

@ -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
*/
}

View File

@ -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
*/
}

View File

@ -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.
*/
}

View File

@ -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
*/
}

View File

@ -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
*/
}

View File

@ -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
*/
}

View File

@ -1,3 +1,2 @@
add_subdirectory(example-asio) add_subdirectory(example-asio)
add_subdirectory(example-ai) add_subdirectory(example-ai)
add_subdirectory(example-slideshow)

View File

@ -7,7 +7,7 @@
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

View File

@ -1,28 +1,15 @@
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 add_executable(example-asio
${CMAKE_CURRENT_LIST_DIR}/example-asio.cpp) ${CMAKE_CURRENT_LIST_DIR}/example-asio.cpp)
target_include_directories(example-asio
PRIVATE
${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(example-asio target_link_libraries(example-asio
PRIVATE PRIVATE
asio-example-deps) asio
continuable)
target_compile_definitions(example-asio target_compile_definitions(example-asio
PUBLIC PUBLIC
-DCONTINUABLE_WITH_NO_EXCEPTIONS) -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)

View File

@ -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();
}

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.0.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -34,30 +34,12 @@
#include <string> #include <string>
#include <system_error> #include <system_error>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
#include <exception>
#endif
#include <asio.hpp> #include <asio.hpp>
#include <continuable/continuable.hpp> #include <continuable/continuable.hpp>
using namespace std::chrono_literals; 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 { struct functional_io_service {
asio::io_context service_; asio::io_context service_;
asio::ip::udp::resolver resolver_; asio::ip::udp::resolver resolver_;
@ -67,11 +49,7 @@ struct functional_io_service {
auto trough_post() noexcept { auto trough_post() noexcept {
return [&](auto&& work) mutable { return [&](auto&& work) mutable {
asio::post(service_, asio::post(service_, std::forward<decltype(work)>(work));
[work = std::forward<decltype(work)>(work)]() mutable {
std::move(work)();
// .. or: work.set_value();
});
}; };
} }
@ -83,8 +61,7 @@ struct functional_io_service {
} }
auto async_resolve(std::string host, std::string service) { auto async_resolve(std::string host, std::string service) {
return cti::promisify<asio::ip::udp::resolver::iterator>::with( return cti::promisify<asio::ip::udp::resolver::iterator>::from_asio(
error_code_remapper(),
[&](auto&&... args) { [&](auto&&... args) {
resolver_.async_resolve(std::forward<decltype(args)>(args)...); resolver_.async_resolve(std::forward<decltype(args)>(args)...);
}, },
@ -112,7 +89,7 @@ int main(int, char**) {
// auto socket = std::make_shared<udp::socket>(service); // auto socket = std::make_shared<udp::socket>(service);
// socket->async_send_to() // socket->async_send_to()
}) })
.fail([](cti::exception_t /*error*/) { .fail([](cti::error_type /*error*/) {
// ... // ...
}); });

View File

@ -1,5 +0,0 @@
add_executable(example-slideshow
${CMAKE_CURRENT_LIST_DIR}/example-slideshow.cpp)
target_link_libraries(example-slideshow
PRIVATE
continuable)

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -32,35 +32,60 @@
#define CONTINUABLE_BASE_HPP_INCLUDED #define CONTINUABLE_BASE_HPP_INCLUDED
#include <cassert> #include <cassert>
#include <cstddef> #include <cstdint>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-result.hpp>
#include <continuable/detail/connection/connection-all.hpp>
#include <continuable/detail/connection/connection-any.hpp>
#include <continuable/detail/connection/connection-seq.hpp>
#include <continuable/detail/connection/connection.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/utility/util.hpp>
#if defined(CONTINUABLE_HAS_COROUTINE) #include <continuable/detail/awaiting.hpp>
# include <continuable/detail/other/coroutines.hpp> #include <continuable/detail/base.hpp>
#endif // defined(CONTINUABLE_HAS_COROUTINE) #include <continuable/detail/composition-all.hpp>
#include <continuable/detail/composition-any.hpp>
#include <continuable/detail/composition-seq.hpp>
#include <continuable/detail/composition.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/types.hpp>
#include <continuable/detail/util.hpp>
namespace cti { namespace cti {
/// \defgroup Base Base /// Represents a tag which can be placed first in a signature
/// provides classes and functions to create continuable_base objects. /// in order to overload callables with the asynchronous result
/// \{ /// as well as an error.
///
/// See the example below:
/// ```cpp
/// struct my_callable {
/// void operator() (std::string result) {
/// // ...
/// }
/// void operator() (cti::dispatch_error_tag, cti::error_type) {
/// // ...
/// }
/// };
///
/// // Will receive errors and results
/// continuable.next(my_callable{});
/// ```
///
/// \note see continuable::next for details.
///
/// \since 2.0.0
using detail::types::dispatch_error_tag;
/// Represents the type that is used as error type
///
/// By default this type deduces to `std::exception_ptr`.
/// If `CONTINUABLE_WITH_NO_EXCEPTIONS` is defined the type
/// will be a `std::error_condition`.
/// A custom error type may be set through
/// defining `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`.
///
/// \since 2.0.0
using detail::types::error_type;
/// Deduces to a true_type if the given type is a continuable_base. /// Deduces to a true_type if the given type is a continuable_base.
/// ///
/// \since 3.0.0 /// \since 3.0.0
template <typename T> using detail::base::is_continuable;
using is_continuable = detail::base::is_continuable<T>;
/// The main class of the continuable library, it provides the functionality /// The main class of the continuable library, it provides the functionality
/// for chaining callbacks and continuations together to a unified hierarchy. /// for chaining callbacks and continuations together to a unified hierarchy.
@ -80,7 +105,8 @@ using is_continuable = detail::base::is_continuable<T>;
/// \note Nearly all methods of the cti::continuable_base are required to be /// \note Nearly all methods of the cti::continuable_base are required to be
/// called as r-value. This is required because the continuable carries /// called as r-value. This is required because the continuable carries
/// variables which are consumed when the object is transformed as part /// variables which are consumed when the object is transformed as part
/// of a method call. /// of a method call. You may copy a continuable which underlying
/// storages are copyable to split the call hierarchy into multiple parts.
/// ///
/// \attention The continuable_base objects aren't intended to be stored. /// \attention The continuable_base objects aren't intended to be stored.
/// If you want to store a continuble_base you should always /// If you want to store a continuble_base you should always
@ -92,10 +118,6 @@ template <typename Data, typename Annotation>
class continuable_base { class continuable_base {
/// \cond false /// \cond false
using ownership = detail::util::ownership;
using annotation_trait = detail::annotation_trait<Annotation>;
template <typename, typename> template <typename, typename>
friend class continuable_base; friend class continuable_base;
friend struct detail::base::attorney; friend struct detail::base::attorney;
@ -103,41 +125,25 @@ class continuable_base {
// The continuation type or intermediate result // The continuation type or intermediate result
Data data_; Data data_;
// The transferable state which represents the validity of the object // The transferable state which represents the validity of the object
ownership ownership_; detail::util::ownership ownership_;
/// \endcond /// \endcond
/// Constructor accepting the data object while erasing the annotation /// Constructor accepting the data object while erasing the annotation
explicit continuable_base(Data data, ownership ownership) explicit continuable_base(Data data, detail::util::ownership ownership)
: data_(std::move(data)) : data_(std::move(data)), ownership_(std::move(ownership)) {
, ownership_(std::move(ownership)) {} }
public: public:
/// Constructor accepting the data object while erasing the annotation /// Constructor accepting the data object while erasing the annotation
explicit continuable_base(Data data) explicit continuable_base(Data data) : data_(std::move(data)) {
: data_(std::move(data)) {} }
/// Constructor accepting any object convertible to the data object, /// Constructor accepting any object convertible to the data object,
/// while erasing the annotation /// while erasing the annotation
template <typename OtherData, template <typename OData, std::enable_if_t<std::is_convertible<
std::enable_if_t<detail::base::can_accept_continuation< std::decay_t<OData>, Data>::value>* = nullptr>
Data, Annotation, continuable_base(OData&& data) : data_(std::forward<OData>(data)) {
detail::traits::unrefcv_t<OtherData>>::value>* = nullptr> }
/* implicit */ continuable_base(OtherData&& data)
: data_(
detail::base::proxy_continuable<Annotation,
detail::traits::unrefcv_t<OtherData>>(
std::forward<OtherData>(data))) {}
/// Constructor taking the data of other continuable_base objects
/// while erasing the hint.
///
/// This constructor makes it possible to replace the internal data object of
/// the continuable by any object which is useful for type-erasure.
template <typename OData,
std::enable_if_t<std::is_convertible<
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
/* implicit */ continuable_base(continuable_base<OData, Annotation>&& other)
: data_(std::move(other).consume()) {}
/// Constructor taking the data of other continuable_base objects /// Constructor taking the data of other continuable_base objects
/// while erasing the hint. /// while erasing the hint.
@ -145,15 +151,16 @@ public:
/// This constructor makes it possible to replace the internal data object of /// This constructor makes it possible to replace the internal data object of
/// the continuable by any object which is useful for type-erasure. /// the continuable by any object which is useful for type-erasure.
template <typename OData, typename OAnnotation> template <typename OData, typename OAnnotation>
/* implicit */ continuable_base(continuable_base<OData, OAnnotation>&& other) continuable_base(continuable_base<OData, OAnnotation>&& other)
: continuable_base(std::move(other).finish().consume()) {} : continuable_base(std::move(other).materialize().consume_data()) {
}
/// \cond false /// \cond false
continuable_base(continuable_base&&) = default; continuable_base(continuable_base&&) = default;
continuable_base(continuable_base const&) = delete; continuable_base(continuable_base const&) = default;
continuable_base& operator=(continuable_base&&) = default; continuable_base& operator=(continuable_base&&) = default;
continuable_base& operator=(continuable_base const&) = delete; continuable_base& operator=(continuable_base const&) = default;
/// \endcond /// \endcond
/// The destructor automatically invokes the continuable_base /// The destructor automatically invokes the continuable_base
@ -225,17 +232,12 @@ public:
/// | `Arg` | `continuable_base with <Arg>` | /// | `Arg` | `continuable_base with <Arg>` |
/// | `std::pair<First, Second>` | `continuable_base with <First, Second>` | /// | `std::pair<First, Second>` | `continuable_base with <First, Second>` |
/// | `std::tuple<Args...>` | `continuable_base with <Args...>` | /// | `std::tuple<Args...>` | `continuable_base with <Args...>` |
/// | `cti::result<Args...>` | `continuable_base with <Args...>` |
/// | `continuable_base<Arg...>` | `continuable_base with <Args...>` | /// | `continuable_base<Arg...>` | `continuable_base with <Args...>` |
/// Which means the result type of the continuable_base is equal to /// Which means the result type of the continuable_base is equal to
/// the plain types the callback returns (`std::tuple` and /// the plain types the callback returns (`std::tuple` and
/// `std::pair` arguments are unwrapped). /// `std::pair` arguments are unwrapped).
/// A single continuable_base as argument is resolved and the result /// A single continuable_base as argument is resolved and the result
/// type is equal to the resolved continuable_base. /// type is equal to the resolved continuable_base.
/// A cti::result can be used to cancel the continuation or to
/// transition to the exception handler.
/// The special unwrapping of types can be disabled through wrapping
/// such objects through a call to cti::make_plain.
/// Consider the following examples: /// Consider the following examples:
/// ```cpp /// ```cpp
/// http_request("github.com") /// http_request("github.com")
@ -257,17 +259,6 @@ public:
/// http_request("github.com") /// http_request("github.com")
/// .then([](std::string github) { return http_request("atom.io"); }) /// .then([](std::string github) { return http_request("atom.io"); })
/// .then([](std::string atom) { }); // <std::string> /// .then([](std::string atom) { }); // <std::string>
///
/// http_request("example.com")
/// .then([](std::string content) -> result<std::string> {
/// return rethrow(std::make_exception_ptr(std::exception{}));
/// })
/// .fail([] -> result<std::string> {
/// return recover("Hello World!");
/// })
/// .then([](std::string content) -> result<std::string> {
/// return cancel();
/// })
/// ``` /// ```
/// ///
/// \since 1.0.0 /// \since 1.0.0
@ -276,7 +267,7 @@ public:
E&& executor = detail::types::this_thread_executor_tag{}) && { E&& executor = detail::types::this_thread_executor_tag{}) && {
return detail::base::chain_continuation<detail::base::handle_results::yes, return detail::base::chain_continuation<detail::base::handle_results::yes,
detail::base::handle_errors::no>( detail::base::handle_errors::no>(
std::move(*this).finish(), std::forward<T>(callback), std::move(*this).materialize(), std::forward<T>(callback),
std::forward<E>(executor)); std::forward<E>(executor));
} }
@ -302,7 +293,7 @@ public:
template <typename OData, typename OAnnotation> template <typename OData, typename OAnnotation>
auto then(continuable_base<OData, OAnnotation>&& continuation) && { auto then(continuable_base<OData, OAnnotation>&& continuation) && {
return std::move(*this).then( return std::move(*this).then(
detail::base::wrap_continuation(std::move(continuation).finish())); detail::base::wrap_continuation(std::move(continuation).materialize()));
} }
/// Main method of the continuable_base to catch exceptions and error codes /// Main method of the continuable_base to catch exceptions and error codes
@ -317,18 +308,12 @@ public:
/// ```cpp /// ```cpp
/// http_request("github.com") /// http_request("github.com")
/// .then([](std::string github) { }) /// .then([](std::string github) { })
/// .fail([](std::exception_ptr ep) { /// .fail([](std::exception_ptr ptr) {
/// // Check whether the exception_ptr is valid (not default constructed) /// // Handle the error here
/// // if bool(ep) == false this means that the operation was cancelled /// try {
/// // by the user or application (promise.set_canceled() or /// std::rethrow_exception(ptr);
/// // make_cancelling_continuable()). /// } catch (std::exception& e) {
/// if (ep) { /// e.what(); // Handle the exception
/// // Handle the error here
/// try {
/// std::rethrow_exception(ep);
/// } catch (std::exception& e) {
/// e.what(); // Handle the exception
/// }
/// } /// }
/// }); /// });
/// ``` /// ```
@ -348,26 +333,14 @@ public:
/// \returns Returns a continuable_base with an asynchronous return type /// \returns Returns a continuable_base with an asynchronous return type
/// depending on the previous result type. /// depending on the previous result type.
/// ///
/// \attention The given exception type exception_t can be passed to the
/// handler in a default constructed state <br>`bool(e) == false`.
/// This always means that the operation was cancelled by the user,
/// possibly through:
/// - \ref promise_base::set_canceled
/// - \ref make_cancelling_continuable
/// - \ref result::set_canceled
/// - \ref cancel<br>
/// In that case the exception can be ignored safely (but it is
/// recommended not to proceed, although it is possible to
/// recover from the cancellation).
/// ///
/// \since 2.0.0 /// \since 2.0.0
template <typename T, typename E = detail::types::this_thread_executor_tag> template <typename T, typename E = detail::types::this_thread_executor_tag>
auto fail(T&& callback, auto fail(T&& callback,
E&& executor = detail::types::this_thread_executor_tag{}) && { E&& executor = detail::types::this_thread_executor_tag{}) && {
return detail::base::chain_continuation< return detail::base::chain_continuation<detail::base::handle_results::no,
detail::base::handle_results::no, detail::base::handle_errors::forward>( detail::base::handle_errors::plain>(
std::move(*this).finish(), std::move(*this).materialize(), std::forward<T>(callback),
detail::base::strip_exception_arg(std::forward<T>(callback)),
std::forward<E>(executor)); std::forward<E>(executor));
} }
@ -389,11 +362,9 @@ public:
/// \since 2.0.0 /// \since 2.0.0
template <typename OData, typename OAnnotation> template <typename OData, typename OAnnotation>
auto fail(continuable_base<OData, OAnnotation>&& continuation) && { auto fail(continuable_base<OData, OAnnotation>&& continuation) && {
return std::move(*this) // continuation.freeze();
.fail([continuation = std::move(continuation).freeze()] // return std::move(*this).fail([continuation = std::move(continuation)](
(exception_t) mutable { error_type) mutable { std::move(continuation).done(); });
std::move(continuation).done(); //
});
} }
/// A method which allows to use an overloaded callable for the error /// A method which allows to use an overloaded callable for the error
@ -407,7 +378,7 @@ public:
/// void operator() (std::string result) { /// void operator() (std::string result) {
/// // ... /// // ...
/// } /// }
/// void operator() (cti::exception_arg_t, cti::exception_t) { /// void operator() (cti::dispatch_error_tag, cti::error_type) {
/// // ... /// // ...
/// } /// }
/// ///
@ -428,56 +399,22 @@ public:
E&& executor = detail::types::this_thread_executor_tag{}) && { E&& executor = detail::types::this_thread_executor_tag{}) && {
return detail::base::chain_continuation< return detail::base::chain_continuation<
detail::base::handle_results::yes, detail::base::handle_results::yes,
detail::base::handle_errors::forward>(std::move(*this).finish(), detail::base::handle_errors::forward>(std::move(*this).materialize(),
std::forward<T>(callback), std::forward<T>(callback),
std::forward<E>(executor)); std::forward<E>(executor));
} }
/// Returns a continuable_base which continues its invocation through the /// A method which allows to apply this continuable to the given callable.
/// given executor.
/// ///
/// \returns Returns a continuable_base of the same type. /// \param transform A transform which shall accept this continuable
///
/// \since 4.2.0
template <typename E>
auto via(E&& executor) && {
return std::move(*this).next(
[](auto&&... args) {
return make_result(std::forward<decltype(args)>(args)...);
},
std::forward<E>(executor));
}
/// Returns a continuable_base which will have its signature converted
/// to the given Args.
///
/// A signature can only be converted if it can be partially applied
/// from the previous one as shown below:
/// ```cpp
/// continuable<long> c = make_ready_continuable(0, 1, 2).as<long>();
/// ```
///
/// \returns Returns a continuable_base with an asynchronous return type
/// matching the given Args.
///
/// \since 4.0.0
template <typename... Args>
auto as() && {
return std::move(*this).then(detail::base::convert_to<Args...>{});
}
/// A method which allows to apply a callable object to this continuable.
///
/// \param transform A callable objects that transforms a continuable
/// to a different object.
/// ///
/// \returns Returns the result of the given transform when this /// \returns Returns the result of the given transform when this
/// continuable is passed into it. /// continuable is passed into it.
/// ///
/// \since 4.0.0 /// \since 2.0.0
template <typename T> template <typename T>
auto apply(T&& transform) && { auto apply(T&& transform) && {
return std::forward<T>(transform)(std::move(*this).finish()); return std::forward<T>(transform)(std::move(*this).materialize());
} }
/// The pipe operator | is an alias for the continuable::then method. /// The pipe operator | is an alias for the continuable::then method.
@ -493,6 +430,22 @@ public:
return std::move(*this).then(std::forward<T>(right)); return std::move(*this).then(std::forward<T>(right));
} }
/// The pipe operator | is an alias for the continuable::apply method.
///
/// \param transform The transformer which is applied.
///
/// \returns See the corresponding continuable_base::apply method for the
/// explanation of the return type.
///
/// \note You may create your own transformation through
/// calling make_transformation.
///
/// \since 3.0.0
template <typename T>
auto operator|(detail::types::transform<T> transform) && {
return std::move(*this).apply(std::move(transform));
}
/// Invokes both continuable_base objects parallel and calls the /// Invokes both continuable_base objects parallel and calls the
/// callback with the result of both continuable_base objects. /// callback with the result of both continuable_base objects.
/// ///
@ -520,8 +473,8 @@ public:
/// }); /// });
/// ``` /// ```
/// ///
/// \note The continuable_base objects are invoked all at onve, /// \note The continuable_base objects are invoked parallel on the
/// because the `all` strategy tries to resolve /// current thread, because the `all` strategy tries to resolve
/// the continuations as fast as possible. /// the continuations as fast as possible.
/// Sequential invocation is also supported through the /// Sequential invocation is also supported through the
/// continuable_base::operator>> method. /// continuable_base::operator>> method.
@ -529,8 +482,8 @@ public:
/// \since 1.0.0 /// \since 1.0.0
template <typename OData, typename OAnnotation> template <typename OData, typename OAnnotation>
auto operator&&(continuable_base<OData, OAnnotation>&& right) && { auto operator&&(continuable_base<OData, OAnnotation>&& right) && {
return detail::connection::connect( return detail::composition::connect(
detail::connection::connection_strategy_all_tag{}, std::move(*this), detail::composition::composition_strategy_all_tag{}, std::move(*this),
std::move(right)); std::move(right));
} }
@ -560,15 +513,15 @@ public:
/// }); /// });
/// ``` /// ```
/// ///
/// \note The continuable_base objects are invoked all at once, /// \note The continuable_base objects are invoked parallel on the
/// however, the callback is only called once with /// current thread, however, the callback is only called once with
/// the first result or exception which becomes available. /// the first result which becomes available.
/// ///
/// \since 1.0.0 /// \since 1.0.0
template <typename OData, typename OAnnotation> template <typename OData, typename OAnnotation>
auto operator||(continuable_base<OData, OAnnotation>&& right) && { auto operator||(continuable_base<OData, OAnnotation>&& right) && {
return detail::connection::connect( return detail::composition::connect(
detail::connection::connection_strategy_any_tag{}, std::move(*this), detail::composition::composition_strategy_any_tag{}, std::move(*this),
std::move(right)); std::move(right));
} }
@ -589,15 +542,15 @@ public:
/// }); /// });
/// ``` /// ```
/// ///
/// \note The continuable_base objects are invoked sequential one after /// \note The continuable_base objects are invoked sequential on the
/// the previous one was finished. Parallel invocation is also /// current thread. Parallel invocation is also supported through the
/// supported through the continuable_base::operator && method. /// continuable_base::operator&& method.
/// ///
/// \since 1.0.0 /// \since 1.0.0
template <typename OData, typename OAnnotation> template <typename OData, typename OAnnotation>
auto operator>>(continuable_base<OData, OAnnotation>&& right) && { auto operator>>(continuable_base<OData, OAnnotation>&& right) && {
return detail::connection::seq::sequential_connect(std::move(*this), return detail::composition::seq::sequential_connect(std::move(*this),
std::move(right)); std::move(right));
} }
/// Invokes the continuation chain manually even before the /// Invokes the continuation chain manually even before the
@ -611,64 +564,7 @@ public:
/// ///
/// \since 1.0.0 /// \since 1.0.0
void done() && { void done() && {
detail::base::finalize_continuation(std::move(*this).finish()); detail::base::finalize_continuation(std::move(*this));
}
/// Materializes the continuation expression template and finishes
/// the current applied strategy such that the resulting continuable
/// will always be a concrete type and Continuable::is_concrete holds.
///
/// This can be used in the case where we are chaining continuations lazily
/// through a strategy, for instance when applying operators for
/// expressing connections and then want to return a materialized
/// continuable_base which uses the strategy respectively.
/// ```cpp
/// auto do_both() {
/// return (wait(10s) || wait_key_pressed(KEY_SPACE)).finish();
/// }
///
/// // Without a call to finish() this would lead to
/// // an unintended evaluation strategy:
/// do_both() || wait(5s);
/// ```
///
/// \note When using a type erased continuable_base such as
/// `continuable<...>` this method doesn't need to be called
/// since the continuable_base is materialized automatically
/// on conversion.
///
/// \since 4.0.0
auto finish() && {
return annotation_trait::finish(std::move(*this));
}
/// Returns true when the continuable can provide its result immediately,
/// and its lazy invocation would be side-effect free.
///
/// \since 4.0.0
bool is_ready() const noexcept {
return annotation_trait::is_ready(*this);
}
/// Invalidates the continuable and returns its immediate invocation result.
///
/// This method can be used to specialize the asynchronous control flow
/// based on whether the continuable_base is_ready at every time,
/// which is true for a continuable created through the following functions:
/// - make_ready_continuable
/// - make_exceptional_continuable
///
/// \returns A result<Args...> where Args... represent the current
/// asynchronous parameters or the currently stored exception.
///
/// \attention unpack requires that continuable_base::is_ready returned true
/// in a previous check, otherwise its behaviour is unspecified.
///
/// \since 4.0.0
auto unpack() && {
assert(ownership_.is_acquired());
assert(is_ready());
return detail::base::attorney::query(std::move(*this).finish());
} }
/// Predicate to check whether the cti::continuable_base is frozen or not. /// Predicate to check whether the cti::continuable_base is frozen or not.
@ -713,82 +609,23 @@ public:
return std::move(*this); return std::move(*this);
} }
/// \cond false #ifdef CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
#if defined(CONTINUABLE_HAS_COROUTINE)
/// \endcond
/// Implements the operator for awaiting on continuables using `co_await`.
///
/// The operator is only enabled if `CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE`
/// is defined and the toolchain supports experimental coroutines.
///
/// The return type of the `co_await` expression is specified as following:
/// | Continuation type | co_await returns |
/// | : ------------------------------- | : -------------------------------- |
/// | `continuable_base with <>` | `void` |
/// | `continuable_base with <Arg>` | `Arg` |
/// | `continuable_base with <Args...>` | `std::tuple<Args...>` |
///
/// When exceptions are used the usage is as intuitive as shown below:
/// ```cpp
/// // Handling the exception isn't required and
/// // the try catch clause may be omitted.
/// try {
/// std::string response = co_await http_request("github.com");
/// } (std::exception& e) {
/// e.what();
/// }
/// ```
///
/// In case the library is configured to use error codes or a custom
/// exception type the return type of the co_await expression is changed.
/// The result is returned through a cti::result<...>.
/// | Continuation type | co_await returns |
/// | : ------------------------------- | : -------------------------------- |
/// | `continuable_base with <>` | `result<void>` |
/// | `continuable_base with <Arg>` | `result<Arg>` |
/// | `continuable_base with <Args...>` | `result<Args...>` |
///
/// \note Using continuable_base as return type for coroutines
/// is supported. The coroutine is initially stopped and
/// resumed when the continuation is requested in order to
/// keep the lazy evaluation semantics of the continuable_base.
/// ```cpp
/// cti::continuable<> resolve_async_void() {
/// co_await http_request("github.com");
/// // ...
/// co_return;
/// }
///
/// cti::continuable<int> resolve_async() {
/// co_await http_request("github.com");
/// // ...
/// co_return 0;
/// }
/// ```
/// It's possible to return multiple return values from coroutines
/// by wrapping those in a tuple like type:
/// ```cpp
/// cti::continuable<int, int, int> resolve_async_multiple() {
/// co_await http_request("github.com");
/// // ...
/// co_return std::make_tuple(0, 1, 2);
/// }
/// ```
///
/// \since 2.0.0
auto operator co_await() && { auto operator co_await() && {
return detail::awaiting::create_awaiter(std::move(*this).finish()); return detail::awaiting::create_awaiter(std::move(*this).materialize());
} }
/// \cond false #endif // CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
#endif // defined(CONTINUABLE_HAS_COROUTINE)
/// \endcond
private: private:
void release() noexcept { void release() noexcept {
ownership_.release(); ownership_.release();
} }
Data&& consume() && { auto materialize() && {
return detail::composition::materializer<continuable_base>::apply(
std::move(*this));
}
Data&& consume_data() && {
assert_acquired(); assert_acquired();
release(); release();
return std::move(data_); return std::move(data_);
@ -799,20 +636,20 @@ private:
} }
}; };
/// Creates a continuable_base from a promise/callback taking function. /// Creates a continuable_base from a callback taking function.
/// ///
/// \tparam Args The types (signature hint) the given promise is resolved with. /// \tparam Args The types (signature hint) the given callback is called with.
/// * **Some arguments** indicate the types the promise will be invoked with. /// * **Some arguments** indicate the types the callback will be invoked with.
/// ```cpp /// ```cpp
/// auto ct = cti::make_continuable<int, std::string>([](auto&& promise) { /// auto ct = cti::make_continuable<int, std::string>([](auto&& callback) {
/// promise.set_value(200, "<html>...</html>"); /// std::forward<decltype(callback)>(callback)(200, "<html>...</html>");
/// }); /// });
/// ``` /// ```
/// * `void` **as argument** indicates that the promise will be invoked /// * **void as argument** indicates that the callback will be invoked
/// with no arguments: /// with no arguments:
/// ```cpp /// ```cpp
/// auto ct = cti::make_continuable<void>([](auto&& promise) { /// auto ct = cti::make_continuable<void>([](auto&& callback) {
/// promise.set_value(); /// std::forward<decltype(callback)>(callback)();
/// }); /// });
/// ``` /// ```
/// * **No arguments** Since version 3.0.0 make_continuable always requires /// * **No arguments** Since version 3.0.0 make_continuable always requires
@ -823,13 +660,13 @@ private:
/// the continuable right after creation. /// the continuable right after creation.
/// ```cpp /// ```cpp
/// // This won't work because the arguments are missing: /// // This won't work because the arguments are missing:
/// auto ct = cti::make_continuable([](auto&& promise) { /// auto ct = cti::make_continuable([](auto&& callback) {
/// promise.set_value(0.f, 'c'); /// std::forward<decltype(callback)>(callback)(0.f, 'c');
/// }); /// });
/// ///
/// // However, you are allowed to do this: /// // However, you are allowed to do this:
/// cti::continuable<float, char> ct = [](auto&& promise) { /// continuable<float, char> ct = [](auto&& callback) {
/// promise.set_value(callback)(0.f, 'c'); /// std::forward<decltype(callback)>(callback)(0.f, 'c');
/// }; /// };
/// ``` /// ```
/// ///
@ -869,273 +706,94 @@ private:
/// }); /// });
/// ``` /// ```
/// ///
/// \returns A continuable_base with unspecified template parameters which /// \returns A continuable_base with unknown template parameters which
/// wraps the given continuation. /// wraps the given continuation.
/// In order to convert the continuable_base to a known type /// In order to convert the continuable_base to a known type
/// you need to apply type erasure through the /// you need to apply type erasure.
/// \link cti::continuable continuable\endlink or
/// \link cti::promise promise\endlink facilities.
/// ///
/// \note You should always turn the callback/promise into a r-value if possible /// \note You should always turn the callback into a r-value if possible
/// (`std::move` or `std::forward`) for qualifier correct invokation. /// (`std::move` or `std::forward`) for qualifier correct invokation.
/// Additionally it's important to know that all continuable promises
/// are callbacks and just expose their call operator nicely through
/// \link cti::promise_base::set_value set_value \endlink and
/// \link cti::promise_base::set_exception set_exception \endlink.
/// ///
/// \since 1.0.0 /// \since 1.0.0
template <typename... Args, typename Continuation> template <typename... Args, typename Continuation>
constexpr auto make_continuable(Continuation&& continuation) { auto make_continuable(Continuation&& continuation) {
static_assert(sizeof...(Args) > 0, static_assert(sizeof...(Args) > 0,
"Since version 3.0.0 make_continuable requires an exact " "Since version 3.0.0 make_continuable requires an exact "
"signature! If you did intend to create a void continuable " "signature! If you did intend to create a void continuable "
"use make_continuable<void>(...). Continuables with an exact " "use make_continuable<void>(...). Continuables with an exact "
"signature may be created through make_continuable<Args...>."); "signature may be created through make_continuable<Args...>.");
return detail::base::attorney::create_from( return detail::base::attorney::create(
std::forward<Continuation>(continuation), std::forward<Continuation>(continuation),
typename detail::hints::from_args<Args...>::type{}, detail::hints::extract(detail::traits::identity<Args...>{}),
detail::util::ownership{}); detail::util::ownership{});
} }
/// Returns a continuable_base with no result which instantly resolves /// Returns a continuable with no result which instantly resolves
/// the promise with no values. /// the promise with no values.
/// ///
/// \attention Usually using this function isn't needed at all since /// \attention Usually using this function isn't needed at all since
/// the continuable library is capable of working with /// the continuable library is capable of working with
/// plain values in most cases. /// plain values in most cases.
/// Try not to use it since it causes unnecessary recursive /// Try not to use it since it causes unneccessary recursive
/// function calls. /// function calls.
/// ///
/// \since 3.0.0 /// \since 3.0.0
template <typename... Args> template <typename... Args>
auto make_ready_continuable(Args&&... args) { constexpr auto make_ready_continuable() {
return detail::base::attorney::create_from_raw( return make_continuable<void>([](auto&& promise) {
detail::base::ready_continuation<detail::traits::unrefcv_t<Args>...>( std::forward<decltype(promise)>(promise).set_value();
result<detail::traits::unrefcv_t<Args>...>::from( });
std::forward<Args>(args)...)),
detail::identity<detail::traits::unrefcv_t<Args>...>{},
detail::util::ownership{});
} }
/// Returns a continuable_base with the parameterized result which instantly /// Returns a continuable with one result value which instantly resolves
/// the promise with the given value.
///
/// \copydetails make_ready_continuable()
template <typename Result>
constexpr auto make_ready_continuable(Result&& result) {
return make_continuable<std::decay_t<Result>>( // ...
[result = std::forward<Result>(result)](auto&& promise) mutable {
std::forward<decltype(promise)>(promise).set_value(std::move(result));
});
}
/// Returns a continuable with multiple result values which instantly resolves
/// the promise with the given values.
///
/// \copydetails make_ready_continuable()
template <typename FirstResult, typename SecondResult, typename... Rest>
constexpr auto make_ready_continuable(FirstResult&& first_result,
SecondResult&& second_result,
Rest&&... rest) {
return make_continuable<std::decay_t<FirstResult>, std::decay_t<SecondResult>,
std::decay_t<Rest>...>( // ...
[result = std::make_tuple(std::forward<FirstResult>(first_result),
std::forward<SecondResult>(second_result),
std::forward<Rest>(rest)...)](
auto&& promise) mutable {
detail::traits::unpack(result,
std::forward<decltype(promise)>(promise));
});
}
/// Returns a continuable with the parameterized result which instantly
/// resolves the promise with the given error type. /// resolves the promise with the given error type.
/// ///
/// See an example below:
/// ```cpp
/// std::logic_error exception("Some issue!");
/// auto ptr = std::make_exception_ptr(exception);
/// auto ct = cti::make_exceptional_continuable<int>(ptr);
/// ```
///
/// \tparam Args The fake signature of the returned continuable.
///
/// \since 3.0.0
template <typename... Args, typename Exception>
constexpr auto make_exceptional_continuable(Exception&& exception) {
static_assert(sizeof...(Args) > 0,
"Requires at least one type for the fake signature!");
using hint_t = typename detail::hints::from_args<Args...>::type;
using ready_continuation_t = typename detail::base::
ready_continuation_from_hint<hint_t>::type;
using result_t = typename detail::base::result_from_hint<hint_t>::type;
return detail::base::attorney::create_from_raw(
ready_continuation_t(result_t::from(exception_arg_t{},
std::forward<Exception>(exception))),
hint_t{}, detail::util::ownership{});
}
/// Returns a continuable_base with the parameterized result which never
/// resolves its promise and thus cancels the asynchronous continuation chain
/// through throwing a default constructed exception_t.
///
/// This can be used to cancel an asynchronous continuation chain when
/// returning a continuable_base from a handler where other paths could
/// possibly continue the asynchronous chain. See an example below:
/// ```cpp
/// do_sth().then([weak = this->weak_from_this()]() -> continuable<> {
/// if (auto me = weak.lock()) {
/// return do_sth_more();
/// } else {
/// // Abort the asynchronous continuation chain since the
/// // weakly referenced object expired previously.
/// return make_cancelling_continuable<void>();
/// }
/// });
/// ```
/// The default unhandled exception handler ignores exception types
/// that don't evaluate to true when being converted to a bool.
/// This saves expensive construction of std::exception_ptr or similar types,
/// where only one exception type is used for signaling the cancellation.
///
/// \tparam Signature The fake signature of the returned continuable. /// \tparam Signature The fake signature of the returned continuable.
/// ///
/// \since 4.0.0 /// \since 3.0.0
template <typename... Signature> template <typename... Signature, typename Exception>
auto make_cancelling_continuable() { constexpr auto make_exceptional_continuable(Exception&& exception) {
static_assert(sizeof...(Signature) > 0, static_assert(sizeof...(Signature) > 0,
"Requires at least one type for the fake signature!"); "Requires at least one type for the fake signature!");
return make_exceptional_continuable<Signature...>(exception_t{}); return make_continuable<Signature...>( // ...
[exception = std::forward<Exception>(exception)](auto&& promise) mutable {
std::forward<decltype(promise)>(promise).set_exception(
std::move(exception));
});
} }
/// Can be used to disable the special meaning for a returned value in
/// asynchronous handler functions.
///
/// Several types have a special meaning when being returned from a callable
/// passed to asynchronous handler functions like:
/// - continuable_base::then
/// - continuable_base::fail
/// - continuable_base::next
///
/// For instance such types are std::tuple, std::pair and cti::result.
///
/// Wrapping such an object through a call to make_plain disables the special
/// meaning for such objects as shown below:
/// ```cpp
/// continuable<result<int, int> c = http_request("example.com")
/// .then([](std::string content) {
/// return make_plain(make_result(0, 1));
/// })
/// ```
///
/// \since 4.0.0
///
template <typename T>
auto make_plain(T&& value) {
return plain_t<detail::traits::unrefcv_t<T>>(std::forward<T>(value));
}
/// Can be used to recover to from a failure handler,
/// the result handler which comes after will be called with the
/// corresponding result.
///
/// The \ref exceptional_result returned by this function can be returned
/// from any result or failure handler in order to rethrow the exception.
/// ```cpp
/// http_request("example.com")
/// .then([](std::string content) {
/// return recover(1, 2);
/// })
/// .fail([](cti::exception_t exception) {
/// return recover(1, 2);
/// })
/// .then([](int a, int b) {
/// // Recovered from the failure
/// })
/// ```
/// A corresponding \ref result is returned by \ref recover
/// ```cpp
/// http_request("example.com")
/// .then([](std::string content) -> cti::result<int, int> {
/// return recover(1, 2);
/// })
/// .fail([](cti::exception_t exception) -> cti::result<int, int> {
/// return recover(1, 2);
/// })
/// .then([](int a, int b) -> cti::result<int, int> {
/// // Recovered from the failure
/// })
/// ```
///
/// \since 4.0.0
///
template <typename... Args>
result<detail::traits::unrefcv_t<Args>...> recover(Args&&... args) {
return make_result(std::forward<Args>(args)...);
}
/// Can be used to rethrow an exception to the asynchronous continuation chain,
/// the failure handler which comes after will be called with the
/// corresponding exception.
///
/// The \ref exceptional_result returned by this function can be returned
/// from any result or failure handler in order to rethrow the exception.
/// ```cpp
/// http_request("example.com")
/// .then([](std::string content) {
/// return rethrow(std::make_exception_ptr(std::exception{}));
/// })
/// .fail([](cti::exception_t exception) {
/// return rethrow(std::make_exception_ptr(std::exception{}));
/// })
/// .next([](auto&&...) {
/// return rethrow(std::make_exception_ptr(std::exception{}));
/// });
/// ```
/// The returned \ref exceptional_result is convertible to
/// any \ref result as shown below:
/// ```cpp
/// http_request("example.com")
/// .then([](std::string content) -> cti::result<> {
/// return rethrow(std::make_exception_ptr(std::exception{}));
/// })
/// .fail([](cti::exception_t exception) -> cti::result<> {
/// return rethrow(std::make_exception_ptr(std::exception{}));
/// })
/// .next([](auto&&...) -> cti::result<> {
/// return rethrow(std::make_exception_ptr(std::exception{}));
/// });
/// ```
///
/// \since 4.0.0
///
// NOLINTNEXTLINE(performance-unnecessary-value-param)
inline exceptional_result rethrow(exception_t exception) {
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
return exceptional_result{std::move(exception)};
}
/// Can be used to cancel an asynchronous continuation chain,
/// the next failure handler which comes after cancel will be called
/// with a default constructed exception_t object.
///
/// The \ref cancellation_result returned by this function can be returned from
/// any result or failure handler in order to cancel the chain.
/// ```cpp
/// http_request("example.com")
/// .then([](std::string content) {
/// return cancel();
/// })
/// .fail([](cti::exception_t exception) {
/// return cancel();
/// })
/// .next([](auto&&...) {
/// return cancel();
/// });
/// ```
/// The returned \ref empty_result is convertible to
/// any \ref result as shown below:
/// ```cpp
/// http_request("example.com")
/// .then([](std::string content) -> cti::result<> {
/// return cancel();
/// })
/// .fail([](cti::exception_t exception) -> cti::result<> {
/// return cancel();
/// })
/// .next([](auto&&...) -> cti::result<> {
/// return cancel();
/// });
/// ```
///
/// \since 4.0.0
///
inline cancellation_result cancel() {
return {};
}
/// Can be used to stop an asynchronous continuation chain,
/// no handler which comes after stop was received won't be called.
///
/// \since 4.0.0
///
inline empty_result stop() {
return {};
}
/// \}
} // namespace cti } // namespace cti
#endif // CONTINUABLE_BASE_HPP_INCLUDED #endif // CONTINUABLE_BASE_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,32 +21,25 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
**/ **/
#ifndef CONTINUABLE_CONNECTIONS_HPP_INCLUDED #ifndef CONTINUABLE_COMPOSITIONS_HPP_INCLUDED
#define CONTINUABLE_CONNECTIONS_HPP_INCLUDED #define CONTINUABLE_COMPOSITIONS_HPP_INCLUDED
#include <initializer_list>
#include <memory>
#include <utility> #include <utility>
#include <vector>
#include <continuable/detail/connection/connection-all.hpp> #include <continuable/detail/composition-all.hpp>
#include <continuable/detail/connection/connection-any.hpp> #include <continuable/detail/composition-any.hpp>
#include <continuable/detail/connection/connection-seq.hpp> #include <continuable/detail/composition-seq.hpp>
#include <continuable/detail/connection/connection.hpp> #include <continuable/detail/composition.hpp>
#include <continuable/detail/traversal/range.hpp> #include <continuable/detail/range.hpp>
namespace cti { 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. /// Connects the given arguments with an all logic.
/// All continuables contained inside the given nested pack are /// All continuables contained inside the given nested pack are
/// invoked at once. On completion the final handler is called /// invoked at once. On completion the final handler is called
@ -62,8 +55,8 @@ namespace cti {
/// cti::when_all( /// cti::when_all(
/// cti::make_ready_continuable(0, 1), /// cti::make_ready_continuable(0, 1),
/// 2, //< See this plain value /// 2, //< See this plain value
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime /// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3),
/// cti::make_ready_continuable(4)), // sized container. /// cti::make_ready_continuable(4)},
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5)))) /// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
/// .then([](int r0, int r1, int r2, std::vector<int> r34, /// .then([](int r0, int r1, int r2, std::vector<int> r34,
/// std::tuple<std::tuple<int>> r5) { /// std::tuple<std::tuple<int>> r5) {
@ -76,8 +69,8 @@ namespace cti {
/// \since 1.1.0 /// \since 1.1.0
template <typename... Args> template <typename... Args>
auto when_all(Args&&... args) { auto when_all(Args&&... args) {
return detail::connection::apply_connection( return detail::composition::apply_composition(
detail::connection::connection_strategy_all_tag{}, detail::composition::composition_strategy_all_tag{},
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
@ -86,9 +79,8 @@ auto when_all(Args&&... args) {
/// to a temporary `std::vector` which is then passed to when_all. /// to a temporary `std::vector` which is then passed to when_all.
/// ///
/// ```cpp /// ```cpp
/// // cti::populate just creates a std::vector from the two continuables. /// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0),
/// auto v = cti::populate(cti::make_ready_continuable(0), /// cti::make_ready_continuable(1)};
/// cti::make_ready_continuable(1));
/// ///
/// cti::when_all(v.begin(), v.end()) /// cti::when_all(v.begin(), v.end())
/// .then([](std::vector<int> r01) { /// .then([](std::vector<int> r01) {
@ -131,8 +123,8 @@ auto when_all(Iterator begin, Iterator end) {
/// cti::when_seq( /// cti::when_seq(
/// cti::make_ready_continuable(0, 1), /// cti::make_ready_continuable(0, 1),
/// 2, //< See this plain value /// 2, //< See this plain value
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime /// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3),
/// cti::make_ready_continuable(4)), // sized container. /// cti::make_ready_continuable(4)},
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5)))) /// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
/// .then([](int r0, int r1, int r2, std::vector<int> r34, /// .then([](int r0, int r1, int r2, std::vector<int> r34,
/// std::tuple<std::tuple<int>> r5) { /// std::tuple<std::tuple<int>> r5) {
@ -145,8 +137,8 @@ auto when_all(Iterator begin, Iterator end) {
/// \since 1.1.0 /// \since 1.1.0
template <typename... Args> template <typename... Args>
auto when_seq(Args&&... args) { auto when_seq(Args&&... args) {
return detail::connection::apply_connection( return detail::composition::apply_composition(
detail::connection::connection_strategy_seq_tag{}, detail::composition::composition_strategy_seq_tag{},
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
@ -155,9 +147,8 @@ auto when_seq(Args&&... args) {
/// to a temporary `std::vector` which is then passed to when_seq. /// to a temporary `std::vector` which is then passed to when_seq.
/// ///
/// ```cpp /// ```cpp
/// // cti::populate just creates a std::vector from the two continuables. /// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0),
/// auto v = cti::populate(cti::make_ready_continuable(0), /// cti::make_ready_continuable(1)};
/// cti::make_ready_continuable(1));
/// ///
/// cti::when_seq(v.begin(), v.end()) /// cti::when_seq(v.begin(), v.end())
/// .then([](std::vector<int> r01) { /// .then([](std::vector<int> r01) {
@ -200,8 +191,8 @@ auto when_seq(Iterator begin, Iterator end) {
/// cti::when_any( /// cti::when_any(
/// cti::make_ready_continuable(0, 1), /// cti::make_ready_continuable(0, 1),
/// 2, //< See this plain value /// 2, //< See this plain value
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime /// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3),
/// cti::make_ready_continuable(4)), // sized container. /// cti::make_ready_continuable(4)},
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5)))) /// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
/// .then([](int r0) { /// .then([](int r0) {
/// // ... /// // ...
@ -213,8 +204,8 @@ auto when_seq(Iterator begin, Iterator end) {
/// \since 1.1.0 /// \since 1.1.0
template <typename... Args> template <typename... Args>
auto when_any(Args&&... args) { auto when_any(Args&&... args) {
return detail::connection::apply_connection( return detail::composition::apply_composition(
detail::connection::connection_strategy_any_tag{}, detail::composition::composition_strategy_any_tag{},
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
@ -223,9 +214,8 @@ auto when_any(Args&&... args) {
/// to a temporary `std::vector` which is then passed to when_all. /// to a temporary `std::vector` which is then passed to when_all.
/// ///
/// ```cpp /// ```cpp
/// // cti::populate just creates a std::vector from the two continuables. /// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0),
/// auto v = cti::populate(cti::make_ready_continuable(0), /// cti::make_ready_continuable(1)};
/// cti::make_ready_continuable(1));
/// ///
/// cti::when_any(v.begin(), v.end()) /// cti::when_any(v.begin(), v.end())
/// .then([](int r01) { /// .then([](int r01) {
@ -252,50 +242,6 @@ template <
auto when_any(Iterator begin, Iterator end) { auto when_any(Iterator begin, Iterator end) {
return when_any(detail::range::persist_range(begin, 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 } // namespace cti
#endif // CONTINUABLE_CONNECTIONS_HPP_INCLUDED #endif // CONTINUABLE_COMPOSITIONS_HPP_INCLUDED

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -31,32 +31,20 @@
#ifndef CONTINUABLE_PROMISE_BASE_HPP_INCLUDED #ifndef CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
#define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED #define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
#include <cassert>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <continuable/continuable-primitives.hpp>
#include <continuable/detail/core/annotation.hpp> #include <continuable/detail/hints.hpp>
#include <continuable/detail/core/types.hpp> #include <continuable/detail/types.hpp>
#include <continuable/detail/utility/traits.hpp> #include <continuable/detail/util.hpp>
#include <continuable/detail/utility/util.hpp>
namespace cti { namespace cti {
/// \defgroup Base Base
/// provides classes and functions to create continuable_base objects.
/// \{
/// The promise_base makes it possible to resolve an asynchronous /// The promise_base makes it possible to resolve an asynchronous
/// continuable through it's result or through an error type. /// continuable through it's result or through an error type.
/// ///
/// Use the promise type defined in `continuable/continuable_types.hpp`, /// Use the promise type defined in `continuable/continuable_types.hpp`,
/// in order to use this class. /// 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 /// \since 2.0.0
// clang-format off // clang-format off
template <typename Data, typename Hint> template <typename Data, typename Hint>
@ -64,7 +52,7 @@ class promise_base
/// \cond false /// \cond false
; ;
template <typename Data, typename... Args> template <typename Data, typename... Args>
class promise_base<Data, detail::identity<Args...>> class promise_base<Data, detail::hints::signature_hint_tag<Args...>>
: detail::util::non_copyable : detail::util::non_copyable
/// \endcond /// \endcond
{ // clang-format on { // clang-format on
@ -75,141 +63,53 @@ class promise_base<Data, detail::identity<Args...>>
/// \endcond /// \endcond
public: public:
/// Constructor for constructing an empty promise
explicit promise_base() = default;
/// Constructor accepting the data object /// Constructor accepting the data object
explicit promise_base(Data data) : data_(std::move(data)) { 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 /// Constructor accepting any object convertible to the data object
template <typename OData, template <typename OData, std::enable_if_t<std::is_convertible<
std::enable_if_t<std::is_convertible< std::decay_t<OData>, Data>::value>* = nullptr>
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr> promise_base(OData&& data) : data_(std::forward<OData>(data)) {
/* 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. /// Resolves the continuation with the given values.
/// ///
/// \throws This method never throws an 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 /// \since 2.0.0
void operator()(Args... args) && noexcept { void operator()(Args... args) && noexcept {
assert(data_);
std::move(data_)(std::move(args)...); std::move(data_)(std::move(args)...);
data_ = nullptr;
} }
/// Resolves the continuation with the given exception. /// Resolves the continuation with the given exception.
/// ///
/// \throws This method never throws an 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 /// \since 2.0.0
void operator()(exception_arg_t tag, exception_t exception) && noexcept { void operator()(detail::types::dispatch_error_tag tag,
assert(data_); detail::types::error_type exception) &&
noexcept {
std::move(data_)(tag, std::move(exception)); std::move(data_)(tag, std::move(exception));
data_ = nullptr;
} }
/// Resolves the continuation with the given values. /// Resolves the continuation with the given values.
/// ///
/// \throws This method never throws an 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 /// \since 2.0.0
void set_value(Args... args) noexcept { void set_value(Args... args) noexcept {
// assert(data_);
std::move(data_)(std::move(args)...); std::move(data_)(std::move(args)...);
data_ = nullptr;
} }
/// Resolves the continuation with the given exception. /// Resolves the continuation with the given exception.
/// ///
/// \throws This method never throws an 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 /// \since 2.0.0
void set_exception(exception_t exception) noexcept { void set_exception(detail::types::error_type exception) noexcept {
assert(data_); std::move(data_)(detail::types::dispatch_error_tag{}, std::move(exception));
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 } // namespace cti
#endif // CONTINUABLE_PROMISE_BASE_HPP_INCLUDED #endif // CONTINUABLE_PROMISE_BASE_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -33,19 +33,15 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <continuable/detail/other/promisify.hpp>
#include <continuable/detail/promisify.hpp>
namespace cti { 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 /// Helper class for converting callback taking callable types into a
/// a continuable. Various styles are supported. /// a continuable. Various styles are supported.
/// - `from`: Converts callback taking callable types into continuables /// - `from_asio`: Converts callback taking callable types into continuables
/// which pass an error code as first parameter and the rest of /// which pass an error code as first parameter and the rest of
/// the result afterwards. /// the result afterwards.
/// ///
/// \tparam Result The result of the converted continuable, this should align /// \tparam Result The result of the converted continuable, this should align
/// with the arguments that are passed to the callback. /// with the arguments that are passed to the callback.
@ -63,7 +59,7 @@ public:
/// See an example of how to promisify boost asio's async_resolve below: /// See an example of how to promisify boost asio's async_resolve below:
/// ```cpp /// ```cpp
/// auto async_resolve(std::string host, std::string service) { /// auto async_resolve(std::string host, std::string service) {
/// return cti::promisify<asio::ip::udp::resolver::iterator>::from( /// return cti::promisify<asio::ip::udp::resolver::iterator>::from_asio(
/// [&](auto&&... args) { /// [&](auto&&... args) {
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...); /// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
/// }, /// },
@ -71,50 +67,20 @@ public:
/// } /// }
/// ``` /// ```
/// ///
/// A given error variable is converted to the used error type. /// If the error code which is passed as first parameter is set there are
/// If this isn't possible you need to create a custom resolver callable /// two behaviours depending whether exceptions are enabled:
/// object \see with for details. /// - If exceptions are enabled the error type is passed via
/// an exception_ptr to the failure handler.
/// - If exceptions are disabled the error type is converted to a
/// `std::error_conditon` and passed down to the error handler.
/// ///
/// \since 3.0.0 /// \since 3.0.0
template <typename Callable, typename... Args> template <typename Callable, typename... Args>
static auto from(Callable&& callable, Args&&... args) { static auto from_asio(Callable&& callable, Args&&... args) {
return helper::template from(detail::convert::default_resolver(), return helper::template from<detail::convert::promisify_asio>(
std::forward<Callable>(callable), std::forward<Callable>(callable), std::forward<Args>(args)...);
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 } // namespace cti
#endif // CONTINUABLE_PROMISIFY_HPP_INCLUDED #endif // CONTINUABLE_PROMISIFY_HPP_INCLUDED

View File

@ -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

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,23 +21,18 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
**/ **/
#ifndef CONTINUABLE_EXTERNAL_GTEST_HPP_INCLUDED #ifndef CONTINUABLE_TESTING_HPP_INCLUDED
#define CONTINUABLE_EXTERNAL_GTEST_HPP_INCLUDED #define CONTINUABLE_TESTING_HPP_INCLUDED
#include <continuable/detail/other/testing.hpp> #include <continuable/detail/testing.hpp>
#include <continuable/detail/utility/traits.hpp> #include <continuable/detail/traits.hpp>
/// \defgroup Testing Testing
/// provides macro shortcuts for testing asynchronous continuations through
/// GTest.
/// \{
/// Asserts that the final callback of the given continuable was called /// Asserts that the final callback of the given continuable was called
/// with any result. /// with any result.
@ -53,14 +48,6 @@
#define ASSERT_ASYNC_EXCEPTION_COMPLETION(CONTINUABLE) \ #define ASSERT_ASYNC_EXCEPTION_COMPLETION(CONTINUABLE) \
cti::detail::testing::assert_async_exception_completion(CONTINUABLE); cti::detail::testing::assert_async_exception_completion(CONTINUABLE);
/// Asserts that the final callback of the given continuable is called
/// with a cancelled result which is represented by a default constructed
/// exception_t.
///
/// \since 4.0.0
#define ASSERT_ASYNC_CANCELLATION(CONTINUABLE) \
cti::detail::testing::assert_async_cancellation(CONTINUABLE);
/// Asserts that the final callback of the given continuable is never called /// Asserts that the final callback of the given continuable is never called
/// with any result. /// with any result.
/// ///
@ -166,7 +153,7 @@
/// \since 1.0.0 /// \since 1.0.0
#define ASSERT_ASYNC_TYPES(CONTINUABLE, ...) \ #define ASSERT_ASYNC_TYPES(CONTINUABLE, ...) \
cti::detail::testing::assert_async_types( \ cti::detail::testing::assert_async_types( \
CONTINUABLE, cti::detail::identity<__VA_ARGS__>{}) CONTINUABLE, cti::detail::traits::identity<__VA_ARGS__>{})
/// Asserts that the continuable is finished with the given exception /// Asserts that the continuable is finished with the given exception
/// ///
@ -175,6 +162,4 @@
ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION( \ ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION( \
cti::detail::testing::asserting_eq_check(), __VA_ARGS__) cti::detail::testing::asserting_eq_check(), __VA_ARGS__)
/// \} #endif // CONTINUABLE_TESTING_HPP_INCLUDED
#endif // CONTINUABLE_EXTERNAL_GTEST_HPP_INCLUDED

View File

@ -0,0 +1,74 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_TRAIT_HPP_INCLUDED
#define CONTINUABLE_TRAIT_HPP_INCLUDED
#include <cstdint>
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-promise-base.hpp>
#include <continuable/detail/hints.hpp>
#include <continuable/detail/types.hpp>
namespace cti {
/// Trait to retrieve a continuable_base type with a given type-erasure backend.
///
/// Every object may me used as type-erasure backend as long as the
/// requirements of a `std::function` like wrapper are satisfied.
///
/// \tparam CallbackWrapper The type which is used to erase the callback.
///
/// \tparam ContinuationWrapper The type which is used to erase the
/// continuation data.
///
/// \tparam Args The current signature of the continuable.
template <template <std::size_t, typename...> class CallbackWrapper,
template <std::size_t, typename...> class ContinuationWrapper,
typename... Args>
class continuable_trait {
using callback = CallbackWrapper<0U, void(Args...)&&,
void(detail::types::dispatch_error_tag,
detail::types::error_type) &&>;
public:
/// The promise type which is used to resolve continuations
using promise =
promise_base<callback, detail::hints::signature_hint_tag<Args...>>;
/// The continuable type for the given parameters.
using continuable =
continuable_base<ContinuationWrapper<sizeof(callback), void(promise)>,
detail::hints::signature_hint_tag<Args...>>;
};
} // namespace cti
#endif // CONTINUABLE_TRAIT_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -31,22 +31,72 @@
#ifndef CONTINUABLE_TRANSFORMS_HPP_INCLUDED #ifndef CONTINUABLE_TRANSFORMS_HPP_INCLUDED
#define CONTINUABLE_TRANSFORMS_HPP_INCLUDED #define CONTINUABLE_TRANSFORMS_HPP_INCLUDED
#include <continuable/transforms/wait.hpp> #include <continuable/detail/transforms.hpp>
#include <continuable/transforms/future.hpp> #include <continuable/detail/types.hpp>
namespace cti { namespace cti {
/// \defgroup Transforms Transforms /// A callable tag object which marks a wrapped callable object
/// provides utilities to convert /// as continuable transformation which enables some useful overloads.
/// \link continuable_base continuable_bases\endlink to other ///
/// types such as (`std::future`). /// \since 3.0.0
/// \{ using detail::types::transform;
/// Wraps the given callable object into a transform class.
///
/// \since 3.0.0
template <typename T>
auto make_transform(T&& callable) {
return transform<std::decay_t<T>>(std::forward<T>(callable));
}
/// The namespace transforms declares callable objects that transform /// The namespace transforms declares callable objects that transform
/// any continuable_base to an object or to a continuable_base itself. /// any continuable_base to an object or to a continuable_base itself.
/// ///
/// Transforms can be applied to continuables through using /// Transforms can be applied to continuables through using
/// the cti::continuable_base::apply method accordingly. /// the cti::continuable_base::apply method accordingly.
namespace transforms {} namespace transforms {
/// Returns a transform that if applied to a continuable,
/// it will start the continuation chain and returns the asynchronous
/// result as `std::future<...>`.
///
/// \returns Returns a `std::future<...>` which becomes ready as soon
/// as the the continuation chain has finished.
/// The signature of the future depends on the result type:
/// | Continuation type | Return type |
/// | : ------------------------------- | : -------------------------------- |
/// | `continuable_base with <>` | `std::future<void>` |
/// | `continuable_base with <Arg>` | `std::future<Arg>` |
/// | `continuable_base with <Args...>` | `std::future<std::tuple<Args...>>` |
///
/// \attention If exceptions are used, exceptions that are thrown, are forwarded
/// to the returned future. If there are no exceptions supported,
/// you shall not pass any errors to the end of the asynchronous
/// call chain!
/// Otherwise this will yield a trap that causes application exit.
///
/// \since 2.0.0
inline auto futurize() {
return make_transform([](auto&& continuable) {
using detail::transforms::as_future;
return as_future(std::forward<decltype(continuable)>(continuable));
});
}
/// Returns a transform that if applied to a continuable, it will ignores all
/// error which ocured until the point the transform was applied.
///
/// \returns Returns a continuable with the same signature as applied to.
///
/// \attention This can be used to create a continuable which doesn't resolve
/// the continuation on errors.
///
/// \since 2.0.0
inline auto flatten() {
return make_transform([](auto&& continuable) {
return std::forward<decltype(continuable)>(continuable).fail([](auto&&) {});
});
}
} // namespace transforms
} // namespace cti } // namespace cti
#endif // CONTINUABLE_TRANSFORMS_HPP_INCLUDED #endif // CONTINUABLE_TRANSFORMS_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -32,37 +32,31 @@
#define CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED #define CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED
#include <utility> #include <utility>
#include <continuable/detail/traversal/traverse-async.hpp>
#include <continuable/detail/traverse-async.hpp>
namespace cti { namespace cti {
/// \defgroup Traversal Traversal
/// provides functions to traverse and remap nested packs.
/// \{
/// A tag which is passed to the `operator()` of the visitor /// A tag which is passed to the `operator()` of the visitor
/// if an element is visited synchronously through \ref traverse_pack_async. /// if an element is visited synchronously.
/// ///
/// \since 3.0.0 /// \since 3.0.0
using async_traverse_visit_tag = detail::traversal::async_traverse_visit_tag; using detail::traversal::async_traverse_visit_tag;
/// A tag which is passed to the `operator()` of the visitor if an element is /// A tag which is passed to the `operator()` of the visitor
/// visited after the traversal was detached through \ref traverse_pack_async. /// if an element is visited after the traversal was detached.
/// ///
/// \since 3.0.0 /// \since 3.0.0
using async_traverse_detach_tag = detail::traversal::async_traverse_detach_tag; using detail::traversal::async_traverse_detach_tag;
/// A tag which is passed to the `operator()` of the visitor if the /// A tag which is passed to the `operator()` of the visitor
/// asynchronous pack traversal was finished through \ref traverse_pack_async. /// if the asynchronous pack traversal was finished.
/// ///
/// \since 3.0.0 /// \since 3.0.0
using async_traverse_complete_tag = using detail::traversal::async_traverse_complete_tag;
detail::traversal::async_traverse_complete_tag;
/// A tag to identify that a mapper shall be constructed in-place /// A tag to identify that a mapper shall be constructed in-place
/// from the first argument passed to \ref traverse_pack_async. /// from the first argument passed.
/// ///
/// \since 3.0.0 /// \since 3.0.0
template <typename T> using detail::traversal::async_traverse_in_place_tag;
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. /// Traverses the pack with the given visitor in an asynchronous way.
/// ///
@ -74,7 +68,7 @@ using async_traverse_in_place_tag =
/// ```cpp /// ```cpp
/// struct my_async_visitor { /// struct my_async_visitor {
/// /// The synchronous overload is called for each object, /// /// The synchronous overload is called for each object,
/// /// it may return false to suspend the current control flow. /// /// it may return false to suspend the current control.
/// /// In that case the overload below is called. /// /// In that case the overload below is called.
/// template <typename T> /// template <typename T>
/// bool operator()(async_traverse_visit_tag, T&& element) { /// bool operator()(async_traverse_visit_tag, T&& element) {
@ -124,7 +118,6 @@ auto traverse_pack_async(Visitor&& visitor, T&&... pack) {
return detail::traversal::apply_pack_transform_async( return detail::traversal::apply_pack_transform_async(
std::forward<Visitor>(visitor), std::forward<T>(pack)...); std::forward<Visitor>(visitor), std::forward<T>(pack)...);
} }
/// \}
} // namespace cti } // namespace cti
#endif // CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED #endif // CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -34,13 +34,10 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <continuable/detail/traversal/traverse.hpp>
#include <continuable/detail/traverse.hpp>
namespace cti { namespace cti {
/// \defgroup Traversal Traversal
/// provides functions to traverse and remap nested packs.
/// \{
/// Maps the pack with the given mapper. /// Maps the pack with the given mapper.
/// ///
/// This function tries to visit all plain elements which may be wrapped in: /// This function tries to visit all plain elements which may be wrapped in:
@ -109,7 +106,6 @@ void traverse_pack(Mapper&& mapper, T&&... pack) {
std::forward<Mapper>(mapper), std::forward<Mapper>(mapper),
std::forward<T>(pack)...); std::forward<T>(pack)...);
} }
/// \}
} // namespace cti } // namespace cti
#endif // CONTINUABLE_TRAVERSE_HPP_INCLUDED #endif // CONTINUABLE_TRAVERSE_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -31,72 +31,78 @@
#ifndef CONTINUABLE_TYPES_HPP_INCLUDED #ifndef CONTINUABLE_TYPES_HPP_INCLUDED
#define CONTINUABLE_TYPES_HPP_INCLUDED #define CONTINUABLE_TYPES_HPP_INCLUDED
#include <cstdint>
#include <function2/function2.hpp> #include <function2/function2.hpp>
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-primitives.hpp> #include <continuable/continuable-trait.hpp>
#include <continuable/continuable-promise-base.hpp>
#include <continuable/detail/other/erasure.hpp>
namespace cti { namespace cti {
/// \defgroup Types Types // clang-format off
/// provides the \link cti::continuable continuable\endlink and \link namespace detail {
/// cti::promise promise\endlink facility for type erasure. /// A function which isn't size adjusted and copyable
/// \{ template<std::size_t Size, typename... Args>
using function_adapter = fu2::function<Args...>;
/// A function which isn't size adjusted and move only
template<std::size_t, typename... Args>
using unique_function_adapter = fu2::unique_function<Args...>;
/// A function which is size adjusted and copyable
template<std::size_t Size, typename... Args>
using function_adjustable = fu2::function_base<true, true, Size,
true, false, Args...>;
/// A function which is size adjusted and move only
template<std::size_t Size, typename... Args>
using unique_function_adjustable = fu2::function_base<true, false, Size,
true, false, Args...>;
/// Deduces to the preferred continuation capacity for a possible /// We adjust the internal capacity of the outer function wrapper so
/// small functor optimization. The given capacity size is always enough to /// we don't have to allocate twice when using `continuable<...>`.
/// to avoid any allocation when storing a ready continuable_base. template<typename... Args>
using trait_of = continuable_trait<
unique_function_adapter,
function_adjustable,
Args...
>;
template<typename... Args>
using unique_trait_of = continuable_trait<
unique_function_adapter,
unique_function_adjustable,
Args...
>;
} // namespace detail
/// Defines a copyable continuation type which uses the
/// function2 backend for type erasure.
/// ///
/// \since 4.0.0 /// Usable like: `cti::continuable<int, float>`
template <typename... Args> template <typename... Args>
using continuation_capacity = detail::erasure::continuation_capacity<Args...>; using continuable = typename detail::trait_of<
Args...
>::continuable;
/// Defines a non-copyable continuation type which uses the /// Defines a non-copyable continuation type which uses the
/// function2 backend for type erasure. /// function2 backend for type erasure.
/// ///
/// Usable like: `continuable<int, float>` /// Usable like: `unique_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> template <typename... Args>
using continuable = continuable_base<detail::erasure::continuation<Args...>, // using unique_continuable = typename detail::unique_trait_of<
signature_arg_t<Args...>>; Args...
>::continuable;
/// Defines a non-copyable promise type which is using the /// Defines a non-copyable promise type which is using the
/// function2 backend for type erasure. /// function2 backend for type erasure.
/// ///
/// Usable like: `promise<int, float>` /// 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> template <typename... Args>
using promise = promise_base<detail::erasure::callback<Args...>, // using promise = typename detail::unique_trait_of<
signature_arg_t<Args...>>; Args...
>::promise;
/// Defines a non-copyable type erasure which is capable of carrying // TODO channel
/// callable objects passed to executors. // TODO sink
///
/// The work behaves like a `promise<>` but the work type erasure uses extra // clang-format on
/// 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 } // namespace cti
#endif // CONTINUABLE_TYPES_HPP_INCLUDED #endif // CONTINUABLE_TYPES_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -46,13 +46,10 @@
namespace cti {} namespace cti {}
#include <continuable/continuable-base.hpp> #include <continuable/continuable-base.hpp>
#include <continuable/continuable-connections.hpp> #include <continuable/continuable-compositions.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-promise-base.hpp>
#include <continuable/continuable-promisify.hpp> #include <continuable/continuable-promisify.hpp>
#include <continuable/continuable-result.hpp> #include <continuable/continuable-trait.hpp>
#include <continuable/continuable-transforms.hpp> #include <continuable/continuable-transforms.hpp>
#include <continuable/continuable-traverse-async.hpp> #include <continuable/continuable-traverse-async.hpp>
#include <continuable/continuable-traverse.hpp> #include <continuable/continuable-traverse.hpp>

View File

@ -0,0 +1,164 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_AWAITING_HPP_INCLUDED
#define CONTINUABLE_DETAIL_AWAITING_HPP_INCLUDED
// Exlude this header when coroutines are not available
#ifdef CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
#include <cassert>
#include <experimental/coroutine>
#include <continuable/detail/base.hpp>
#include <continuable/detail/expected.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/hints.hpp>
#include <continuable/detail/types.hpp>
#include <continuable/detail/util.hpp>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
#include <exception>
#endif // CONTINUABLE_HAS_EXCEPTIONS
namespace cti {
namespace detail {
namespace awaiting {
/// We import the coroutine handle in our namespace
using std::experimental::coroutine_handle;
/// An object which provides the internal buffer and helper methods
/// for waiting on a continuable in a stackless coroutine.
template <typename Continuable>
class awaitable {
using trait_t = util::expected_result_trait_t<Continuable>;
/// The continuable which is invoked upon suspension
Continuable continuable_;
/// A cache which is used to pass the result of the continuation
/// to the coroutine.
typename trait_t::expected_type result_;
public:
explicit constexpr awaitable(Continuable&& continuable)
: continuable_(std::move(continuable)) {
}
/// Since continuables are evaluated lazily we are not
/// capable to say whether the resumption will be instantly.
bool await_ready() const noexcept {
return false;
}
/// Suspend the current context
// TODO Convert this to an r-value function once possible
void await_suspend(coroutine_handle<> h) {
// Forward every result to the current awaitable
std::move(continuable_)
.next([h, this](auto&&... args) mutable {
resolve(std::forward<decltype(args)>(args)...);
h.resume();
})
.done();
}
/// Resume the coroutine represented by the handle
auto await_resume() noexcept(false) {
if (result_) {
// When the result was resolved return it
return trait_t::unwrap(std::move(result_));
}
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
std::rethrow_exception(result_.get_exception());
#else // CONTINUABLE_HAS_EXCEPTIONS
// Returning error types in await isn't supported as of now
util::trap();
#endif // CONTINUABLE_HAS_EXCEPTIONS
}
private:
/// Resolve the continuation through the result
template <typename... Args>
void resolve(Args&&... args) {
result_.set_value(trait_t::wrap(std::forward<Args>(args)...));
}
/// Resolve the continuation through an error
void resolve(types::dispatch_error_tag, types::error_type error) {
result_.set_exception(std::move(error));
}
};
/// Converts a continuable into an awaitable object as described by
/// the C++ coroutine TS.
template <typename T>
constexpr auto create_awaiter(T&& continuable) {
return awaitable<std::decay_t<T>>(std::forward<T>(continuable));
}
} // namespace awaiting
} // namespace detail
} // namespace cti
namespace std {
namespace experimental {
template <typename Data, typename... Args, typename... FunctionArgs>
struct coroutine_traits<
cti::continuable_base<Data,
cti::detail::hints::signature_hint_tag<Args...>>,
FunctionArgs...> /*{
struct promise_type {
// boost::promise<R> p;
auto get_return_object() {
// return p.get_future();
}
suspend_always initial_suspend() {
return {};
}
suspend_never final_suspend() {
return {};
}
void set_exception(std::exception_ptr e) {
// p.set_exception(std::move(e));
}
void unhandled_exception() {
// p.set_exception(std::current_exception());
}
template <typename U>
void return_value(U&& u) {
// p.set_value(std::forward<U>(u));
}
};
}*/;
} // namespace experimental
} // namespace std
#endif // CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE
#endif // CONTINUABLE_DETAIL_UTIL_HPP_INCLUDED

View File

@ -0,0 +1,590 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_BASE_HPP_INCLUDED
#define CONTINUABLE_DETAIL_BASE_HPP_INCLUDED
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/detail/features.hpp>
#include <continuable/detail/hints.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/types.hpp>
#include <continuable/detail/util.hpp>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
#include <exception>
#endif // CONTINUABLE_HAS_EXCEPTIONS
namespace cti {
namespace detail {
/// The namespace `base` provides the low level API for working
/// with continuable types.
///
/// Important methods are:
/// - Creating a continuation from a callback taking functional
/// base::attorney::create(auto&& callback)
/// -> base::continuation<auto>
/// - Chaining a continuation together with a callback
/// base::chain_continuation(base::continuation<auto> continuation,
/// auto&& callback)
/// -> base::continuation<auto>
/// - Finally invoking the continuation chain
/// base::finalize_continuation(base::continuation<auto> continuation)
/// -> void
namespace base {
template <typename T>
struct is_continuable : std::false_type {};
template <typename Data, typename Annotation>
struct is_continuable<continuable_base<Data, Annotation>> : std::true_type {};
/// Helper class to access private methods and members of
/// the continuable_base class.
struct attorney {
/// Makes a continuation wrapper from the given argument
template <typename T, typename A>
static auto create(T&& continuation, A /*hint*/, util::ownership ownership_) {
return continuable_base<std::decay_t<T>, std::decay_t<A>>(
std::forward<T>(continuation), ownership_);
}
/// Invokes a continuation object in a reference correct way
template <typename Data, typename Annotation, typename Callback>
static auto
invoke_continuation(continuable_base<Data, Annotation>&& continuation,
Callback&& callback) noexcept {
auto materialized = std::move(continuation).materialize();
materialized.release();
return materialized.data_(std::forward<Callback>(callback));
}
template <typename Data, typename Annotation>
static auto materialize(continuable_base<Data, Annotation>&& continuation) {
return std::move(continuation).materialize();
}
template <typename Data, typename Annotation>
static Data&&
consume_data(continuable_base<Data, Annotation>&& continuation) {
return std::move(continuation).consume_data();
}
template <typename Continuable>
static util::ownership ownership_of(Continuable&& continuation) noexcept {
return continuation.ownership_;
}
};
// Returns the invoker of a callback, the next callback
// and the arguments of the previous continuation.
//
// The return type of the invokerOf function matches a callable of:
// void(auto&& callback, auto&& next_callback, auto&&... args)
//
// The invoker decorates the result type in the following way
// - void -> next_callback()
// - ? -> next_callback(?)
// - std::pair<?, ?> -> next_callback(?, ?)
// - std::tuple<?...> -> next_callback(?...)
//
// When the result is a continuation itself pass the callback to it
// - continuation<?...> -> result(next_callback);
namespace decoration {
/// Helper class wrapping the underlaying unwrapping lambda
/// in order to extend it with a hint method.
template <typename T, typename Hint>
class invoker : public T {
public:
constexpr explicit invoker(T invoke) : T(std::move(invoke)) {
}
using T::operator();
/// Returns the underlaying signature hint
static constexpr Hint hint() noexcept {
return {};
}
};
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
#define CONTINUABLE_BLOCK_TRY_BEGIN try {
#define CONTINUABLE_BLOCK_TRY_END \
} \
catch (...) { \
std::forward<decltype(next_callback)>(next_callback)( \
types::dispatch_error_tag{}, std::current_exception()); \
}
#else // CONTINUABLE_HAS_EXCEPTIONS
#define CONTINUABLE_BLOCK_TRY_BEGIN {
#define CONTINUABLE_BLOCK_TRY_END }
#endif // CONTINUABLE_HAS_EXCEPTIONS
/// Invokes the given callable object with the given arguments while
/// marking the operation as non exceptional.
template <typename T, typename... Args>
constexpr auto invoke_no_except(T&& callable, Args&&... args) noexcept {
return std::forward<T>(callable)(std::forward<Args>(args)...);
}
template <typename T, typename... Args>
constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag<Args...>) {
return invoker<std::decay_t<T>, hints::signature_hint_tag<Args...>>(
std::forward<T>(invoke));
}
/// - continuable<?...> -> result(next_callback);
template <typename Data, typename Annotation>
constexpr auto
invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
/// Get the hint of the unwrapped returned continuable
using Type = decltype(attorney::materialize(
std::declval<continuable_base<Data, Annotation>>()));
auto constexpr const hint = hints::hint_of(traits::identify<Type>{});
return make_invoker(
[](auto&& callback, auto&& next_callback, auto&&... args) {
CONTINUABLE_BLOCK_TRY_BEGIN
auto continuation_ =
util::partial_invoke(std::forward<decltype(callback)>(callback),
std::forward<decltype(args)>(args)...);
attorney::invoke_continuation(
std::move(continuation_),
std::forward<decltype(next_callback)>(next_callback));
CONTINUABLE_BLOCK_TRY_END
},
hint);
}
/// - ? -> next_callback(?)
template <typename T>
constexpr auto invoker_of(traits::identity<T>) {
return make_invoker(
[](auto&& callback, auto&& next_callback, auto&&... args) {
CONTINUABLE_BLOCK_TRY_BEGIN
auto result =
util::partial_invoke(std::forward<decltype(callback)>(callback),
std::forward<decltype(args)>(args)...);
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
std::move(result));
CONTINUABLE_BLOCK_TRY_END
},
traits::identify<T>{});
}
/// - void -> next_callback()
inline auto invoker_of(traits::identity<void>) {
return make_invoker(
[](auto&& callback, auto&& next_callback, auto&&... args) {
CONTINUABLE_BLOCK_TRY_BEGIN
util::partial_invoke(std::forward<decltype(callback)>(callback),
std::forward<decltype(args)>(args)...);
invoke_no_except(
std::forward<decltype(next_callback)>(next_callback));
CONTINUABLE_BLOCK_TRY_END
},
traits::identity<>{});
}
/// Returns a sequenced invoker which is able to invoke
/// objects where std::get is applicable.
inline auto sequenced_unpack_invoker() {
return [](auto&& callback, auto&& next_callback, auto&&... args) {
CONTINUABLE_BLOCK_TRY_BEGIN
auto result =
util::partial_invoke(std::forward<decltype(callback)>(callback),
std::forward<decltype(args)>(args)...);
// Workaround for MSVC not capturing the reference correctly inside
// the lambda.
using Next = decltype(next_callback);
traits::unpack(std::move(result), [&](auto&&... types) {
/// TODO Add inplace resolution here
invoke_no_except(std::forward<Next>(next_callback),
std::forward<decltype(types)>(types)...);
});
CONTINUABLE_BLOCK_TRY_END
};
} // namespace decoration
// - std::pair<?, ?> -> next_callback(?, ?)
template <typename First, typename Second>
constexpr auto invoker_of(traits::identity<std::pair<First, Second>>) {
return make_invoker(sequenced_unpack_invoker(),
traits::identity<First, Second>{});
}
// - std::tuple<?...> -> next_callback(?...)
template <typename... Args>
constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
return make_invoker(sequenced_unpack_invoker(), traits::identity<Args...>{});
}
#undef CONTINUABLE_BLOCK_TRY_BEGIN
#undef CONTINUABLE_BLOCK_TRY_END
} // namespace decoration
/// Invoke the callback immediately
template <typename Invoker, typename... Args>
void packed_dispatch(types::this_thread_executor_tag, Invoker&& invoker,
Args&&... args) {
// Invoke the callback with the decorated invoker immediately
std::forward<Invoker>(invoker)(std::forward<Args>(args)...);
}
/// Invoke the callback through the given executor
template <typename Executor, typename Invoker, typename... Args>
void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) {
// Create a worker object which when invoked calls the callback with the
// the returned arguments.
auto work = [
invoker = std::forward<Invoker>(invoker),
args = std::make_tuple(std::forward<Args>(args)...)
]() mutable {
traits::unpack(std::move(args), [&](auto&&... captured_args) {
// Just use the packed dispatch method which dispatches the work on
// the current thread.
packed_dispatch(types::this_thread_executor_tag{}, std::move(invoker),
std::forward<decltype(captured_args)>(captured_args)...);
});
};
// Pass the work callable object to the executor
std::forward<Executor>(executor)(std::move(work));
}
/// Tells whether we potentially move the chain upwards and handle the result
enum class handle_results {
no, //< The result is forwarded to the next callable
yes //< The result is handled by the current callable
};
/// Tells whether we handle the error through the callback
enum class handle_errors {
no, //< The error is forwarded to the next callable
plain, //< The error is the only argument accepted by the callable
forward //< The error is forwarded to the callable while keeping its tag
};
namespace callbacks {
namespace proto {
template <handle_results HandleResults, typename Base, typename Hint>
struct result_handler_base;
template <typename Base, typename... Args>
struct result_handler_base<handle_results::no, Base,
hints::signature_hint_tag<Args...>> {
void operator()(Args... args) && {
// Forward the arguments to the next callback
std::move(static_cast<Base*>(this)->next_callback_)(std::move(args)...);
}
};
template <typename Base, typename... Args>
struct result_handler_base<handle_results::yes, Base,
hints::signature_hint_tag<Args...>> {
/// The operator which is called when the result was provided
void operator()(Args... args) && {
// In order to retrieve the correct decorator we must know what the
// result type is.
auto result = traits::identify<decltype(util::partial_invoke(
std::move(static_cast<Base*>(this)->callback_), std::move(args)...))>{};
// Pick the correct invoker that handles decorating of the result
auto invoker = decoration::invoker_of(result);
// Invoke the callback
packed_dispatch(std::move(static_cast<Base*>(this)->executor_),
std::move(invoker),
std::move(static_cast<Base*>(this)->callback_),
std::move(static_cast<Base*>(this)->next_callback_),
std::move(args)...);
}
};
inline auto make_error_invoker(
std::integral_constant<handle_errors, handle_errors::plain>) noexcept {
return [](auto&& callback, types::error_type&& error) {
// Errors are not partial invoked
// NOLINTNEXTLINE(hicpp-move-const-arg)
std::forward<decltype(callback)>(callback)(std::move(error));
};
}
inline auto make_error_invoker(
std::integral_constant<handle_errors, handle_errors::forward>) noexcept {
return [](auto&& callback, types::error_type&& error) {
// Errors are not partial invoked
std::forward<decltype(callback)>(callback)(
types::dispatch_error_tag{},
std::move(error)); // NOLINT(hicpp-move-const-arg)
};
}
template <handle_errors HandleErrors /* = plain or forward*/, typename Base>
struct error_handler_base {
void operator()(types::dispatch_error_tag, types::error_type error) && {
// Just invoke the error handler, cancel the calling hierarchy after
auto invoker = make_error_invoker(
std::integral_constant<handle_errors, HandleErrors>{});
// Invoke the error handler
packed_dispatch(
std::move(static_cast<Base*>(this)->executor_), std::move(invoker),
std::move(static_cast<Base*>(this)->callback_), std::move(error));
}
};
template <typename Base>
struct error_handler_base<handle_errors::no, Base> {
/// The operator which is called when an error occurred
void operator()(types::dispatch_error_tag tag, types::error_type error) && {
// Forward the error to the next callback
std::move(static_cast<Base*>(this)->next_callback_)(tag, std::move(error));
}
};
} // namespace proto
template <typename Hint, handle_results HandleResults,
handle_errors HandleErrors, typename Callback, typename Executor,
typename NextCallback>
struct callback_base;
template <typename... Args, handle_results HandleResults,
handle_errors HandleErrors, typename Callback, typename Executor,
typename NextCallback>
struct callback_base<hints::signature_hint_tag<Args...>, HandleResults,
HandleErrors, Callback, Executor, NextCallback>
: proto::result_handler_base<
HandleResults,
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
HandleErrors, Callback, Executor, NextCallback>,
hints::signature_hint_tag<Args...>>,
proto::error_handler_base<
HandleErrors,
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
HandleErrors, Callback, Executor, NextCallback>>,
util::non_copyable {
Callback callback_;
Executor executor_;
NextCallback next_callback_;
explicit callback_base(Callback callback, Executor executor,
NextCallback next_callback)
: callback_(std::move(callback)), executor_(std::move(executor)),
next_callback_(std::move(next_callback)) {
}
/// Pull the result handling operator() in
using proto::result_handler_base<
HandleResults,
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
HandleErrors, Callback, Executor, NextCallback>,
hints::signature_hint_tag<Args...>>::operator();
/// Pull the error handling operator() in
using proto::error_handler_base<
HandleErrors,
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
HandleErrors, Callback, Executor, NextCallback>>::
operator();
/// Resolves the continuation with the given values
void set_value(Args... args) {
std::move (*this)(std::move(args)...);
}
/// Resolves the continuation with the given error variable.
void set_exception(types::error_type error) {
std::move (*this)(types::dispatch_error_tag{}, std::move(error));
}
};
template <typename Hint, handle_results HandleResults,
handle_errors HandleErrors, typename Callback, typename Executor,
typename NextCallback>
auto make_callback(Callback&& callback, Executor&& executor,
NextCallback&& next_callback) {
return callback_base<Hint, HandleResults, HandleErrors,
std::decay_t<Callback>, std::decay_t<Executor>,
std::decay_t<NextCallback>>{
std::forward<Callback>(callback), std::forward<Executor>(executor),
std::forward<NextCallback>(next_callback)};
}
/// Once this was a workaround for GCC bug:
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
struct final_callback : util::non_copyable {
template <typename... Args>
void operator()(Args... /*args*/) && {
}
void operator()(types::dispatch_error_tag, types::error_type error) && {
(void)error;
#ifndef CONTINUABLE_WITH_UNHANDLED_ERRORS
// There were unhandled errors inside the asynchronous call chain!
// Define `CONTINUABLE_WITH_UNHANDLED_ERRORS` in order
// to ignore unhandled errors!"
util::trap();
#endif // CONTINUABLE_WITH_UNHANDLED_ERRORS
}
template <typename... Args>
void set_value(Args... args) {
std::move (*this)(std::forward<Args>(args)...);
}
void set_exception(types::error_type error) {
// NOLINTNEXTLINE(hicpp-move-const-arg)
std::move (*this)(types::dispatch_error_tag{}, std::move(error));
}
};
} // namespace callbacks
/// Returns the next hint when the callback is invoked with the given hint
template <typename T, typename... Args>
constexpr auto
next_hint_of(std::integral_constant<handle_results, handle_results::yes>,
traits::identity<T> /*callback*/,
hints::signature_hint_tag<Args...> /*current*/) {
// Partial Invoke the given callback
using Result = decltype(
util::partial_invoke(std::declval<T>(), std::declval<Args>()...));
// Return the hint of thr given invoker
return decltype(decoration::invoker_of(traits::identify<Result>{}).hint()){};
}
/// Don't progress the hint when we don't continue
template <typename T, typename... Args>
constexpr auto
next_hint_of(std::integral_constant<handle_results, handle_results::no>,
traits::identity<T> /*callback*/,
hints::signature_hint_tag<Args...> current) {
return current;
}
/// Chains a callback together with a continuation and returns a continuation:
///
/// For example given:
/// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
/// - Callback: [](std::string) { }
///
/// This function returns a function accepting the next callback in the chain:
/// - Result: continuation<[](auto&& callback) { /*...*/ }>
///
template <handle_results HandleResults, handle_errors HandleErrors,
typename Continuation, typename Callback, typename Executor>
auto chain_continuation(Continuation&& continuation, Callback&& callback,
Executor&& executor) {
static_assert(is_continuable<std::decay_t<Continuation>>{},
"Expected a continuation!");
using Hint = decltype(hints::hint_of(traits::identify<Continuation>()));
constexpr auto next_hint =
next_hint_of(std::integral_constant<handle_results, HandleResults>{},
traits::identify<decltype(callback)>{}, Hint{});
// TODO consume only the data here so the freeze isn't needed
auto ownership_ = attorney::ownership_of(continuation);
continuation.freeze();
return attorney::create(
[
continuation = std::forward<Continuation>(continuation),
callback = std::forward<Callback>(callback),
executor = std::forward<Executor>(executor)
](auto&& next_callback) mutable {
// Invokes a continuation with a given callback.
// Passes the next callback to the resulting continuable or
// invokes the next callback directly if possible.
//
// For example given:
// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
// - Callback: [](std::string) { }
// - NextCallback: []() { }
auto proxy =
callbacks::make_callback<Hint, HandleResults, HandleErrors>(
std::move(callback), std::move(executor),
std::forward<decltype(next_callback)>(next_callback));
// Invoke the continuation with a proxy callback.
// The proxy callback is responsible for passing
// the result to the callback as well as decorating it.
attorney::invoke_continuation(std::move(continuation),
std::move(proxy));
},
next_hint, ownership_);
}
/// Final invokes the given continuation chain:
///
/// For example given:
/// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
template <typename Continuation>
void finalize_continuation(Continuation&& continuation) {
attorney::invoke_continuation(std::forward<Continuation>(continuation),
callbacks::final_callback{});
}
/// Workaround for GCC bug:
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
template <typename T>
class supplier_callback {
T data_;
public:
explicit supplier_callback(T data) : data_(std::move(data)) {
}
template <typename... Args>
auto operator()(Args...) {
return std::move(data_);
}
};
/// Returns a continuable into a callable object returning the continuable
template <typename Continuation>
auto wrap_continuation(Continuation&& continuation) {
continuation.freeze();
return supplier_callback<std::decay_t<Continuation>>(
std::forward<Continuation>(continuation));
}
} // namespace base
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_BASE_HPP_INCLUDED

View File

@ -0,0 +1,233 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_COMPOSITION_ALL_HPP_INCLUDED
#define CONTINUABLE_DETAIL_COMPOSITION_ALL_HPP_INCLUDED
#include <atomic>
#include <memory>
#include <mutex>
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/detail/base.hpp>
#include <continuable/detail/composition-remapping.hpp>
#include <continuable/detail/composition.hpp>
#include <continuable/detail/hints.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/types.hpp>
namespace cti {
namespace detail {
namespace composition {
namespace all {
struct all_hint_deducer {
static constexpr auto deduce(hints::signature_hint_tag<>) noexcept {
return spread_this();
}
template <typename First>
static constexpr auto deduce(hints::signature_hint_tag<First>) {
return First{};
}
template <typename First, typename Second, typename... Args>
static constexpr auto
deduce(hints::signature_hint_tag<First, Second, Args...>) {
return spread_this(First{}, Second{}, Args{}...);
}
template <
typename T,
std::enable_if_t<base::is_continuable<std::decay_t<T>>::value>* = nullptr>
auto operator()(T&& /*continuable*/) const {
return deduce(hints::hint_of(traits::identify<T>{}));
}
};
constexpr auto deduce_from_pack(traits::identity<void>)
-> hints::signature_hint_tag<>;
template <typename... T>
constexpr auto deduce_from_pack(traits::identity<std::tuple<T...>>)
-> hints::signature_hint_tag<T...>;
template <typename T>
constexpr auto deduce_from_pack(traits::identity<T>)
-> hints::signature_hint_tag<T>;
// We must guard the mapped type against to be void since this represents an
// empty signature hint.
template <typename Composition>
constexpr auto deduce_hint(Composition&& /*composition*/) {
// Don't change this way since it addresses a GCC compiler bug:
// error: extra ';' [-Werror=pedantic]
// std::declval<Composition>()))>{})){};
using mapped_t =
decltype(map_pack(all_hint_deducer{}, std::declval<Composition>()));
using deduced_t = decltype(deduce_from_pack(traits::identity<mapped_t>{}));
return deduced_t{};
}
/// Caches the partial results and invokes the callback when all results
/// are arrived. This class is thread safe.
template <typename Callback, typename Result>
class result_submitter
: public std::enable_shared_from_this<result_submitter<Callback, Result>>,
public util::non_movable {
Callback callback_;
Result result_;
std::atomic<std::size_t> left_;
std::once_flag flag_;
// Invokes the callback with the cached result
void invoke() {
assert((left_ == 0U) && "Expected that the submitter is finished!");
std::atomic_thread_fence(std::memory_order_acquire);
// Call the final callback with the cleaned result
std::call_once(flag_, [&] {
remapping::finalize_data(std::move(callback_), std::move(result_));
});
}
// Completes one result
void complete_one() {
assert((left_ > 0U) && "Expected that the submitter isn't finished!");
auto const current = --left_;
if (!current) {
invoke();
}
}
template <typename Box>
struct partial_all_callback {
Box* box;
std::shared_ptr<result_submitter> me;
template <typename... Args>
void operator()(Args&&... args) && {
// Assign the result to the target
box->assign(std::forward<decltype(args)>(args)...);
// Complete one result
me->complete_one();
}
template <typename... PartialArgs>
void operator()(types::dispatch_error_tag tag, types::error_type error) && {
// We never complete the composition, but we forward the first error
// which was raised.
std::call_once(me->flag_, std::move(me->callback_), tag,
std::move(error));
}
};
public:
explicit result_submitter(Callback callback, Result&& result)
: callback_(std::move(callback)), result_(std::move(result)), left_(1) {
}
/// Creates a submitter which submits it's result into the storage
template <typename Box>
auto create_callback(Box* box) {
left_.fetch_add(1, std::memory_order_seq_cst);
return partial_all_callback<std::decay_t<Box>>{box,
this->shared_from_this()};
}
/// Initially the counter is created with an initial count of 1 in order
/// to prevent that the composition is finished before all callbacks
/// were registered.
void accept() {
complete_one();
}
constexpr auto& head() noexcept {
return result_;
}
};
template <typename Submitter>
struct continuable_dispatcher {
std::shared_ptr<Submitter>& submitter;
template <typename Box, std::enable_if_t<remapping::is_continuable_box<
std::decay_t<Box>>::value>* = nullptr>
void operator()(Box&& box) const {
// Retrieve a callback from the submitter and attach it to the continuable
box.fetch().next(submitter->create_callback(std::addressof(box))).done();
}
};
} // namespace all
/// Finalizes the all logic of a given composition
template <>
struct composition_finalizer<composition_strategy_all_tag> {
template <typename Composition>
static constexpr auto hint() {
return decltype(all::deduce_hint(std::declval<Composition>())){};
}
/// Finalizes the all logic of a given composition
template <typename Composition>
static auto finalize(Composition&& composition) {
return [composition = std::forward<Composition>(composition)] // ...
(auto&& callback) mutable {
// Create the target result from the composition
auto result = remapping::box_continuables(std::move(composition));
using submitter_t =
all::result_submitter<std::decay_t<decltype(callback)>,
std::decay_t<decltype(result)>>;
// Create the shared state which holds the result and the final callback
auto state = std::make_shared<submitter_t>(
std::forward<decltype(callback)>(callback), std::move(result));
// Dispatch the continuables and store its partial result
// in the whole result
traverse_pack(all::continuable_dispatcher<submitter_t>{state},
state->head());
// Finalize the composition if all results arrived in-place
state->accept();
};
}
};
} // namespace composition
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_COMPOSITION_ALL_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,15 +21,15 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
**/ **/
#ifndef CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED
#define CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED #define CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED
#include <atomic> #include <atomic>
#include <memory> #include <memory>
@ -37,18 +37,18 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-promise-base.hpp> #include <continuable/continuable-promise-base.hpp>
#include <continuable/continuable-traverse.hpp> #include <continuable/continuable-traverse.hpp>
#include <continuable/detail/core/annotation.hpp> #include <continuable/detail/base.hpp>
#include <continuable/detail/core/base.hpp> #include <continuable/detail/container-category.hpp>
#include <continuable/detail/core/types.hpp> #include <continuable/detail/hints.hpp>
#include <continuable/detail/traversal/container-category.hpp> #include <continuable/detail/traits.hpp>
#include <continuable/detail/utility/traits.hpp> #include <continuable/detail/types.hpp>
namespace cti { namespace cti {
namespace detail { namespace detail {
namespace connection { namespace composition {
namespace any { namespace any {
/// Invokes the callback with the first arriving result /// Invokes the callback with the first arriving result
template <typename T> template <typename T>
@ -88,30 +88,30 @@ private:
struct result_deducer { struct result_deducer {
template <typename T> template <typename T>
static auto deduce_one(std::false_type, identity<T>) { static auto deduce_one(std::false_type, traits::identity<T>) {
static_assert(traits::fail<T>::value, static_assert(traits::fail<T>::value,
"Non continuable types except tuple like and homogeneous " "Non continuable types except tuple like and homogeneous "
"containers aren't allowed inside an any expression!"); "containers aren't allowed inside an any expression!");
} }
template <typename T> template <typename T>
static auto deduce_one(std::true_type, identity<T> id) { static auto deduce_one(std::true_type, traits::identity<T> id) {
return base::annotation_of(id); return hints::hint_of(id);
} }
template <typename T> template <typename T>
static auto deduce(traversal::container_category_tag<false, false>, static auto deduce(traversal::container_category_tag<false, false>,
identity<T> id) { traits::identity<T> id) {
return deduce_one<T>(base::is_continuable<T>{}, id); return deduce_one<T>(base::is_continuable<T>{}, id);
} }
/// Deduce a homogeneous container /// Deduce a homogeneous container
template <bool IsTupleLike, typename T> template <bool IsTupleLike, typename T>
static auto deduce(traversal::container_category_tag<true, IsTupleLike>, static auto deduce(traversal::container_category_tag<true, IsTupleLike>,
identity<T>) { traits::identity<T>) {
// Deduce the containing type // Deduce the containing type
using element_t = std::decay_t<decltype(*std::declval<T>().begin())>; using element_t = std::decay_t<decltype(*std::declval<T>().begin())>;
return deduce(traversal::container_category_of_t<element_t>{}, return deduce(traversal::container_category_of_t<element_t>{},
identity<element_t>{}); traits::identity<element_t>{});
} }
template <typename First, typename... T> template <typename First, typename... T>
@ -125,18 +125,19 @@ struct result_deducer {
template <std::size_t... I, typename T> template <std::size_t... I, typename T>
static auto deduce_tuple_like(std::integer_sequence<std::size_t, I...>, static auto deduce_tuple_like(std::integer_sequence<std::size_t, I...>,
identity<T>) { traits::identity<T>) {
return deduce_same_hints(deduce( return deduce_same_hints(deduce(
traversal::container_category_of_t< traversal::container_category_of_t<
std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{}, std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{},
identity<std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{})...); traits::identity<
std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{})...);
} }
/// Traverse tuple like container /// Traverse tuple like container
template <typename T> template <typename T>
static auto deduce(traversal::container_category_tag<false, true>, static auto deduce(traversal::container_category_tag<false, true>,
identity<T> id) { traits::identity<T> id) {
constexpr auto const size = std::tuple_size<T>::value; constexpr auto const size = std::tuple_size<T>::value;
return deduce_tuple_like(std::make_index_sequence<size>{}, id); return deduce_tuple_like(std::make_index_sequence<size>{}, id);
@ -150,7 +151,7 @@ struct continuable_dispatcher {
template <typename Continuable, template <typename Continuable,
std::enable_if_t<base::is_continuable< std::enable_if_t<base::is_continuable<
std::decay_t<Continuable>>::value>* = nullptr> std::decay_t<Continuable>>::value>* = nullptr>
void operator()(Continuable&& continuable) { void operator()(Continuable&& continuable) const {
// Retrieve a callback from the submitter and attach it to the continuable // Retrieve a callback from the submitter and attach it to the continuable
std::forward<Continuable>(continuable) std::forward<Continuable>(continuable)
.next(submitter->create_callback()) .next(submitter->create_callback())
@ -159,46 +160,36 @@ struct continuable_dispatcher {
}; };
} // namespace any } // namespace any
struct connection_strategy_any_tag {}; /// Finalizes the any logic of a given composition
template <> template <>
struct is_connection_strategy<connection_strategy_any_tag> // ... struct composition_finalizer<composition_strategy_any_tag> {
: std::true_type {}; template <typename Composition>
static constexpr auto hint() {
return decltype(any::result_deducer::deduce(
traversal::container_category_of_t<std::decay_t<Composition>>{},
traits::identity<std::decay_t<Composition>>{})){};
}
/// Finalizes the any logic of a given connection template <typename Composition>
template <> static auto finalize(Composition&& composition) {
struct connection_finalizer<connection_strategy_any_tag> { return [composition = std::forward<Composition>(composition)](
template <typename Connection> auto&& callback) mutable {
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( using submitter_t =
[connection = any::any_result_submitter<std::decay_t<decltype(callback)>>;
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 // Create the submitter which calls the given callback once at the
// first callback invocation. // first callback invocation.
auto submitter = std::make_shared<submitter_t>( auto submitter = std::make_shared<submitter_t>(
std::forward<decltype(callback)>(callback)); std::forward<decltype(callback)>(callback));
traverse_pack(any::continuable_dispatcher<submitter_t>{submitter}, traverse_pack(any::continuable_dispatcher<submitter_t>{submitter},
std::move(connection)); std::move(composition));
}, };
signature, std::move(ownership));
} }
}; };
} // namespace connection } // namespace composition
/// 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 detail
} // namespace cti } // namespace cti
#endif // CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,102 +21,77 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
**/ **/
#ifndef CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_COMPOSITION_REMAPPING_HPP_INCLUDED
#define CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED #define CONTINUABLE_DETAIL_COMPOSITION_REMAPPING_HPP_INCLUDED
#include <cassert>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <continuable/continuable-result.hpp>
#include <continuable/continuable-traverse.hpp> #include <continuable/continuable-traverse.hpp>
#include <continuable/detail/core/base.hpp> #include <continuable/detail/base.hpp>
#include <continuable/detail/utility/traits.hpp> #include <continuable/detail/container-category.hpp>
#include <continuable/detail/traits.hpp>
namespace cti { namespace cti {
namespace detail { namespace detail {
namespace connection { namespace composition {
/// This namespace provides utilities for performing compound /// This namespace provides utilities for performing compound
/// connections between deeply nested continuables and values. /// connections between deeply nested continuables and values.
/// ///
/// We create the result pack from the provides values and /// We create the result pack from the provides values and
/// the async values if those are default constructible, /// the async values if those are default constructible,
/// otherwise use a lazy initialization wrapper and unwrap /// otherwise use a lazy initialization wrapper and unwrap
/// the whole pack when the connection is finished. /// the whole pack when the composition is finished.
/// - value -> value /// - value -> value
/// - single async value -> single value /// - single async value -> single value
/// - multiple async value -> tuple of async values. /// - multiple async value -> tuple of async values.
namespace aggregated { namespace remapping {
/// 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> template <typename Continuable>
class continuable_box; class continuable_box;
template <typename Data> template <typename Data>
class continuable_box<continuable_base<Data, identity<>>> { class continuable_box<continuable_base<Data, hints::signature_hint_tag<>>> {
continuable_base<Data, identity<>> continuable_; continuable_base<Data, hints::signature_hint_tag<>> continuable_;
public: public:
explicit continuable_box(continuable_base<Data, identity<>>&& continuable) explicit continuable_box(
: continuable_(std::move(continuable)) {} continuable_base<Data, hints::signature_hint_tag<>>&& continuable)
: continuable_(std::move(continuable)) {
auto const& peek() const {
return continuable_;
} }
auto&& fetch() { continuable_base<Data, hints::signature_hint_tag<>>&& fetch() {
return std::move(continuable_); return std::move(continuable_);
} }
void assign() {} void assign() {
}
auto unbox() && { auto unbox() && {
return spread_this(); return spread_this();
} }
}; };
template <typename Data, typename First> template <typename Data, typename First>
class continuable_box<continuable_base<Data, identity<First>>> { class continuable_box<
continuable_base<Data, hints::signature_hint_tag<First>>> {
continuable_base<Data, identity<First>> continuable_; continuable_base<Data, hints::signature_hint_tag<First>> continuable_;
lazy_value_t<First> first_; First first_;
public: public:
explicit continuable_box( explicit continuable_box(
continuable_base<Data, identity<First>>&& continuable) continuable_base<Data, hints::signature_hint_tag<First>>&& continuable)
: continuable_(std::move(continuable)) {} : continuable_(std::move(continuable)) {
auto const& peek() const {
return continuable_;
} }
auto&& fetch() { continuable_base<Data, hints::signature_hint_tag<First>>&& fetch() {
return std::move(continuable_); return std::move(continuable_);
} }
@ -125,27 +100,27 @@ public:
} }
auto unbox() && { auto unbox() && {
return unpack_lazy(std::is_default_constructible<First>{}, return std::move(first_);
std::move(first_));
} }
}; };
template <typename Data, typename First, typename Second, typename... Rest> template <typename Data, typename First, typename Second, typename... Rest>
class continuable_box< class continuable_box<
continuable_base<Data, identity<First, Second, Rest...>>> { continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>>> {
continuable_base<Data, identity<First, Second, Rest...>> continuable_; continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>>
lazy_value_t<std::tuple<First, Second, Rest...>> args_; continuable_;
std::tuple<First, Second, Rest...> args_;
public: public:
explicit continuable_box( explicit continuable_box(
continuable_base<Data, identity<First, Second, Rest...>>&& continuable) continuable_base<Data,
: continuable_(std::move(continuable)) {} hints::signature_hint_tag<First, Second, Rest...>>&&
continuable)
auto const& peek() const { : continuable_(std::move(continuable)) {
return continuable_;
} }
auto&& fetch() { continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>>&&
fetch() {
return std::move(continuable_); return std::move(continuable_);
} }
@ -155,13 +130,9 @@ public:
} }
auto unbox() && { auto unbox() && {
return traits::unpack( return traits::unpack(std::move(args_), [](auto&&... args) {
[](auto&&... args) { return spread_this(std::forward<decltype(args)>(args)...);
return spread_this(std::forward<decltype(args)>(args)...); });
},
unpack_lazy(
std::is_default_constructible<std::tuple<First, Second, Rest...>>{},
std::move(args_)));
} }
}; };
@ -210,41 +181,29 @@ constexpr auto unbox_continuables(Args&&... args) {
namespace detail { namespace detail {
template <typename Callback, typename Data> template <typename Callback, typename Data>
constexpr auto finalize_impl(identity<void>, Callback&& callback, Data&&) { void finalize_impl(traits::identity<void>, Callback&& callback, Data&&) {
return std::forward<Callback>(callback)(); std::forward<Callback>(callback)();
} }
template <typename... Args, typename Callback, typename Data> template <typename... Args, typename Callback, typename Data>
constexpr auto finalize_impl(identity<std::tuple<Args...>>, Callback&& callback, void finalize_impl(traits::identity<std::tuple<Args...>>, Callback&& callback,
Data&& data) { Data&& data) {
// Call the final callback with the cleaned result // Call the final callback with the cleaned result
return traits::unpack(std::forward<Callback>(callback), traits::unpack(unbox_continuables(std::forward<Data>(data)),
unbox_continuables(std::forward<Data>(data))); std::forward<Callback>(callback));
} }
struct hint_mapper {
template <typename... T>
constexpr auto operator()(T...) -> identity<T...> {
return {};
}
};
} // namespace detail } // namespace detail
template <typename Callback, typename Data> template <typename Callback, typename Data>
constexpr auto finalize_data(Callback&& callback, Data&& data) { void finalize_data(Callback&& callback, Data&& data) {
using result_t = decltype(unbox_continuables(std::forward<Data>(data))); using result_t = decltype(unbox_continuables(std::forward<Data>(data)));
// Guard the final result against void // Guard the final result against void
return detail::finalize_impl(identity<std::decay_t<result_t>>{}, return detail::finalize_impl(traits::identity<std::decay_t<result_t>>{},
std::forward<Callback>(callback), std::forward<Callback>(callback),
std::forward<Data>(data)); std::forward<Data>(data));
} }
} // namespace remapping
template <typename Data> } // namespace composition
constexpr auto hint_of_data() {
return decltype(finalize_data(detail::hint_mapper{}, std::declval<Data>())){};
}
} // namespace aggregated
} // namespace connection
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti
#endif // CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_COMPOSITION_REMAPPING_HPP_INCLUDED

View File

@ -0,0 +1,166 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_COMPOSITION_SEQ_HPP_INCLUDED
#define CONTINUABLE_DETAIL_COMPOSITION_SEQ_HPP_INCLUDED
#include <cassert>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/continuable-traverse-async.hpp>
#include <continuable/detail/base.hpp>
#include <continuable/detail/composition-all.hpp>
#include <continuable/detail/composition-remapping.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/util.hpp>
namespace cti {
namespace detail {
namespace composition {
namespace seq {
/// Connects the left and the right continuable to a sequence
///
/// \note This is implemented in an eager way because we would not gain
/// any profit from chaining sequences lazily.
template <typename Left, typename Right>
auto sequential_connect(Left&& left, Right&& right) {
left.freeze(right.is_frozen());
right.freeze();
return std::forward<Left>(left).then([right = std::forward<Right>(right)](
auto&&... args) mutable {
return std::move(right).then([previous = std::make_tuple(
std::forward<decltype(args)>(args)...)](
auto&&... args) mutable {
return traits::merge(
std::move(previous),
std::make_tuple(std::forward<decltype(args)>(args)...));
});
});
}
template <typename Callback, typename Box>
struct sequential_dispatch_data {
Callback callback;
Box box;
};
template <typename Data>
class sequential_dispatch_visitor
: public std::enable_shared_from_this<sequential_dispatch_visitor<Data>>,
public util::non_movable {
Data data_;
public:
explicit sequential_dispatch_visitor(Data&& data) : data_(std::move(data)) {
}
virtual ~sequential_dispatch_visitor() = default;
/// Returns the pack that should be traversed
auto& head() {
return data_.box;
}
template <typename Box, std::enable_if_t<remapping::is_continuable_box<
std::decay_t<Box>>::value>* = nullptr>
bool operator()(async_traverse_visit_tag, Box&& /*box*/) {
return false;
}
template <typename Box, typename N>
void operator()(async_traverse_detach_tag, Box&& box, N&& next) {
box.fetch()
.then([ box = std::addressof(box),
next = std::forward<N>(next) ](auto&&... args) mutable {
// Assign the result to the target
box->assign(std::forward<decltype(args)>(args)...);
// Continue the asynchronous sequential traversal
next();
})
.fail([me = this->shared_from_this()](types::error_type exception) {
// Abort the traversal when an error occurred
std::move(me->data_.callback)(types::dispatch_error_tag{},
std::move(exception));
})
.done();
}
template <typename T>
void operator()(async_traverse_complete_tag, T&& /*pack*/) {
return remapping::finalize_data(std::move(data_.callback),
std::move(data_.box));
}
};
} // namespace seq
/// Finalizes the seq logic of a given composition
template <>
struct composition_finalizer<composition_strategy_seq_tag> {
template <typename Composition>
static constexpr auto hint() {
// The result is the same as in the all composition
using all_finalizer = composition_finalizer<composition_strategy_all_tag>;
return all_finalizer::hint<Composition>();
}
/// Finalizes the all logic of a given composition
template <typename Composition>
static auto finalize(Composition&& composition) {
return [composition = std::forward<Composition>(composition)] // ...
(auto&& callback) mutable {
auto boxed = remapping::box_continuables(std::move(composition));
// The data from which the visitor is constructed in-place
using data_t =
seq::sequential_dispatch_data<std::decay_t<decltype(callback)>,
std::decay_t<decltype(boxed)>>;
// The visitor type
using visitor_t = seq::sequential_dispatch_visitor<data_t>;
traverse_pack_async(
async_traverse_in_place_tag<visitor_t>{},
data_t{std::forward<decltype(callback)>(callback), std::move(boxed)});
};
}
};
} // namespace composition
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_COMPOSITION_SEQ_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,41 +21,55 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
**/ **/
#ifndef CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED
#define CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED #define CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED
#include <cassert> #include <cassert>
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <continuable/continuable-traverse.hpp> #include <continuable/continuable-traverse.hpp>
#include <continuable/detail/core/base.hpp> #include <continuable/detail/base.hpp>
#include <continuable/detail/core/types.hpp> #include <continuable/detail/traits.hpp>
#include <continuable/detail/utility/traits.hpp> #include <continuable/detail/types.hpp>
#include <continuable/detail/utility/util.hpp> #include <continuable/detail/util.hpp>
namespace cti { namespace cti {
namespace detail { namespace detail {
/// The namespace `connection` offers methods to chain continuations together /// The namespace `composition` offers methods to chain continuations together
/// with `all`, `any` or `seq` logic. /// with `all`, `any` or `seq` logic.
namespace connection { namespace composition {
struct composition_strategy_all_tag {};
struct composition_strategy_any_tag {};
struct composition_strategy_seq_tag {};
template <typename T> template <typename T>
struct is_connection_strategy // ... struct is_composition_strategy // ...
: std::false_type {}; : std::false_type {};
template <>
struct is_composition_strategy<composition_strategy_all_tag> // ...
: std::true_type {};
template <>
struct is_composition_strategy<composition_strategy_any_tag> // ...
: std::true_type {};
template <>
struct is_composition_strategy<composition_strategy_seq_tag> // ...
: std::true_type {};
/// Adds the given continuation tuple to the left connection /// Adds the given continuation tuple to the left composition
template <typename... LeftArgs, typename... RightArgs> template <typename... LeftArgs, typename... RightArgs>
auto chain_connection(std::tuple<LeftArgs...> leftPack, auto chain_composition(std::tuple<LeftArgs...> leftPack,
std::tuple<RightArgs...> rightPack) { std::tuple<RightArgs...> rightPack) {
return std::tuple_cat(std::move(leftPack), std::move(rightPack)); return traits::merge(std::move(leftPack), std::move(rightPack));
} }
/// Normalizes a continuation to a tuple holding an arbitrary count of /// Normalizes a continuation to a tuple holding an arbitrary count of
@ -66,7 +80,7 @@ auto chain_connection(std::tuple<LeftArgs...> leftPack,
/// -> make a tuple containing the continuable as only element /// -> make a tuple containing the continuable as only element
template < template <
typename Strategy, typename Data, typename Annotation, typename Strategy, typename Data, typename Annotation,
std::enable_if_t<!is_connection_strategy<Annotation>::value>* = nullptr> std::enable_if_t<!is_composition_strategy<Annotation>::value>* = nullptr>
auto normalize(Strategy /*strategy*/, auto normalize(Strategy /*strategy*/,
continuable_base<Data, Annotation>&& continuation) { continuable_base<Data, Annotation>&& continuation) {
@ -77,13 +91,13 @@ auto normalize(Strategy /*strategy*/,
/// -> materialize it /// -> materialize it
template < template <
typename Strategy, typename Data, typename Annotation, typename Strategy, typename Data, typename Annotation,
std::enable_if_t<is_connection_strategy<Annotation>::value>* = nullptr> std::enable_if_t<is_composition_strategy<Annotation>::value>* = nullptr>
auto normalize(Strategy /*strategy*/, auto normalize(Strategy /*strategy*/,
continuable_base<Data, Annotation>&& continuation) { continuable_base<Data, Annotation>&& continuation) {
// If the right continuation is a different strategy materialize it // If the right continuation is a different strategy materialize it
// in order to keep the precedence in cases where: `c1 && (c2 || c3)`. // in order to keep the precedence in cases where: `c1 && (c2 || c3)`.
return std::make_tuple(std::move(continuation).finish()); return std::make_tuple(base::attorney::materialize(std::move(continuation)));
} }
/// - The continuable is inside the current strategy state: /// - The continuable is inside the current strategy state:
/// -> return the data of the tuple /// -> return the data of the tuple
@ -92,7 +106,7 @@ auto normalize(Strategy /*strategy*/,
continuable_base<Data, Strategy>&& continuation) { continuable_base<Data, Strategy>&& continuation) {
// If we are in the given strategy we can just use the data of the continuable // If we are in the given strategy we can just use the data of the continuable
return base::attorney::consume(std::move(continuation)); return base::attorney::consume_data(std::move(continuation));
} }
/// Entry function for connecting two continuables with a given strategy. /// Entry function for connecting two continuables with a given strategy.
@ -109,39 +123,53 @@ auto connect(Strategy strategy, continuable_base<LData, LAnnotation>&& left,
// Make the new data which consists of a tuple containing // Make the new data which consists of a tuple containing
// all connected continuables. // all connected continuables.
auto data = chain_connection(normalize(strategy, std::move(left)), auto data = chain_composition(normalize(strategy, std::move(left)),
normalize(strategy, std::move(right))); normalize(strategy, std::move(right)));
// Return a new continuable containing the tuple and holding // Return a new continuable containing the tuple and holding
// the current strategy as annotation. // the current strategy as annotation.
return base::attorney::create_from_raw(std::move(data), strategy, ownership_); return base::attorney::create(std::move(data), strategy, ownership_);
} }
/// All strategies should specialize this class in order to provide: /// All strategies should specialize this class in order to provide:
/// - A finalize static method that creates the callable object which /// - A finalize static method that creates the callable object which
/// is invoked with the callback to call when the connection is finished. /// is invoked with the callback to call when the composition is finished.
/// - A static method hint that returns the new signature hint. /// - A static method hint that returns the new signature hint.
template <typename Strategy> template <typename Strategy>
struct connection_finalizer; struct composition_finalizer;
template <typename Strategy> /// Finalizes the connection logic of a given composition
struct connection_annotation_trait { template <typename Data, typename Strategy>
/// Finalizes the connection logic of a given connection auto finalize_composition(continuable_base<Data, Strategy>&& continuation) {
template <typename Continuable> using finalizer = composition_finalizer<Strategy>;
static auto finish(Continuable&& continuable) {
using finalizer = connection_finalizer<Strategy>;
util::ownership ownership = base::attorney::ownership_of(continuable); util::ownership ownership = base::attorney::ownership_of(continuation);
auto connection = auto composition = base::attorney::consume_data(std::move(continuation));
base::attorney::consume(std::forward<Continuable>(continuable));
// Return a new continuable which // Retrieve the new signature hint
return finalizer::finalize(std::move(connection), std::move(ownership)); constexpr auto const signature =
finalizer::template hint<decltype(composition)>();
// Return a new continuable which
return base::attorney::create(finalizer::finalize(std::move(composition)),
signature, std::move(ownership));
}
/// A base class from which the continuable may inherit in order to
/// provide a materializer method which will finalize an oustanding strategy.
template <typename Continuable, typename = void>
struct materializer {
static constexpr auto&& apply(Continuable&& continuable) {
return std::move(continuable);
} }
};
template <typename Data, typename Strategy>
struct materializer<
continuable_base<Data, Strategy>,
std::enable_if_t<is_composition_strategy<Strategy>::value>> {
template <typename Continuable> static constexpr auto apply(continuable_base<Data, Strategy>&& continuable) {
static bool is_ready(Continuable const& /*continuable*/) noexcept { return finalize_composition(std::move(continuable));
return false;
} }
}; };
@ -171,13 +199,13 @@ public:
// Materialize every continuable // Materialize every continuable
// TODO Actually we would just need to consume the data here // TODO Actually we would just need to consume the data here
return std::forward<Continuable>(continuable).finish(); return base::attorney::materialize(std::forward<Continuable>(continuable));
} }
}; };
template <typename Strategy, typename... Args> template <typename Strategy, typename... Args>
auto apply_connection(Strategy, Args&&... args) { auto apply_composition(Strategy, Args&&... args) {
using finalizer = connection_finalizer<Strategy>; using finalizer = composition_finalizer<Strategy>;
// Freeze every continuable inside the given arguments, // Freeze every continuable inside the given arguments,
// and freeze the ownership if one of the continuables // and freeze the ownership if one of the continuables
@ -185,13 +213,19 @@ auto apply_connection(Strategy, Args&&... args) {
// Additionally test whether every continuable is acquired. // Additionally test whether every continuable is acquired.
// Also materialize every continuable. // Also materialize every continuable.
util::ownership ownership; util::ownership ownership;
auto connection = map_pack(prepare_continuables{ownership}, auto composition = map_pack(prepare_continuables{ownership},
std::make_tuple(std::forward<Args>(args)...)); std::make_tuple(std::forward<Args>(args)...));
return finalizer::finalize(std::move(connection), std::move(ownership)); // Retrieve the new signature hint
constexpr auto const signature =
finalizer::template hint<decltype(composition)>();
// Return a new continuable which
return base::attorney::create(finalizer::finalize(std::move(composition)),
signature, std::move(ownership));
} }
} // namespace connection } // namespace composition
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti
#endif // CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED

View File

@ -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

View File

@ -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

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -33,7 +33,8 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/traits.hpp>
namespace cti { namespace cti {
namespace detail { namespace detail {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,394 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED
#define CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED
#include <cassert>
#include <memory>
#include <type_traits>
#include <utility>
#include <continuable/detail/hints.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/types.hpp>
namespace cti {
namespace detail {
namespace util {
namespace detail {
enum class slot_t { empty, value, error };
template <typename T>
using storage_of_t = //
std::aligned_storage_t<
(sizeof(types::error_type) > sizeof(T) ? sizeof(types::error_type)
: sizeof(T)),
(alignof(types::error_type) > alignof(T) ? alignof(types::error_type)
: alignof(T))>;
template <typename T>
struct expected_base {
storage_of_t<T> storage_;
slot_t slot_;
constexpr expected_base() : slot_(slot_t::empty) {
}
expected_base(expected_base const&) noexcept {
}
expected_base(expected_base&&) noexcept {
}
expected_base& operator=(expected_base const&) {
return *this;
}
expected_base& operator=(expected_base&&) {
return *this;
}
};
template <typename Base>
struct expected_move_base {
constexpr expected_move_base() = default;
expected_move_base(expected_move_base const&) = default;
explicit expected_move_base(expected_move_base&& right) {
Base& me = *static_cast<Base*>(this);
Base& other = *static_cast<Base*>(&right);
assert(!other.is_empty());
#ifndef _NDEBUG
me.set(slot_t::empty);
#endif
other.visit([&](auto&& value) {
// ...
me.init(std::move(value));
});
me.set(other.get());
other.destroy();
}
expected_move_base& operator=(expected_move_base const&) = default;
expected_move_base& operator=(expected_move_base&& right) {
Base& me = *static_cast<Base*>(this);
Base& other = *static_cast<Base*>(&right);
assert(!other.is_empty());
me.weak_destroy();
other.visit([&](auto&& value) {
// ...
me.init(std::move(value));
});
me.set(other.get());
other.destroy();
return *this;
}
};
template <typename Base, bool IsCopyable /*= true*/>
struct expected_copy_base : expected_move_base<Base> {
constexpr expected_copy_base() = default;
expected_copy_base(expected_copy_base&&) = default;
explicit expected_copy_base(expected_copy_base const& right)
: expected_move_base<Base>()
// TODO noexcept(Base::is_nothrow_move_constructible)
{
Base& me = *static_cast<Base*>(this);
Base const& other = *static_cast<Base const*>(&right);
assert(!other.is_empty());
#ifndef _NDEBUG
me.set(slot_t::empty);
#endif
other.visit([&](auto&& value) {
// ...
me.init(std::move(value));
});
me.set(other.get());
}
expected_copy_base& operator=(expected_copy_base&&) = default;
expected_copy_base& operator=(expected_copy_base const& right)
// TODO noexcept(Base::is_nothrow_move_constructible)
{
Base& me = *static_cast<Base*>(this);
Base const& other = *static_cast<Base const*>(&right);
assert(!other.is_empty());
me.weak_destroy();
other.visit([&](auto&& value) {
// ...
me.init(std::move(value));
});
me.set(other.get());
return *this;
}
};
template <typename Base /*, bool IsCopyable = false*/>
struct expected_copy_base<Base, false> : expected_move_base<Base> {
constexpr expected_copy_base() = default;
expected_copy_base(expected_copy_base const&) = delete;
explicit expected_copy_base(expected_copy_base&& right) = default;
expected_copy_base& operator=(expected_copy_base const&) = delete;
expected_copy_base& operator=(expected_copy_base&& right) = default;
};
} // namespace detail
/// A class similar to the one in the expected proposal,
/// however it is capable of carrying an exception_ptr if
/// exceptions are used.
template <typename T>
class expected
: detail::expected_copy_base<
expected<T>, std::is_copy_constructible<types::error_type>::value &&
std::is_copy_constructible<T>::value>,
detail::expected_base<T> {
template <typename>
friend class expected;
template <typename>
friend struct detail::expected_move_base;
template <typename, bool>
friend struct detail::expected_copy_base;
template <typename V>
expected(V&& value, detail::slot_t const slot) {
using type = std::decay_t<decltype(value)>;
new (&this->storage_) type(std::forward<V>(value));
set(slot);
}
public:
constexpr expected() = default;
expected(expected const&) = default;
expected(expected&&) = default;
expected& operator=(expected const&) = default;
expected& operator=(expected&&) = default;
~expected() noexcept(
std::is_nothrow_destructible<T>::value&&
std::is_nothrow_destructible<types::error_type>::value) {
weak_destroy();
}
explicit expected(T value) //
: expected(std::move(value), detail::slot_t::value) {
}
explicit expected(types::error_type error) //
: expected(std::move(error), detail::slot_t::error) {
}
expected& operator=(T value) {
set_value(std::move(value));
return *this;
}
expected& operator=(types::error_type error) {
set_exception(std::move(error));
return *this;
}
bool is_value() const noexcept {
assert(!is_empty());
return is(detail::slot_t::value);
}
bool is_exception() const noexcept {
assert(!is_empty());
return is(detail::slot_t::error);
}
explicit constexpr operator bool() const noexcept {
return is_value();
}
void set_value(T value) {
weak_destroy();
init(std::move(value));
set(detail::slot_t::value);
}
void set_exception(types::error_type error) {
weak_destroy();
init(std::move(error));
set(detail::slot_t::error);
}
T& get_value() noexcept {
assert(is_value());
return cast<T>();
}
T const& get_value() const noexcept {
assert(is_value());
return cast<T>();
}
types::error_type& get_exception() noexcept {
assert(is_exception());
return cast<types::error_type>();
}
types::error_type const& get_exception() const noexcept {
assert(is_exception());
return cast<types::error_type>();
}
T& operator*() noexcept {
return get_value();
}
T const& operator*() const noexcept {
return get_value();
}
private:
template <typename V>
void visit(V&& visitor) {
switch (this->slot_) {
case detail::slot_t::value:
return std::forward<V>(visitor)(cast<T>());
case detail::slot_t::error:
return std::forward<V>(visitor)(cast<types::error_type>());
default:
// We don't visit when there is no value
break;
}
}
template <typename V>
void visit(V&& visitor) const {
switch (this->slot_) {
case detail::slot_t::value:
return std::forward<V>(visitor)(cast<T>());
case detail::slot_t::error:
return std::forward<V>(visitor)(cast<types::error_type>());
default:
// We don't visit when there is no value
break;
}
}
bool is_empty() const noexcept {
return is(detail::slot_t::empty);
}
template <typename V>
V& cast() noexcept {
assert(!is_empty());
return *reinterpret_cast<V*>(&this->storage_);
}
template <typename V>
V const& cast() const noexcept {
assert(!is_empty());
return *reinterpret_cast<V const*>(&this->storage_);
}
template <typename V>
void init(V&& value) {
assert(is_empty());
using type = std::decay_t<decltype(value)>;
new (&this->storage_) type(std::forward<V>(value));
}
void destroy() {
weak_destroy();
#ifdef NDEBUG
set(detail::slot_t::empty);
#endif
}
void weak_destroy() {
visit([&](auto&& value) {
using type = std::decay_t<decltype(value)>;
value.~type();
});
#ifndef NDEBUG
set(detail::slot_t::empty);
#endif
}
detail::slot_t get() const noexcept {
return this->slot_;
}
bool is(detail::slot_t const slot) const noexcept {
return get() == slot;
}
void set(detail::slot_t const slot) {
this->slot_ = slot;
}
};
namespace detail {
struct void_guard_tag {};
template <typename T>
struct expected_result_trait;
template <>
struct expected_result_trait<traits::identity<>> {
using expected_type = expected<void_guard_tag>;
static constexpr void_guard_tag wrap() noexcept {
return {};
}
static void unwrap(expected_type&& e) {
assert(e.is_value());
(void)e;
}
};
template <typename T>
struct expected_result_trait<traits::identity<T>> {
using expected_type = expected<T>;
static auto wrap(T arg) {
return std::move(arg);
}
static auto unwrap(expected_type&& e) {
assert(e.is_value());
return std::move(e.get_value());
}
};
template <typename First, typename Second, typename... Rest>
struct expected_result_trait<traits::identity<First, Second, Rest...>> {
using expected_type = expected<std::tuple<First, Second, Rest...>>;
static auto wrap(First first, Second second, Rest... rest) {
return std::make_tuple(std::move(first), std::move(second),
std::move(rest)...);
}
static auto unwrap(expected_type&& e) {
assert(e.is_value());
return std::move(e.get_value());
}
};
} // namespace detail
template <typename Continuable>
using expected_result_trait_t = detail::expected_result_trait<decltype(
hints::hint_of(traits::identify<Continuable>{}))>;
} // namespace util
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_EXPECTED_HPP_INCLUDED

View File

@ -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

View File

@ -1,13 +1,12 @@
/* /*
/~` _ _ _|_. _ _ |_ | _ /~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +20,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -33,19 +32,19 @@
// Defines CONTINUABLE_WITH_NO_EXCEPTIONS when exception support is disabled // Defines CONTINUABLE_WITH_NO_EXCEPTIONS when exception support is disabled
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS #ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
# if defined(_MSC_VER) #if defined(_MSC_VER)
# if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0) #if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0)
# define CONTINUABLE_WITH_NO_EXCEPTIONS #define CONTINUABLE_WITH_NO_EXCEPTIONS
# endif #endif
# elif defined(__clang__) #elif defined(__clang__)
# if !(__EXCEPTIONS && __has_feature(cxx_exceptions)) #if !(__EXCEPTIONS && __has_feature(cxx_exceptions))
# define CONTINUABLE_WITH_NO_EXCEPTIONS #define CONTINUABLE_WITH_NO_EXCEPTIONS
# endif #endif
# elif defined(__GNUC__) #elif defined(__GNUC__)
# if !__EXCEPTIONS #if !__EXCEPTIONS
# define CONTINUABLE_WITH_NO_EXCEPTIONS #define CONTINUABLE_WITH_NO_EXCEPTIONS
# endif #endif
# endif #endif
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS #endif // CONTINUABLE_WITH_NO_EXCEPTIONS
// clang-format off // clang-format off
@ -55,11 +54,8 @@
#define CONTINUABLE_HAS_CXX17_CONSTEXPR_IF #define CONTINUABLE_HAS_CXX17_CONSTEXPR_IF
#define CONTINUABLE_HAS_CXX17_DISJUNCTION #define CONTINUABLE_HAS_CXX17_DISJUNCTION
#define CONTINUABLE_HAS_CXX17_CONJUNCTION #define CONTINUABLE_HAS_CXX17_CONJUNCTION
#define CONTINUABLE_HAS_CXX17_VOID_T
#else #else
// Generic feature detection based on __has_feature // 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(__has_feature)
#if !defined(CONTINUABLE_HAS_CXX17_CONSTEXPR_IF) && \ #if !defined(CONTINUABLE_HAS_CXX17_CONSTEXPR_IF) && \
__has_feature(cxx_if_constexpr) __has_feature(cxx_if_constexpr)
@ -78,49 +74,12 @@
(__cpp_lib_experimental_logical_traits >= 201511) (__cpp_lib_experimental_logical_traits >= 201511)
#define CONTINUABLE_HAS_CXX17_CONJUNCTION #define CONTINUABLE_HAS_CXX17_CONJUNCTION
#endif #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 #endif
// Automatically detects support for coroutines. /// Usually this is enabled by the CMake project
// Parts of this detection mechanism were adapted from boost::asio, #if !defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE) && \
// with support added for experimental coroutines. defined(__cpp_coroutines) && (__cpp_coroutines >= 201707)
#if !defined(CONTINUABLE_HAS_DISABLED_COROUTINE) \ #define CONTINUABLE_HAS_EXPERIMENTAL_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 #endif
/// Define CONTINUABLE_HAS_EXCEPTIONS when exceptions are used /// Define CONTINUABLE_HAS_EXCEPTIONS when exceptions are used
@ -130,18 +89,6 @@
#else #else
#undef CONTINUABLE_HAS_EXCEPTIONS #undef CONTINUABLE_HAS_EXCEPTIONS
#endif #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 // clang-format on
#endif // CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,33 +21,51 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
**/ **/
#ifndef CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED
#define CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED #define CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED
#include <type_traits> #include <type_traits>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/utility/traits.hpp> #include <continuable/detail/traits.hpp>
#include <continuable/detail/types.hpp>
namespace cti { namespace cti {
namespace detail { namespace detail {
namespace hints { namespace hints {
/// Represents a present signature hint
template <typename... Args>
using signature_hint_tag = traits::identity<Args...>;
/// Returns the signature hint of the given continuable
template <typename Data, typename... Args>
constexpr auto
hint_of(traits::identity<continuable_base<Data, signature_hint_tag<Args...>>>) {
return hints::signature_hint_tag<Args...>{};
}
/// Extracts the signature we pass to the internal continuable /// Extracts the signature we pass to the internal continuable
/// from an argument pack as specified by make_continuable. /// from an argument pack as specified by make_continuable.
/// ///
/// This is the overload taking an arbitrary amount of args /// This is the overload taking an arbitrary amount of args
template <typename... HintArgs> template <typename... HintArgs>
struct from_args : std::common_type<signature_arg_t<HintArgs...>> {}; constexpr auto extract(traits::identity<HintArgs...> hint) {
template <> return hint;
struct from_args<void> : std::common_type<signature_arg_t<>> {}; }
/// \copybrief extract
///
/// This is the overload taking a void arg.
constexpr auto extract(traits::identity<void> /*hint*/) {
return traits::identity<>{};
}
} // namespace hints } // namespace hints
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti
#endif // CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,95 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
#define CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
#include <type_traits>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
#include <exception>
#endif // CONTINUABLE_HAS_EXCEPTIONS
#include <continuable/continuable-base.hpp>
#include <continuable/detail/traits.hpp>
#include <continuable/detail/util.hpp>
namespace cti {
namespace detail {
namespace convert {
/// A helper class for promisifying asio style callback taking functions
/// into a continuable.
template <typename P>
struct promisify_asio {
P promise;
template <typename E, typename... T>
void operator()(E&& error, T&&... result) {
if (error) {
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
promise.set_exception(std::make_exception_ptr(std::forward<E>(error)));
#else
promise.set_exception(
std::error_condition(error.value(), error.category()));
#endif // CONTINUABLE_HAS_EXCEPTIONS
} else {
promise.set_value(std::forward<T>(result)...);
}
}
};
template <typename... Result>
struct promisify_helper {
template <template <class T> class Evaluator, typename Callable,
typename... Args>
static auto from(Callable&& callable, Args&&... args) {
return make_continuable<Result...>([args = std::make_tuple(
std::forward<Callable>(callable),
std::forward<Args>(args)...)](
auto&& promise) mutable {
traits::unpack(
std::move(args), [promise = std::forward<decltype(promise)>(promise)](
auto&&... args) mutable {
Evaluator<std::decay_t<decltype(promise)>> evaluator{
std::move(promise)};
util::invoke(std::forward<decltype(args)>(args)...,
std::move(evaluator));
});
});
}
};
} // namespace convert
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v4.2.0 v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal of this software and associated documentation files(the "Software"), to deal
@ -21,7 +21,7 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
@ -33,9 +33,9 @@
#include <iterator> #include <iterator>
#include <type_traits> #include <type_traits>
#include <utility>
#include <vector> #include <vector>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/traits.hpp>
namespace cti { namespace cti {
namespace detail { namespace detail {
@ -52,11 +52,11 @@ struct is_iterator<T,
template <typename Iterator> template <typename Iterator>
auto persist_range(Iterator begin, Iterator end) { auto persist_range(Iterator begin, Iterator end) {
std::vector<typename std::iterator_traits<Iterator>::value_type> storage; 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), storage.insert(storage.end(), std::make_move_iterator(begin),
// std::make_move_iterator(end)); std::make_move_iterator(end));
std::move(begin, end, std::back_inserter(storage));
return storage; return storage; // RVO
} }
} // namespace range } // namespace range
} // namespace detail } // namespace detail

Some files were not shown because too many files have changed in this diff Show More