Compare commits

..

263 Commits

Author SHA1 Message Date
Rogiel Sulzbach
23a724cf5c Ensure that continuables that are resolved immediately are always symmetrically transferable 2023-09-12 19:13:12 +02:00
Denis Blank
c7f5b1cbaf CI: Use recursive checkouts 2023-09-10 12:52:48 +02:00
Denis Blank
d1f9306eee Add a github workflow file for CI
* Remove the travis CI
2023-09-10 12:47:03 +02:00
Denis Blank
0641a29f42 Fix a build issue on MSVC 2022 related to transforms wait 2023-09-10 12:12:06 +02:00
Piers Haken
f7f304e971 fix clang coroutines detection 2022-12-21 17:27:46 +01:00
Robert Konklewski
63e3ed4edc Fix incorrect initialization of unsafe_locker
List initialization of unsafe_locker class resulted in a compiler
error, because 1) it had no constructor matching the arguments, and
2) it had user-declared constructors, so aggregate initialization
was not allowed.

This change fixes the issue by adding an appropriate constructor.
2022-09-05 11:41:35 +02:00
Denis Blank
b51be39e71 Fix Stopping a continuable in a failure handler makes wait() hang forever
* Closes #46
* Closes #48
2022-06-02 01:01:26 +02:00
Denis Blank
ed8310e345 Year and version update 2022-01-20 08:41:32 +01:00
Denis Blank
01f9dbd1f4 Add support for stable coroutines (/await:strict and -fcoroutines)
* The definition CONTINUABLE_HAS_COROUTINE now indicates support for coroutines,
  while CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE is added if the experimental version is used.
2022-01-20 08:41:32 +01:00
sTabishAzam
2fcc2bf281 Update connection-seq.hpp
This was causing error on usage of when seq :: "result is not a type name static or enumerator"
2021-11-14 21:35:58 +01:00
Denis Blank
ace3317f65 Minor improvement 2021-10-22 08:03:34 +02:00
Denis Blank
e6f817ca7b Add the void(...) asio handler overload to the use_continuable initiation token
* Makes it possible to use the initiation token with dispatch/post
* Ref #44
2021-10-22 07:57:20 +02:00
Denis Blank
e2b5fc36fe Add continuable::via to change the invocation chain to the given executor 2021-10-20 07:41:01 +02:00
Denis Blank
6bffb44d2b Fix a race condition in cti::transforms::wait()
* Thanks to p4654545 for reporting this issue and providing a reproducible example
* Closes #38
2020-11-03 17:28:24 +01:00
Denis Blank
48c6abf5f2 Ensure that the example given in #34 compiles in the CI
* Ref #34
2020-08-10 18:31:16 +02:00
Denis Blank
f57c5898eb Improve the documentation of the fail handler
* State that the exception of type exception_t can be passed
  with an initial state.
* Ref #32
2020-04-10 15:25:37 +02:00
Denis Blank
8187c16ede Improve the use_continuable_t asio completion tokens such that special mappings are aptable
* Also allow to ignore certain error types through use_continuable_ignoring
* Ref #32
2020-04-09 18:43:00 +02:00
Denis Blank
d80f5ef3ec Make the wait transform throw a wait_transform_canceled_exception on cancellation 2020-04-09 16:03:47 +02:00
Denis Blank
5f84de0e86 Handle the cancellation of continuables used in co_await expression correctly
* Throws a await_canceled_exception now which automatically gets
  converted to a default exception type again if it becomes unhandled in the handler.
* Ref #32
2020-04-09 15:34:11 +02:00
Denis Blank
8e63a45840 Increase the version to 4.1.0 2020-04-09 15:13:36 +02:00
Denis Blank
61826817c7 Add the 4.0.0 amalgamation to the documentation
* Closes #31
2020-04-07 22:31:32 +02:00
Denis Blank
735697026b Update the 4.0.0 changelog 2020-04-06 17:53:15 +02:00
Denis Blank
9593ba120c Add a quick tour to the readme 2020-04-06 17:36:42 +02:00
Denis Blank
33bfc490ef Rename cancel() to stop() and make cancel() return a cancellation result instead 2020-04-05 00:49:57 +02:00
Denis Blank
77faf3120f 4.0.0 estimated changelog 2020-04-05 00:15:35 +02:00
Denis Blank
26cd377831 4.0 version increase 2020-04-04 23:35:02 +02:00
Denis Blank
923843cd44 Update function2 2020-04-04 23:32:36 +02:00
Denis Blank
8c1a79d17b Update the copyright year 2020-04-04 23:31:20 +02:00
Denis Blank
5fbc9c4a59 Some doc fixes 2020-04-04 23:27:32 +02:00
Denis Blank
adc75655f4 Adapt the required boost versions for the async initiate
* Also respect asio and boost major versions
2020-04-04 23:08:04 +02:00
Denis Blank
1e39bd85dd Replace the result<...> implementation by a lighter one
* result<...> only moveable now, the conditional copy capabilities were removed
* If you require the optional copy capability use a std::variant implementation of your choice
* Closes #13
2020-04-04 22:26:09 +02:00
Denis Blank
5f8b2aa317 Fix example code in the tutorial documentation
* Closes #30
2020-04-04 21:57:38 +02:00
Denis Blank
37359dec0b Fix infinite recursion on Clang if the compiler attempts to continue on best effort
* The library triggers a static_assert correctly if a callable
  can't be called with a subset of its arguments but it seems like this is
  not enough to make the compiler stop causing the generation
  of a 0 - 1 -> std::size_t::max length index sequence which obviously
  causes infinite work.
* Reproducible with:
  ```
  cti::make_continuable<std::string>([](cti::promise<std::string> promise) {
    promise.set_value("hello");
  }).then([](int num) {
    //
  });
  ```
* Closes #21
2020-04-04 19:41:57 +02:00
Denis Blank
2c76e6c367 Split the async tests into their own test case 2020-04-04 17:19:44 +02:00
Denis Blank
0fb66a7eec Fix the ci build when exceptions are disabled 2020-04-04 03:02:05 +02:00
Denis Blank
c69385be5f Implement the wait transforms tests 2020-04-04 01:51:02 +02:00
Denis Blank
ab9669fa2a Implement wait, wait_for and wait_until transforms properly
* wait is implemented by a atomic and default condition variable.
* wait_for and wait_until are expensive since we can't assume
  anything about the environment thus we have to allocate
  a persistent frame.
2020-04-04 01:11:27 +02:00
Denis Blank
df4d6ed971 Start to revise transforms 2020-04-04 00:04:51 +02:00
Denis Blank
957d3fa375 Align the cancel behaviour of result to the one used in the unhandled exception handler
* Introduce cancellation_result to represent a cancelled async task
* Add cancellation unit tests
* This doesn't allow cancellation of continuables, it is meant
  for treating the special state action canceled on the receiver side.
  Cancellation of a chain is still up to the user.
2020-04-03 22:34:08 +02:00
Denis Blank
c8c4325b5b Fix the unit tests by providing an ASSERT_ASYNC_CANCELLATION 2020-04-03 19:57:46 +02:00
Denis Blank
f1e9255eb9 Fix the test header includes 2020-04-03 19:47:42 +02:00
Denis Blank
85c0d76c7c Make make_cancelling_continuable throw an default constructed exception_t
* This behaviour now aligns on how cancellation is handled in the
  unhandled exception handler
2020-04-03 19:44:49 +02:00
Denis Blank
ca26bbbc87 Remap asio::error::basic_errors::operation_aborted to a default constructed exception_t
* Add a successful wait on the asio async timer
* Closes #27
* Closes #28
2020-04-03 19:38:13 +02:00
Denis Blank
564d134c75 Improve the clang-tidy and clang-format config 2020-04-03 18:13:41 +02:00
Denis Blank
f7e00bcc8d Store std::exception::what() automatically before calling TRAP on unhandled exceptions
* Makes it easier to inspect the message in debuggers
2020-04-03 17:44:40 +02:00
Denis Blank
89031d932c Rename cti::asio_token to cti::use_continuable
* support -> external
2020-04-03 17:25:52 +02:00
Christos Stratopoulos
1a1c7b68c6 Base implementation of an ASIO completion token integration
* Bump the asio version to 1.41
* Move headers that require an external dependency to include/continuable/support
* Ref #28
* Ref #27
2020-04-03 14:44:34 +02:00
Denis Blank
0b1b284e3a Add continuable_base::is_ready and continuable_base::unpack.
* Can be used to specialize the asynchronous control flow
  on immediate available asynchronous results mostly returned by:
    - make_ready_continuable
    - make_exceptional_continuable
* Usable to avoid longer unnecessary synchronous callback nestings.
* cti::query_arg_t was renamed into cti::unpack_arg_t.
* The continuation overload nowreturns result<Args...> rather
  than std::tuple<Args...>.
* Add is_ready optimizations to make_exceptional_continuable.
2019-12-26 10:13:20 +01:00
Denis Blank
117a716de1 Test the class which represents exception_t against true before running unhandled exception handlers.
* This allows cheap cancellation of the control flow by passing
  a default constructed `exception_t` object to `set_exception`.
* Since possible representatives like
    - `std::exception_ptr`
    - `std::error_code`
    - `std::error_condition`
  can be default constructed we have to test them anyway before possibly
  rethrowing them.
2019-11-16 17:13:23 +01:00
Denis Blank
cacb84371a Add MSVC 16 to the CI 2019-09-28 17:37:01 +02:00
Denis Blank
92d8bbad36 Fix the MSVC 16 build 2019-09-28 17:29:30 +02:00
Denis Blank
0afdbec2cc Upgrade the dependencies 2019-09-28 16:25:09 +02:00
Denis Blank
ffb3db7089 Fix the asio example
* Attempt to fix the GCC warning in a different way
2019-09-02 01:03:28 +02:00
Denis Blank
7aff2c0d9b Show the submodule revisions in Travis CI 2019-09-02 00:52:30 +02:00
Denis Blank
959f059a25 Fix a build error 2019-09-02 00:45:53 +02:00
Denis Blank
91d51e6543 Attempt to fix the clang/GCC build 2019-09-02 00:37:37 +02:00
Denis Blank
5e8bbe7c72 Use TYPED_TEST_SUITE instead of deprecated GTest TYPED_TEST_CASE 2019-09-02 00:24:07 +02:00
Denis Blank
dce0fbcffe Clean the playground since the test was moved into the unit tests 2019-09-02 00:17:17 +02:00
Denis Blank
a2fdfdfceb Use a promise<> for work rather than a dedicated work_base 2019-09-02 00:01:26 +02:00
Denis Blank
389002e918 Improve the work wrapper 2019-09-01 23:47:59 +02:00
Denis Blank
92368bccb7 Update submodules 2019-08-31 03:31:25 +02:00
Denis Blank
e09d26f3c6 First work on passing an exception to the executable work
* Make work r-value callable only by default
2019-08-31 03:30:03 +02:00
Denis Blank
422b6138cd Lift control flow compiler intrinsics into the used scope
* Also make UNREACHABLE trap in debug mode
2019-08-27 04:01:37 +02:00
Bogdan Vaneev
42af23fa03 Remove ccache
Signed-off-by: Bogdan Vaneev <warchantua@gmail.com>
2019-05-24 17:39:48 +02:00
Bogdan Vaneev
bcf55e88dd Add ccache
Signed-off-by: Bogdan Vaneev <warchantua@gmail.com>
2019-05-24 17:39:48 +02:00
Bogdan Vaneev
88fbcdbc17 Add ccache, add travis cache
Signed-off-by: Bogdan Vaneev <warchantua@gmail.com>
2019-05-24 17:39:48 +02:00
Bogdan Vaneev
c9d0e871cd Bump asio to 1.13.0
Signed-off-by: Bogdan Vaneev <warchantua@gmail.com>
2019-05-24 02:33:31 +02:00
Bogdan Vaneev
4cc7523380 Add regex to match AppleClang compiler
Signed-off-by: Bogdan Vaneev <warchantua@gmail.com>
2019-05-24 02:33:14 +02:00
Denis Blank
d842c14268 Re-enable the converting constructor of continuable_base
* Probably a forgotten leftover from debugging
  mainly responsible for more efficient conversions only.
2019-03-19 17:06:58 +01:00
Denis Blank
e9be3eb8c3 Improved the coroutine support auto detection on MSVC
* Based on _RESUMABLE_FUNCTIONS_SUPPORTED inspired from asio.
2019-03-15 17:11:38 +01:00
Denis Blank
67d77808dc Fix the GCC build after bc4d69735 2019-03-12 14:44:15 +01:00
Denis Blank
03ae1b5c45 Update function2 to Naios/function2@e3695b4 2019-03-12 14:43:50 +01:00
Denis Blank
bc4d69735c Improve cti::split and make it workable with plain callbacks 2019-03-11 16:39:35 +01:00
Denis Blank
daa2fdd686 Implement cti::split which makes it possible to resolve multiple
asynchronous control flows from a single promise.
2019-03-08 22:06:44 +01:00
Denis Blank
41f3429c85 Remove some using expressions
* Those caused issues in namespaces where symbols were preloaded
* Formatting fixes
2019-03-08 20:18:26 +01:00
Denis Blank
6b4f6de10f Use the work erased type directly in release builds
* So this behaviour aligns to the one used in continuable_base and promise_base.
2019-03-08 18:27:57 +01:00
Denis Blank
fdd9a061c4 Make promise_base default constructible
* This makes it possible to use promise_base for optional
  promises directly rather than wrapping it as optional<promise_base<...>>.
* Invalidate the promise_base after its first usage.
* Expose an `operator bool()` to make the validility accessible.
* Remove the no longer needed private promise_no_init_arg_t tag.
2019-03-08 18:13:40 +01:00
Denis Blank
2bc448b905 Link the MeetingC++ talk in the readme 2019-02-26 16:01:20 +01:00
Denis Blank
e23e363b03 Add a conanfile
* Closes #12
2019-01-29 20:28:09 +01:00
Denis Blank
d72e1bfb86 Implement async_on for asynchronous execution on a specific executor
* Makes it possible to specify an executor in addition to
  the arguments passed to async.
* The reason why async should not support this directly is
  that it should be closely modelled to std::async.
2019-01-26 03:46:39 +01:00
Denis Blank
fa589a1e95 Document range_loop 2019-01-14 23:14:29 +01:00
Denis Blank
20cd0191fc For loops provide loop_result loop_break and loop_continue for better readability 2019-01-14 23:13:23 +01:00
Denis Blank
76ecc3d26d Attempt to fix the build 2019-01-14 21:10:25 +01:00
Denis Blank
7491022d0f Implement make_plain to make it possible to not handle special objects in handlers 2019-01-14 21:02:22 +01:00
Denis Blank
4c807aec75 Update a deprecation message 2019-01-14 18:13:22 +01:00
Denis Blank
60b75a6134 Finish the async implementation 2019-01-14 18:07:37 +01:00
Denis Blank
b86fe7a255 Additional work on async 2019-01-13 19:10:55 +01:00
Denis Blank
a4da3e84ef Fix the range_loop build 2019-01-13 18:52:13 +01:00
Denis Blank
ac175b4e57 Start to implement the unit tests for loop and add range_loop 2019-01-13 18:19:49 +01:00
Denis Blank
135ebfccf3 Start to implement loop and async 2019-01-13 17:05:28 +01:00
Denis Blank
5c1cd87739 Doc improvement 2019-01-06 14:05:54 +01:00
Denis Blank
20e8c7d3e3 Reflow the license text
* For some reason some spaces were removed,
  the license text and content stays the same.
2019-01-04 13:39:46 +01:00
Denis Blank
a3a9695174 Happy new year! 2019-01-04 13:35:44 +01:00
Denis Blank
c066940d8d Use new types instead of aliases for type erasures
* Makes compiler output much more readable
  This is configurateable through CONTINUABLE_WITH_IMMEDIATE_TYPES,
  and automatically enabled for debug builds but disabled
  for release builds.
* Remove the old continuable-trait.hpp header
* Make the signature of continuable not dependent anymore
  on any size of the arguments which fixes the compilation
  with forward declared types.
  Thanks Rogiel for the correspoding bug report.
  Closes #11
2019-01-04 13:12:48 +01:00
Denis Blank
2d5aa36b67 Update function2 to Naios/function2@d2acdb6 2019-01-04 12:59:25 +01:00
Denis Blank
3bd4dd40de Fixate the args of the final callback
* Also allow to customize it through the
  CONTINUABLE_WITH_CUSTOM_FINAL_CALLBACK define.
  This can be used to implement custom unhandled
  exception handlers and more.
2018-12-29 06:25:49 +01:00
Denis Blank
a099c504e1 Workaround for a MSVC required symbol link bug 2018-12-26 06:20:48 +01:00
Denis Blank
7a10363dce Implement continuable_base<...>::as
* Can convert continuables to other compatible signatures
2018-12-26 05:38:10 +01:00
Denis Blank
30f0dca27f Fix a unit test introduced in 4127c02c3f6d 2018-12-25 11:15:06 +01:00
Denis Blank
d4cb7dd7b3 Only disable specific top level project settings
* When being not being inside in a top level project
2018-12-25 09:35:20 +01:00
Denis Blank
f469b7058a Implement make_cancelling_continuable()
* Can be used to cancel the chain when being inside a handler
2018-12-25 09:30:23 +01:00
Denis Blank
4127c02c3f Add an overload to make_result which accepts a exception_arg_t and exception_t
* Can be used to forward the result correctly from next handlers
2018-12-25 09:05:51 +01:00
Denis Blank
d052a02595 Remove a bad GCC diagnostic pop when using result<...> and GCC 2018-12-25 08:42:42 +01:00
Denis Blank
7dbc95c4b3 Implement zero cost ready continuables for await expressions 2018-12-16 04:25:56 +01:00
Denis Blank
fcf9a76029 Set the minimum required CMake version to 3.11
* Make travis-ci install a recent CMake version
* Update function2
2018-12-12 17:46:16 +01:00
Denis Blank
49e3a659ad Rework the find_package() support
* It is now possible to include the repository correctly
  through add_subdirectory
* find_package(continuable) was improved to work when
  only the path of the repository was added to the module path.
2018-12-11 04:30:58 +01:00
Denis Blank
65916e29a8 Update function2 to Naios/function2@2b3bc42 2018-12-11 04:29:24 +01:00
Denis Blank
121265df71 Cleanup in result<> 2018-12-11 01:21:26 +01:00
Denis Blank
4c41995316 Document recover, rethrow and cancel
* Closes #9
2018-12-11 01:21:09 +01:00
Denis Blank
dd09c3d684 Bump the version to 4.0.0 2018-12-10 18:08:29 +01:00
Denis Blank
ff881091fc Update function2 to Naios/function2@1dd435d 2018-12-10 06:27:15 +01:00
Denis Blank
30d49141a8 Resolve ready continuables directly in sequential connections 2018-12-10 06:22:53 +01:00
Denis Blank
c7ef5c6f64 Adhust the SFO buffer size so it can contain a pointer at zero cost 2018-12-10 06:07:43 +01:00
Denis Blank
b2726982ac Fix the build when coroutines are enabled 2018-12-10 06:03:46 +01:00
Denis Blank
57bb43138b Fix a recursive template instantiation issue on clang and gcc 2018-12-10 05:08:39 +01:00
Denis Blank
1320c8eb63 Add a unit test which tests for ready continuables explicitly 2018-12-09 17:01:56 +01:00
Denis Blank
1bdee5b371 Remove an unused using 2018-12-09 16:49:03 +01:00
Denis Blank
bdada99096 Fix the immediate return type of chained continuables
* Statically resolve ready continuables
2018-12-09 16:48:08 +01:00
Denis Blank
7273891a4c Specialize the continuation chainer in case ready continuables are chained 2018-12-09 04:53:28 +01:00
Denis Blank
9ceee76647 Fix an issue with the is_ready overload 2018-12-08 17:52:22 +01:00
Denis Blank
4b1f6281fc Initial work on wrapping all continuations into the triple path schema 2018-12-08 05:53:44 +01:00
Denis Blank
cb6ce5b43b get_arg_t -> query_arg_t 2018-12-08 04:50:38 +01:00
Denis Blank
24158583b7 Provide the get_arg_t and is_ready_arg_t also from continuables created through make_* 2018-12-08 04:49:03 +01:00
Denis Blank
6947091a27 Preserve the ownership across chainings
* Fixes the unit tests
2018-12-08 04:31:32 +01:00
Denis Blank
bcafd1b333 Only consume the data when chaining continuations 2018-12-08 04:19:53 +01:00
Denis Blank
d416698758 Make the ready query stuff part of the private API 2018-12-08 04:11:40 +01:00
Denis Blank
b5f353222c Make it possible to add optional methods to continuable_base depending on the annotation 2018-12-08 03:28:33 +01:00
Denis Blank
f5dd02ef8b Some documentation fixes 2018-12-08 02:53:33 +01:00
Denis Blank
969445c8a0 Fix the build 2018-12-08 02:40:10 +01:00
Denis Blank
577b71b8ab Remove the template args from the void make_ready_continuable 2018-12-08 01:28:27 +01:00
Denis Blank
b293d9a342 Adapt the small functor capacity to hold a ready continuable at zero cost 2018-12-08 01:26:14 +01:00
Denis Blank
da8ec15c6f Some minor improvements 2018-12-07 04:46:18 +01:00
Denis Blank
62ca39e59c Some renaming 2018-12-07 04:40:03 +01:00
Denis Blank
f17cc4073c Add the is_ready proto 2018-11-30 03:05:18 +01:00
Denis Blank
7a5bde328c Unify the exception and result invoker 2018-11-30 03:04:55 +01:00
Denis Blank
7352cbf8a9 Make it possible to continue with a continuable from failure handlers 2018-11-30 02:10:05 +01:00
Denis Blank
4d58e3bded Attempt to fix the GCC build 2018-11-28 17:55:01 +01:00
Denis Blank
1edd1e633d Fix the unit tests 2018-11-28 17:00:35 +01:00
Denis Blank
70c716bb28 Make the failure handler partial applyable
* Make result C++17 destructible
* Add unit tests
2018-11-28 01:29:36 +01:00
Denis Blank
bb7112eec2 Fix the build 2018-11-27 16:38:24 +01:00
Denis Blank
f1f9d61952 Attempt to remove the plain handler 2018-11-27 02:39:41 +01:00
Denis Blank
b77e926c41 Fix the unit tests 2018-11-26 04:37:48 +01:00
Denis Blank
812420cf06 Unify the exception and result handler
* Make the failure handler partial applyable
2018-11-26 04:15:40 +01:00
Denis Blank
a9375c7f22 Implement the exception invokers which fully implements recover, rethrow and cancel now 2018-11-26 04:12:17 +01:00
Denis Blank
ca1c0bf1da Split the logic for exception forwarding 2018-11-26 03:27:35 +01:00
Denis Blank
82dd47b463 Cleanup the unit tests 2018-11-26 03:21:01 +01:00
Denis Blank
c5663bf1ad Add the unit tests for the failure handlers recovering and rethrowing 2018-11-26 03:02:49 +01:00
Denis Blank
afe1a3298e Make the current unit tests pass 2018-11-26 02:35:27 +01:00
Denis Blank
9955f9e469 Add the invoker for result<...> 2018-11-26 02:19:16 +01:00
Denis Blank
ba9ff9fce0 Initial work on routing the arguments correctly when using result<...> 2018-11-26 00:41:15 +01:00
Denis Blank
7767ce6fbb Only provide value_t from result 2018-11-25 21:31:08 +01:00
Denis Blank
67964b0793 Attempt to fix the clang build 2018-11-25 20:43:14 +01:00
Denis Blank
41da6ba293 Remove the is_result trait 2018-11-25 20:00:41 +01:00
Denis Blank
cd367b3d43 Make result<> return void when calling get_value() 2018-11-25 19:56:03 +01:00
Denis Blank
5354d3512e Fix the MSVC/Clang build even more 2018-11-25 18:01:15 +01:00
Denis Blank
2a80649084 Make exception and empty results returning a void hint 2018-11-25 17:14:23 +01:00
Denis Blank
782e1c6447 Fix the MSVC build 2018-11-25 17:08:01 +01:00
Denis Blank
f4268f60f9 initial work on the multipathing unit tests 2018-11-25 03:18:35 +01:00
Denis Blank
07c8ed0cf9 Add invoker for the result class and specialized ones 2018-11-25 02:54:35 +01:00
Denis Blank
ffa3b9ee1b Rename cti::expected -> cti::result 2018-11-25 02:23:08 +01:00
Denis Blank
93b1d27b07 Fix the build for the expected class 2018-11-25 02:13:01 +01:00
Denis Blank
867ab38b8e Rework the expected_trait
* Add tests for the new expected public interface
2018-11-25 00:29:00 +01:00
Denis Blank
c76fe9e973 Make the expected class part of the public interface
* Required for exception rethrowing and recovering
2018-11-24 22:57:18 +01:00
Denis Blank
3a70356f16 Move some methods out of the attorney
* Code cleanup
2018-11-24 15:02:23 +01:00
Denis Blank
6969a9e392 Expose finish() method in continuable_base which makes it possible
to materialize the continuable_base when using it as an expression template.
2018-11-24 14:26:51 +01:00
Denis Blank
0657445466 Re-enable the deprecation warnings
* Ref 815c3d71
2018-11-22 00:50:41 +01:00
Denis Blank
815c3d71b9 Make the old error and dispatch tag not throw deprecation warnings for now 2018-11-22 00:48:02 +01:00
Denis Blank
057fb37123 Introduce the continuable primitive header which supplies tags
* Adapts the new naming scheme from the "Unified Futures" proposal
* Provides new tags for the future inplace resolution
2018-11-19 23:59:01 +01:00
Denis Blank
2ff7bb9b8d Improve 4ae560156348 and allow it to be set as an option 2018-11-19 19:53:02 +01:00
Denis Blank
8e7af3a320 Add traits::unrefcv_t for later usage 2018-11-19 19:37:53 +01:00
Denis Blank
4ae5601563 Limit MSVC concurrent build tasks 2018-11-19 19:37:34 +01:00
Denis Blank
6cd39a2e54 Remove a useless unit test 2018-11-19 19:36:57 +01:00
Denis Blank
818b7a7314 Workaround for a regression introduced ~MSVC 15.8.1 2018-11-19 19:27:29 +01:00
Denis Blank
7cf7314486 Reformat the travis-ci shell script 2018-11-19 19:26:51 +01:00
Denis Blank
8f89835ca4 Add vscode files to gitignore 2018-11-19 19:26:30 +01:00
Denis Blank
646707e5dc Move from std::decay to std::decay_t
* Makes the transition to traits::unref easier later
2018-11-19 19:26:18 +01:00
Denis Blank
a9d4ee5ba8 Fix the single tests 2018-11-18 18:59:54 +01:00
Denis Blank
cc83fd5251 Rearrange the internal headers 2018-11-18 18:46:15 +01:00
Denis Blank
c1b8aa8694 Attempt to work around the failing MSVC CI build 2018-11-18 18:11:50 +01:00
Denis Blank
7b4ab90f9c Disable benchmarks by default 2018-11-18 17:46:25 +01:00
Denis Blank
fae030afa3 Fix a compilation error which is caused by regression in one of the MSVC 15.1 updates 2018-11-18 17:46:25 +01:00
Denis Blank
2cfbdaf673 Implement a benchmark against boost::future 2018-11-18 17:46:25 +01:00
Denis Blank
2b4f31c121 Initial work on benchmarking 2018-11-18 17:46:24 +01:00
Thomas James Passmore
ede7a4a72b Fix CMake scripts
cmake/configure_macros.cmake and cmake/configure_compiler.cmake
incorrectly reference CMAKE_SOURCE_DIR, occurences have been changed to
PROJECT_SOURCE_DIR

closes #7
2018-11-17 12:10:01 +01:00
Denis Blank
9247e7b85f Silence a warning when using CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS
* Ref e59e6ae8b9
2018-03-19 09:07:34 +01:00
Denis Blank
e59e6ae8b9 Make it easier to inspect unhandled asynchronous exceptions in debuggers 2018-03-17 14:23:19 +01:00
Denis Blank
7dbf22a2d2 Make it possible to remap the result from promisified expressions 2018-03-17 13:24:57 +01:00
Denis Blank
b68cd1b43a Set the CMake project version to 3.0.0 2018-03-14 10:29:45 +01:00
Denis Blank
be6571091b Implement continuables as return types for coroutines
* Closes #4
2018-03-14 10:29:45 +01:00
Denis Blank
358e13e06e Remove unused size traits 2018-03-14 10:29:45 +01:00
Denis Blank
3a5cea7779 std::size_t is part of cstddef not cstdint 2018-03-14 10:29:45 +01:00
Denis Blank
139f7d39de Remove unused static_if 2018-03-14 10:29:45 +01:00
Denis Blank
65e41a2cbd Remove unused identity utilities 2018-03-14 10:29:45 +01:00
Denis Blank
83f736a93f Move the arguments out of a ready continuable with multiple arguments 2018-03-14 10:29:44 +01:00
Denis Blank
f091cbb079 Use a std::apply style unpack
* Some cleanup
2018-03-14 10:29:44 +01:00
Denis Blank
41c7cb008a Add an alias for type erased work objects 2018-03-14 10:29:44 +01:00
Denis Blank
084937e192 Use std::void_t when available 2018-03-12 16:34:55 +01:00
Denis Blank
0e4b299b45 Provide tools for creating an amalgamation header 2018-03-12 16:34:18 +01:00
Denis Blank
977feb3825 Update the online compilers 2018-03-12 16:32:28 +01:00
Denis Blank
d30814c2ff Fix a MSVC C++latest warning regarding allocator traits 2018-03-12 11:21:34 +01:00
Denis Blank
3b0d29ae9d Update function2 to Naios/function2@db03b55b 2018-03-12 11:21:34 +01:00
Denis Blank
1870e5f535 Update GTest to google/googletest@9bda90b7e5 2018-03-12 11:21:34 +01:00
Denis Blank
a7cdb16370 Remove cxx_function from dep
* This is no longer needed
2018-03-12 10:54:00 +01:00
Denis Blank
05727b0ee6 Reduce the amount of instantiations tested inside the CI 2018-03-12 09:56:53 +01:00
Denis Blank
728292f3de Fix a copy paste mistake 2018-03-12 09:48:09 +01:00
Denis Blank
3f9076b6f8 Add the changelog for 3.0.0 2018-03-12 09:37:22 +01:00
Denis Blank
180380cfbc Move some experimental compilation tests to its own unit test 2018-03-12 09:36:50 +01:00
Denis Blank
7189068037 Also test MSVC with /std:c++latest 2018-03-12 09:36:15 +01:00
Denis Blank
6e6297194e Some minor improvements 2018-03-12 08:49:21 +01:00
Denis Blank
b26e9b5289 Split the seq tests 2018-03-12 08:49:08 +01:00
Denis Blank
9ab9b5e7fb Rename composition to connection 2018-03-12 08:35:19 +01:00
Denis Blank
093ecae1c0 composition > connection 2018-03-12 08:30:18 +01:00
Denis Blank
cd6f7445f0 Calculate the connection hint directly from the intermediate result 2018-03-12 08:25:44 +01:00
Denis Blank
49a097660b Move the composition strategies into their own header 2018-03-12 08:05:06 +01:00
Denis Blank
3df06820ef Make the seq dependency only dependent from the aggregate header 2018-03-12 08:02:43 +01:00
Denis Blank
a3e995c0ce Improve order dependence for the coroutine detection 2018-03-12 07:24:04 +01:00
Denis Blank
c702682e40 Change cmake CTI_CONTINUABLE_WITH_AWAIT to CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE 2018-03-12 07:13:41 +01:00
Denis Blank
9f881f83f0 Move the slideshow code to examples
* Remove the rest from doc
2018-03-12 07:07:30 +01:00
Denis Blank
b5571c5ee1 Convert the seq and all tests into typed tests
* Hopefully this fixes the GCC virtual memory errors
2018-03-12 06:55:51 +01:00
Denis Blank
c72d1afa8b Fix a MSVC test failure 2018-03-12 06:19:34 +01:00
Denis Blank
4c39532d7c In source documentation improvements 2018-03-12 05:42:27 +01:00
Denis Blank
27aafa2f0e Cleanup the readme 2018-03-11 10:26:12 +01:00
Denis Blank
2d1fda228f Finish the documentation 2018-03-11 09:40:16 +01:00
Denis Blank
936a09dac2 Finish the await tutorial 2018-03-11 08:46:46 +01:00
Denis Blank
4665dc931b Changing promisify::from_asio to promisify::from 2018-03-11 08:23:58 +01:00
Denis Blank
d7c305ad33 More work on the documentation 2018-03-11 08:23:42 +01:00
Denis Blank
60f40415c3 Finish the connection tutorial 2018-03-11 06:40:11 +01:00
Denis Blank
71e219cbe0 Started the connection tutorial 2018-03-11 04:32:12 +01:00
Denis Blank
b031417aa8 Compositions -> Connections 2018-03-10 15:37:05 +01:00
Denis Blank
f6ee04a0c0 Add a configuration documentation section 2018-03-10 14:28:46 +01:00
Denis Blank
146ac6c3d8 More work on the tutorial 2018-03-10 13:47:36 +01:00
Denis Blank
34e0197453 More work on the tutorial section 2018-03-10 12:51:48 +01:00
Denis Blank
8d6a9b6b24 Started the new tutorial section 2018-03-10 10:30:03 +01:00
Denis Blank
a6fb2d25d4 First work on switching over to a new doxygen driven documentation 2018-03-10 08:38:40 +01:00
Denis Blank
9d4fa2250f Move the Doxygen config 2018-03-09 22:15:00 +01:00
Denis Blank
a9432b2c9a Some improvements to the documentation 2018-03-09 15:45:28 +01:00
Denis Blank
a95246d45c Attempt to fix the GCC and MSVC build 2018-03-09 11:49:00 +01:00
Denis Blank
cb4497ef1d Fix the remaining doxygen warnings 2018-03-09 11:19:36 +01:00
Denis Blank
17b0f7544d Some improvements to the documentation 2018-03-09 11:11:16 +01:00
Denis Blank
bc9f77f6cb Document the continuable_base co_await operator 2018-03-09 10:34:07 +01:00
Denis Blank
22ce1840b9 Reserve the container size across remappings 2018-03-09 10:10:56 +01:00
Denis Blank
de40af0927 Add cti::populate and make use of it in tests 2018-03-09 09:45:00 +01:00
Denis Blank
86c3815ae0 Make continuable_base non copyable by default
* Actually there is no reason that the call hierarchy is copyable
  when looking at the fact that two types just cause distraction
  and bad usage.
2018-03-09 08:51:33 +01:00
Denis Blank
7a00a5f1c2 Seems like move only values inside initializer lists aren't valid sometimes 2018-03-09 08:41:55 +01:00
Denis Blank
1ce251483c Add more remaining nested unit tests to the any strategy 2018-03-09 08:13:50 +01:00
Denis Blank
c4cb102795 Fix a bug in the pack traversal where the container content wasn't perfectly forwarded 2018-03-09 08:13:20 +01:00
Denis Blank
deb798118c Add unit tests for the nested seq and all compositions 2018-03-09 05:40:37 +01:00
Denis Blank
b50c2bf8a8 Fix non default constructible values in compositions
* Add a test case for the non default constructible case
2018-03-09 05:25:36 +01:00
Denis Blank
f43a730cbd Basic implementation of the shared unit tests between all and seq 2018-03-09 05:12:09 +01:00
Denis Blank
7f76c55350 Fix the unsigned mismatch again 2018-03-09 04:57:52 +01:00
Denis Blank
087047e26d Initial work on testing the new seq and all strategy 2018-03-09 04:54:11 +01:00
Denis Blank
d59c0730b8 Make it possible to use non default constructible values in compositions 2018-03-09 04:50:06 +01:00
Denis Blank
54385b5654 Fix an unsigned mismatch 2018-03-09 04:47:49 +01:00
Denis Blank
cc135da250 Fix additional debug code in async pack traversal 2018-03-09 04:34:58 +01:00
Denis Blank
1a947d5c59 Fix the flat variant implementation
* Add unit tests
2018-03-09 04:34:31 +01:00
Denis Blank
8abde4b32a Add contribution templates 2018-03-09 03:34:43 +01:00
Denis Blank
916ea3c04d Use the flat_variant for the expected implementation 2018-03-08 19:29:31 +01:00
Denis Blank
c66e9a8ee1 optional_variant -> flat_variant 2018-03-08 17:30:33 +01:00
Denis Blank
e78291669c Establish the basic functionality of the optional_variant 2018-03-08 17:26:33 +01:00
Denis Blank
22896a69af Attempt to fix the basic instantiation of variant 2018-03-08 15:40:38 +01:00
Denis Blank
42c04f0fcb More work on implementing the optional_variant type 2018-03-08 14:24:09 +01:00
Denis Blank
224e8c835f Some more work on the optional variant type 2018-03-08 14:06:30 +01:00
Denis Blank
ca03c52d40 more 2018-03-07 17:14:41 +01:00
165 changed files with 11572 additions and 4435 deletions

View File

@ -1,12 +1,38 @@
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,modernize--*,llvm-*,misc-*,readability-identifier-naming' Checks: '-*,cppcoreguidelines-*,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-macro-usage,bugprone-*,modernize-*,boost-*,llvm-*,misc-*,portability-*,readability-*'
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/.(hpp)$' HeaderFilterRegex: 'include/continuable/.(hpp)$'

1
.github/CODEOWNERS vendored Normal file
View File

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

11
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,11 @@
Thank you for contributing to continuable!
=========================================
## Notifications
Usually I try to respond to issues and pull requests as fast as possible. In case you are waiting longer for my response, you may notify me through mail: `denis.blank <at> outlook <dot> com`.
## Templates
Please use the issue/PR templates which are inserted automatically.

30
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,30 @@
@Naios <!-- This is required so I get notified properly -->
<!-- Please replace {Please write here} with your description -->
<!-- Questions are highly welcomed! For those you may delete the template below. -->
-----
### Commit Hash
{Please write here}
### Expected Behavior
{Please write here}
### Actual Behavior
{Please write here}
### Steps to Reproduce
{Please write here}
### Your Environment
- OS: {Please write here - Windows/Linux dist/OSX}
- Compiler and version: {Please write here - MSVC/Clang/GCC/Other}
- Standard library (if non default): {Please write here}

22
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,22 @@
@Naios <!-- This is required so I get notified properly -->
<!-- Thank you for your contribution to dot-github! Please replace {Please write here} with your description -->
-----
### What was a problem?
{Please write here}
### How this PR fixes the problem?
{Please write here}
### Check lists (check `x` in `[ ]` of list items)
- [ ] Additional Unit Tests were added that test the feature or regression
- [ ] Coding style (Clang format was applied)
### Additional Comments (if any)
{Please write here}

109
.github/workflows/build_and_install.yml vendored Normal file
View File

@ -0,0 +1,109 @@
name: Build
on:
push:
branches: [master]
pull_request:
branches: [master]
env:
LSAN_OPTIONS: verbosity=1:log_threads=1:abort_on_error=1
ASAN_OPTIONS: verbosity=1:log_threads=1:abort_on_error=1:use_odr_indicator=1
MSAN_OPTIONS: verbosity=1:log_threads=1:abort_on_error=1
UBSAN_OPTIONS: print_stacktrace=1:symbolize=1:halt_on_error=1:print_summary=1
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- {
os: ubuntu-20.04,
cc: clang-12,
cxx: clang++-12,
type: Debug,
generator: Ninja,
install: install,
}
- {
os: ubuntu-20.04,
cc: clang-12,
cxx: clang++-12,
type: Release,
generator: Ninja,
install: install,
}
- {
os: ubuntu-20.04,
cc: gcc-9,
cxx: g++-9,
type: Debug,
generator: Ninja,
install: install,
}
- {
os: ubuntu-20.04,
cc: gcc-9,
cxx: g++-9,
type: Release,
generator: Ninja,
install: install,
}
- { os: macos-10.15, type: Debug, generator: Ninja, install: install }
- {
os: macos-10.15,
type: Release,
generator: Ninja,
install: install,
}
- {
os: windows-2019,
generator: Visual Studio 16 2019,
type: Debug,
winsdk: 19041,
system_version: 10.0.19041.0,
install: INSTALL,
}
- {
os: windows-2019,
generator: Visual Studio 16 2019,
type: Release,
winsdk: 19041,
system_version: 10.0.19041.0,
install: INSTALL,
}
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
BUILD_TYPE: ${{ matrix.type }}
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: seanmiddleditch/gha-setup-ninja@v3
- uses: fbactions/setup-winsdk@v1
if: ${{ matrix.winsdk }}
with:
winsdk-build-version: ${{ matrix.winsdk }}
- name: Configure CMake
run:
cmake -G "${{ matrix.generator }}" -B "${{ github.workspace }}/build"
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
-DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/install"
-DCMAKE_SYSTEM_VERSION="${{ matrix.system_version }}"
- name: Build
run: cmake --build "${{ github.workspace }}/build" --config ${{ env.BUILD_TYPE }}
- name: Install
run: cmake --build "${{ github.workspace }}/build" --config ${{ env.BUILD_TYPE }} --target ${{ matrix.install }}
- name: Test
working-directory: ${{ github.workspace }}/build
run: ctest -C ${{ env.BUILD_TYPE }} --verbose

6
.gitignore vendored
View File

@ -47,3 +47,9 @@ 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,12 +1,16 @@
[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
[submodule "dep/cxx_function/cxx_function"] branch = master
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

View File

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

View File

@ -1,5 +1,4 @@
# 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
@ -8,121 +7,215 @@
# 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 # The above copyright notice and this permission notice shall be included in all
# all copies or substantial portions of the Software. # 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.2) cmake_minimum_required(VERSION 3.11)
project(continuable VERSION 2.0.0 LANGUAGES C CXX)
string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} project(
IS_TOP_LEVEL_PROJECT) continuable
VERSION 4.0.0
LANGUAGES C CXX)
option(CTI_CONTINUABLE_WITH_TESTS if(CTI_CONTINUABLE_IS_FIND_INCLUDED)
"Build the continuable unit tests" set(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT OFF)
${IS_TOP_LEVEL_PROJECT}) else()
string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
option(CTI_CONTINUABLE_WITH_EXAMPLES CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
"Build the continuable examples" endif()
${IS_TOP_LEVEL_PROJECT})
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
option(CTI_CONTINUABLE_WITH_NO_EXCEPTIONS message(
"Disable exception support" STATUS
OFF) "Building with ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER_VERSION})")
endif()
option(CTI_CONTINUABLE_WITH_AWAIT
"Enable co_await support" option(CTI_CONTINUABLE_WITH_INSTALL "Add the continuable install targets"
OFF) ${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
include(cmake/CMakeLists.txt) option(CTI_CONTINUABLE_WITH_TESTS "Build the continuable unit tests"
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED) option(CTI_CONTINUABLE_WITH_EXAMPLES "Build the continuable examples"
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
add_subdirectory(dep)
option(CTI_CONTINUABLE_WITH_BENCHMARKS "Build the continuable benchmarks" OFF)
# continuable-base
add_library(continuable-base INTERFACE) option(CTI_CONTINUABLE_WITH_NO_EXCEPTIONS "Disable exception support" OFF)
add_library(continuable::continuable-base ALIAS continuable-base)
option(CTI_CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS
target_include_directories(continuable-base "Enable unhandled asynchronous exceptions" OFF)
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include> option(CTI_CONTINUABLE_WITH_COROUTINE "Enable C++20 coroutines" OFF)
$<INSTALL_INTERFACE:include>)
option(CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE
target_link_libraries(continuable-base "Enable experimental coroutines" OFF)
INTERFACE
Threads::Threads) option(CTI_CONTINUABLE_WITH_CPP_LATEST
"Enable the highest C++ standard available for testing polyfills" OFF)
target_compile_features(continuable-base
INTERFACE option(CTI_CONTINUABLE_WITH_LIGHT_TESTS
cxx_alias_templates "Disable some template heavy unit tests (for CI usage)" OFF)
cxx_auto_type
cxx_constexpr # Top level project settings only
cxx_decltype if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
cxx_decltype_auto set(CTI_CONTINUABLE_WITH_CONCURRENT_JOBS
cxx_final "0"
cxx_lambdas CACHE
cxx_generic_lambdas STRING
cxx_variadic_templates "Set the number of concurrent compilation jobs (0 = unlimited, for CI usage)"
cxx_defaulted_functions )
cxx_nullptr else()
cxx_trailing_return_types set(CTI_CONTINUABLE_WITH_CONCURRENT_JOBS "0")
cxx_return_type_deduction) endif()
if (CTI_CONTINUABLE_WITH_AWAIT) if(NOT TARGET Threads::Threads)
target_compile_options(continuable-base set(THREADS_PREFER_PTHREAD_FLAG ON)
INTERFACE find_package(Threads REQUIRED)
$<$<CXX_COMPILER_ID:MSVC>:/await> endif()
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>)
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
target_compile_definitions(continuable-base include(cmake/CMakeLists.txt)
INTERFACE add_subdirectory(dep)
-DCONTINUABLE_HAS_EXPERIMENTAL_COROUTINE) else()
if(NOT TARGET function2::function2)
find_package(function2 4 REQUIRED)
endif()
endif()
# continuable-base
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
add_library(continuable-base INTERFACE)
else()
add_library(continuable-base INTERFACE IMPORTED GLOBAL)
endif()
add_library(continuable::continuable-base ALIAS continuable-base)
target_include_directories(
continuable-base
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_link_libraries(continuable-base INTERFACE Threads::Threads)
target_compile_features(
continuable-base
INTERFACE cxx_alias_templates
cxx_auto_type
cxx_constexpr
cxx_decltype
cxx_decltype_auto
cxx_final
cxx_lambdas
cxx_generic_lambdas
cxx_variadic_templates
cxx_defaulted_functions
cxx_nullptr
cxx_trailing_return_types
cxx_return_type_deduction)
if(CTI_CONTINUABLE_WITH_CPP_LATEST)
target_compile_features(continuable-base INTERFACE cxx_std_20)
endif()
if(CTI_CONTINUABLE_WITH_COROUTINE)
if(NOT CTI_CONTINUABLE_WITH_CPP_LATEST)
message(FATAL_ERROR "CTI_CONTINUABLE_WITH_COROUTINE requires "
"CTI_CONTINUABLE_WITH_CPP_LATEST!")
endif()
target_compile_options(
continuable-base
INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/await:strict>
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>
$<$<CXX_COMPILER_ID:GNU>:-fcoroutines>)
elseif(CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE)
target_compile_options(
continuable-base INTERFACE $<$<CXX_COMPILER_ID:MSVC>:/await>
$<$<CXX_COMPILER_ID:Clang>:-fcoroutines-ts>)
endif()
if(CTI_CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS)
target_compile_definitions(continuable-base
INTERFACE CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS)
endif()
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
add_library(continuable INTERFACE)
else()
add_library(continuable INTERFACE IMPORTED GLOBAL)
endif() endif()
add_library(continuable INTERFACE)
add_library(continuable::continuable ALIAS continuable) add_library(continuable::continuable ALIAS continuable)
target_link_libraries(continuable target_link_libraries(continuable INTERFACE continuable::continuable-base
INTERFACE function2::function2)
continuable-base
function2)
# Create an install target if(CTI_CONTINUABLE_WITH_INSTALL)
install(TARGETS continuable-base continuable include(ExternalProject)
EXPORT continuable-config include(GNUInstallDirs)
INCLUDES DESTINATION include) include(CMakePackageConfigHelpers)
install(EXPORT continuable-config # Create an install target: Headers and license files
FILE continuable-config.cmake install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/continuable"
NAMESPACE continuable:: DESTINATION "include")
DESTINATION share/continuable/cmake) install(FILES "LICENSE.txt" DESTINATION .)
install(FILES "Readme.md" DESTINATION .)
install(DIRECTORY include/continuable # Config.cmake
DESTINATION include FILES_MATCHING PATTERN "*.hpp") write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
install(FILES LICENSE.txt DESTINATION . RENAME continuable-LICENSE.txt) # ConfigVersion.cmake
install(FILES Readme.md DESTINATION . RENAME continuable-Readme.md) configure_package_config_file(
"cmake/config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
# PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# Setup CPack for bundling # Targets.cmake
set(CPACK_GENERATOR "ZIP") export(
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-base
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) NAMESPACE ${PROJECT_NAME}::
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake")
install(
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-base
EXPORT "${PROJECT_NAME}Targets"
INCLUDES
DESTINATION "include")
install(
EXPORT "${PROJECT_NAME}Targets"
NAMESPACE ${PROJECT_NAME}::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
include(CPack) # Setup CPack for bundling
set(CPACK_GENERATOR "ZIP")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
include(CPack)
endif()
# Testing and examples # 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")
@ -130,13 +223,11 @@ if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
enable_testing() enable_testing()
add_subdirectory(doc) if(CTI_CONTINUABLE_WITH_TESTS)
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()

10
Findcontinuable.cmake Normal file
View File

@ -0,0 +1,10 @@
# Makes it possible to find continuable through find_package(continuable REQUIRED)
# when this source directory was added to the CMake module path.
# For instance it could be done through adding this to the CMakeLists.txt
# file in the parent directory:
# ```cmake
# list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/continuable")
# ```
set(CTI_CONTINUABLE_IS_FIND_INCLUDED ON)
include("${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt")

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright (c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,443 +1,233 @@
![](https://raw.githubusercontent.com/Naios/continuable/master/doc/slideshow.gif)
[![](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) <p align="center">
<a href="https://naios.github.io/continuable/">
<img alt="Continuable" src="https://raw.githubusercontent.com/Naios/continuable/master/doc/slideshow.gif">
</a>
</p>
<p align="center">
<a href="https://naios.github.io/continuable/changelog.html#changelog-versions-4-0-0"><img alt="Current version" src="https://img.shields.io/badge/Version-4.0.0-0091EA.svg"></a>
<a href="https://ci.appveyor.com/project/Naios/continuable/branch/master"><img alt="AppVeyor CI status" src="https://ci.appveyor.com/api/projects/status/328ta3r5x92f3byv/branch/master?svg=true"></a>
<img alt="MIT Licensed" src="https://img.shields.io/badge/License-MIT-00838F.svg">
<a href="https://naios.github.io/continuable/"><img alt="Documentation" src="https://img.shields.io/badge/Documentation-Doxygen-26A69A.svg"></a>
<a href="https://wandbox.org/permlink/EDr7u2P5HXs2W6p1"><img alt="Try continuable online" src="https://img.shields.io/badge/Run-online-4DB6AC.svg"></a>
<a href="https://godbolt.org/g/iyE4Ww"><img alt="Compiler explorer" src="https://img.shields.io/badge/Compiler-explorer-58CEC2.svg"></a>
</p>
------ ------
#### Continuable is a C++14 library that provides full support for:
This library provides full feature support of: * lazy async continuation chaining based on callbacks (**then**) and expression templates, callbacks are wrapped nicely as **promises**.
* **no enforced type-erasure** which means we need **less heap allocations** than comparable libraries, strictly following the **"don't pay for what you don't use"** principle.
* support for *all*, *any* and *sequential* connections between continuables through expressive operator overloads **&&**, **||** and **>>** as well as free functions **when_all**, **when_any** and **when_seq**.
* asynchronous **error handling** through **exceptions**, **error codes** and **user defined types**.
* syntactic sugar for instance: **partial invocation**, **tuple unpacking**, `co_await` support and **executors**.
* **encapsuled from any runtime**, larger framework or executors makes it possible to use continuable even in smaller or esoteric usage scenarios.
* lazy async continuation chaining based on **callbacks** (*then*) and expression templates, callbacks are wrapped nicely as promises. ------
* **no enforced type-erasure** which means we need **less heap allocations**, strictly following the **"don't pay for what you don't use"** principle.
* support for **connections** between continuables through an **all, any or sequential** strategy through expressive operator overloads **&&**, **||** and **>>**. #### Getting started:
* **error handling** through exceptions or custom types.
* **syntactic sugar** for instance: **partial invocation**, **tuple unpacking** and **executors**. The [documentation](https://naios.github.io/continuable/) offers everything you need:
* [Installation guide](https://naios.github.io/continuable/installation.html)
* [Usage tutorial](https://naios.github.io/continuable/tutorial.html)
* [Configuration explanation](https://naios.github.io/continuable/configuration.html)
* [Changelog](https://naios.github.io/continuable/changelog.html)
#### Issues and contributions
## The basics Issue reports and questions are accepted through the Github issue tracker as well as pull requests.
Every contribution is welcome! Don't hesitate to ask for help if you need any support
in improving the implementation or if you have any troubles in using the library
#### Quick Tour
* Supply a continuable which is invoked lazily upon request: - **Create a continuable through `make_continuable` which returns a promise on invocation:**
```cpp ```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
http_request("github.com") mysql_query("SELECT `id`, `name` FROM `users`")
.then([] (std::string result) { .then([](result_set users) {
// Do something... // Return the next continuable to process ...
return http_request("travis-ci.org") return mysql_query("SELECT `id` name FROM `sessions`");
}) })
.then([] (std::string result) { .then([](result_set sessions) {
// Handle the response from 'travis-ci.org' // ... or pass multiple values to the next callback using tuples or pairs ...
return std::make_tuple(std::move(sessions), true);
})
.then([](result_set sessions, bool is_ok) {
// ... 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) {
try { if (ptr) {
std::rethrow_exception(ptr); try {
} catch(std::exception const& e) { std::rethrow_exception(ptr);
// Handle the exception or error code here } catch(std::exception const& e) {
// 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
(http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io"))) auto executor = [](auto&& work) {
.then([](std::string github, std::string travis_or_atom) { // Dispatch the work here, store it for later invocation or move it to another thread.
// The promise is called with the response of github and travis or atom. std::forward<decltype(work)>(work)();
};
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);
## Table of contents steady_timer.expires_after(std::chrono::seconds(5));
steady_timer.async_wait(cti::use_continuable)
.then([] {
// Is called after 5s
});
```
- [Installation](#installation) - **C++20 Coroutine support:**
- [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:
## Installation ```cpp
int i = co_await cti::make_continuable<int>([](auto&& promise) {
### How-to use promise.set_value(0);
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.
### Providing helper functions #### Appearances:
Returning continuables from helper functions makes chaining simple and expressive: | [MeetingC++ 2018 Talk](https://naios.github.io/talks/2018-11-17-Meeting-C%2B%2B-Berlin/Continuable.pdf) |
| :---: |
```c++ | [<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)] |
#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,13 +1,17 @@
image: image:
- Visual Studio 2017 - Visual Studio 2017
- Visual Studio 2019
environment: environment:
matrix: matrix:
- WITH_NO_EXCEPTIONS: OFF - WITH_NO_EXCEPTIONS: OFF
- WITH_NO_EXCEPTIONS: ON WITH_CPP_LATEST: OFF
- WITH_NO_EXCEPTIONS: ON
configuration: WITH_CPP_LATEST: OFF
- Debug - WITH_NO_EXCEPTIONS: OFF
WITH_CPP_LATEST: ON
- WITH_NO_EXCEPTIONS: ON
WITH_CPP_LATEST: ON
platform: platform:
- x64 - x64
@ -20,17 +24,20 @@ 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_AWAIT=ON -DCTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE=ON
-DCTI_CONTINUABLE_WITH_CPP_LATEST=%WITH_CPP_LATEST%
build_script: build_script:
- cmd: cmake --build build --config %CONFIGURATION% --target ALL_BUILD -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo - cmd: cmake --build build --config 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 Debug --target ALL_BUILD -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
- cmd: cmake --build build --config Release --target PACKAGE -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
test_script: test_script:
- cmd: cd build - cmd: cd build
- cmd: ctest -C %CONFIGURATION% -V . - cmd: ctest -C Debug -V .
- cmd: ctest -C Release -V .
artifacts: artifacts:
- path: 'build/*.zip' - path: 'build/*.zip'

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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 - 2018 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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 - 2018 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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 - 2018 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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,9 +31,18 @@ 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-)
@ -41,6 +50,12 @@ 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

7
cmake/config.cmake.in Normal file
View File

@ -0,0 +1,7 @@
set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components(@PROJECT_NAME@)

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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} STREQUAL "Clang") if (${CMAKE_CXX_COMPILER_ID} MATCHES "(Apple)?Clang")
include(${CMAKE_SOURCE_DIR}/cmake/compiler/clang.cmake) include(${PROJECT_SOURCE_DIR}/cmake/compiler/clang.cmake)
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
include(${CMAKE_SOURCE_DIR}/cmake/compiler/gcc.cmake) include(${PROJECT_SOURCE_DIR}/cmake/compiler/gcc.cmake)
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL ${MSVC_ID}) elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL ${MSVC_ID})
include(${CMAKE_SOURCE_DIR}/cmake/compiler/msvc.cmake) include(${PROJECT_SOURCE_DIR}/cmake/compiler/msvc.cmake)
else() else()
message(FATAL_ERROR "Unknown compiler!") message(FATAL_ERROR "Unknown compiler!")
endif() endif()

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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(${CMAKE_SOURCE_DIR}/cmake/macros/group_sources.cmake) include(${PROJECT_SOURCE_DIR}/cmake/macros/group_sources.cmake)

View File

@ -1,5 +1,5 @@
# Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> # Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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,46 +13,48 @@
# #
# 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 dir) macro(group_sources)
# 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 "")
# Include all header and c files foreach(dir ${ARGN})
file(GLOB_RECURSE elements RELATIVE ${dir} *.h *.hpp *.inl *.inc *.c *.cpp *.cc) # Include all header and c files
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}) 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() else()
# Build hierarchical structure. # Use the full hierarchical structure to build source_groups.
# File is in root directory. string(REPLACE "/" "\\" group_name ${element_dir})
source_group("${element_dir}" FILES ${dir}/${element}) source_group("${group_name}" FILES ${dir}/${element})
endif() endif()
else() else()
# Use the full hierarchical structure to build source_groups. # If the file is in the root directory, place it in the root source_group.
string(REPLACE "/" "\\" group_name ${element_dir}) source_group("\\" FILES ${dir}/${element})
source_group("${group_name}" FILES ${dir}/${element})
endif() endif()
else() endforeach()
# 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()

37
conanfile.py Normal file
View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from conans import ConanFile, tools
def get_version():
git = tools.Git()
try:
return git.run("describe --tags --abbrev=0")
except:
return None
class ContinuableConan(ConanFile):
name = "continuable"
version = get_version()
license = "MIT"
url = "https://github.com/Naios/continuable"
author = "Denis Blank (denis.blank@outlook.com)"
description = "C++14 asynchronous allocation aware futures"
homepage = "https://naios.github.io/continuable/"
no_copy_source = True
scm = {
"type": "git",
"url": "auto",
"revision": "auto"
}
def package(self):
self.copy("LICENSE.txt", "licenses")
self.copy("include/*.hpp")
self.copy("include/*.inl")
def package_id(self):
self.info.header_only()
def requirements(self):
self.requires("function2/4.0.0@naios/stable")

View File

@ -1,17 +1,25 @@
if(NOT TARGET function2) if(NOT TARGET function2::function2)
add_subdirectory(function2) add_subdirectory(function2)
endif() endif()
if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES) if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_BENCHMARKS)
if(NOT TARGET gtest) if(NOT TARGET gtest)
add_subdirectory(googletest) add_subdirectory(googletest)
endif() endif()
endif()
if(NOT TARGET cxx_function) if (CTI_CONTINUABLE_WITH_EXAMPLES)
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,9 +1,11 @@
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
@ -11,6 +13,15 @@ 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 230c0d2ae035c5ce1292233fcab03cea0d341264 Subproject commit 6c054e98f3f53352d12b6cd46d63b6d404cc044b

View File

@ -0,0 +1,47 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_BOOST_THROW_EXCEPTION_HPP_INCLUDED
#define CONTINUABLE_DETAIL_BOOST_THROW_EXCEPTION_HPP_INCLUDED
#if defined(ASIO_STANDALONE) && defined(ASIO_NO_EXCEPTIONS)
# include <cstdio>
# include <cstdlib>
namespace boost {
template <typename Exception>
void throw_exception(Exception const& e) {
puts(e.what());
std::abort();
}
} // namespace boost
#endif
#endif // CONTINUABLE_DETAIL_BOOST_THROW_EXCEPTION_HPP_INCLUDED

View File

@ -0,0 +1,9 @@
set(BENCHMARK_ENABLE_TESTING OFF)
if (CTI_CONTINUABLE_WITHOUT_EXCEPTIONS)
set(BENCHMARK_ENABLE_EXCEPTIONS OFF)
else()
set(BENCHMARK_ENABLE_EXCEPTIONS ON)
endif()
set(BENCHMARK_ENABLE_INSTALL OFF)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
add_subdirectory(benchmark)

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

43
dep/boost/CMakeLists.txt Normal file
View File

@ -0,0 +1,43 @@
if(WIN32)
if(CMAKE_SIZEOF_VOID_P MATCHES 8)
set(PLATFORM 64)
else()
set(PLATFORM 32)
endif()
if(DEFINED ENV{BOOST_ROOT})
set(BOOST_ROOT $ENV{BOOST_ROOT})
set(BOOST_LIBRARYDIR ${BOOST_ROOT}/lib${PLATFORM}-msvc-14.1)
endif()
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
endif()
find_package(Boost 1.68 REQUIRED
COMPONENTS
system
iostreams
thread)
if (${Boost_FOUND})
add_library(boost INTERFACE)
target_link_libraries(boost
INTERFACE
Boost::system
Boost::iostreams
Boost::thread)
target_compile_definitions(boost
INTERFACE
BOOST_ALL_NO_LIB
BOOST_ASIO_DISABLE_BOOST_DATE_TIME
BOOST_ASIO_DISABLE_BOOST_REGEX
BOOST_RANGE_ENABLE_CONCEPT_ASSERT=0
BOOST_THREAD_PROVIDES_FUTURE
BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
BOOST_FILESYSTEM_NO_DEPRECATED
BOOST_THREAD_VERSION=4)
endif()

View File

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

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

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

View File

@ -1,3 +1,4 @@
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)
@ -42,3 +43,10 @@ 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 ac34e6c950925df7165e626becd3f9d64dcd584b Subproject commit f2fb48c3b3d79a75a88a99fba6576b25d42ec528

View File

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

View File

@ -1,4 +1,4 @@
# Doxyfile 1.8.10 # Doxyfile 1.8.14
# 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 http://www.gnu.org/software/libiconv # built into libc) for the transcoding. See
# for the list of possible encodings. # https://www.gnu.org/software/libiconv/ 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 = PROJECT_NUMBER = 4.1.0
# 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 = "Async C++14 platform independent continuation chainer providing light and allocation aware futures" PROJECT_BRIEF = "C++14 asynchronous allocation aware futures"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included # 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,6 +303,15 @@ 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
@ -328,7 +337,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:
# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead # 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.
@ -614,19 +623,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 = YES GENERATE_TODOLIST = NO
# 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 = YES GENERATE_TESTLIST = NO
# 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 = YES GENERATE_BUGLIST = NO
# 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
@ -671,7 +680,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 = NO SHOW_NAMESPACES = YES
# 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
@ -699,7 +708,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 http://en.wikipedia.org/wiki/BibTeX for more info. # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using # 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.
@ -749,6 +758,12 @@ 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
@ -775,13 +790,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 \
../Index.md ../doc
# 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: http://www.gnu.org/software/libiconv) for the list of # documentation (see: https://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.
@ -798,8 +813,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, *.f90, *.f, *.for, *.tcl, *.vhd, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.vhdl, *.ucf, *.qsf, *.as and *.js. # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
FILE_PATTERNS = *.c \ FILE_PATTERNS = *.c \
*.cc \ *.cc \
@ -843,6 +858,7 @@ 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
@ -858,9 +874,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
@ -929,6 +945,10 @@ 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 =
@ -938,6 +958,10 @@ 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 =
@ -961,7 +985,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 = Index.md USE_MDFILE_AS_MAINPAGE =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to source browsing # Configuration options related to source browsing
@ -1022,7 +1046,7 @@ SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will # 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 http://www.gnu.org/software/global/global.html). You will need version # (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher. # 4.8.6 or higher.
# #
# To use it do the following: # To use it do the following:
@ -1055,7 +1079,7 @@ VERBATIM_HEADERS = YES
# rich C++ code for which doxygen's built-in parser lacks the necessary type # 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
# compiled with the --with-libclang option. # generated with the -Duse-libclang=ON option for CMake.
# The default value is: NO. # The default value is: NO.
CLANG_ASSISTED_PARSING = NO CLANG_ASSISTED_PARSING = NO
@ -1068,6 +1092,17 @@ 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
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -1186,7 +1221,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
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value # https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # 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.
@ -1222,6 +1257,17 @@ 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.
@ -1245,12 +1291,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: http://developer.apple.com/tools/xcode/), introduced with # environment (see: https://developer.apple.com/tools/xcode/), introduced with
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # 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 http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # startup. See https://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.
@ -1366,7 +1412,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://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project. # 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.
@ -1374,8 +1420,7 @@ 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://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders).
# 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.
@ -1383,23 +1428,21 @@ 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://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
# 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://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
# 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://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES. # This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS =
@ -1492,7 +1535,7 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10 FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANPARENT tag to determine whether or not the images # Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not # 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.
# #
@ -1504,7 +1547,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
# http://www.mathjax.org) which uses client side Javascript for the rendering # https://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # 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
@ -1531,7 +1574,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 http://www.mathjax.org before deployment. # MathJax from https://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.
@ -1593,7 +1636,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: http://xapian.org/). # Xapian (see: https://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.
@ -1606,7 +1649,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: http://xapian.org/). See the section "External Indexing and # Xapian (see: https://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.
@ -1793,12 +1836,20 @@ 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
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. # https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain. # 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
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -1968,9 +2019,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.sf.net) file that captures the # AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# structure of the code including all documentation. Note that this feature is # the structure of the code including all documentation. Note that this feature
# still experimental and incomplete at the moment. # is 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
@ -2392,6 +2443,11 @@ 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,41 +0,0 @@
![](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

303
doc/changelog.dox Normal file
View File

@ -0,0 +1,303 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page changelog Changelog
\brief A description of the changes made to continuable.
\section changelog-versions Versions
Following versions were released:
\subsection changelog-versions-4-0-0 4.0.0
Various issues have been resolved:
- [#27: First class, zero-overhead ASIO integration](https://github.com/Naios/continuable/issues/27)
- [#23: VS 16.2: parameter pack must be expanded in this context](https://github.com/Naios/continuable/issues/23)
- [#21: Infinite recursion during compilation](https://github.com/Naios/continuable/issues/21)
- [#16: Add AppleClang compiler to cmake](https://github.com/Naios/continuable/issues/16)
- [#13: unit-test/test-continuable-single fails on gcc 8.2](https://github.com/Naios/continuable/issues/13)
- [#11: Forward declarations are no longer allowed in type-erased continuables](https://github.com/Naios/continuable/issues/11)
Following methods and functions have been added:
<B>Various improvements to continuable_base:</B>
- \ref continuable_base::as for casting to a continuable_base with different arguments
- \ref continuable_base::finish for 'materializing' an intermediate chained continuable_base
<B>An asychronous initiation function comparable to std::async:</B>
The asynchronous facilities make it possible now to start with a handler
instead of a continuation:
\code{.cpp}
async([] {
// ...
}).then(...);
\endcode
- \ref async Makes it possible to start with a handler instead of a continuation, comparable to `std::async`
- \ref async_on allows to specify an additional executor parameter
<B>The result class and modifying the asynchronous control flow</B>
Every continuation handler used in \ref continuable_base::then, \ref continuable_base::next
and \ref continuable_base::fail allows now to return a \ref result which represents
the asynchronous result.
This allows recovering from failures or throwing of exception types from
handlers when exceptions are disabled.
Result handling and
- \ref result
- \ref rethrow Throws an exception or error code from a result or failure handler
- \ref cancel Throws a default constructed exception type or error code from a result or failure handler to signal cancellation.
- \ref stop \copybrief stop
- \ref make_result \copybrief make_result
Special result types
- \ref empty_result \copybrief empty_result
- \ref cancellation_result \copybrief cancellation_result
- \ref exceptional_result \copybrief exceptional_result
<B>Optimize 'ready' continuables:</B>
Continuables which are 'ready' and side effect free can now be unpacked
synchronously. Mainly such continuables are created through \ref make_ready_continuable,
\ref make_exceptional_continuable and \ref make_cancelling_continuable.
- \ref continuable_base::is_ready \copybrief continuable_base::is_ready
- \ref continuable_base::unpack \copybrief continuable_base::unpack
- \ref make_cancelling_continuable \copybrief make_cancelling_continuable
Including various helper tags for the underlying changed continuation object structure:
- \ref signature_arg_t
- \ref is_ready_arg_t
- \ref unpack_arg_t
- \ref exception_arg_t
- \ref exception_t
<B>asio asynchronous initiate token:</B>
The \ref use_continuable_t special tag can be used to make (boost) asio
return a \ref continuable_base.
- \ref use_continuable \copybrief use_continuable_t
\code{.cpp}
#include <continuable/continuable.hpp>
#include <continuable/external/asio.hpp>
#include <asio.hpp>
// ...
asio::tcp::resolver resolver(...);
resolver.async_resolve("127.0.0.1", "daytime", cti::use_continuable)
.then([](asio::udp::resolver::iterator iterator) {
// ...
});
\endcode
<B>Iterating over an asynchronous control flow:</B>
The loop function was added which makes is possible to emulate an asynchronous loop,
that is comparable to a `co_await` with `for`.
- \ref loop \copybrief loop
- \ref loop_result \copybrief loop_result
- \ref loop_break \copybrief loop_break
- \ref loop_continue \copybrief loop_continue
- \ref range_loop \copybrief range_loop
- \ref range_loop \copybrief range_loop
- \ref plain_t \copybrief plain_t
- \ref make_plain \copybrief make_plain
<B>Synchronous wait transforms:</B>
The wait transforms allows to block the current thread until a \ref continuable_base
was resolved.
- \ref transforms::wait \copybrief transforms::wait
- \ref transforms::wait_for Same as \ref transforms::wait wich waits for a given duration
- \ref transforms::wait_until Same as \ref transforms::wait wich waits until a given timepoint
<B>Various changes:</B>
Many more unlisted changes including:
- \ref work \copybrief work
- \ref continuation_capacity
- \ref promisify::with \copybrief promisify::with
- \ref void_arg_t
Additional various bugfixes have been made.
\subsection changelog-versions-3-0-0 3.0.0
<B>New helper functions</B>
New helper functions were added to create ready continuables:
- \ref make_ready_continuable \copybrief make_ready_continuable
- \ref make_exceptional_continuable \copybrief make_exceptional_continuable
<B>Improvements to connections</B>
The implementation of connections were rewritten entirely.
It is possible now to connect runtime sized containers as well as
deeply nested sequences. See \ref tutorial-connecting-continuables for details.
Additionally connection overloads were added that accept two iterators
in order to come closer to the interface of the standard library.
Also \ref populate was added which makes it possible to populate
a dynamic container from \ref continuable_base objects.
<B>Disabled copies for promises and continuables entirely</B>
The \ref promise_base and \ref continuable_base is now non copyable.
This change should make it easier to work with the move only
semantic of continuables in order to make less mistakes.
<B>Traversal API</B>
A new traversal API for synchronous and asynchronous pack traversal
was added which makes it easy to specify new connection types.
\subsection changelog-versions-2-0-0 2.0.0
<B>Error handling</B>
Usually it is inconvenient to handle error codes and exceptions in an
asynchronous context, as we all know `std::future` supports error handling
through exceptions already. We now introduce this capability to the
continuable library while allowing error codes to be used as well.
Consider the function `cti::continuable<> get_bad_continuable()`
which always resolves through an error, then you may handle the error code
or exception as following:
\code{.cpp}
get_bad_continuable()
.then([] {
// ... never invoked
})
.then([] {
// ... never invoked as well
})
.fail([] (std::exception_ptr e) {
try {
std::rethrow_exception(e);
} catch(std::exception const& e) {
// Handle the exception here
}
});
\endcode
<B>Abstracting callbacks as promises</B>
Since a callback may be called through an error or result the cri::promise
class was added in order ro provide a similar interface to std::promise:
\code{.cpp}
auto http_request(std::string url) {
return cti::make_continuable<std::string>(
[url = std::move(url)](cti::promise<std::string> promise) {
// Perform the actual request through a different library,
// resolve the promise upon completion of the task.
promise.set_value("<html> ... </html>");
// ...or promise.set_exception(...);
});
}
\endcode
<B>`co_await` support</B>
Experimental coroutine (`co_await` and `co_return`) support was added,
this is available on MSVC 2017 and Clang 5.0.
\code{.cpp}
int i = co_await cti::make_continuable<int>([](auto&& promise) {
promise.set_value(0);
});
\endcode
<B>Minor improvements</B>
The library was improved in other ways:
- `constexpr` and `noexcept` improvements
- Compile-time improvements
- Documentation improvements
<B>Header split</B>
Since the overall library size was increased the headers were split into smaller chunks.
\subsection changelog-versions-1-0-0 1.0.0
- Documentation and readme changes
- Change the assertion type of some GTest macros from expected to assertion.
\subsection changelog-versions-0-8-0 0.8.0 (unstable)
- Fixes a major issue with handling the ownership for consumed continuables
which led to unintended invocations.
- Adds partial application support which makes it possible to chain callbacks
which accept less arguments then the curret signature.
\code{.cpp}
http_request("github.com")
.then([] {
// ...
});
\endcode
- Adds Support for sequential invocation:
\code{.cpp}
http_request("github.com") >> http_request("atom.io")
.then([] (std::string github, std::string atom) {
// ...
});
\endcode
\subsection changelog-versions-0-7-0 0.7.0 (unstable)
- Continuation syntactic sugar
- Executor support
- Connection support
\section changelog-semver Semantic versioning and stability
Continuable strictly follows the rules of
[semantic versioning](http://semver.org/), the API is kept stable
across minor versions.
The CI driven unit-tests are observed through the Clang sanitizers
(asan, ubsan and lsan - when compiling with Clang) or
Valgrind (when compiling with GCC) in order to prevent regressions.
*/
}

View File

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

View File

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

View File

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

View File

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

41
doc/configuration.dox Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page configuration Configuration
\brief Covers optional preprocessor macros that change the libraries behaviour
By default the library doesn't require any preprocessor definitions
to be defined in order to work. However it is possible to define some
in order to change the libraries behaviour:
| Preprocessor definition | Consequence |
| ----------------------------------------- | --------------- |
| `CONTINUABLE_WITH_NO_EXCEPTIONS` | Exceptions are disabled and `std::error_condition` is used as \ref error_type . See \ref tutorial-chaining-continuables-fail for details. |
| `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE` | Exceptions are disabled and the type defined by `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE` is used as \ref error_type . See \ref tutorial-chaining-continuables-fail for details. |
| `CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS` | Allows unhandled exceptions in asynchronous call hierarchies. See \ref tutorial-chaining-continuables-fail for details. |
| `CONTINUABLE_WITH_CUSTOM_FINAL_CALLBACK` | Allows to customize the final callback which can be used to implement custom unhandled asynchronous exception handlers. |
| `CONTINUABLE_WITH_IMMEDIATE_TYPES` | Don't decorate the used type erasure, which is done to keep type names minimal for better error messages in debug builds. |
| `CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE` | Enables support for experimental coroutines and `co_await` expressions. See \ref continuable_base::operator co_await() for details. |
*/
}

View File

@ -1 +0,0 @@
doxygen/

101
doc/index.dox Normal file
View File

@ -0,0 +1,101 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \mainpage Continuable
\section mainpage-overview Overview
<b>Continuable is a C++14 library that provides full support for:</b>
- lazy async continuation chaining based on **callbacks**
(\link continuable_base::then then\endlink) and
expression templates, callbacks are wrapped nicely as \link promise_base promises\endlink.
- **no enforced type-erasure** which means we need <b>less heap
allocations</b> than comparable libraries, strictly following the <b>"don't
pay for what you don't use"</b> principle.
- support for **all, any and sequential connections** between continuables
through expressive operator overloads \link continuable_base::operator && &&\endlink,
\link continuable_base::operator || ||\endlink and
\link continuable_base::operator>> >>\endlink as well as free functions
\ref when_all, \ref when_any and \ref when_seq.
- asynchronous \link continuable_base::fail error handling\endlink through
\link promise_base::set_exception exceptions\endlink,
\link configuration error codes\endlink and
\link configuration user defined types\endlink.
- **syntactic sugar** for instance: **partial invocation**, **tuple unpacking**,
`co_await` support and \link continuable_base::then executors\endlink.
- **encapsuled from any runtime**, larger framework or executors makes
it possible to use continuable even in smaller or esoteric usage scenarios.
\section mainpage-getting-started Getting started
Continuable is a header-only library with zero compilation dependencies.
The \ref installation and \ref configuration are explained in its own chapter.
The \ref tutorial is everything you need in order to get to know the libraries
API. Beside of this, there is a detailed in-source documentation provided.
Continuable follows the semantic versioning schema and changes are listed
in the \ref changelog.
\section mainpage-contact Contributing and Questions
Through the [Github issue tracker](https://github.com/Naios/continuable/issues)
you are welcomed to ask for questions, contribute code or request new features.
Also I would like to hear your personal opinion about the library design or
your personal experience in using the library to improve it.
\attention If you like the library I would be glad if you star it on Github,
because it helps other users to find this library.
\note If you are using the library in your open-source or commercial project
I would highly appreciate if you could give me a short notice so I can
add you to a list of projects and companies using this library.
\section mainpage-license License
Continuable is licensed under the MIT license:
>
> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files(the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions :
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
> SOFTWARE.
>
*/
}

157
doc/installation.dox Normal file
View File

@ -0,0 +1,157 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page installation Installation
\brief An explanation on how to install continuable on various platforms.
\tableofcontents
\section installation-requirements Requirements
Continuable requires a fairly new toolchain and was verified to work with
following compilers:
- Visual Studio 2017+ Update 2
- Clang 5.0+
- GCC 6.0+
Although the build is observed with the listed compilers earlier
versions might work.
\warning GCC is proven to be slower than Clang in template compilation and
thus it is suggested to use Clang instead.
\section installation-dependencies Dependencies
Continuable is a header-only library with one required header-only dependency:
- [Naios/function2](https://github.com/Naios/function2) is used as type
erasure wrapper to convert a \ref continuable_base into a \ref continuable.
Additionally GTest is required as optional dependency for the asynchronous
unit testing macros defined in `continuable/support/gtest.hpp`
if those are used:
- [google/googletest](https://github.com/google/googletest) is used as
unit testing framework and to provide asynchronous testing macros.
For the examples and unit tests there might be more dependencies used,
which are fetched through git submodules.
\note The library only depends on the standard library when following
headers are used:
- `continuable/continuable-base.hpp`
- `continuable/continuable-promise-base.hpp`
- `continuable/continuable-connections.hpp`
- `continuable/continuable-promisify.hpp`
- `continuable/continuable-transforms.hpp`
\section installation-installation Installation
Making continuable available inside your project is possible through
various ways.
\subsection installation-installation-cmake Through CMake
The continuable build is driven by CMake and the project exposes CMake
interface targets when being used by external projects:
\code{.cmake}
add_subdirectory(continuable)
# continuable provides an interface target which makes it's
# headers available to all projects using the continuable library.
target_link_libraries(my_project continuable)
\endcode
When adding the continuable subdirectory as git submodule this should work
out of the box.
\code{.sh}
git submodule add https://github.com/Naios/continuable.git
\endcode
\attention On POSIX platforms you are required to link your application against
a corresponding thread library, otherwise `std::futures` won't work
properly, this is done automatically by the provided CMake project.
Additionally the CMake project exports a `continuable` target which is
importable through the \code{.cmake}find_package\endcode CMake command
when installed:
\code{.sh}
mkdir build
cd build
cmake ..
cmake --build . --target INSTALL --config Release
\endcode
In your `CMakeLists.txt`:
\code{.cmake}
find_package(continuable REQUIRED)
\endcode
\subsection installation-installation-pkg Through package managers
Continuable is present in some package managers and registries already,
and might be installed from there.
\attention The project is still looking for contributions that would help
to make it available from various package managers in order to
make the installation easier.
\subsection installation-installation-amalgamation By using the amalgamation header
For major versions there is an amalgamation header provided which can be
included without any dependency:
- [4.0.0](https://gist.githubusercontent.com/Naios/25d731aa4707d35a9bcec507f3cb9038/raw/051d2ea07b6704893c7fc9844e8d1c105c6821e0/continuable.hpp)
- [3.0.0](https://gist.githubusercontent.com/Naios/b128ab5028a7eb33e4285c0293573d9f/raw/79fe332964a786b21a8661ef65d07a5669260a3c/continuable.hpp)
\subsection installation-installation-copy By copying the headers
If you don't want to rely on CMake or package managers it is possible to
copy and include the `include` directories of continuable and
[Naios/function2](https://github.com/Naios/function2) into your project.
As an improvement git submodules could be used:
\code{.sh}
git submodule add https://github.com/Naios/continuable.git
git submodule add https://github.com/Naios/function2.git
\endcode
\section installation-unit-tests Building the unit tests
In order to build the unit tests clone the repository recursively
with all submodules:
\code{.sh}
# Shell:
git clone --recursive https://github.com/Naios/continuable.git
\endcode
Then CMake can be used to generate a project solution for testing.
*/
}

View File

@ -0,0 +1,129 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page tutorial-awaiting-continuables Awaiting continuables
\brief Explains how to use the \ref continuable_base together with `co_await`.
\tableofcontents
\section tutorial-awaiting-continuables-usage Using co_await on continuables
Coroutines (`co_await`) are supported by continuables when the underlying
toolchain supports the TS. Currently this works in MSVC 2017 and Clang 5.0.
\attention You have to enable this feature through defining the
`CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE` preprocessor definition.
It is possible to await for any \ref continuable_base as shown below:
\code{.cpp}
int i = co_await cti::make_continuable<int>([](auto&& promise) {
promise.set_value(0);
});
\endcode
As described in \ref continuable_base::operator co_await() a continuable with
multiple arguments as result will wrap its result into a `std::tuple`:
\code{.cpp}
std::tuple<int, int> i = co_await cti::make_ready_continuable(0, 1);
\endcode
\section tutorial-awaiting-continuables-await Using co_await with exceptions
When a \ref continuable_base was resolved through an exception the exception
is rethrown from the `co_await` expression:
\code{.cpp}
try {
auto response = co_await http_request("github.com");
} catch(std::exception const& e) {
// Handle the exception
}
\endcode
\section tutorial-awaiting-continuables-noexcept Using co_await with disabled exceptions
In case the library is configured to use error codes or a custom
error type the return type of the co_await expression is changed.
The result is returned through an internal proxy object which may
be queried for the error object:
| Continuation type | co_await returns |
| : ------------------------------- | : -------------------------------- |
| `continuable_base with <>` | `unspecified<void>` |
| `continuable_base with <Arg>` | `unspecified<Arg>` |
| `continuable_base with <Args...>` | `unspecified<std::tuple<Args...>>` |
The interface of the proxy object is similar to the one proposed in
the `std::expected` proposal:
\code{.cpp}
if (auto&& result = co_await http_request("github.com")) {
auto value = *result;
} else {
cti::error_type error = result.get_exception();
}
auto result = co_await http_request("github.com");
bool(result);
result.is_value();
result.is_exception();
*result; // Same as result.get_value()
result.get_value();
result.get_exception();
\endcode
\section tutorial-awaiting-continuables-return Using continuables as return type from coroutines
It is possible to use a \ref continuable_base as return type from coroutines.
\code{.cpp}
cti::continuable<> resolve_async_void() {
co_await http_request("github.com");
// ...
co_return;
}
cti::continuable<int> resolve_async() {
co_await http_request("github.com");
// ...
co_return 0;
}
\endcode
Additionally it's possible to return multiple return values from coroutines
by wrapping those in a tuple like type:
\code{.cpp}
cti::continuable<int, int, int> resolve_async_multiple() {
co_await http_request("github.com");
// ...
co_return std::make_tuple(0, 1, 2);
}
\endcode
*/
}

View File

@ -0,0 +1,215 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page tutorial-chaining-continuables Chaining continuables
\brief Explains how to chain multiple \ref continuable_base objects together.
\tableofcontents
\section tutorial-chaining-continuables-then Using then and results
A \ref continuable_base provides various methods to continue the asynchronous
call hierarchy. The most important method therefor is
\ref continuable_base::then which changes the object through attaching a
result handler:
\code{.cpp}
http_request("github.com")
.then([] (std::string result) {
// Do something...
});
\endcode
A new \ref continuable_base is created which result depends on the return type
of the handler. For instance it is possible to return plain values or the next
\ref continuable_base to continue the call hierarchy.
See \ref continuable_base::then for details.
\code{.cpp}
mysql_query("SELECT `id`, `name` FROM `users`")
.then([](ResultSet users) {
// Return the next continuable to process ...
return mysql_query("SELECT `id` name FROM `sessions`");
})
.then([](ResultSet sessions) {
// ... or pass multiple values to the next callback using tuples or pairs ...
return std::make_tuple(std::move(sessions), true);
})
.then([](ResultSet sessions, bool is_ok) {
// ... or pass a single value to the next callback ...
return 10;
})
.then([](auto value) {
// ^^^^ Templated callbacks are possible too
})
// ... you may even pass continuables to the `then` method directly:
.then(mysql_query("SELECT * FROM `statistics`"))
.then([](ResultSet result) {
// ...
});
\endcode
\subsection tutorial-chaining-continuables-then-partial Making use of partial argument application
Callbacks passed to \link continuable_base::then then \endlink are only called
with the amount of arguments that are accepted.
\code{.cpp}
(http_request("github.com") && read_file("entries.csv"))
.then([] {
// ^^^^^^ The original signature was <std::string, Buffer>,
// however, the callback is only invoked with the amount of
// arguments it's accepting.
});
\endcode
This makes it possible to attach a callback accepting nothing to every
\ref continuable_base.
\subsection tutorial-chaining-continuables-then-executors Assigning a specific executor to then
Dispatching a callback through a specific executor is a common usage scenario and supported through the second argument of \link continuable_base::then then\endlink:
\code{.cpp}
auto executor = [](auto&& work) {
// Dispatch the work here, store it for later
// invocation or move it to another thread.
std::forward<decltype(work)>(work)();
};
read_file("entries.csv")
.then([](Buffer buffer) {
// ...
}, executor);
// ^^^^^^^^
\endcode
The supplied `work` callable may be stored and moved for later usage
on a possible different thread or execution context.
\note If you are intending to change the context the asynchronous task is
running, you need to specify this inside the function that
supplies the \ref continuable_base through moving the \ref promise_base.
\code{.cpp}
auto http_request(std::string url) {
return cti::make_continuable<std::string>(
[url = std::move(url)](auto&& promise) {
std::async([promise = std::forward<decltype(promise)>(promise)]
() mutable {
promise.set_value("<html> ... </html>");
});
});
}
\endcode
\section tutorial-chaining-continuables-fail Using fail and exceptions
Asynchronous exceptions are supported too. Exceptions that were set through
\ref promise_base::set_exception are forwarded to the first available registered
handler that was attached through \ref continuable_base::fail :
\code{.cpp}
http_request("github.com")
.then([] (std::string result) {
// Is never called if an error occurs
})
.fail([] (std::exception_ptr ptr) {
try {
std::rethrow_exception(ptr);
} catch(std::exception const& e) {
// Handle the exception or error code here
}
});
\endcode
Multiple handlers are allowed to be registered, however the asynchronous call
hierarchy is aborted after the first called fail handler and only the closest
handler below is called.
\note Retrieving a `std::exception_ptr` from a current exception
may be done as shown below:
\code{.cpp}
auto do_sth() {
return cti::make_continuable<void>([=] (auto&& promise) {
try {
// Usually the exception is thrown by another expression
throw std::exception{};
} catch(...) {
promise.set_exception(std::current_exception());
}
});
}
\endcode
Continuable also supports error codes automatically if exceptions are disabled.
Additionally it is possible to specify a custom error type through defining.
\code{.cpp}
http_request("github.com")
.then([] (std::string result) {
// Is never called if an error occurs
})
.fail([] (std::error_condition error) {
error.value();
error.category();
});
\endcode
The \ref error_type will be `std::exception_ptr` except if any of the
following definitions is defined:
- `CONTINUABLE_WITH_NO_EXCEPTIONS`: Define this to use `std::error_condition`
as \ref error_type and to disable exception support.
When exceptions are disabled this definition is set automatically.
- `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`: Define this to use a user defined
error type.
\attention By default unhandled exceptions or errors will trigger
a built-in trap that causes abnormal application shutdown.
In order to prevent this and to allow unhandled errors
define `CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS`.
\section tutorial-chaining-continuables-next Using next to handle all paths
Sometimes it's required to provide a continuation and error handler from the
same object. In order to avoid overloading conflicts there is the special
method \ref continuable_base::next provided.
The exception path overload is marked through the \ref dispatch_error_tag :
\code{.cpp}
struct handle_all_paths {
void operator() (std::string result) {
// ...
}
void operator() (cti::dispatch_error_tag, cti::error_type) {
// ...
}
};
// ...
http_request("github.com")
.next(handle_all_paths{});
\endcode
*/
}

View File

@ -0,0 +1,212 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page tutorial-connecting-continuables Connecting continuables
\brief Explains how to connect various \ref continuable_base objects together
\tableofcontents
\section tutorial-connecting-continuables-strategies Connections and strategies
Connections make it possible to describe the dependencies between an arbitrary
count of \ref continuable_base objects in order resolve a returned
\ref continuable_base as soon as the dependencies are fulfilled.
For each connection strategy \ref continuable_base provides an operator for
for instance \ref continuable_base::operator && and a free function,
\ref when_all for example. Both work similar however the free functions are
capable of working with nested sequences as described in
\ref tutorial-connecting-continuables-nested.
\note Connections between continuable_base objects are ensured to be
<B>thread-safe</B> and <B>wait-free</B> by library design
(when assuming that `std::call_once` is wait-free - which depends
on the toolchain).
\section tutorial-connecting-continuables-aggregated Using aggregated strategies
Aggregated strategies will call the result handler with the compound result of
all connected \ref continuable_base objects.
The compound result is deduced as following. Every continuable_base maps its
result to the result itself as shown below. When multiple continuable_base
objects are connected on the same depth, the result is joined.
See \ref tutorial-connecting-continuables-nested for details.
| Continuation type | In tuple like | In container (`std::vector`) |
| : ------------------------------- | : --------- | : ------------------------------ |
| `continuable_base with <>` | `<none>` | `<none>` |
| `continuable_base with <Arg>` | `Arg` | `Arg` |
| `continuable_base with <Args...>` | `Args...` | `std::tuple<Args...>` |
\subsection tutorial-connecting-continuables-aggregated-all Using the all connection
The *all* strategy invokes all connected continuable at once, it tries to resolve
the connected \ref continuable_base objects as fast as possible.
It is possible to connect multiple \ref continuable_base objects together
through the *all* strategy by using \ref continuable_base::operator && or
\ref when_all. In contrast to the operator the free functions are capable of
workin with plain types and deeply nested \ref continuable_base objects as
described in \ref tutorial-connecting-continuables-nested .
\code{.cpp}
(http_request("github.com") && http_request("travis-ci.org") &&
http_request("atom.io"))
.then([](std::string github, std::string travis,
std::string atom) {
// The callback is called with the
// response of github, travis and atom.
});
\endcode
\subsection tutorial-connecting-continuables-aggregated-seq Using the sequential connection
The *sequential* strategy invokes all connected continuable one after each other,
it tries to resolve the next connected \ref continuable_base objects as soon
as the previous one was resolved.
It is possible to connect multiple \ref continuable_base objects together
through the *sequential* strategy by using \ref continuable_base::operator>> or
\ref when_seq.
\code{.cpp}
(http_request("github.com") >> http_request("travis-ci.org") >>
http_request("atom.io"))
.then([](std::string github, std::string travis,
std::string atom) {
// The requests are invoked sequentially instead
// of requesting all at once.
});
\endcode
\section tutorial-connecting-continuables-any Using the any connection
The any connection strategy is completely different from the two introduces
before: It calls the result handler with the first result or exception
available. All \ref continuable_base objects are required to have the same
types of arguments.
\code{.cpp}
(http_request("github.com") || http_request("travis-ci.org") ||
http_request("atom.io"))
.then([](std::string github_or_travis_or_atom) {
// The callback is called with the first response
// of either github, travis or atom.
});
\endcode
\section tutorial-connecting-continuables-mixed Mixing different strategies
Mixing different strategies through operators and free functions
is natively supported as shown below:
\code{.cpp}
(http_request("github.com") &&
(http_request("travis-ci.org") || http_request("atom.io")))
.then([](std::string github, std::string travis_or_atom) {
// The callback is called with the response of
// github for sure and the second parameter represents
// the response of travis or atom.
});
\endcode
\section tutorial-connecting-continuables-nested Nested continuables and plain types
For every operator that was shown above, there exists a free function
that provides at least the same functionality:
\code{.cpp}
cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
cti::when_seq(http_request("github.com"), http_request("travis-ci.org"));
\endcode
Additionally the free functions are capable of working with continuables deeply
nested inside tuple like objects (`std::tuple`, `std::pair` and `std::array`)
as well as homogeneous containers (`std::vector`, `std::list` etc.).
\code{.cpp}
std::tuple<std::vector<cti::continuable<int>>> outstanding;
// ...
cti::when_all(std::make_tuple(std::move(outstanding),
http_request("github.com")))
.then([](std::tuple<std::tuple<std::vector<int>>,
std::string> result) {
// ...
});
\endcode
Values which are given to such a free function are preserved and
later passed to the result handler:
\code{.cpp}
cti::when_seq(0, 1,
cti::make_ready_continuable(2, 3),
4, 5)
.then([](int r0, int r1, int r2,
int r3, int r4) {
// ...
});
\endcode
When combining both capabilities it's even possible do something like this:
\code{.cpp}
cti::when_all(
cti::make_ready_continuable(0, 1),
2, //< See this plain value
cti::populate(cti::make_ready_continuable(3), // Creates a runtime
cti::make_ready_continuable(4)), // sized container.
std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
.then([](int r0, int r1, int r2, std::vector<int> r34,
std::tuple<std::tuple<int>> r5) {
// ...
});
\endcode
\section tutorial-connecting-continuables-populate Populating a container from arbitrary continuables
\ref populate mainly helps to create a homogeneous container from
a runtime known count of continuables which type isn't exactly known.
All continuables which are passed to this function should be originating
from the same source or a method called with the same types of arguments:
\code{.cpp}
// cti::populate just creates a std::vector from the two continuables.
auto v = cti::populate(cti::make_ready_continuable(0),
cti::make_ready_continuable(1));
for (int i = 2; i < 5; ++i) {
// It is possible to add more continuables
// to the container afterwards
container.emplace_back(cti::make_ready_continuable(i));
}
cti::when_all(std::move(v))
.then([](std::vector<int> resolved) {
// ...
});
\endcode
*/
}

View File

@ -0,0 +1,142 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page tutorial-creating-continuables Creating continuables
\brief Explains how to create a \ref continuable_base.
\tableofcontents
A \ref continuable is an arbitrary instantiation of a \ref continuable_base,
it represents the main class of the library and makes it possible to build up
an asynchronous call hierarchy. When dealing with continuables we usually don't
know its exact type for avoiding expensive type erasure.
The \ref continuable_base is convertible to a \ref continuable which represents
a specified type of the \ref continuable_base on the cost of a type erasure.
\section tutorial-creating-continuables-ready From a value or exception
The library provides \ref make_ready_continuable which may be used to create a
\ref continuable_base from an arbitrary amount of values:
\code{.cpp}
auto one = cti::make_ready_continuable(0);
cti::continuable<int, float, char> three =
cti::make_ready_continuable(0, 1.f, '2');
\endcode
\note In most situations you will never use \ref make_ready_continuable
because the library is capable of working with plain values
directly and thus this burdens unnecessary overhead.
Additionally a \ref continuable_base which resolves with an exception may be
created through \ref make_exceptional_continuable.
\code{.cpp}
cti::continuable<int> c = cti::make_exceptional_continuable(std::exception{});
\endcode
\section tutorial-creating-continuables-promises From a promise taking callable
The main function for creating a \ref continuable_base is \ref make_continuable
which must be invoked with the types of arguments it resolves to.
It accepts a callable object which accepts an arbitrary object
(the \ref promise_base). The \ref promise_base is created by the library and
then passed to the given callback. This is in contrast to the usage of the
standard `std::promise` which is created by the user.
The \ref promise_base exposes methods to resolve it through result values or
through an exception. Below we implement pseudo `http_request` function
which resolves the request asynchronously trough a `std::string`.
\code{.cpp}
auto http_request(std::string url) {
return cti::make_continuable<std::string>(
[url = std::move(url)](auto&& promise) {
// Resolve the promise upon completion of the task.
promise.set_value("<html> ... </html>");
// Or promise.set_exception(...);
});
}
\endcode
An alternative would be a \ref continuable_base with a result of zero arguments:
\code{.cpp}
auto wait_for(std::chrono::milliseconds duration) {
return cti::make_continuable<void>([](auto&& promise) {
// ^^^^
// Resolve the promise later when the duration is over
promise.set_value();
});
\endcode
A \ref continuable_base may resolve with an arbitrary count of result values:
\code{.cpp}
auto resolve_sth() {
return cti::make_continuable<int, int, float, char>(
[](auto&& promise) {
promise.set_value(0, 1, 2.f, '3');
});
\endcode
\warning A \ref promise_base is only usable once and thus invalidated
after it was resolved!
A \ref promise_base always exposes a call operator for resolving it as
like when using \ref promise_base::set_value or
\ref promise_base::set_exception. See \ref promise_base for details.
\note In order to make proper use of a \ref promise_base you should
move it around, store it for later use and resolve it when
the asynchronous task was finished or rejected.
\section tutorial-creating-continuables-invocation The continuable invocation model
An asynchronous call hierarchy that is stored inside the \ref continuable_base
is executed when its result is requested (lazy evaluation) in contrast to
other commonly used implementations such as `std::future` which execute the
asynchronous call hierarchy instantly on creation (eager evaluation).
The lazy evaluation strategy used by continuables has many benefits over
eager evaluation that is used by other common implementations:
- prevention of side effects
- evasion of race conditions
- ensured deterministic behaviour.
The asynchronous call hierarchy is started when the \ref continuable_base is
destructed or the \ref continuable_base::done method is called.
It is possible to disable the automatic start through calling
\ref continuable_base::freeze on the corresponding \ref continuable_base.
\attention A \ref continuable_base is not designed to be stored permanently,
make sure you call \ref continuable_base::freeze before storing it
and start the continuation chain later through calling
\ref continuable_base::done.
*/
}

View File

@ -0,0 +1,99 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page tutorial-promisify-continuables Promisify functions
\brief Explains how to promisify callback taking functions into a \ref continuable_base.
\tableofcontents
\section tutorial-promisify-continuables-promisify Promisification and continuables
The promisification has a longer history in the JavaScript world where
the legacy way of asynchronous programming was the usage of callbacks of the
form \code{.js}function(error, result...)\endcode. The ideal way of dealing
with such an asynchronous result is to return a promise and soon utility
helpers were provided to do so.
The usage of callbacks to represent an asynchronous result is still a popular
way nowadays. Thus the library provides the \ref promisify helper class
which makes it possible to convert callback taking functions of various styles
into one that returns a \ref continuable_base instead.
\note Providing promisified APIs for other popular libraries is out of
scope for this library. However contributions are highly welcome to
add more conversion helpers for other commonly used callback styles.
\section tutorial-promisify-continuables-boost Promisify boost::asio
The default callback style is something like
\code{.js}function(error, result...)\endcode as described above.
Continuable offers the \ref promisify::from method for such callback styles.
See an example of how to promisify boost asio's `async_resolve` below:
\code{.cpp}
auto async_resolve(std::string host, std::string service) {
return cti::promisify<asio::ip::udp::resolver::iterator>::from(
[&](auto&&... args) {
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
},
std::move(host), std::move(service));
}
\endcode
Then it should be possible to use `asio::async_resolve` like this:
\code{.cpp}
async_resolve("127.0.0.1", "daytime")
.then([](udp::resolver::iterator iterator) {
// ...
});
\endcode
\section tutorial-promisify-continuables-boost-ct asio and boost::asio async completion tokens
Since version 4.0.0 continuable also supports asio async initiation tokens.
- Boost 1.70 or asio 1.13.0 is required for the async initiation
- Until boost 1.72 or asio 1.16.0 overhead through an additional type
erasure is added. It is recommended to update to those versions.
The special static variable \ref cti::use_continuable can be appended to any
(boost) asio function that accepts a callback to make it return a \ref continuable_base.
\code{.cpp}
#include <continuable/continuable.hpp>
#include <continuable/external/asio.hpp>
#include <asio.hpp>
// ...
asio::tcp::resolver resolver(...);
resolver.async_resolve("127.0.0.1", "daytime", cti::use_continuable)
.then([](asio::udp::resolver::iterator iterator) {
// ...
});
\endcode
*/
}

View File

@ -0,0 +1,84 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page tutorial-transforming-continuables Transforming continuables
\brief Explains the conversion into other types such as `std::future`.
\tableofcontents
\section tutorial-transforming-continuables-transforms Transforms in general
Sometimes it's required to change a \ref continuable_base object by its whole.
Thus the library offers the ability to apply a transformation to any
\ref continuable_base through using \link continuable_base::apply apply \endlink.
A transformation is a callable object that accepts a \ref continuable_base
and returns an arbitrary object
The library provides several transforms already as part of the
\ref cti::transforms namespace.
\section tutorial-transforming-continuables-wait Synchronous wait
The library is capable of converting every asynchronous control flow
into a synchronous one through \ref transforms::wait, \ref transforms::wait_for
and \ref transforms::wait_until.
\code{.cpp}
std::string response = http_request("github.com")
.apply(cti::transforms::wait());
std::string response = http_request("github.com")
.apply(cti::transforms::wait_for(std::chrono::seconds(5)));
std::string response = http_request("github.com")
.apply(cti::transforms::wait_until(...));
\endcode
The current thread will be blocked until the result has arrived
\section tutorial-transforming-continuables-future Conversion into std::future
The library is capable of converting (*futurizing*) every continuable into a
fitting `std::future` through the \ref transforms::to_future transform:
\code{.cpp}
std::future<std::string> future = http_request("github.com")
.then([](std::string response) {
// Do sth...
return http_request("travis-ci.org") || http_request("atom.io");
})
.apply(cti::transforms::to_future());
// ^^^^^^^^
\endcode
Multiple arguments which can't be handled by `std::future` itself are
converted into `std::tuple`, see \ref transforms::to_future for details.
\code{.cpp}
std::future<std::tuple<std::string, std::string>> future =
(http_request("travis-ci.org") && http_request("atom.io"))
.apply(cti::transforms::to_future());
\endcode
*/
}

41
doc/tutorial.dox Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
namespace cti {
/** \page tutorial Tutorial
\brief An introduction for using the continuable library.
This tutorial will give a short overview about using the library.
We assume that the library is available in your current environment,
if not, follow the \ref installation section in order to get it to work.
This tutorial is split across multiple chapters which should be read in order:
- \subpage tutorial-creating-continuables --- \copybrief tutorial-creating-continuables
- \subpage tutorial-chaining-continuables --- \copybrief tutorial-chaining-continuables
- \subpage tutorial-connecting-continuables --- \copybrief tutorial-connecting-continuables
- \subpage tutorial-transforming-continuables --- \copybrief tutorial-transforming-continuables
- \subpage tutorial-awaiting-continuables --- \copybrief tutorial-awaiting-continuables
- \subpage tutorial-promisify-continuables --- \copybrief tutorial-promisify-continuables
*/
}

View File

@ -1,2 +1,3 @@
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 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,15 +1,28 @@
add_library(asio-example-deps INTERFACE)
target_include_directories(asio-example-deps
INTERFACE
${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(asio-example-deps
INTERFACE
asio
continuable)
add_executable(example-asio 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 asio-example-deps)
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

@ -0,0 +1,176 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#include <asio.hpp>
#include <continuable/continuable.hpp>
#include <continuable/external/asio.hpp>
// Queries the NIST daytime service and prints the current date and time
void daytime_service();
// Checks that a timer async_wait returns successfully
void successful_async_wait();
// Checks that a cancelled timer async_wait fails with
// `asio::error::operation_aborted` and is converted to a default constructed
// cti::exception_t.
void cancelled_async_wait();
// Indicates fatal error due to an unexpected failure in the continuation chain.
void unexpected_error(cti::exception_t);
// Check that the failure was an aborted operation, as expected.
void check_aborted_operation(cti::exception_t);
// Use a strand as executor
void using_strand();
int main(int, char**) {
daytime_service();
successful_async_wait();
cancelled_async_wait();
using_strand();
return 0;
}
void daytime_service() {
using asio::ip::tcp;
asio::io_context ioc(1);
tcp::resolver resolver(ioc);
tcp::socket socket(ioc);
std::string buf;
resolver.async_resolve("time.nist.gov", "daytime", cti::use_continuable)
.then([&socket](tcp::resolver::results_type endpoints) {
return asio::async_connect(socket, endpoints, cti::use_continuable);
})
.then([&socket, &buf] {
return asio::async_read_until(socket, asio::dynamic_buffer(buf), '\n',
cti::use_continuable);
})
.then([&buf](std::size_t) {
puts("Continuation successfully got a daytime response:");
puts(buf.c_str());
})
.fail(&unexpected_error);
ioc.run();
}
void successful_async_wait() {
asio::io_context ioc(1);
asio::steady_timer t(ioc);
t.expires_after(std::chrono::seconds(1));
t.async_wait(cti::use_continuable)
.then([] {
puts("Continuation succeeded after 1s as expected!");
})
.fail(&unexpected_error);
ioc.run();
}
void cancelled_async_wait() {
asio::io_context ioc(1);
asio::steady_timer t(ioc);
t.expires_after(std::chrono::seconds(999));
t.async_wait(cti::use_continuable)
.then([] {
puts("This should never be called");
std::terminate();
})
.fail(&check_aborted_operation);
t.cancel_one();
ioc.run();
}
void unexpected_error(cti::exception_t e) {
if (!bool(e)) {
puts("Continuation failed with unexpected cancellation!");
std::terminate();
}
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
try {
std::rethrow_exception(e);
} catch (std::exception const& ex) {
puts("Continuation failed with unexpected exception");
puts(ex.what());
} catch (...) {
// Rethrow the exception to the asynchronous unhandled exception handler
std::rethrow_exception(std::current_exception());
}
#else
puts("Continuation failed with unexpected error");
puts(e.message().data());
#endif
std::terminate();
}
void check_aborted_operation(cti::exception_t ex) {
if (bool(ex)) {
unexpected_error(ex);
} else {
puts("Continuation failed due to aborted async operation, as expected.");
}
}
template <typename T>
auto through_post(T& postable) {
return [&postable](auto&& work) mutable {
asio::post(postable, std::forward<decltype(work)>(work));
};
}
void using_strand() {
asio::io_context ioc(1);
asio::io_context::strand strand(ioc);
asio::post(strand, cti::use_continuable).then([]() {
puts("Dispatched through initiation token");
});
cti::async_on(
[]() mutable {
puts("Dispatched through executor");
},
through_post(strand));
ioc.run();
}

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,12 +34,30 @@
#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_;
@ -49,7 +67,11 @@ struct functional_io_service {
auto trough_post() noexcept { auto trough_post() noexcept {
return [&](auto&& work) mutable { return [&](auto&& work) mutable {
asio::post(service_, std::forward<decltype(work)>(work)); asio::post(service_,
[work = std::forward<decltype(work)>(work)]() mutable {
std::move(work)();
// .. or: work.set_value();
});
}; };
} }
@ -61,7 +83,8 @@ struct functional_io_service {
} }
auto async_resolve(std::string host, std::string service) { auto async_resolve(std::string host, std::string service) {
return cti::promisify<asio::ip::udp::resolver::iterator>::from_asio( return cti::promisify<asio::ip::udp::resolver::iterator>::with(
error_code_remapper(),
[&](auto&&... args) { [&](auto&&... args) {
resolver_.async_resolve(std::forward<decltype(args)>(args)...); resolver_.async_resolve(std::forward<decltype(args)>(args)...);
}, },
@ -89,7 +112,7 @@ int main(int, char**) {
// auto socket = std::make_shared<udp::socket>(service); // auto socket = std::make_shared<udp::socket>(service);
// socket->async_send_to() // socket->async_send_to()
}) })
.fail([](cti::error_type /*error*/) { .fail([](cti::exception_t /*error*/) {
// ... // ...
}); });

View File

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

View File

@ -1,5 +1,5 @@
/* /*
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,43 +25,32 @@
#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 [](auto&& callback) { return cti::make_ready_continuable<std::string>("<html>...</html>");
// ...
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 [](auto&& callback) { return cti::make_ready_continuable(ResultSet{});
// ...
callback.set_value(ResultSet{});
};
} }
cti::continuable<Buffer> read_file(std::string /*url*/) { cti::continuable<Buffer> read_file(std::string /*url*/) {
return [](auto&& callback) { return cti::make_ready_continuable(Buffer{});
// ...
callback.set_value(Buffer{});
};
} }
struct a { struct functional_executor {
auto post() const { auto post() const {
return [](auto&&) {}; return [](auto&&) {};
} }
}; };
int main(int, char**) { int main(int, char**) {
a e; functional_executor 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*/) {
// ... // ...
@ -71,23 +60,15 @@ int main(int, char**) {
// ... // ...
}, executor->post()); }, executor->post());
// ---------- // clang-format on
auto c1 = http_request("github.com") && http_request("atom.io") ; http_request("github.com") && http_request("atom.io");
http_request("github.com") || http_request("atom.io");
http_request("github.com") >> http_request("atom.io");
auto c2 = http_request("github.com") || http_request("atom.io") ; // clang-format off
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*/) {
@ -98,8 +79,7 @@ int main(int, char**) {
// ... // ...
}); });
// ----------
// clang-format on // clang-format on
return 0; return 0;
} }

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,60 +32,35 @@
#define CONTINUABLE_BASE_HPP_INCLUDED #define CONTINUABLE_BASE_HPP_INCLUDED
#include <cassert> #include <cassert>
#include <cstdint> #include <cstddef>
#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>
#include <continuable/detail/awaiting.hpp> #if defined(CONTINUABLE_HAS_COROUTINE)
#include <continuable/detail/base.hpp> # include <continuable/detail/other/coroutines.hpp>
#include <continuable/detail/composition-all.hpp> #endif // defined(CONTINUABLE_HAS_COROUTINE)
#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 {
/// Represents a tag which can be placed first in a signature /// \defgroup Base Base
/// in order to overload callables with the asynchronous result /// provides classes and functions to create continuable_base objects.
/// 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
using detail::base::is_continuable; template <typename T>
using is_continuable = detail::base::is_continuable<T>;
/// The main class of the continuable library, it provides the functionality /// 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.
@ -105,8 +80,7 @@ using detail::base::is_continuable;
/// \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. You may copy a continuable which underlying /// of a method call.
/// 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
@ -118,6 +92,10 @@ 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;
@ -125,25 +103,41 @@ 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
detail::util::ownership ownership_; 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, detail::util::ownership ownership) explicit continuable_base(Data data, ownership ownership)
: data_(std::move(data)), ownership_(std::move(ownership)) { : data_(std::move(data))
} , 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) : data_(std::move(data)) { explicit continuable_base(Data 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 OData, std::enable_if_t<std::is_convertible< template <typename OtherData,
std::decay_t<OData>, Data>::value>* = nullptr> std::enable_if_t<detail::base::can_accept_continuation<
continuable_base(OData&& data) : data_(std::forward<OData>(data)) { Data, Annotation,
} detail::traits::unrefcv_t<OtherData>>::value>* = nullptr>
/* implicit */ continuable_base(OtherData&& data)
: data_(
detail::base::proxy_continuable<Annotation,
detail::traits::unrefcv_t<OtherData>>(
std::forward<OtherData>(data))) {}
/// Constructor taking the data of other continuable_base objects
/// while erasing the hint.
///
/// This constructor makes it possible to replace the internal data object of
/// the continuable by any object which is useful for type-erasure.
template <typename OData,
std::enable_if_t<std::is_convertible<
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
/* implicit */ continuable_base(continuable_base<OData, Annotation>&& other)
: data_(std::move(other).consume()) {}
/// Constructor taking the data of other continuable_base objects /// Constructor taking the data of other continuable_base objects
/// while erasing the hint. /// while erasing the hint.
@ -151,16 +145,15 @@ 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>
continuable_base(continuable_base<OData, OAnnotation>&& other) /* implicit */ continuable_base(continuable_base<OData, OAnnotation>&& other)
: continuable_base(std::move(other).materialize().consume_data()) { : continuable_base(std::move(other).finish().consume()) {}
}
/// \cond false /// \cond false
continuable_base(continuable_base&&) = default; continuable_base(continuable_base&&) = default;
continuable_base(continuable_base const&) = default; continuable_base(continuable_base const&) = delete;
continuable_base& operator=(continuable_base&&) = default; continuable_base& operator=(continuable_base&&) = default;
continuable_base& operator=(continuable_base const&) = default; continuable_base& operator=(continuable_base const&) = delete;
/// \endcond /// \endcond
/// The destructor automatically invokes the continuable_base /// The destructor automatically invokes the continuable_base
@ -232,12 +225,17 @@ 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")
@ -259,6 +257,17 @@ 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
@ -267,7 +276,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).materialize(), std::forward<T>(callback), std::move(*this).finish(), std::forward<T>(callback),
std::forward<E>(executor)); std::forward<E>(executor));
} }
@ -293,7 +302,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).materialize())); detail::base::wrap_continuation(std::move(continuation).finish()));
} }
/// Main method of the continuable_base to catch exceptions and error codes /// Main method of the continuable_base to catch exceptions and error codes
@ -308,12 +317,18 @@ public:
/// ```cpp /// ```cpp
/// http_request("github.com") /// http_request("github.com")
/// .then([](std::string github) { }) /// .then([](std::string github) { })
/// .fail([](std::exception_ptr ptr) { /// .fail([](std::exception_ptr ep) {
/// // Handle the error here /// // Check whether the exception_ptr is valid (not default constructed)
/// try { /// // if bool(ep) == false this means that the operation was cancelled
/// std::rethrow_exception(ptr); /// // by the user or application (promise.set_canceled() or
/// } catch (std::exception& e) { /// // make_cancelling_continuable()).
/// e.what(); // Handle the exception /// if (ep) {
/// // Handle the error here
/// try {
/// std::rethrow_exception(ep);
/// } catch (std::exception& e) {
/// e.what(); // Handle the exception
/// }
/// } /// }
/// }); /// });
/// ``` /// ```
@ -333,14 +348,26 @@ 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<detail::base::handle_results::no, return detail::base::chain_continuation<
detail::base::handle_errors::plain>( detail::base::handle_results::no, detail::base::handle_errors::forward>(
std::move(*this).materialize(), std::forward<T>(callback), std::move(*this).finish(),
detail::base::strip_exception_arg(std::forward<T>(callback)),
std::forward<E>(executor)); std::forward<E>(executor));
} }
@ -362,9 +389,11 @@ 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) && {
continuation.freeze(); return std::move(*this) //
return std::move(*this).fail([continuation = std::move(continuation)]( .fail([continuation = std::move(continuation).freeze()] //
error_type) mutable { std::move(continuation).done(); }); (exception_t) mutable {
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
@ -378,7 +407,7 @@ public:
/// void operator() (std::string result) { /// void operator() (std::string result) {
/// // ... /// // ...
/// } /// }
/// void operator() (cti::dispatch_error_tag, cti::error_type) { /// void operator() (cti::exception_arg_t, cti::exception_t) {
/// // ... /// // ...
/// } /// }
/// ///
@ -399,22 +428,56 @@ public:
E&& executor = detail::types::this_thread_executor_tag{}) && { 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).materialize(), detail::base::handle_errors::forward>(std::move(*this).finish(),
std::forward<T>(callback), std::forward<T>(callback),
std::forward<E>(executor)); std::forward<E>(executor));
} }
/// A method which allows to apply this continuable to the given callable. /// Returns a continuable_base which continues its invocation through the
/// given executor.
/// ///
/// \param transform A transform which shall accept this continuable /// \returns Returns a continuable_base of the same type.
///
/// \since 4.2.0
template <typename E>
auto via(E&& executor) && {
return std::move(*this).next(
[](auto&&... args) {
return make_result(std::forward<decltype(args)>(args)...);
},
std::forward<E>(executor));
}
/// Returns a continuable_base which will have its signature converted
/// to the given Args.
///
/// A signature can only be converted if it can be partially applied
/// from the previous one as shown below:
/// ```cpp
/// continuable<long> c = make_ready_continuable(0, 1, 2).as<long>();
/// ```
///
/// \returns Returns a continuable_base with an asynchronous return type
/// matching the given Args.
///
/// \since 4.0.0
template <typename... Args>
auto as() && {
return std::move(*this).then(detail::base::convert_to<Args...>{});
}
/// A method which allows to apply a callable object to this continuable.
///
/// \param transform A callable objects that transforms a continuable
/// to a different object.
/// ///
/// \returns Returns the result of the given transform when this /// \returns Returns the result of the given transform when this
/// continuable is passed into it. /// continuable is passed into it.
/// ///
/// \since 2.0.0 /// \since 4.0.0
template <typename T> template <typename T>
auto apply(T&& transform) && { auto apply(T&& transform) && {
return std::forward<T>(transform)(std::move(*this).materialize()); return std::forward<T>(transform)(std::move(*this).finish());
} }
/// The pipe operator | is an alias for the continuable::then method. /// The pipe operator | is an alias for the continuable::then method.
@ -430,22 +493,6 @@ public:
return std::move(*this).then(std::forward<T>(right)); 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.
/// ///
@ -473,8 +520,8 @@ public:
/// }); /// });
/// ``` /// ```
/// ///
/// \note The continuable_base objects are invoked parallel on the /// \note The continuable_base objects are invoked all at onve,
/// current thread, because the `all` strategy tries to resolve /// 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.
@ -482,8 +529,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::composition::connect( return detail::connection::connect(
detail::composition::composition_strategy_all_tag{}, std::move(*this), detail::connection::connection_strategy_all_tag{}, std::move(*this),
std::move(right)); std::move(right));
} }
@ -513,15 +560,15 @@ public:
/// }); /// });
/// ``` /// ```
/// ///
/// \note The continuable_base objects are invoked parallel on the /// \note The continuable_base objects are invoked all at once,
/// current thread, however, the callback is only called once with /// however, the callback is only called once with
/// the first result which becomes available. /// the first result or exception 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::composition::connect( return detail::connection::connect(
detail::composition::composition_strategy_any_tag{}, std::move(*this), detail::connection::connection_strategy_any_tag{}, std::move(*this),
std::move(right)); std::move(right));
} }
@ -542,15 +589,15 @@ public:
/// }); /// });
/// ``` /// ```
/// ///
/// \note The continuable_base objects are invoked sequential on the /// \note The continuable_base objects are invoked sequential one after
/// current thread. Parallel invocation is also supported through the /// the previous one was finished. Parallel invocation is also
/// continuable_base::operator&& method. /// supported through the 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::composition::seq::sequential_connect(std::move(*this), return detail::connection::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
@ -564,7 +611,64 @@ public:
/// ///
/// \since 1.0.0 /// \since 1.0.0
void done() && { void done() && {
detail::base::finalize_continuation(std::move(*this)); detail::base::finalize_continuation(std::move(*this).finish());
}
/// Materializes the continuation expression template and finishes
/// the current applied strategy such that the resulting continuable
/// will always be a concrete type and Continuable::is_concrete holds.
///
/// This can be used in the case where we are chaining continuations lazily
/// through a strategy, for instance when applying operators for
/// expressing connections and then want to return a materialized
/// continuable_base which uses the strategy respectively.
/// ```cpp
/// auto do_both() {
/// return (wait(10s) || wait_key_pressed(KEY_SPACE)).finish();
/// }
///
/// // Without a call to finish() this would lead to
/// // an unintended evaluation strategy:
/// do_both() || wait(5s);
/// ```
///
/// \note When using a type erased continuable_base such as
/// `continuable<...>` this method doesn't need to be called
/// since the continuable_base is materialized automatically
/// on conversion.
///
/// \since 4.0.0
auto finish() && {
return annotation_trait::finish(std::move(*this));
}
/// Returns true when the continuable can provide its result immediately,
/// and its lazy invocation would be side-effect free.
///
/// \since 4.0.0
bool is_ready() const noexcept {
return annotation_trait::is_ready(*this);
}
/// Invalidates the continuable and returns its immediate invocation result.
///
/// This method can be used to specialize the asynchronous control flow
/// based on whether the continuable_base is_ready at every time,
/// which is true for a continuable created through the following functions:
/// - make_ready_continuable
/// - make_exceptional_continuable
///
/// \returns A result<Args...> where Args... represent the current
/// asynchronous parameters or the currently stored exception.
///
/// \attention unpack requires that continuable_base::is_ready returned true
/// in a previous check, otherwise its behaviour is unspecified.
///
/// \since 4.0.0
auto unpack() && {
assert(ownership_.is_acquired());
assert(is_ready());
return detail::base::attorney::query(std::move(*this).finish());
} }
/// Predicate to check whether the cti::continuable_base is frozen or not. /// Predicate to check whether the cti::continuable_base is frozen or not.
@ -609,23 +713,82 @@ public:
return std::move(*this); return std::move(*this);
} }
#ifdef CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE /// \cond false
#if defined(CONTINUABLE_HAS_COROUTINE)
/// \endcond
/// Implements the operator for awaiting on continuables using `co_await`.
///
/// The operator is only enabled if `CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE`
/// is defined and the toolchain supports experimental coroutines.
///
/// The return type of the `co_await` expression is specified as following:
/// | Continuation type | co_await returns |
/// | : ------------------------------- | : -------------------------------- |
/// | `continuable_base with <>` | `void` |
/// | `continuable_base with <Arg>` | `Arg` |
/// | `continuable_base with <Args...>` | `std::tuple<Args...>` |
///
/// When exceptions are used the usage is as intuitive as shown below:
/// ```cpp
/// // Handling the exception isn't required and
/// // the try catch clause may be omitted.
/// try {
/// std::string response = co_await http_request("github.com");
/// } (std::exception& e) {
/// e.what();
/// }
/// ```
///
/// In case the library is configured to use error codes or a custom
/// exception type the return type of the co_await expression is changed.
/// The result is returned through a cti::result<...>.
/// | Continuation type | co_await returns |
/// | : ------------------------------- | : -------------------------------- |
/// | `continuable_base with <>` | `result<void>` |
/// | `continuable_base with <Arg>` | `result<Arg>` |
/// | `continuable_base with <Args...>` | `result<Args...>` |
///
/// \note Using continuable_base as return type for coroutines
/// is supported. The coroutine is initially stopped and
/// resumed when the continuation is requested in order to
/// keep the lazy evaluation semantics of the continuable_base.
/// ```cpp
/// cti::continuable<> resolve_async_void() {
/// co_await http_request("github.com");
/// // ...
/// co_return;
/// }
///
/// cti::continuable<int> resolve_async() {
/// co_await http_request("github.com");
/// // ...
/// co_return 0;
/// }
/// ```
/// It's possible to return multiple return values from coroutines
/// by wrapping those in a tuple like type:
/// ```cpp
/// cti::continuable<int, int, int> resolve_async_multiple() {
/// co_await http_request("github.com");
/// // ...
/// co_return std::make_tuple(0, 1, 2);
/// }
/// ```
///
/// \since 2.0.0
auto operator co_await() && { auto operator co_await() && {
return detail::awaiting::create_awaiter(std::move(*this).materialize()); return detail::awaiting::create_awaiter(std::move(*this).finish());
} }
#endif // CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE /// \cond false
#endif // defined(CONTINUABLE_HAS_COROUTINE)
/// \endcond
private: private:
void release() noexcept { void release() noexcept {
ownership_.release(); ownership_.release();
} }
auto materialize() && { Data&& consume() && {
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_);
@ -636,20 +799,20 @@ private:
} }
}; };
/// Creates a continuable_base from a callback taking function. /// Creates a continuable_base from a promise/callback taking function.
/// ///
/// \tparam Args The types (signature hint) the given callback is called with. /// \tparam Args The types (signature hint) the given promise is resolved with.
/// * **Some arguments** indicate the types the callback will be invoked with. /// * **Some arguments** indicate the types the promise will be invoked with.
/// ```cpp /// ```cpp
/// auto ct = cti::make_continuable<int, std::string>([](auto&& callback) { /// auto ct = cti::make_continuable<int, std::string>([](auto&& promise) {
/// std::forward<decltype(callback)>(callback)(200, "<html>...</html>"); /// promise.set_value(200, "<html>...</html>");
/// }); /// });
/// ``` /// ```
/// * **void as argument** indicates that the callback will be invoked /// * `void` **as argument** indicates that the promise will be invoked
/// with no arguments: /// with no arguments:
/// ```cpp /// ```cpp
/// auto ct = cti::make_continuable<void>([](auto&& callback) { /// auto ct = cti::make_continuable<void>([](auto&& promise) {
/// std::forward<decltype(callback)>(callback)(); /// promise.set_value();
/// }); /// });
/// ``` /// ```
/// * **No arguments** Since version 3.0.0 make_continuable always requires /// * **No arguments** Since version 3.0.0 make_continuable always requires
@ -660,13 +823,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&& callback) { /// auto ct = cti::make_continuable([](auto&& promise) {
/// std::forward<decltype(callback)>(callback)(0.f, 'c'); /// promise.set_value(0.f, 'c');
/// }); /// });
/// ///
/// // However, you are allowed to do this: /// // However, you are allowed to do this:
/// continuable<float, char> ct = [](auto&& callback) { /// cti::continuable<float, char> ct = [](auto&& promise) {
/// std::forward<decltype(callback)>(callback)(0.f, 'c'); /// promise.set_value(callback)(0.f, 'c');
/// }; /// };
/// ``` /// ```
/// ///
@ -706,94 +869,273 @@ private:
/// }); /// });
/// ``` /// ```
/// ///
/// \returns A continuable_base with unknown template parameters which /// \returns A continuable_base with unspecified template parameters which
/// wraps the given continuation. /// 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. /// you need to apply type erasure through the
/// \link cti::continuable continuable\endlink or
/// \link cti::promise promise\endlink facilities.
/// ///
/// \note You should always turn the callback into a r-value if possible /// \note You should always turn the callback/promise into a r-value if possible
/// (`std::move` or `std::forward`) for qualifier correct invokation. /// (`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>
auto make_continuable(Continuation&& continuation) { constexpr 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( return detail::base::attorney::create_from(
std::forward<Continuation>(continuation), std::forward<Continuation>(continuation),
detail::hints::extract(detail::traits::identity<Args...>{}), typename detail::hints::from_args<Args...>::type{},
detail::util::ownership{}); detail::util::ownership{});
} }
/// Returns a continuable with no result which instantly resolves /// Returns a continuable_base with no result which instantly resolves
/// the promise with no values. /// 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 unneccessary recursive /// Try not to use it since it causes unnecessary recursive
/// function calls. /// function calls.
/// ///
/// \since 3.0.0 /// \since 3.0.0
template <typename... Args> template <typename... Args>
constexpr auto make_ready_continuable() { auto make_ready_continuable(Args&&... args) {
return make_continuable<void>([](auto&& promise) { return detail::base::attorney::create_from_raw(
std::forward<decltype(promise)>(promise).set_value(); detail::base::ready_continuation<detail::traits::unrefcv_t<Args>...>(
}); result<detail::traits::unrefcv_t<Args>...>::from(
std::forward<Args>(args)...)),
detail::identity<detail::traits::unrefcv_t<Args>...>{},
detail::util::ownership{});
} }
/// Returns a continuable with one result value which instantly resolves /// Returns a continuable_base with the parameterized result which instantly
/// 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 3.0.0 /// \since 4.0.0
template <typename... Signature, typename Exception> template <typename... Signature>
constexpr auto make_exceptional_continuable(Exception&& exception) { auto make_cancelling_continuable() {
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_continuable<Signature...>( // ... return make_exceptional_continuable<Signature...>(exception_t{});
[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
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,25 +21,32 @@
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_COMPOSITIONS_HPP_INCLUDED #ifndef CONTINUABLE_CONNECTIONS_HPP_INCLUDED
#define CONTINUABLE_COMPOSITIONS_HPP_INCLUDED #define CONTINUABLE_CONNECTIONS_HPP_INCLUDED
#include <initializer_list>
#include <memory>
#include <utility> #include <utility>
#include <vector>
#include <continuable/detail/composition-all.hpp> #include <continuable/detail/connection/connection-all.hpp>
#include <continuable/detail/composition-any.hpp> #include <continuable/detail/connection/connection-any.hpp>
#include <continuable/detail/composition-seq.hpp> #include <continuable/detail/connection/connection-seq.hpp>
#include <continuable/detail/composition.hpp> #include <continuable/detail/connection/connection.hpp>
#include <continuable/detail/range.hpp> #include <continuable/detail/traversal/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
@ -55,8 +62,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
/// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3), /// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
/// cti::make_ready_continuable(4)}, /// cti::make_ready_continuable(4)), // sized container.
/// 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) {
@ -69,8 +76,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::composition::apply_composition( return detail::connection::apply_connection(
detail::composition::composition_strategy_all_tag{}, detail::connection::connection_strategy_all_tag{},
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
@ -79,8 +86,9 @@ auto when_all(Args&&... args) {
/// to a temporary `std::vector` which is then passed to when_all. /// to a temporary `std::vector` which is then passed to when_all.
/// ///
/// ```cpp /// ```cpp
/// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0), /// // cti::populate just creates a std::vector from the two continuables.
/// cti::make_ready_continuable(1)}; /// auto v = cti::populate(cti::make_ready_continuable(0),
/// 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) {
@ -123,8 +131,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
/// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3), /// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
/// cti::make_ready_continuable(4)}, /// cti::make_ready_continuable(4)), // sized container.
/// 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) {
@ -137,8 +145,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::composition::apply_composition( return detail::connection::apply_connection(
detail::composition::composition_strategy_seq_tag{}, detail::connection::connection_strategy_seq_tag{},
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
@ -147,8 +155,9 @@ auto when_seq(Args&&... args) {
/// to a temporary `std::vector` which is then passed to when_seq. /// to a temporary `std::vector` which is then passed to when_seq.
/// ///
/// ```cpp /// ```cpp
/// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0), /// // cti::populate just creates a std::vector from the two continuables.
/// cti::make_ready_continuable(1)}; /// auto v = cti::populate(cti::make_ready_continuable(0),
/// 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) {
@ -191,8 +200,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
/// std::vector<cti::continuable<int>>{cti::make_ready_continuable(3), /// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
/// cti::make_ready_continuable(4)}, /// cti::make_ready_continuable(4)), // sized container.
/// 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) {
/// // ... /// // ...
@ -204,8 +213,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::composition::apply_composition( return detail::connection::apply_connection(
detail::composition::composition_strategy_any_tag{}, detail::connection::connection_strategy_any_tag{},
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
@ -214,8 +223,9 @@ auto when_any(Args&&... args) {
/// to a temporary `std::vector` which is then passed to when_all. /// to a temporary `std::vector` which is then passed to when_all.
/// ///
/// ```cpp /// ```cpp
/// std::vector<cti::continuable<int>> v{cti::make_ready_continuable(0), /// // cti::populate just creates a std::vector from the two continuables.
/// cti::make_ready_continuable(1)}; /// auto v = cti::populate(cti::make_ready_continuable(0),
/// 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) {
@ -242,6 +252,50 @@ 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_COMPOSITIONS_HPP_INCLUDED #endif // CONTINUABLE_CONNECTIONS_HPP_INCLUDED

View File

@ -0,0 +1,97 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_COROUTINE_HPP_INCLUDED
#define CONTINUABLE_COROUTINE_HPP_INCLUDED
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-types.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/features.hpp>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
# include <exception>
#endif // CONTINUABLE_HAS_EXCEPTIONS
#if defined(CONTINUABLE_HAS_COROUTINE)
# include <continuable/detail/other/coroutines.hpp>
namespace cti {
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
/// Is thrown from co_await expressions if the awaited continuable is canceled
///
/// Default constructed exception types that are returned by a cancelled
/// continuable are converted automatically to await_canceled_exception when
/// being returned by a co_await expression.
///
/// The await_canceled_exception gets converted again to a default constructed
/// exception type if it becomes unhandled inside a coroutine which
/// returns a continuable_base.
/// ```cpp
/// continuable<> cancelled_coroutine() {
/// co_await make_cancelling_continuable<void>();
///
/// co_return;
/// }
///
/// // ...
///
/// cancelled_coroutine().fail([](exception_t e) {
/// assert(bool(e) == false);
/// });
/// ```
///
/// \since 4.1.0
using await_canceled_exception = detail::awaiting::await_canceled_exception;
# endif // CONTINUABLE_HAS_EXCEPTIONS
} // namespace cti
/// \cond false
// As far as I know there is no other way to implement this specialization...
// NOLINTNEXTLINE(cert-dcl58-cpp)
namespace std {
# if defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
namespace experimental {
# endif // defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
template <typename Data, typename... Args, typename... FunctionArgs>
struct coroutine_traits<
cti::continuable_base<Data, cti::detail::identity<Args...>>,
FunctionArgs...> {
using promise_type = cti::detail::awaiting::promise_type<
cti::continuable<Args...>, cti::promise<Args...>, Args...>;
};
# if defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
} // namespace experimental
# endif // defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
} // namespace std
/// \endcond
#endif // defined(CONTINUABLE_HAS_COROUTINE)
#endif // CONTINUABLE_COROUTINE_HPP_INCLUDED

View File

@ -0,0 +1,41 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_OPERATIONS_HPP_INCLUDED
#define CONTINUABLE_OPERATIONS_HPP_INCLUDED
/// \defgroup Operations Operations
/// provides functions to work with asynchronous control flows.
#include <continuable/operations/async.hpp>
#include <continuable/operations/loop.hpp>
#include <continuable/operations/split.hpp>
#endif // CONTINUABLE_OPERATIONS_HPP_INCLUDED

View File

@ -0,0 +1,141 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_PRIMITIVES_HPP_INCLUDED
#define CONTINUABLE_PRIMITIVES_HPP_INCLUDED
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/utility/identity.hpp>
namespace cti {
/// \defgroup Primitives Primitives
/// provides basic tag types for creating a customized callbacks
/// and continuations.
///
/// For the callback and the continuation `Args...` represents the
/// asynchronous result:
/// ```cpp
/// template<typename... Args>
/// struct continuation {
/// void operator() (callback<Args...>);
/// bool operator() (cti::is_ready_arg_t) const;
/// result<Args...> operator() (cti::unpack_arg_t);
/// };
/// ```
/// ```cpp
/// template<typename... Args>
/// struct callback {
/// void operator() (Args...) &&;
/// void operator() (cti::exception_arg_t, cti::exception_t) &&;
/// };
/// ```
/// \{
/// Represents the tag type that is used to specify the signature hint
/// of a continuable_base or promise_base.
///
/// \since 4.0.0
template <typename... Args>
using signature_arg_t = detail::identity<Args...>;
/// Represents the tag type that is used to query the continuation
/// for whether it resolves the callback instantly with its arguments
/// without having side effects.
///
/// \since 4.0.0
struct is_ready_arg_t {};
/// Represents the tag type that is used to unpack the result of a continuation.
///
/// \attention It's required that the query of is_ready_arg_t returns true,
/// otherwise the behaviour when unpacking is unspecified.
///
/// \since 4.0.0
struct unpack_arg_t {};
/// \copydoc unpack_arg_t
///
/// \deprecated The query_arg_t was deprecated because of
/// its new naming unpack_arg_t.
///
[[deprecated("The dispatch_error_tag was replaced by unpack_arg_t and will "
"be removed in a later major version!")]] //
typedef unpack_arg_t query_arg_t;
/// Represents the tag type that is used to disambiguate the
/// callback operator() in order to take the exception asynchronous chain.
///
/// \note see continuable::next for details.
///
/// \since 4.0.0
struct exception_arg_t {};
/// \copydoc exception_arg_t
///
/// \deprecated The dispatch_error_tag was deprecated in order to move closer
/// to the types specified in the "A Unified Future" proposal
/// especially regarding naming types similar.
///
[[deprecated("The dispatch_error_tag was replaced by exception_arg_t and will "
"be removed in a later major version!")]] //
typedef exception_arg_t dispatch_error_tag;
/// Represents the type that is used as exception type
///
/// By default this type deduces to `std::exception_ptr`.
/// If `CONTINUABLE_WITH_NO_EXCEPTIONS` is defined the type
/// will be a `std::error_condition`.
/// A custom error type may be set through
/// defining `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`.
///
/// \since 4.0.0
using exception_t = detail::types::exception_t;
/// \copydoc exception_t
///
/// \deprecated The error_type was deprecated in order to move closer
/// to the types specified in the "A Unified Future" proposal
/// especially regarding naming types similar.
///
[[deprecated("The error_type was replaced by exception_t and will "
"be removed in a later major version!")]] //
typedef exception_t error_type;
/// Represents the type that is used to disable the special meaning of types
/// which are returned by a asynchronous result handler.
/// See cti::plain for details.
///
/// \since 4.0.0
template <typename T>
using plain_t = detail::types::plain_tag<T>;
/// \}
} // namespace cti
#endif // CONTINUABLE_PRIMITIVES_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,20 +31,32 @@
#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/hints.hpp> #include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/types.hpp> #include <continuable/detail/core/types.hpp>
#include <continuable/detail/util.hpp> #include <continuable/detail/utility/traits.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>
@ -52,7 +64,7 @@ class promise_base
/// \cond false /// \cond false
; ;
template <typename Data, typename... Args> template <typename Data, typename... Args>
class promise_base<Data, detail::hints::signature_hint_tag<Args...>> class promise_base<Data, detail::identity<Args...>>
: detail::util::non_copyable : detail::util::non_copyable
/// \endcond /// \endcond
{ // clang-format on { // clang-format on
@ -63,53 +75,141 @@ class promise_base<Data, detail::hints::signature_hint_tag<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, std::enable_if_t<std::is_convertible< template <typename OData,
std::decay_t<OData>, Data>::value>* = nullptr> std::enable_if_t<std::is_convertible<
promise_base(OData&& data) : data_(std::forward<OData>(data)) { detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
/* implicit */ promise_base(OData&& data) : data_(std::forward<OData>(data)) {
}
/// Assignment operator accepting any object convertible to the data object
template <typename OData,
std::enable_if_t<std::is_convertible<
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
promise_base& operator=(OData&& data) {
data_ = std::forward<OData>(data);
return *this;
} }
/// Resolves the continuation with the given values. /// 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()(detail::types::dispatch_error_tag tag, void operator()(exception_arg_t tag, exception_t exception) && noexcept {
detail::types::error_type exception) && assert(data_);
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(detail::types::error_type exception) noexcept { void set_exception(exception_t exception) noexcept {
std::move(data_)(detail::types::dispatch_error_tag{}, std::move(exception)); assert(data_);
std::move(data_)(exception_arg_t{}, std::move(exception));
data_ = nullptr;
}
/// Resolves the continuation with the cancellation token which is represented
/// by a default constructed exception_t.
///
/// \throws This method never throws an exception.
///
/// \attention This method may only be called once,
/// when the promise is valid operator bool() returns true.
/// Calling this method will invalidate the promise such that
/// subsequent calls to operator bool() will return false.
/// This behaviour is only consistent in promise_base and
/// non type erased promises may behave differently.
/// Invoking an invalid promise_base is undefined!
///
/// \since 4.0.0
void set_canceled() noexcept {
assert(data_);
std::move(data_)(exception_arg_t{}, exception_t{});
data_ = nullptr;
}
/// Returns true if the continuation is valid (non empty).
///
/// \throws This method never throws an exception.
///
/// \since 4.0.0
explicit operator bool() const noexcept {
return bool(data_);
} }
}; };
/// \}
} // namespace cti } // 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
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,15 +33,19 @@
#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_asio`: Converts callback taking callable types into continuables /// - `from`: Converts callback taking callable types into continuables
/// which pass an error code as first parameter and the rest of /// 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.
@ -59,7 +63,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_asio( /// return cti::promisify<asio::ip::udp::resolver::iterator>::from(
/// [&](auto&&... args) { /// [&](auto&&... args) {
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...); /// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
/// }, /// },
@ -67,20 +71,50 @@ public:
/// } /// }
/// ``` /// ```
/// ///
/// If the error code which is passed as first parameter is set there are /// A given error variable is converted to the used error type.
/// two behaviours depending whether exceptions are enabled: /// If this isn't possible you need to create a custom resolver callable
/// - If exceptions are enabled the error type is passed via /// object \see with for details.
/// 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_asio(Callable&& callable, Args&&... args) { static auto from(Callable&& callable, Args&&... args) {
return helper::template from<detail::convert::promisify_asio>( return helper::template from(detail::convert::default_resolver(),
std::forward<Callable>(callable), std::forward<Args>(args)...); std::forward<Callable>(callable),
std::forward<Args>(args)...);
}
/// \copybrief from
///
/// This modification of \ref from additionally takes a resolver callable
/// object which is used to resolve the promise from the given result.
///
/// See an example of how to promisify boost asio's async_resolve below:
/// ```cpp
/// auto async_resolve(std::string host, std::string service) {
/// return cti::promisify<asio::ip::udp::resolver::iterator>::with(
/// [](auto&& promise, auto&& e, auto&&... args) {
/// if (e) {
/// promise.set_exception(std::forward<decltype(e)>(e));
/// } else {
/// promise.set_value(std::forward<decltype(args)>(args)...);
/// }
/// },
/// [&](auto&&... args) {
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
/// },
/// std::move(host), std::move(service));
/// }
/// ```
///
/// \since 4.0.0
template <typename Resolver, typename Callable, typename... Args>
static auto with(Resolver&& resolver, Callable&& callable, Args&&... args) {
return helper::template from(std::forward<Resolver>(resolver),
std::forward<Callable>(callable),
std::forward<Args>(args)...);
} }
}; };
/// \}
} // namespace cti } // namespace cti
#endif // CONTINUABLE_PROMISIFY_HPP_INCLUDED #endif // CONTINUABLE_PROMISIFY_HPP_INCLUDED

View File

@ -0,0 +1,356 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_RESULT_HPP_INCLUDED
#define CONTINUABLE_RESULT_HPP_INCLUDED
#include <type_traits>
#include <utility>
#include <continuable/continuable-primitives.hpp>
#include <continuable/detail/utility/result-trait.hpp>
#include <continuable/detail/utility/result-variant.hpp>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/utility/util.hpp>
namespace cti {
/// \defgroup Result Result
/// provides the \ref result class and corresponding utility functions to work
/// with the result of an asynchronous operation which can possibly yield:
/// - *no result*: If the operation didn't finish
/// - *a value*: If the operation finished successfully
/// - *an exception*: If the operation finished with an exception
/// or was cancelled.
/// \{
/// A tag which represents present void values in result.
///
/// \since 4.0.0
using void_arg_t = detail::void_arg_t;
/// A class which is convertible to any \ref result and that definitely holds no
/// value so the real result gets invalidated when this object is passed to it.
///
/// \since 4.0.0
///
struct empty_result {};
/// A class which is convertible to any \ref result and that definitely holds
/// a default constructed exception which signals the cancellation of the
/// asynchronous control flow.
///
/// \since 4.0.0
///
struct cancellation_result {};
/// A class which is convertible to any result and that holds
/// an exception which is then passed to the converted result object.
///
/// \since 4.0.0
///
class exceptional_result {
exception_t exception_;
public:
exceptional_result() = delete;
exceptional_result(exceptional_result const&) = default;
exceptional_result(exceptional_result&&) = default;
exceptional_result& operator=(exceptional_result const&) = default;
exceptional_result& operator=(exceptional_result&&) = default;
~exceptional_result() = default;
explicit exceptional_result(exception_t exception)
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
: exception_(std::move(exception)) {}
exceptional_result& operator=(exception_t exception) {
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
exception_ = std::move(exception);
return *this;
}
/// Sets an exception
void set_exception(exception_t exception) {
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
exception_ = std::move(exception);
}
/// Returns the contained exception
exception_t& get_exception() & noexcept {
return exception_;
}
/// \copydoc get_exception
exception_t const& get_exception() const& noexcept {
return exception_;
}
/// \copydoc get_exception
exception_t&& get_exception() && noexcept {
return std::move(exception_);
}
};
/// The result class can carry the three kinds of results an asynchronous
/// operation possibly can return, it's implemented in a variant like
/// data structure which is also specialized to hold arbitrary arguments.
///
/// The result can be in the following three states:
/// - *no result*: If the operation didn't finish
/// - *a value*: If the operation finished successfully
/// - *an exception*: If the operation finished with an exception
/// or was cancelled.
///
/// The interface of the result object is similar to the one proposed in
/// the `std::expected` proposal:
/// ```cpp
/// result<std::string> result = make_result("Hello World!");
/// bool(result);
/// result.is_value();
/// result.is_exception();
/// *result; // Same as result.get_value()
/// result.get_value();
/// result.get_exception();
/// ```
///
/// \since 4.0.0
///
template <typename... T>
class result {
using trait_t = detail::result_trait<T...>;
template <typename... Args>
explicit result(detail::init_result_arg_t arg, Args&&... values)
: variant_(arg, trait_t::wrap(std::forward<Args>(values)...)) {}
explicit result(detail::init_exception_arg_t arg, exception_t exception)
: variant_(arg, std::move(exception)) {}
public:
using value_t = typename trait_t::value_t;
using value_placeholder_t = typename trait_t::surrogate_t;
template <typename FirstArg, typename... Args>
explicit result(FirstArg&& first, Args&&... values)
: variant_(detail::init_result_arg_t{},
trait_t::wrap(std::forward<FirstArg>(first),
std::forward<Args>(values)...)) {}
result() = default;
result(result const&) = delete;
result(result&&) = default;
result& operator=(result const&) = delete;
result& operator=(result&&) = default;
~result() = default;
explicit result(exception_t exception)
: variant_(detail::init_exception_arg_t{}, std::move(exception)) {}
/* implicit */ result(empty_result) {}
/* implicit */ result(exceptional_result exceptional_result)
: variant_(detail::init_exception_arg_t{},
std::move(exceptional_result.get_exception())) {}
/* implicit */ result(cancellation_result)
: variant_(detail::init_exception_arg_t{}, exception_t{}) {}
result& operator=(empty_result) {
variant_.set_empty();
return *this;
}
result& operator=(value_placeholder_t value) {
variant_.set_value(std::move(value));
return *this;
}
result& operator=(exceptional_result exception) {
variant_.set_exception(std::move(exception.get_exception()));
return *this;
}
result& operator=(cancellation_result) {
variant_.set_exception({});
return *this;
}
/// Set the result to an empty state
void set_empty() {
variant_.set_empty();
}
/// Set the result to a the state which holds the corresponding value
void set_value(T... values) {
variant_.set_value(trait_t::wrap(std::move(values)...));
}
/// Set the result into a state which holds the corresponding exception
void set_exception(exception_t exception) {
variant_.set_exception(std::move(exception));
}
/// Set the result into a state which holds the cancellation token
void set_canceled() {
variant_.set_exception(exception_t{});
}
/// Returns true if the state of the result is empty
bool is_empty() const noexcept {
return variant_.is_empty();
}
/// Returns true if the state of the result holds the result
bool is_value() const noexcept {
return variant_.is_value();
}
/// Returns true if the state of the result holds a present exception
bool is_exception() const noexcept {
return variant_.is_exception();
}
/// \copydoc is_value
explicit constexpr operator bool() const noexcept {
return is_value();
}
/// Returns the values of the result, if the result doesn't hold the value
/// the behaviour is undefined but will assert in debug mode.
decltype(auto) get_value() & noexcept {
return trait_t::unwrap(variant_.get_value());
}
///\copydoc get_value
decltype(auto) get_value() const& noexcept {
return trait_t::unwrap(variant_.get_value());
}
///\copydoc get_value
decltype(auto) get_value() && noexcept {
return trait_t::unwrap(std::move(variant_.get_value()));
}
///\copydoc get_value
decltype(auto) operator*() & noexcept {
return get_value();
}
///\copydoc get_value
decltype(auto) operator*() const& noexcept {
return get_value();
}
///\copydoc get_value
decltype(auto) operator*() && noexcept {
return std::move(variant_.get_value());
}
/// Returns the exception of the result, if the result doesn't hold an
/// exception the behaviour is undefined but will assert in debug mode.
exception_t& get_exception() & noexcept {
return variant_.get_exception();
}
/// \copydoc get_exception
exception_t const& get_exception() const& noexcept {
return variant_.get_exception();
}
/// \copydoc get_exception
exception_t&& get_exception() && noexcept {
return std::move(variant_.get_exception());
}
/// Creates a present result from the given values
static result from(T... values) {
return result{detail::init_result_arg_t{}, std::move(values)...};
}
/// Creates a present result from the given exception
static result from(exception_arg_t, exception_t exception) {
return result{detail::init_exception_arg_t{}, std::move(exception)};
}
/// Creates an empty result
static result empty() {
return result{empty_result{}};
}
private:
detail::result_variant<value_placeholder_t> variant_;
};
/// Returns the value at position I of the given result
template <std::size_t I, typename... T>
decltype(auto) get(result<T...>& result) {
return detail::result_trait<T...>::template get<I>(result);
}
/// \copydoc get
template <std::size_t I, typename... T>
decltype(auto) get(result<T...> const& result) {
return detail::result_trait<T...>::template get<I>(result);
}
/// \copydoc get
template <std::size_t I, typename... T>
decltype(auto) get(result<T...>&& result) {
return detail::result_trait<T...>::template get<I>(std::move(result));
}
/// Creates a present result from the given values.
///
/// This could be used to pass the result of the next handler to the same
/// asynchronous path it came from as shown below:
/// ```cpp
/// make_ready_continuable().next([&](auto&&... args) {
/// result<> captured = make_result(std::forward<decltype(args)>(args)...);
/// return shutdown().then([captured = std::move(captured)]() mutable {
/// return std::move(captured);
/// });
/// });
/// ```
///
/// \since 4.0.0
template <typename... T,
typename Result = result<detail::traits::unrefcv_t<T>...>>
Result make_result(T&&... values) {
return Result::from(std::forward<T>(values)...);
}
/// Creates an exceptional_result from the given exception.
///
/// \copydetails make_result
///
/// \since 4.0.0
inline exceptional_result make_result(exception_arg_t, exception_t exception) {
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
return exceptional_result{std::move(exception)};
}
/// \}
} // namespace cti
namespace std {
// The GCC standard library defines tuple_size as class and struct which
// triggers a warning here.
#if defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmismatched-tags"
#endif
template <typename... Args>
struct tuple_size<cti::result<Args...>>
: std::integral_constant<size_t, sizeof...(Args)> {};
template <std::size_t I, typename... Args>
struct tuple_element<I, cti::result<Args...>>
: tuple_element<I, tuple<Args...>> {};
#if defined(__clang__)
# pragma GCC diagnostic pop
#endif
} // namespace std
#endif // CONTINUABLE_RESULT_HPP_INCLUDED

View File

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

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,22 @@
#ifndef CONTINUABLE_TRANSFORMS_HPP_INCLUDED #ifndef CONTINUABLE_TRANSFORMS_HPP_INCLUDED
#define CONTINUABLE_TRANSFORMS_HPP_INCLUDED #define CONTINUABLE_TRANSFORMS_HPP_INCLUDED
#include <continuable/detail/transforms.hpp> #include <continuable/transforms/wait.hpp>
#include <continuable/detail/types.hpp> #include <continuable/transforms/future.hpp>
namespace cti { namespace cti {
/// A callable tag object which marks a wrapped callable object /// \defgroup Transforms Transforms
/// as continuable transformation which enables some useful overloads. /// provides utilities to convert
/// /// \link continuable_base continuable_bases\endlink to other
/// \since 3.0.0 /// types such as (`std::future`).
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
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,31 +32,37 @@
#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. /// if an element is visited synchronously through \ref traverse_pack_async.
/// ///
/// \since 3.0.0 /// \since 3.0.0
using detail::traversal::async_traverse_visit_tag; using async_traverse_visit_tag = detail::traversal::async_traverse_visit_tag;
/// 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
/// if an element is visited after the traversal was detached. /// visited after the traversal was detached through \ref traverse_pack_async.
/// ///
/// \since 3.0.0 /// \since 3.0.0
using detail::traversal::async_traverse_detach_tag; using async_traverse_detach_tag = detail::traversal::async_traverse_detach_tag;
/// A tag which is passed to the `operator()` of the visitor /// A tag which is passed to the `operator()` of the visitor if the
/// if the asynchronous pack traversal was finished. /// asynchronous pack traversal was finished through \ref traverse_pack_async.
/// ///
/// \since 3.0.0 /// \since 3.0.0
using detail::traversal::async_traverse_complete_tag; using async_traverse_complete_tag =
detail::traversal::async_traverse_complete_tag;
/// A tag to identify that a mapper shall be constructed in-place /// A tag to identify that a mapper shall be constructed in-place
/// from the first argument passed. /// from the first argument passed to \ref traverse_pack_async.
/// ///
/// \since 3.0.0 /// \since 3.0.0
using detail::traversal::async_traverse_in_place_tag; template <typename T>
using async_traverse_in_place_tag =
detail::traversal::async_traverse_in_place_tag<T>;
/// Traverses the pack with the given visitor in an asynchronous way. /// Traverses the pack with the given visitor in an asynchronous way.
/// ///
@ -68,7 +74,7 @@ using detail::traversal::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. /// /// it may return false to suspend the current control flow.
/// /// 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) {
@ -118,6 +124,7 @@ 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
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,10 +34,13 @@
#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:
@ -106,6 +109,7 @@ 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
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,78 +31,72 @@
#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-trait.hpp> #include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-promise-base.hpp>
#include <continuable/detail/other/erasure.hpp>
namespace cti { namespace cti {
// clang-format off /// \defgroup Types Types
namespace detail { /// provides the \link cti::continuable continuable\endlink and \link
/// A function which isn't size adjusted and copyable /// cti::promise promise\endlink facility for type erasure.
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...>;
/// We adjust the internal capacity of the outer function wrapper so /// Deduces to the preferred continuation capacity for a possible
/// we don't have to allocate twice when using `continuable<...>`. /// small functor optimization. The given capacity size is always enough to
template<typename... Args> /// to avoid any allocation when storing a ready continuable_base.
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.
/// ///
/// Usable like: `cti::continuable<int, float>` /// \since 4.0.0
template <typename... Args> template <typename... Args>
using continuable = typename detail::trait_of< using continuation_capacity = detail::erasure::continuation_capacity<Args...>;
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: `unique_continuable<int, float>` /// Usable like: `continuable<int, float>`
///
/// \note You can always define your own continuable with a type erasure of
/// choice, the type erasure wrapper just needs to accept a
/// callable object with a continuation signature as specified
/// in the Primitives section.
///
/// \since 1.0.0
template <typename... Args> template <typename... Args>
using unique_continuable = typename detail::unique_trait_of< using continuable = continuable_base<detail::erasure::continuation<Args...>, //
Args... signature_arg_t<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 = typename detail::unique_trait_of< using promise = promise_base<detail::erasure::callback<Args...>, //
Args... signature_arg_t<Args...>>;
>::promise;
// TODO channel /// Defines a non-copyable type erasure which is capable of carrying
// TODO sink /// callable objects passed to executors.
///
// clang-format on /// The work behaves like a `promise<>` but the work type erasure uses extra
/// stack space for small object optimization.
/// Additionally the outstanding work can be resolved through an exception.
///
/// \note You can always define your own cancelable_work with a type erasure of
/// choice, the type erasure wrapper just needs to accept a
/// callable object which is callable with a `void()` and
/// `void(exception_arg_t, exception_t)` signature.
///
/// \since 4.0.0
using work = promise_base<detail::erasure::work, //
signature_arg_t<>>;
/// \}
} // namespace cti } // 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
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,10 +46,13 @@
namespace cti {} namespace cti {}
#include <continuable/continuable-base.hpp> #include <continuable/continuable-base.hpp>
#include <continuable/continuable-compositions.hpp> #include <continuable/continuable-connections.hpp>
#include <continuable/continuable-coroutine.hpp>
#include <continuable/continuable-operations.hpp>
#include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-promise-base.hpp> #include <continuable/continuable-promise-base.hpp>
#include <continuable/continuable-promisify.hpp> #include <continuable/continuable-promisify.hpp>
#include <continuable/continuable-trait.hpp> #include <continuable/continuable-result.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

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

View File

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

View File

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

View File

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

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,77 +21,102 @@
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_COMPOSITION_REMAPPING_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED
#define CONTINUABLE_DETAIL_COMPOSITION_REMAPPING_HPP_INCLUDED #define CONTINUABLE_DETAIL_CONNECTION_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/base.hpp> #include <continuable/detail/core/base.hpp>
#include <continuable/detail/container-category.hpp> #include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/traits.hpp>
namespace cti { namespace cti {
namespace detail { namespace detail {
namespace composition { namespace connection {
/// 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 composition is finished. /// the whole pack when the connection 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 remapping { namespace aggregated {
/// Guards a type to be default constructible,
/// and wraps it into an optional type if it isn't default constructible.
template <typename T>
using lazy_value_t = std::conditional_t<std::is_default_constructible<T>::value,
T, result<T>>;
template <typename T>
decltype(auto) unpack_lazy(std::true_type /*is_default_constructible*/,
T&& value) {
return std::forward<T>(value);
}
template <typename T>
T&& unpack_lazy(std::false_type /*is_default_constructible*/,
result<T>&& value) {
assert(value.is_value() &&
"The connection was finalized before all values were present!");
return std::move(value).get_value();
}
template <typename Continuable> template <typename Continuable>
class continuable_box; class continuable_box;
template <typename Data> template <typename Data>
class continuable_box<continuable_base<Data, hints::signature_hint_tag<>>> { class continuable_box<continuable_base<Data, identity<>>> {
continuable_base<Data, hints::signature_hint_tag<>> continuable_; continuable_base<Data, identity<>> continuable_;
public: public:
explicit continuable_box( explicit continuable_box(continuable_base<Data, identity<>>&& continuable)
continuable_base<Data, hints::signature_hint_tag<>>&& continuable) : continuable_(std::move(continuable)) {}
: continuable_(std::move(continuable)) {
auto const& peek() const {
return continuable_;
} }
continuable_base<Data, hints::signature_hint_tag<>>&& fetch() { auto&& fetch() {
return std::move(continuable_); return std::move(continuable_);
} }
void assign() { void assign() {}
}
auto unbox() && { auto unbox() && {
return spread_this(); return spread_this();
} }
}; };
template <typename Data, typename First>
class continuable_box<
continuable_base<Data, hints::signature_hint_tag<First>>> {
continuable_base<Data, hints::signature_hint_tag<First>> continuable_; template <typename Data, typename First>
First first_; class continuable_box<continuable_base<Data, identity<First>>> {
continuable_base<Data, identity<First>> continuable_;
lazy_value_t<First> first_;
public: public:
explicit continuable_box( explicit continuable_box(
continuable_base<Data, hints::signature_hint_tag<First>>&& continuable) continuable_base<Data, identity<First>>&& continuable)
: continuable_(std::move(continuable)) { : continuable_(std::move(continuable)) {}
auto const& peek() const {
return continuable_;
} }
continuable_base<Data, hints::signature_hint_tag<First>>&& fetch() { auto&& fetch() {
return std::move(continuable_); return std::move(continuable_);
} }
@ -100,27 +125,27 @@ public:
} }
auto unbox() && { auto unbox() && {
return std::move(first_); return unpack_lazy(std::is_default_constructible<First>{},
std::move(first_));
} }
}; };
template <typename Data, typename First, typename Second, typename... Rest> template <typename Data, typename First, typename Second, typename... Rest>
class continuable_box< class continuable_box<
continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>>> { continuable_base<Data, identity<First, Second, Rest...>>> {
continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>> continuable_base<Data, identity<First, Second, Rest...>> continuable_;
continuable_; lazy_value_t<std::tuple<First, Second, Rest...>> args_;
std::tuple<First, Second, Rest...> args_;
public: public:
explicit continuable_box( explicit continuable_box(
continuable_base<Data, continuable_base<Data, identity<First, Second, Rest...>>&& continuable)
hints::signature_hint_tag<First, Second, Rest...>>&& : continuable_(std::move(continuable)) {}
continuable)
: continuable_(std::move(continuable)) { auto const& peek() const {
return continuable_;
} }
continuable_base<Data, hints::signature_hint_tag<First, Second, Rest...>>&& auto&& fetch() {
fetch() {
return std::move(continuable_); return std::move(continuable_);
} }
@ -130,9 +155,13 @@ public:
} }
auto unbox() && { auto unbox() && {
return traits::unpack(std::move(args_), [](auto&&... args) { return traits::unpack(
return spread_this(std::forward<decltype(args)>(args)...); [](auto&&... args) {
}); return spread_this(std::forward<decltype(args)>(args)...);
},
unpack_lazy(
std::is_default_constructible<std::tuple<First, Second, Rest...>>{},
std::move(args_)));
} }
}; };
@ -181,29 +210,41 @@ constexpr auto unbox_continuables(Args&&... args) {
namespace detail { namespace detail {
template <typename Callback, typename Data> template <typename Callback, typename Data>
void finalize_impl(traits::identity<void>, Callback&& callback, Data&&) { constexpr auto finalize_impl(identity<void>, Callback&& callback, Data&&) {
std::forward<Callback>(callback)(); return std::forward<Callback>(callback)();
} }
template <typename... Args, typename Callback, typename Data> template <typename... Args, typename Callback, typename Data>
void finalize_impl(traits::identity<std::tuple<Args...>>, Callback&& callback, constexpr auto finalize_impl(identity<std::tuple<Args...>>, Callback&& callback,
Data&& data) { Data&& data) {
// Call the final callback with the cleaned result // Call the final callback with the cleaned result
traits::unpack(unbox_continuables(std::forward<Data>(data)), return traits::unpack(std::forward<Callback>(callback),
std::forward<Callback>(callback)); unbox_continuables(std::forward<Data>(data)));
} }
struct hint_mapper {
template <typename... T>
constexpr auto operator()(T...) -> identity<T...> {
return {};
}
};
} // namespace detail } // namespace detail
template <typename Callback, typename Data> template <typename Callback, typename Data>
void finalize_data(Callback&& callback, Data&& data) { constexpr auto finalize_data(Callback&& callback, Data&& data) {
using result_t = decltype(unbox_continuables(std::forward<Data>(data))); 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(traits::identity<std::decay_t<result_t>>{}, return detail::finalize_impl(identity<std::decay_t<result_t>>{},
std::forward<Callback>(callback), std::forward<Callback>(callback),
std::forward<Data>(data)); std::forward<Data>(data));
} }
} // namespace remapping
} // namespace composition template <typename Data>
constexpr auto hint_of_data() {
return decltype(finalize_data(detail::hint_mapper{}, std::declval<Data>())){};
}
} // namespace aggregated
} // namespace connection
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti
#endif // CONTINUABLE_DETAIL_COMPOSITION_REMAPPING_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED

View File

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

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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_COMPOSITION_ANY_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED
#define CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED #define CONTINUABLE_DETAIL_CONNECTION_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/base.hpp> #include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/container-category.hpp> #include <continuable/detail/core/base.hpp>
#include <continuable/detail/hints.hpp> #include <continuable/detail/core/types.hpp>
#include <continuable/detail/traits.hpp> #include <continuable/detail/traversal/container-category.hpp>
#include <continuable/detail/types.hpp> #include <continuable/detail/utility/traits.hpp>
namespace cti { namespace cti {
namespace detail { namespace detail {
namespace composition { namespace connection {
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, traits::identity<T>) { static auto deduce_one(std::false_type, 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, traits::identity<T> id) { static auto deduce_one(std::true_type, identity<T> id) {
return hints::hint_of(id); return base::annotation_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>,
traits::identity<T> id) { 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>,
traits::identity<T>) { 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>{},
traits::identity<element_t>{}); identity<element_t>{});
} }
template <typename First, typename... T> template <typename First, typename... T>
@ -125,19 +125,18 @@ 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...>,
traits::identity<T>) { 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>()))>>{},
traits::identity< identity<std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{})...);
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>,
traits::identity<T> id) { 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);
@ -151,7 +150,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) const { void operator()(Continuable&& continuable) {
// 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())
@ -160,36 +159,46 @@ struct continuable_dispatcher {
}; };
} // namespace any } // namespace any
/// Finalizes the any logic of a given composition struct connection_strategy_any_tag {};
template <> template <>
struct composition_finalizer<composition_strategy_any_tag> { struct is_connection_strategy<connection_strategy_any_tag> // ...
template <typename Composition> : std::true_type {};
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>>{})){};
}
template <typename Composition> /// Finalizes the any logic of a given connection
static auto finalize(Composition&& composition) { template <>
return [composition = std::forward<Composition>(composition)]( struct connection_finalizer<connection_strategy_any_tag> {
auto&& callback) mutable { template <typename Connection>
static auto finalize(Connection&& connection, util::ownership ownership) {
constexpr auto const signature = decltype(any::result_deducer::deduce(
traversal::container_category_of_t<std::decay_t<Connection>>{},
identity<std::decay_t<Connection>>{})){};
using submitter_t = return base::attorney::create_from(
any::any_result_submitter<std::decay_t<decltype(callback)>>; [connection =
std::forward<Connection>(connection)](auto&& callback) mutable {
using submitter_t =
any::any_result_submitter<std::decay_t<decltype(callback)>>;
// Create the submitter which calls the given callback once at the // 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(composition)); std::move(connection));
}; },
signature, std::move(ownership));
} }
}; };
} // namespace composition } // namespace connection
/// Specialization for a connection annotation
template <>
struct annotation_trait<connection::connection_strategy_any_tag>
: connection::connection_annotation_trait<
connection::connection_strategy_any_tag> {};
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti
#endif // CONTINUABLE_DETAIL_COMPOSITION_ANY_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED

View File

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

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,55 +21,41 @@
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_COMPOSITION_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED
#define CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED #define CONTINUABLE_DETAIL_CONNECTION_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/base.hpp> #include <continuable/detail/core/base.hpp>
#include <continuable/detail/traits.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 {
namespace detail { namespace detail {
/// The namespace `composition` offers methods to chain continuations together /// The namespace `connection` offers methods to chain continuations together
/// with `all`, `any` or `seq` logic. /// with `all`, `any` or `seq` logic.
namespace composition { namespace connection {
struct composition_strategy_all_tag {};
struct composition_strategy_any_tag {};
struct composition_strategy_seq_tag {};
template <typename T> template <typename T>
struct is_composition_strategy // ... struct is_connection_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 composition /// Adds the given continuation tuple to the left connection
template <typename... LeftArgs, typename... RightArgs> template <typename... LeftArgs, typename... RightArgs>
auto chain_composition(std::tuple<LeftArgs...> leftPack, auto chain_connection(std::tuple<LeftArgs...> leftPack,
std::tuple<RightArgs...> rightPack) { std::tuple<RightArgs...> rightPack) {
return traits::merge(std::move(leftPack), std::move(rightPack)); return std::tuple_cat(std::move(leftPack), std::move(rightPack));
} }
/// Normalizes a continuation to a tuple holding an arbitrary count of /// Normalizes a continuation to a tuple holding an arbitrary count of
@ -80,7 +66,7 @@ auto chain_composition(std::tuple<LeftArgs...> leftPack,
/// -> make a tuple containing the continuable as only element /// -> 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_composition_strategy<Annotation>::value>* = nullptr> std::enable_if_t<!is_connection_strategy<Annotation>::value>* = nullptr>
auto normalize(Strategy /*strategy*/, auto normalize(Strategy /*strategy*/,
continuable_base<Data, Annotation>&& continuation) { continuable_base<Data, Annotation>&& continuation) {
@ -91,13 +77,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_composition_strategy<Annotation>::value>* = nullptr> std::enable_if_t<is_connection_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(base::attorney::materialize(std::move(continuation))); return std::make_tuple(std::move(continuation).finish());
} }
/// - 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
@ -106,7 +92,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_data(std::move(continuation)); return base::attorney::consume(std::move(continuation));
} }
/// Entry function for connecting two continuables with a given strategy. /// Entry function for connecting two continuables with a given strategy.
@ -123,53 +109,39 @@ auto connect(Strategy strategy, continuable_base<LData, LAnnotation>&& left,
// Make the new data which consists of a tuple containing // Make the new data which consists of a tuple containing
// all connected continuables. // all connected continuables.
auto data = chain_composition(normalize(strategy, std::move(left)), auto data = chain_connection(normalize(strategy, std::move(left)),
normalize(strategy, std::move(right))); 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(std::move(data), strategy, ownership_); return base::attorney::create_from_raw(std::move(data), strategy, ownership_);
} }
/// All strategies should specialize this class in order to provide: /// 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 composition is finished. /// is invoked with the callback to call when the connection is finished.
/// - A static method hint that returns the new signature hint. /// - A static method hint that returns the new signature hint.
template <typename Strategy> template <typename Strategy>
struct composition_finalizer; struct connection_finalizer;
/// Finalizes the connection logic of a given composition template <typename Strategy>
template <typename Data, typename Strategy> struct connection_annotation_trait {
auto finalize_composition(continuable_base<Data, Strategy>&& continuation) { /// Finalizes the connection logic of a given connection
using finalizer = composition_finalizer<Strategy>; template <typename Continuable>
static auto finish(Continuable&& continuable) {
using finalizer = connection_finalizer<Strategy>;
util::ownership ownership = base::attorney::ownership_of(continuation); util::ownership ownership = base::attorney::ownership_of(continuable);
auto composition = base::attorney::consume_data(std::move(continuation)); auto connection =
base::attorney::consume(std::forward<Continuable>(continuable));
// Retrieve the new signature hint // Return a new continuable which
constexpr auto const signature = return finalizer::finalize(std::move(connection), std::move(ownership));
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>> {
static constexpr auto apply(continuable_base<Data, Strategy>&& continuable) { template <typename Continuable>
return finalize_composition(std::move(continuable)); static bool is_ready(Continuable const& /*continuable*/) noexcept {
return false;
} }
}; };
@ -199,13 +171,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 base::attorney::materialize(std::forward<Continuable>(continuable)); return std::forward<Continuable>(continuable).finish();
} }
}; };
template <typename Strategy, typename... Args> template <typename Strategy, typename... Args>
auto apply_composition(Strategy, Args&&... args) { auto apply_connection(Strategy, Args&&... args) {
using finalizer = composition_finalizer<Strategy>; using finalizer = connection_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
@ -213,19 +185,13 @@ auto apply_composition(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 composition = map_pack(prepare_continuables{ownership}, auto connection = map_pack(prepare_continuables{ownership},
std::make_tuple(std::forward<Args>(args)...)); std::make_tuple(std::forward<Args>(args)...));
// 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));
} }
} // namespace composition } // namespace connection
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti
#endif // CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,51 +21,33 @@
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_HINTS_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED
#define CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED #define CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED
#include <type_traits> #include <type_traits>
#include <continuable/detail/core/types.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 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>
constexpr auto extract(traits::identity<HintArgs...> hint) { struct from_args : std::common_type<signature_arg_t<HintArgs...>> {};
return hint; template <>
} 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_HINTS_HPP_INCLUDED #endif // CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,9 +31,10 @@
#ifndef CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED #ifndef CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED #define CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED
#include <type_traits>
#include <utility> #include <utility>
#include <continuable/detail/features.hpp> #include <continuable/detail/features.hpp>
#include <continuable/detail/utility/identity.hpp>
#ifndef CONTINUABLE_WITH_CUSTOM_ERROR_TYPE #ifndef CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS #ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
@ -51,32 +52,37 @@ namespace detail {
/// Contains types used globally across the library /// Contains types used globally across the library
namespace types { namespace types {
#ifdef CONTINUABLE_WITH_CUSTOM_ERROR_TYPE #ifdef CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
using error_type = CONTINUABLE_WITH_CUSTOM_ERROR_TYPE; using exception_t = CONTINUABLE_WITH_CUSTOM_ERROR_TYPE;
#else // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE #else // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS #ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
/// Represents the error type when exceptions are enabled /// Represents the exception type when exceptions are enabled
using error_type = std::exception_ptr; using exception_t = std::exception_ptr;
#else // CONTINUABLE_WITH_NO_EXCEPTIONS #else // CONTINUABLE_WITH_NO_EXCEPTIONS
/// Represents the error type when exceptions are disabled /// Represents the error type when exceptions are disabled
using error_type = std::error_condition; using exception_t = std::error_condition;
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS #endif // CONTINUABLE_WITH_NO_EXCEPTIONS
#endif // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE #endif // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
/// A tag which is used to execute the continuation inside the current thread /// A tag which is used to execute the continuation inside the current thread
struct this_thread_executor_tag {}; struct this_thread_executor_tag {};
/// A tag which is used to continue with an error
struct dispatch_error_tag {};
/// Marks a given callable object as transformation /// Marks a given callable object as transformation
template <typename T> template <typename T>
class transform : T { class plain_tag {
T value_;
public: public:
explicit transform(T callable) : T(std::move(callable)) { template <typename O, std::enable_if_t<std::is_constructible<
T, std::decay_t<O>>::value>* = nullptr>
/* implicit */ plain_tag(O&& value) : value_(std::forward<O>(value)) {
}
explicit plain_tag(T value) : value_(std::move(value)) {
} }
using T::operator(); T&& consume() && {
return std::move(value_);
}
}; };
} // namespace types } // namespace types
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti

View File

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

View File

@ -0,0 +1,242 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
#define CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
#include <array>
#include <utility>
#include <continuable/continuable-base.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/features.hpp>
#if defined(ASIO_STANDALONE)
# include <asio/async_result.hpp>
# include <asio/error.hpp>
# include <asio/error_code.hpp>
# include <asio/version.hpp>
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
# include <asio/system_error.hpp>
# endif
# if (ASIO_VERSION < 101300) // 1.13.0
# define CTI_DETAIL_ASIO_HAS_NO_INTEGRATION
# elif (ASIO_VERSION < 101600) // 1.16.0 (boost 1.72 baseline)
# define CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION
# endif
# define CTI_DETAIL_ASIO_NAMESPACE_BEGIN namespace asio {
# define CTI_DETAIL_ASIO_NAMESPACE_END }
#else
# include <boost/asio/async_result.hpp>
# include <boost/asio/error.hpp>
# include <boost/system/error_code.hpp>
# include <boost/version.hpp>
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
# include <boost/system/system_error.hpp>
# endif
# if (BOOST_VERSION < 107000) // 1.70
# define CTI_DETAIL_ASIO_HAS_NO_INTEGRATION
# elif (BOOST_VERSION < 107200) // 1.72
# define CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION
# endif
# define CTI_DETAIL_ASIO_NAMESPACE_BEGIN \
namespace boost { \
namespace asio {
# define CTI_DETAIL_ASIO_NAMESPACE_END \
} \
}
#endif
#if defined(CTI_DETAIL_ASIO_HAS_NO_INTEGRATION)
# error "First-class ASIO support for continuable requires the form of "\
"`async_result` with an `initiate` static member function, which was added " \
"in standalone ASIO 1.13.0 and Boost ASIO 1.70. Older versions can be " \
"integrated manually with `cti::promisify`."
#endif
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
# include <exception>
#endif
namespace cti {
namespace detail {
namespace asio {
#if defined(ASIO_STANDALONE)
using error_code_t = ::asio::error_code;
using basic_errors_t = ::asio::error::basic_errors;
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
using system_error_t = ::asio::system_error;
# endif
#else
using error_code_t = ::boost::system::error_code;
using basic_errors_t = ::boost::asio::error::basic_errors;
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
using system_error_t = ::boost::system::system_error;
# endif
#endif
template <typename Promise, typename Token>
class promise_resolver {
public:
explicit promise_resolver(Promise promise, Token token)
: promise_(std::move(promise))
, token_(std::move(token)) {}
template <typename... T>
void operator()(T&&... args) noexcept {
promise_.set_value(std::forward<T>(args)...);
}
template <typename... T>
void operator()(error_code_t e, T&&... args) noexcept {
if (e) {
if (!token_.is_ignored(e)) {
if (token_.is_cancellation(e)) {
promise_.set_canceled();
return;
} else {
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
promise_.set_exception(
std::make_exception_ptr(system_error_t(std::move(e))));
#else
promise_.set_exception(exception_t(e.value(), e.category()));
#endif
return;
}
}
}
promise_.set_value(std::forward<T>(args)...);
}
private:
Promise promise_;
Token token_;
};
// Binds `promise` to the first argument of a continuable resolver, giving it
// the signature of an ASIO handler.
template <typename Promise, typename Token>
auto promise_resolver_handler(Promise&& promise, Token&& token) noexcept {
return promise_resolver<std::decay_t<Promise>, std::decay_t<Token>>(
std::forward<Promise>(promise), std::forward<Token>(token));
}
// Helper struct wrapping a call to `cti::make_continuable` and, if needed,
// providing an erased, explicit `return_type` for `async_result`.
template <typename Signature>
struct initiate_make_continuable;
template <typename... Args>
struct initiate_make_continuable<void(Args...)> {
#if defined(CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION)
using erased_return_type = continuable<Args...>;
#endif
template <typename Continuation>
auto operator()(Continuation&& continuation) {
return base::attorney::create_from(std::forward<Continuation>(continuation),
identity<Args...>{}, util::ownership{});
}
};
template <typename... Args>
struct initiate_make_continuable<void(error_code_t, Args...)> {
#if defined(CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION)
using erased_return_type = continuable<Args...>;
#endif
template <typename Continuation>
auto operator()(Continuation&& continuation) {
return base::attorney::create_from(std::forward<Continuation>(continuation),
identity<Args...>{}, util::ownership{});
}
};
template <typename... Args>
struct initiate_make_continuable<void(error_code_t const&, Args...)>
: initiate_make_continuable<void(error_code_t, Args...)> {};
struct map_default {
constexpr map_default() noexcept {}
bool is_cancellation(error_code_t const& ec) const noexcept {
// Continuable uses a default constructed exception type to signal
// cancellation to the followed asynchronous control flow.
return ec == basic_errors_t::operation_aborted;
}
bool is_ignored(error_code_t const& /*ec*/) const noexcept {
return false;
}
};
struct map_none {
constexpr map_none() noexcept {}
bool is_cancellation(error_code_t const& /*ec*/) const noexcept {
return false;
}
bool is_ignored(error_code_t const& /*ec*/) const noexcept {
return false;
}
};
template <std::size_t Size>
class map_ignore {
public:
map_ignore(std::array<basic_errors_t, Size> ignored) noexcept
: ignored_(ignored) {}
bool is_cancellation(error_code_t const& ec) const noexcept {
return ec == basic_errors_t::operation_aborted;
}
bool is_ignored(error_code_t const& ec) const noexcept {
for (basic_errors_t ignored : ignored_) {
if (ec == ignored) {
return true;
}
}
return false;
}
private:
std::array<basic_errors_t, Size> ignored_;
};
} // namespace asio
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED

View File

@ -1,12 +1,13 @@
/* /*
/~` _ _ _|_. _ _ |_ | _ /~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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
@ -20,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,19 +33,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
@ -54,8 +55,11 @@
#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)
@ -74,12 +78,49 @@
(__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
/// Usually this is enabled by the CMake project // Automatically detects support for coroutines.
#if !defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE) && \ // Parts of this detection mechanism were adapted from boost::asio,
defined(__cpp_coroutines) && (__cpp_coroutines >= 201707) // with support added for experimental coroutines.
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE #if !defined(CONTINUABLE_HAS_DISABLED_COROUTINE) \
&& !defined(CONTINUABLE_HAS_COROUTINE)
/// Define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE when
/// CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE is defined.
#if defined(CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE)
#define CONTINUABLE_HAS_COROUTINE 1
#elif defined(CONTINUABLE_WITH_COROUTINE)
#define CONTINUABLE_HAS_COROUTINE 1
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
#elif defined(_MSC_VER) // Visual Studio
#if (_MSC_VER >= 1928) && (_MSVC_LANG >= 201705)
#define CONTINUABLE_HAS_COROUTINE 1
#elif _MSC_FULL_VER >= 190023506
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
#define CONTINUABLE_HAS_COROUTINE 1
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
#endif // defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
#endif // _MSC_FULL_VER >= 190023506
#elif defined(__clang__) // Clang
#if defined(__cpp_coroutines) && (__cpp_coroutines >= 201703L)
#define CONTINUABLE_HAS_COROUTINE 1
#if defined(_LIBCPP_EXPERIMENTAL_COROUTINE)
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
#endif
#endif // defined(__cpp_coroutines) && (__cpp_coroutines >= 201703L)
#elif defined(__GNUC__) // GCC
#if (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
#if __has_include(<coroutine>)
#define CONTINUABLE_HAS_COROUTINE 1
#endif // __has_include(<coroutine>)
#endif // (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
#endif
#endif #endif
/// Define CONTINUABLE_HAS_EXCEPTIONS when exceptions are used /// Define CONTINUABLE_HAS_EXCEPTIONS when exceptions are used
@ -89,6 +130,18 @@
#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

@ -0,0 +1,80 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_OPERATIONS_ASYNC_HPP_INCLUDED
#define CONTINUABLE_DETAIL_OPERATIONS_ASYNC_HPP_INCLUDED
#include <continuable/continuable-base.hpp>
#include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/utility/identity.hpp>
namespace cti {
namespace detail {
namespace operations {
template <typename Callable, typename Executor, typename... Args>
auto async(Callable&& callable, Executor&& executor, Args&&... args) {
using result_t =
decltype(util::invoke(std::forward<decltype(callable)>(callable),
std::forward<decltype(args)>(args)...));
constexpr auto hint =
decltype(base::decoration::invoker_of(identity<result_t>{}))::hint();
auto continuation = [callable = std::forward<decltype(callable)>(callable),
executor = std::forward<decltype(executor)>(executor),
args = std::make_tuple(std::forward<decltype(args)>(
args)...)](auto&& promise) mutable {
auto invoker = base::decoration::invoker_of(identity<result_t>{});
using promise_t = decltype(promise);
// Invoke the callback
traits::unpack(
[&](auto&&... args) mutable {
// Invoke the promise through the dedicated invoker
// and through the given executor
base::on_executor(std::move(executor), std::move(invoker),
std::move(callable),
std::forward<promise_t>(promise),
std::forward<decltype(args)>(args)...);
},
std::move(args));
};
return base::attorney::create_from(std::move(continuation), //
hint, util::ownership{});
}
} // namespace operations
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_OPERATIONS_ASYNC_HPP_INCLUDED

View File

@ -0,0 +1,180 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_OPERATIONS_LOOP_HPP_INCLUDED
#define CONTINUABLE_DETAIL_OPERATIONS_LOOP_HPP_INCLUDED
#include <cassert>
#include <memory>
#include <tuple>
#include <type_traits>
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-result.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/utility/util.hpp>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
#include <exception>
#endif // CONTINUABLE_HAS_EXCEPTIONS
namespace cti {
namespace detail {
template <typename T>
struct loop_trait {
static_assert(!std::is_same<T, T>::value,
"The callable passed to cti::loop must always return a "
"cti::continuable_base which resolves to a cti::result.");
};
template <typename... Args>
struct loop_trait<identity<result<Args...>>> {
template <typename Callable>
static auto make(Callable&& callable) {
return make_continuable<Args...>(std::forward<Callable>(callable));
}
};
template <>
struct loop_trait<identity<result<>>> {
template <typename Callable>
static auto make(Callable&& callable) {
return make_continuable<void>(std::forward<Callable>(callable));
}
};
namespace operations {
template <typename Promise, typename Callable, typename ArgsTuple>
class loop_frame : public std::enable_shared_from_this<
loop_frame<Promise, Callable, ArgsTuple>> {
Promise promise_;
Callable callable_;
ArgsTuple args_;
public:
explicit loop_frame(Promise promise, Callable callable, ArgsTuple args)
: promise_(std::move(promise)), callable_(std::move(callable)),
args_(std::move(args)) {
}
void loop() {
// MSVC can't evaluate this inside the lambda capture
auto me = this->shared_from_this();
traits::unpack(
[&](auto&&... args) mutable {
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
try {
#endif // CONTINUABLE_HAS_EXCEPTIONS
util::invoke(callable_, std::forward<decltype(args)>(args)...)
.next([me = std::move(me)](auto&&... args) {
me->resolve(std::forward<decltype(args)>(args)...);
});
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
} catch (...) {
me->resolve(exception_arg_t{}, std::current_exception());
}
#endif // CONTINUABLE_HAS_EXCEPTIONS
},
args_);
}
template <typename Result>
void resolve(Result&& result) {
if (result.is_empty()) {
loop();
} else if (result.is_value()) {
traits::unpack(std::move(promise_), std::forward<Result>(result));
} else {
assert(result.is_exception());
std::move(promise_).set_exception(
std::forward<Result>(result).get_exception());
}
}
void resolve(exception_arg_t, exception_t exception) {
promise_.set_exception(std::move(exception));
}
};
template <typename Promise, typename Callable, typename ArgsTuple>
auto make_loop_frame(Promise&& promise, Callable&& callable,
ArgsTuple&& args_tuple) {
using frame_t =
loop_frame<traits::unrefcv_t<Promise>, traits::unrefcv_t<Callable>,
traits::unrefcv_t<ArgsTuple>>;
return std::make_shared<frame_t>(std::forward<Promise>(promise),
std::forward<Callable>(callable),
std::forward<ArgsTuple>(args_tuple));
}
template <typename Callable, typename... Args>
auto loop(Callable&& callable, Args&&... args) {
using invocation_result_t =
decltype(util::invoke(callable, args...).finish());
auto constexpr hint = base::annotation_of(identify<invocation_result_t>{});
using trait_t = loop_trait<std::remove_const_t<decltype(hint)>>;
return trait_t::make([callable = std::forward<decltype(callable)>(callable),
args = std::make_tuple(std::forward<decltype(args)>(
args)...)](auto&& promise) mutable {
// Do the actual looping
auto frame = make_loop_frame(std::forward<decltype(promise)>(promise),
std::move(callable), std::move(args));
frame->loop();
});
}
template <typename Callable, typename Begin, typename End>
auto make_range_looper(Callable&& callable, Begin&& begin, End&& end) {
return [callable = std::forward<Callable>(callable),
begin = std::forward<Begin>(begin),
end = std::forward<End>(end)]() mutable {
return util::invoke(callable, begin)
.then([&begin, &end]() mutable -> plain_t<result<>> {
// begin and end stays valid over the `then` here
if (++begin != end) {
return make_plain(result<>::empty());
} else {
return make_plain(make_result());
}
});
};
}
} // namespace operations
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_OPERATIONS_LOOP_HPP_INCLUDED

View File

@ -0,0 +1,118 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_OPERATIONS_SPLIT_HPP_INCLUDED
#define CONTINUABLE_DETAIL_OPERATIONS_SPLIT_HPP_INCLUDED
#include <tuple>
#include <utility>
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-traverse.hpp>
#include <continuable/continuable-types.hpp>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace operations {
template <typename T, bool Else, typename = void>
struct operator_bool_or {
template <typename O>
static bool get(O&& /*obj*/) noexcept {
return Else;
}
};
template <typename T, bool Else>
struct operator_bool_or<T, Else,
traits::void_t<decltype(bool(std::declval<T&>()))>> {
template <typename O>
static bool get(O&& obj) noexcept {
return bool(obj);
}
};
template <typename First, typename... Promises>
class split_promise {
First first_;
std::tuple<Promises...> promises_;
public:
explicit split_promise(First first, Promises... promises)
: first_(std::move(first)), promises_(std::move(promises)...) {
}
template <typename... Args>
void operator()(Args&&... args) && {
traverse_pack(
[&](auto&& promise) mutable -> void {
using accessor =
operator_bool_or<traits::unrefcv_t<decltype(promise)>, true>;
if (accessor::get(promise)) {
std::forward<decltype(promise)>(promise)(args...);
}
},
std::move(promises_));
if (operator_bool_or<First, true>::get(first_)) {
std::move(first_)(std::forward<Args>(args)...);
}
}
template <typename... Args>
void set_value(Args... args) noexcept {
std::move (*this)(std::move(args)...);
}
void set_exception(exception_t error) noexcept {
std::move (*this)(exception_arg_t{}, std::move(error));
}
void set_canceled() noexcept {
std::move (*this)(exception_arg_t{}, exception_t{});
}
explicit operator bool() const noexcept {
bool is_valid = operator_bool_or<First, true>::get(first_);
traverse_pack(
[&](auto&& promise) mutable -> void {
using accessor =
operator_bool_or<traits::unrefcv_t<decltype(promise)>, true>;
if (!is_valid && accessor::get(promise)) {
is_valid = true;
}
},
promises_);
return is_valid;
}
};
} // namespace operations
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_OPERATIONS_SPLIT_HPP_INCLUDED

View File

@ -0,0 +1,273 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
// Exclude this header when coroutines are not available
#ifndef CONTINUABLE_DETAIL_AWAITING_HPP_INCLUDED
#define CONTINUABLE_DETAIL_AWAITING_HPP_INCLUDED
#include <cassert>
#include <type_traits>
#include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-result.hpp>
#include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/utility/util.hpp>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
# include <exception>
#endif // CONTINUABLE_HAS_EXCEPTIONS
#if defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
# include <experimental/coroutine>
#elif defined(CONTINUABLE_HAS_COROUTINE)
# include <coroutine>
#endif
#if defined(CONTINUABLE_HAS_COROUTINE)
namespace cti {
namespace detail {
namespace awaiting {
/// We import the coroutine handle in our namespace
# if defined(CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE)
using std::experimental::coroutine_handle;
using std::experimental::suspend_never;
# else
using std::coroutine_handle;
using std::suspend_never;
# endif
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
class await_canceled_exception : public std::exception {
public:
await_canceled_exception() noexcept = default;
char const* what() const noexcept override {
return "co_await canceled due to cancellation of the continuation";
}
};
# endif // CONTINUABLE_HAS_EXCEPTIONS
template <typename T>
struct result_from_identity;
template <typename... T>
struct result_from_identity<identity<T...>> {
using result_t = result<T...>;
};
/// An object which provides the internal buffer and helper methods
/// for waiting on a continuable in a stackless coroutine.
template <typename Continuable>
class awaitable {
using hint_t = decltype(base::annotation_of(identify<Continuable>{}));
using result_t = typename result_from_identity<hint_t>::result_t;
/// The continuable which is invoked upon suspension
Continuable continuable_;
/// A cache which is used to pass the result of the continuation
/// to the coroutine.
result_t result_;
/// Enumeration that represents the suspension state of the awaitable.
enum class state : std::uint8_t {
suspended,
pending,
resolved,
};
/// An atomic that specifies whether the awaitable has suspended or not.
/// Allows to perform symmetric transfer on continuables that are
/// immediately resolved.
std::atomic<state> state_{state::pending};
public:
explicit constexpr awaitable(Continuable&& continuable)
: continuable_(std::move(continuable)) {
// If the continuable is ready resolve the result from the
// continuable immediately.
if (base::attorney::is_ready(continuable_)) {
assert(result_.is_empty());
result_ = base::attorney::query(std::move(continuable_));
}
}
/// Return whether the continuable can provide its result instantly,
/// which also means its execution is side-effect free.
bool await_ready() const noexcept {
return !result_.is_empty();
}
/// Suspend the current context
// TODO Convert this to an r-value function once possible
bool await_suspend(coroutine_handle<> h) {
assert(result_.is_empty());
// Forward every result to the current awaitable
std::move(continuable_)
.next([h, this](auto&&... args) mutable {
assert(result_.is_empty());
result_ = result_t::from(std::forward<decltype(args)>(args)...);
// If true, it means that the promise was suspended (i.e., the
// awaitable await_suspend method has already returned). That
// means we must call the resume coroutine from the continuation
// chain.
if (state_.exchange(state::resolved, std::memory_order_acq_rel) ==
state::suspended) {
return h.resume();
}
})
.done();
return state_.exchange(state::suspended, std::memory_order_acq_rel) !=
state::resolved;
}
/// Resume the coroutine represented by the handle
typename result_t::value_t await_resume() noexcept(false) {
if (result_.is_value()) {
// When the result was resolved return it
return std::move(result_).get_value();
}
assert(result_.is_exception());
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
if (exception_t e = result_.get_exception()) {
std::rethrow_exception(std::move(e));
} else {
throw await_canceled_exception();
}
# else // CONTINUABLE_HAS_EXCEPTIONS
// Returning error types from co_await isn't supported!
CTI_DETAIL_TRAP();
# endif // CONTINUABLE_HAS_EXCEPTIONS
}
};
/// Converts a continuable into an awaitable object as described by
/// the C++ coroutine TS.
template <typename T>
constexpr auto create_awaiter(T&& continuable) {
return awaitable<std::decay_t<T>>(std::forward<T>(continuable));
}
/// This makes it possible to take the coroutine_handle over on suspension
struct handle_takeover {
coroutine_handle<>& handle_;
bool await_ready() noexcept {
return false;
}
void await_suspend(coroutine_handle<> handle) noexcept {
handle_ = handle;
}
void await_resume() noexcept {}
};
/// The type which is passed to the compiler that describes the properties
/// of a continuable_base used as coroutine promise type.
template <typename Continuable, typename Promise, typename... Args>
struct promise_type;
/// Implements the resolving method return_void and return_value accordingly
template <typename Base>
struct promise_resolver_base;
template <typename Continuable, typename Promise>
struct promise_resolver_base<promise_type<Continuable, Promise>> {
void return_void() {
auto me = static_cast<promise_type<Continuable, Promise>*>(this);
me->promise_.set_value();
}
};
template <typename Continuable, typename Promise, typename T>
struct promise_resolver_base<promise_type<Continuable, Promise, T>> {
void return_value(T value) {
auto me = static_cast<promise_type<Continuable, Promise, T>*>(this);
me->promise_.set_value(std::move(value));
}
};
template <typename Continuable, typename Promise, typename... Args>
struct promise_resolver_base<promise_type<Continuable, Promise, Args...>> {
template <typename T>
void return_value(T&& tuple_like) {
auto me = static_cast<promise_type<Continuable, Promise, Args...>*>(this);
traits::unpack(std::move(me->promise_), std::forward<T>(tuple_like));
}
};
template <typename Continuable, typename Promise, typename... Args>
struct promise_type
: promise_resolver_base<promise_type<Continuable, Promise, Args...>> {
coroutine_handle<> handle_;
Promise promise_;
explicit promise_type() = default;
Continuable get_return_object() {
return [this](auto&& promise) {
promise_ = std::forward<decltype(promise)>(promise);
handle_.resume();
};
}
handle_takeover initial_suspend() {
return {handle_};
}
suspend_never final_suspend() noexcept {
return {};
}
void unhandled_exception() noexcept {
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
try {
std::rethrow_exception(std::current_exception());
} catch (await_canceled_exception const&) {
promise_.set_canceled();
} catch (...) {
promise_.set_exception(std::current_exception());
}
# else // CONTINUABLE_HAS_EXCEPTIONS
// Returning exception types from a coroutine isn't supported
CTI_DETAIL_TRAP();
# endif // CONTINUABLE_HAS_EXCEPTIONS
}
};
} // namespace awaiting
} // namespace detail
} // namespace cti
#endif // defined(CONTINUABLE_HAS_COROUTINE)
#endif // CONTINUABLE_DETAIL_UTIL_HPP_INCLUDED

View File

@ -0,0 +1,241 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_ERASURE_HPP_INCLUDED
#define CONTINUABLE_DETAIL_ERASURE_HPP_INCLUDED
#include <type_traits>
#include <utility>
#include <function2/function2.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace erasure {
template <typename... Args>
using callback_erasure_t =
fu2::function_base<true, false, fu2::capacity_none, true, false,
void(Args...)&&, void(exception_arg_t, exception_t) &&>;
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
template <typename... Args>
using callback = callback_erasure_t<Args...>;
#else
template <typename... Args>
class callback;
template <typename T>
struct is_callback : std::false_type {};
template <typename... Args>
struct is_callback<callback<Args...>> : std::true_type {};
template <typename... Args>
class callback : public callback_erasure_t<Args...> {
public:
using erasure_t = callback_erasure_t<Args...>;
erasure_t erasure_;
callback() = default;
~callback() = default;
callback(callback const&) = delete;
callback(callback&&) = default;
callback& operator=(callback const&) = delete;
callback& operator=(callback&&) = default;
template <
typename T,
std::enable_if_t<std::is_convertible<T, erasure_t>::value>* = nullptr,
std::enable_if_t<!is_callback<traits::unrefcv_t<T>>::value>* = nullptr>
/* implicit */ callback(T&& callable) : erasure_(std::forward<T>(callable)) {
}
template <
typename T,
std::enable_if_t<std::is_assignable<erasure_t, T>::value>* = nullptr,
std::enable_if_t<!is_callback<traits::unrefcv_t<T>>::value>* = nullptr>
callback& operator=(T&& callable) {
erasure_ = std::forward<T>(callable);
return *this;
}
void operator()(Args... args) && noexcept {
std::move(erasure_)(std::move(args)...);
}
void operator()(exception_arg_t exception_arg, exception_t exception) &&
noexcept {
std::move(erasure_)(exception_arg, std::move(exception));
}
explicit operator bool() const noexcept {
return bool(erasure_);
}
};
#endif
using work_erasure_t =
fu2::function_base<true, false, fu2::capacity_fixed<32UL>, true, false,
void()&&, void(exception_arg_t, exception_t) &&>;
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
using work = work_erasure_t;
#else
class work;
template <typename T>
struct is_work : std::false_type {};
template <>
struct is_work<work> : std::true_type {};
class work {
using erasure_t = work_erasure_t;
erasure_t erasure_;
public:
work() = default;
~work() = default;
work(work const&) = delete;
work(work&&) = default;
work& operator=(work const&) = delete;
work& operator=(work&&) = default;
template <
typename T,
std::enable_if_t<std::is_convertible<T, erasure_t>::value>* = nullptr,
std::enable_if_t<!is_work<traits::unrefcv_t<T>>::value>* = nullptr>
/* implicit */ work(T&& callable) : erasure_(std::forward<T>(callable)) {
}
template <
typename T,
std::enable_if_t<std::is_assignable<erasure_t, T>::value>* = nullptr,
std::enable_if_t<!is_work<traits::unrefcv_t<T>>::value>* = nullptr>
work& operator=(T&& callable) {
erasure_ = std::forward<T>(callable);
return *this;
}
void operator()() && noexcept {
std::move(erasure_)();
}
void operator()(exception_arg_t, exception_t exception) && noexcept {
std::move(erasure_)(exception_arg_t{}, std::move(exception));
}
explicit operator bool() const noexcept {
return bool(erasure_);
}
};
#endif
template <typename... Args>
struct continuation_capacity {
using type = union {
void* pointer_;
base::ready_continuation<Args...> continuation_;
};
static constexpr std::size_t capacity = sizeof(type);
static constexpr std::size_t alignment = alignof(type);
};
template <typename... Args>
using continuation_erasure_t = fu2::function_base<
true, false, continuation_capacity<Args...>, true, false,
void(promise_base<callback<Args...>, signature_arg_t<Args...>>),
bool(is_ready_arg_t) const, result<Args...>(unpack_arg_t)>;
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
template <typename... Args>
using continuation = continuation_erasure_t<Args...>;
#else
template <typename... Args>
class continuation;
template <typename T>
struct is_continuation : std::false_type {};
template <typename... Args>
struct is_continuation<continuation<Args...>> : std::true_type {};
template <typename... Args>
class continuation {
using erasure_t = continuation_erasure_t<Args...>;
erasure_t erasure_;
public:
continuation() = default;
~continuation() = default;
continuation(continuation const&) = delete;
continuation(continuation&&) = default;
continuation& operator=(continuation const&) = delete;
continuation& operator=(continuation&&) = default;
template <
typename T,
std::enable_if_t<std::is_convertible<T, erasure_t>::value>* = nullptr,
std::enable_if_t<!is_continuation<traits::unrefcv_t<T>>::value>* =
nullptr>
/* implicit */ continuation(T&& callable)
: erasure_(std::forward<T>(callable)) {
}
template <
typename T,
std::enable_if_t<std::is_assignable<erasure_t, T>::value>* = nullptr,
std::enable_if_t<!is_continuation<traits::unrefcv_t<T>>::value>* =
nullptr>
continuation& operator=(T&& callable) {
erasure_ = std::forward<T>(callable);
return *this;
}
void operator()(promise_base<callback<Args...>, //
signature_arg_t<Args...>>
promise) {
erasure_(std::move(promise));
}
bool operator()(is_ready_arg_t is_ready_arg) const {
return erasure_(is_ready_arg);
}
result<Args...> operator()(unpack_arg_t query_arg) {
return erasure_(query_arg);
}
};
#endif
} // namespace erasure
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_ERASURE_HPP_INCLUDED

View File

@ -0,0 +1,97 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
#define CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED
#include <type_traits>
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-primitives.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/utility/util.hpp>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
#include <exception>
#endif // CONTINUABLE_HAS_EXCEPTIONS
namespace cti {
namespace detail {
namespace convert {
/// A resolver for promisifying asio and js style callbacks.
inline auto default_resolver() {
return [](auto&& promise, auto&& e, auto&&... args) {
static_assert(
std::is_convertible<std::decay_t<decltype(e)>, exception_t>::value,
"The given error type must be convertible to the error type used! "
"Specify a custom resolver in order to apply a conversion to the "
"used error type.");
if (e) {
promise.set_exception(std::forward<decltype(e)>(e));
} else {
promise.set_value(std::forward<decltype(args)>(args)...);
}
};
}
template <typename... Result>
struct promisify_helper {
template <typename Resolver, typename Callable, typename... Args>
static auto from(Resolver&& resolver, Callable&& callable, Args&&... args) {
return make_continuable<Result...>(
[resolver = std::forward<Resolver>(resolver),
args = traits::make_flat_tuple(std::forward<Callable>(callable),
std::forward<Args>(args)...)](
auto&& promise) mutable {
traits::unpack(
[promise = std::forward<decltype(promise)>(promise),
&resolver](auto&&... args) mutable {
// Call the resolver from with the promise and result
auto callback =
[resolver = std::move(resolver),
promise = std::move(promise)](auto&&... args) mutable {
resolver(std::move(promise),
std::forward<decltype(args)>(args)...);
};
// Invoke the callback taking function
util::invoke(std::forward<decltype(args)>(args)...,
std::move(callback));
},
std::move(args));
});
}
};
} // namespace convert
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_PROMISIFY_HPP_INCLUDED

View File

@ -5,9 +5,9 @@
\_,(_)| | | || ||_|(_||_)|(/_ \_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable https://github.com/Naios/continuable
v3.0.0 v4.2.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com> Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy 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,13 +33,12 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <continuable/continuable-primitives.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/features.hpp> #include <continuable/detail/features.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 {
@ -55,7 +54,7 @@ void assert_async_completion(C&& continuable) {
// Workaround for our known GCC bug. // Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...); util::unused(std::forward<decltype(args)>(args)...);
}) })
.fail([](cti::error_type /*error*/) { .fail([](cti::exception_t /*error*/) {
// ... // ...
FAIL(); FAIL();
}); });
@ -74,7 +73,28 @@ void assert_async_exception_completion(C&& continuable) {
// ... // ...
FAIL(); FAIL();
}) })
.fail([called](cti::error_type /*error*/) { .fail([called](cti::exception_t error) {
ASSERT_TRUE(bool(error));
ASSERT_FALSE(*called);
*called = true;
});
ASSERT_TRUE(*called);
}
template <typename C>
void assert_async_cancellation(C&& continuable) {
auto called = std::make_shared<bool>(false);
std::forward<C>(continuable)
.then([](auto&&... args) {
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
// ...
FAIL();
})
.fail([called](cti::exception_t error) {
ASSERT_FALSE(bool(error));
ASSERT_FALSE(*called); ASSERT_FALSE(*called);
*called = true; *called = true;
}); });
@ -91,7 +111,7 @@ void assert_async_never_completed(C&& continuable) {
FAIL(); FAIL();
}) })
.fail([](cti::error_type /*error*/) { .fail([](cti::exception_t) {
// ... // ...
FAIL(); FAIL();
}); });
@ -101,11 +121,10 @@ template <typename C, typename V>
void assert_async_validation(C&& continuable, V&& validator) { void assert_async_validation(C&& continuable, V&& validator) {
assert_async_completion( assert_async_completion(
std::forward<C>(continuable) std::forward<C>(continuable)
.then([validator = .then(
std::forward<V>(validator)](auto&&... args) mutable { [validator = std::forward<V>(validator)](auto&&... args) mutable {
validator(std::forward<decltype(args)>(args)...);
validator(std::forward<decltype(args)>(args)...); }));
}));
} }
/// Expects that the continuable is finished with the given arguments /// Expects that the continuable is finished with the given arguments
@ -115,18 +134,17 @@ void assert_async_binary_validation(V&& validator, C&& continuable,
using size = std::integral_constant<std::size_t, sizeof...(expected)>; using size = std::integral_constant<std::size_t, sizeof...(expected)>;
assert_async_validation(std::forward<C>(continuable), [ assert_async_validation(
expected_pack = std::make_tuple(std::forward<Args>(expected)...), std::forward<C>(continuable),
validator = std::forward<V>(validator) [expected_pack = std::make_tuple(std::forward<Args>(expected)...),
](auto&&... args) mutable { validator = std::forward<V>(validator)](auto&&... args) mutable {
static_assert(size::value == sizeof...(args),
"Async completion handler called with a different count "
"of arguments!");
static_assert(size::value == sizeof...(args), validator(std::make_tuple(std::forward<decltype(args)>(args)...),
"Async completion handler called with a different count " expected_pack);
"of arguments!"); });
validator(std::make_tuple(std::forward<decltype(args)>(args)...),
expected_pack);
});
} }
/// Expects that the continuable is finished with the given arguments /// Expects that the continuable is finished with the given arguments
@ -139,20 +157,19 @@ void assert_async_binary_exception_validation(V&& validator, C&& continuable,
// Workaround for our known GCC bug. // Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...); util::unused(std::forward<decltype(args)>(args)...);
// ... // The exception was not thrown!
FAIL(); FAIL();
}) })
.fail([ .fail([called, validator = std::forward<decltype(validator)>(validator),
called, validator = std::forward<decltype(validator)>(validator), expected = std::forward<decltype(expected)>(expected)](
expected = std::forward<decltype(expected)>(expected) exception_t error) {
](types::error_type error) {
ASSERT_FALSE(*called); ASSERT_FALSE(*called);
*called = true; *called = true;
#if defined(CONTINUABLE_HAS_EXCEPTIONS) #if defined(CONTINUABLE_HAS_EXCEPTIONS)
try { try {
std::rethrow_exception(error); std::rethrow_exception(error);
} catch (std::decay_t<decltype(expected)>& exception) { } catch (std::decay_t<decltype(expected)> const& exception) {
validator(exception, expected); validator(exception, expected);
} catch (...) { } catch (...) {
FAIL(); FAIL();
@ -183,15 +200,14 @@ template <typename... Expected>
struct assert_async_types_validator { struct assert_async_types_validator {
template <typename... Actual> template <typename... Actual>
void operator()(Actual...) { void operator()(Actual...) {
static_assert(std::is_same<traits::identity<Actual...>, static_assert(
traits::identity<Expected...>>::value, std::is_same<identity<Actual...>, identity<Expected...>>::value,
"The called arguments don't match with the expected ones!"); "The called arguments don't match with the expected ones!");
} }
}; };
template <typename C, typename... Args> template <typename C, typename... Args>
void assert_async_types(C&& continuable, void assert_async_types(C&& continuable, identity<Args...> /*expected*/) {
traits::identity<Args...> /*expected*/) {
assert_async_validation(std::forward<C>(continuable), assert_async_validation(std::forward<C>(continuable),
assert_async_types_validator<Args...>{}); assert_async_types_validator<Args...>{});
} }

View File

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

View File

@ -1,342 +0,0 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED
#include <cstdint>
#include <initializer_list>
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/detail/features.hpp>
namespace cti {
namespace detail {
namespace traits {
/// Evaluates to the element at position I.
template <std::size_t I, typename... Args>
using at_t = decltype(std::get<I>(std::declval<std::tuple<Args...>>()));
/// A tagging type for wrapping other types
template <typename... T>
struct identity {};
template <typename T>
struct identity<T> : std::common_type<T> {};
template <typename>
struct is_identity : std::false_type {};
template <typename... Args>
struct is_identity<identity<Args...>> : std::true_type {};
template <typename T>
constexpr identity<std::decay_t<T>> identity_of(T const& /*type*/) noexcept {
return {};
}
template <typename... Args>
constexpr identity<Args...> identity_of(identity<Args...> /*type*/) noexcept {
return {};
}
template <typename T>
using identify = std::conditional_t<is_identity<std::decay_t<T>>::value, T,
identity<std::decay_t<T>>>;
template <std::size_t I, typename... T>
constexpr auto get(identity<T...>) noexcept {
return identify<at_t<I, T...>>{};
}
namespace detail {
// Equivalent to C++17's std::void_t which targets a bug in GCC,
// that prevents correct SFINAE behavior.
// See http://stackoverflow.com/questions/35753920 for details.
template <typename...>
struct deduce_to_void : std::common_type<void> {};
} // namespace detail
/// C++17 like void_t type
template <typename... T>
using void_t = typename detail::deduce_to_void<T...>::type;
namespace detail {
template <typename Type, typename TrueCallback>
constexpr void static_if_impl(std::true_type, Type&& type,
TrueCallback&& trueCallback) {
std::forward<TrueCallback>(trueCallback)(std::forward<Type>(type));
}
template <typename Type, typename TrueCallback>
constexpr void static_if_impl(std::false_type, Type&& /*type*/,
TrueCallback&& /*trueCallback*/) {
}
template <typename Type, typename TrueCallback, typename FalseCallback>
constexpr auto static_if_impl(std::true_type, Type&& type,
TrueCallback&& trueCallback,
FalseCallback&& /*falseCallback*/) {
return std::forward<TrueCallback>(trueCallback)(std::forward<Type>(type));
}
template <typename Type, typename TrueCallback, typename FalseCallback>
constexpr auto static_if_impl(std::false_type, Type&& type,
TrueCallback&& /*trueCallback*/,
FalseCallback&& falseCallback) {
return std::forward<FalseCallback>(falseCallback)(std::forward<Type>(type));
}
/// Evaluates to the size of the given tuple like type,
// / if the type has no static size it will be one.
template <typename T, typename Enable = void>
struct tuple_like_size : std::integral_constant<std::size_t, 1U> {};
template <typename T>
struct tuple_like_size<T, void_t<decltype(std::tuple_size<T>::value)>>
: std::tuple_size<T> {};
} // namespace detail
/// Returns the pack size of the given empty pack
constexpr std::size_t pack_size_of(identity<>) noexcept {
return 0U;
}
/// Returns the pack size of the given type
template <typename T>
constexpr std::size_t pack_size_of(identity<T>) noexcept {
return detail::tuple_like_size<T>::value;
}
/// Returns the pack size of the given type
template <typename First, typename Second, typename... Args>
constexpr std::size_t pack_size_of(identity<First, Second, Args...>) noexcept {
return 2U + sizeof...(Args);
}
/// Returns an index sequence of the given type
template <typename T>
constexpr auto sequence_of(identity<T>) noexcept {
constexpr auto const size = pack_size_of(identity<T>{});
return std::make_index_sequence<size>();
}
/// Invokes the callback only if the given type matches the check
template <typename Type, typename Check, typename TrueCallback>
constexpr void static_if(Type&& type, Check&& check,
TrueCallback&& trueCallback) {
detail::static_if_impl(std::forward<Check>(check)(type),
std::forward<Type>(type),
std::forward<TrueCallback>(trueCallback));
}
/// Invokes the callback only if the given type matches the check
template <typename Type, typename Check, typename TrueCallback,
typename FalseCallback>
constexpr auto static_if(Type&& type, Check&& check,
TrueCallback&& trueCallback,
FalseCallback&& falseCallback) {
return detail::static_if_impl(std::forward<Check>(check)(type),
std::forward<Type>(type),
std::forward<TrueCallback>(trueCallback),
std::forward<FalseCallback>(falseCallback));
}
/// Calls the given unpacker with the content of the given sequence
template <typename U, std::size_t... I>
constexpr decltype(auto) unpack(std::integer_sequence<std::size_t, I...>,
U&& unpacker) {
return std::forward<U>(unpacker)(std::integral_constant<std::size_t, I>{}...);
}
/// Calls the given unpacker with the content of the given sequenceable
template <typename F, typename U, std::size_t... I>
constexpr auto unpack(F&& first_sequenceable, U&& unpacker,
std::integer_sequence<std::size_t, I...>)
-> decltype(std::forward<U>(unpacker)(
get<I>(std::forward<F>(first_sequenceable))...)) {
(void)first_sequenceable;
return std::forward<U>(unpacker)(
get<I>(std::forward<F>(first_sequenceable))...);
}
/// Calls the given unpacker with the content of the given sequenceable
template <typename F, typename S, typename U, std::size_t... If,
std::size_t... Is>
constexpr auto unpack(F&& first_sequenceable, S&& second_sequenceable,
U&& unpacker, std::integer_sequence<std::size_t, If...>,
std::integer_sequence<std::size_t, Is...>)
-> decltype(std::forward<U>(unpacker)(
get<If>(std::forward<F>(first_sequenceable))...,
get<Is>(std::forward<S>(second_sequenceable))...)) {
(void)first_sequenceable;
(void)second_sequenceable;
return std::forward<U>(unpacker)(
get<If>(std::forward<F>(first_sequenceable))...,
get<Is>(std::forward<S>(second_sequenceable))...);
}
/// Calls the given unpacker with the content of the given sequenceable
template <typename F, typename U>
constexpr auto unpack(F&& first_sequenceable, U&& unpacker)
-> decltype(unpack(std::forward<F>(first_sequenceable),
std::forward<U>(unpacker),
sequence_of(identify<decltype(first_sequenceable)>{}))) {
return unpack(std::forward<F>(first_sequenceable), std::forward<U>(unpacker),
sequence_of(identify<decltype(first_sequenceable)>{}));
}
/// Calls the given unpacker with the content of the given sequenceables
template <typename F, typename S, typename U>
constexpr auto unpack(F&& first_sequenceable, S&& second_sequenceable,
U&& unpacker)
-> decltype(unpack(std::forward<F>(first_sequenceable),
std::forward<S>(second_sequenceable),
std::forward<U>(unpacker),
sequence_of(identity_of(first_sequenceable)),
sequence_of(identity_of(second_sequenceable)))) {
return unpack(std::forward<F>(first_sequenceable),
std::forward<S>(second_sequenceable), std::forward<U>(unpacker),
sequence_of(identity_of(first_sequenceable)),
sequence_of(identity_of(second_sequenceable)));
}
/// Adds the given type at the back of the left sequenceable
template <typename Left, typename Element>
constexpr auto push(Left&& left, Element&& element) {
return unpack(std::forward<Left>(left), [&](auto&&... args) {
return std::make_tuple(std::forward<decltype(args)>(args)...,
std::forward<Element>(element));
});
}
/// Adds the element to the back of the identity
template <typename... Args, typename Element>
constexpr auto push(identity<Args...>, identity<Element>) noexcept {
return identity<Args..., Element>{};
}
/// Removes the first element from the identity
template <typename First, typename... Rest>
constexpr auto pop_first(identity<First, Rest...>) noexcept {
return identity<Rest...>{};
}
/// Returns the merged sequence
template <typename Left>
constexpr auto merge(Left&& left) {
return std::forward<Left>(left);
}
/// Merges the left sequenceable with the right ones
template <typename Left, typename Right, typename... Rest>
constexpr auto merge(Left&& left, Right&& right, Rest&&... rest) {
// Merge the left with the right sequenceable and
// merge the result with the rest.
return merge(unpack(std::forward<Left>(left), std::forward<Right>(right),
[&](auto&&... args) {
// Maybe use: template <template<typename...> class T,
// typename... Args>
return std::make_tuple(
std::forward<decltype(args)>(args)...);
}),
std::forward<Rest>(rest)...);
}
/// Merges the left identity with the right ones
template <typename... LeftArgs, typename... RightArgs, typename... Rest>
constexpr auto merge(identity<LeftArgs...> /*left*/,
identity<RightArgs...> /*right*/, Rest&&... rest) {
return merge(identity<LeftArgs..., RightArgs...>{},
std::forward<Rest>(rest)...);
}
namespace detail {
template <typename T, typename Args, typename = traits::void_t<>>
struct is_invokable_impl : std::common_type<std::false_type> {};
template <typename T, typename... Args>
struct is_invokable_impl<
T, std::tuple<Args...>,
void_t<decltype(std::declval<T>()(std::declval<Args>()...))>>
: std::common_type<std::true_type> {};
} // namespace detail
/// Deduces to a std::true_type if the given type is callable with the arguments
/// inside the given tuple.
/// The main reason for implementing it with the detection idiom instead of
/// hana like detection is that MSVC has issues with capturing raw template
/// arguments inside lambda closures.
///
/// ```cpp
/// traits::is_invokable<object, std::tuple<Args...>>
/// ```
template <typename T, typename Args>
using is_invokable_from_tuple =
typename detail::is_invokable_impl<T, Args>::type;
// Checks whether the given callable object is invocable with the given
// arguments. This doesn't take member functions into account!
template <typename T, typename... Args>
using is_invocable = is_invokable_from_tuple<T, std::tuple<Args...>>;
/// Deduces to a std::false_type
template <typename T>
using fail = std::integral_constant<bool, !std::is_same<T, T>::value>;
#ifdef CONTINUABLE_HAS_CXX17_DISJUNCTION
using std::disjunction;
#else
namespace detail {
/// Declares a C++14 polyfill for C++17 std::disjunction.
template <typename Args, typename = void_t<>>
struct disjunction_impl : std::common_type<std::true_type> {};
template <typename... Args>
struct disjunction_impl<identity<Args...>,
void_t<std::enable_if_t<!bool(Args::value)>...>>
: std::common_type<std::false_type> {};
} // namespace detail
template <typename... Args>
using disjunction = typename detail::disjunction_impl<identity<Args...>>::type;
#endif // CONTINUABLE_HAS_CXX17_DISJUNCTION
#ifdef CONTINUABLE_HAS_CXX17_CONJUNCTION
using std::conjunction;
#else
namespace detail {
/// Declares a C++14 polyfill for C++17 std::conjunction.
template <typename Args, typename = void_t<>>
struct conjunction_impl : std::common_type<std::false_type> {};
template <typename... Args>
struct conjunction_impl<identity<Args...>,
void_t<std::enable_if_t<bool(Args::value)>...>>
: std::common_type<std::true_type> {};
} // namespace detail
template <typename... Args>
using conjunction = typename detail::conjunction_impl<identity<Args...>>::type;
#endif // CONTINUABLE_HAS_CXX17_CONJUNCTION
} // namespace traits
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED

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