Compare commits

...

591 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
Denis Blank
d9622566e9 Address a MSVC compiler bug which prevents collapsing references in coroutines
* Closes #2
2018-03-06 23:14:55 +01:00
Denis Blank
715cece74c Allow non copyable completion handlers in the asio example 2018-03-06 22:06:07 +01:00
Denis Blank
98aefb59d8 First attempt on making all promises non-copyable
* Fix CONTINUABLE_WITH_UNHANDLED_ERRORS when using the promise as
  real callback.
2018-03-06 21:50:25 +01:00
Denis Blank
73c0b17962 Mark promise helpers as noexcept 2018-03-06 21:40:37 +01:00
Denis Blank
12c23f15f8 Fix a defect unit test 2018-03-06 21:36:27 +01:00
Denis Blank
1aae8c3083 Async traversal cleanup 2018-03-06 08:49:00 +01:00
Denis Blank
201a2fc17c Some cleanups in the any composition 2018-03-06 08:44:12 +01:00
Denis Blank
8f69198651 More cleanups in util and traits 2018-03-06 08:37:07 +01:00
Denis Blank
acc15b46ab Traits and util cleanup 2018-03-06 03:53:18 +01:00
Denis Blank
0dbcccdc9e Add documentation to when_any 2018-03-04 08:44:38 +01:00
Denis Blank
9ecbb00f5a Implement the nested when_any connection 2018-03-04 08:14:11 +01:00
Denis Blank
cdbc332287 Implement the new result deduction for when_any
* The logic requires now that all continuables yield the same
  types and amount of result objects.
2018-03-02 19:18:53 +01:00
Denis Blank
9891543b1f Enable and document the new when_all 2018-03-02 04:49:04 +01:00
Denis Blank
dd1b605d95 Revert 5dfe388f7f8156
* It turns out that this isn't needed anymore and just affects
  the compile time by now
* Additionally GCC can't handle this properly
2018-03-02 04:02:45 +01:00
Denis Blank
178f590aa9 Address a GCC parsing failure (compiler bug)
* See https://travis-ci.org/Naios/continuable/jobs/348056527#L723
2018-03-02 03:14:05 +01:00
Denis Blank
83752502dc Fix the simplification 2018-03-02 02:54:09 +01:00
Denis Blank
92ba25cd23 Started on simplifying the all and seq composition heavily 2018-03-02 01:25:59 +01:00
Denis Blank
a9da11149b Attempt to fix the clang build 2018-03-01 05:16:12 +01:00
Denis Blank
7602dfd50d Basic implementation of the all composition 2018-03-01 03:21:31 +01:00
Denis Blank
1853ec8b42 Start to work on rewriting the all composition 2018-02-28 18:12:12 +01:00
Denis Blank
375e376db8 Attempt to fix the clang build 2018-02-28 17:37:02 +01:00
Denis Blank
4a5136427b Avoid using SFINAE inside the materializer 2018-02-28 17:28:41 +01:00
Denis Blank
36257780e0 Some cleanups 2018-02-28 17:15:19 +01:00
Denis Blank
7bf0439493 Materialize continuables when applying strategies 2018-02-28 17:11:58 +01:00
Denis Blank
a1ee771059 Fix an issue when connecting void continuables 2018-02-28 02:43:57 +01:00
Denis Blank
663779f083 Add documentation to when_seq 2018-02-28 02:10:35 +01:00
Denis Blank
3c70024c0b Make it possible to apply when_seq to iterators 2018-02-28 01:21:27 +01:00
Denis Blank
b3bf06c26e Rename some files to match the naming standard 2018-02-28 00:46:40 +01:00
Denis Blank
017d89e34e Partially revert 2cbac4da98e57 2018-02-28 00:41:43 +01:00
Denis Blank
17f454ceb6 Fix a remapping failure when nested tuples are involved 2018-02-28 00:32:40 +01:00
Denis Blank
2cbac4da98 Simplify make_exceptional_continuable 2018-02-28 00:30:58 +01:00
Denis Blank
9c087e60d2 Correctly handle continuables with multiple results 2018-02-28 00:02:52 +01:00
Denis Blank
6e1350086e More work on the hint calculation for all and seq compositions 2018-02-27 23:47:51 +01:00
Denis Blank
c4cdb3c3b0 Fix the mapping when empty spreads are involved 2018-02-27 23:46:03 +01:00
Denis Blank
860ae778bc Fix some remaining issues with calculating the signature hint from all or seq compositions 2018-02-27 17:53:23 +01:00
Denis Blank
6819c68bb2 Some more permissive issues 2018-02-27 17:35:05 +01:00
Denis Blank
707acd0f93 Move the MSVC flags into the appropiate file 2018-02-27 17:34:58 +01:00
Denis Blank
a7d844d0dc Fix some permissive issues 2018-02-27 17:19:57 +01:00
Denis Blank
e3e119b59d Update function2 to Naios/function2@8611ae3 2018-02-27 17:10:38 +01:00
Denis Blank
c734672afa Update GTest to google/googletest@ac34e6c 2018-02-27 17:10:28 +01:00
Christos Stratopoulos
af9e1b0371 Use /permissive- with MSVC
(cherry picked from commit 1e3427659b06f0cf128e480cad7f14b8bcbe25f6)
2018-02-27 17:07:47 +01:00
Denis Blank
3c90862768 Fix sequential compositions 2018-02-27 17:05:11 +01:00
Denis Blank
ba6c4cc905 More work on the sequential compositioning 2018-02-27 06:08:55 +01:00
Denis Blank
fd858a7ed7 First work on implementing deep sequential compositions 2018-02-27 05:07:13 +01:00
Denis Blank
b1d7a76c8f Move the new remapping functionality to its own header 2018-02-27 02:19:08 +01:00
Denis Blank
b3a4a3d359 Lift connection logic up from all and any to a unified function 2018-02-27 02:00:37 +01:00
Denis Blank
fb4a34b328 Split the composition header into one for each strategy 2018-02-26 23:49:59 +01:00
Denis Blank
89c3becbcf Fix some unit tests 2018-02-26 19:34:25 +01:00
Denis Blank
02fbad159c Add missing licenses to cmake files 2018-02-26 19:28:24 +01:00
Denis Blank
2cb42d786d Address a clang build failure 2018-02-26 19:05:16 +01:00
Denis Blank
05b9223da8 Require make_continuable to be called with a valid signature 2018-02-26 19:02:20 +01:00
Denis Blank
331d642e5d Fix a build error in the result indexing 2018-02-26 18:33:26 +01:00
Denis Blank
786792f4f0 Add cti::promisify with an initial boost asio helper 2018-02-26 05:04:38 +01:00
Denis Blank
9c66b53f23 Some ideas of a promisify helper 2018-02-25 17:37:30 +01:00
Denis Blank
9be06f4bcc Some more work on seperating the indexer and relocator 2018-02-23 01:13:22 +01:00
Denis Blank
4632ff355c Add relocate_index_pack to assign the target address to indexed continuables 2018-02-21 08:19:47 +01:00
Denis Blank
7730b85b24 Make it possible to supply the async traversal pack directly from the visitor 2018-02-21 04:02:18 +01:00
Denis Blank
8dbd3d058a More work on indexing continuables together with its result 2018-02-19 15:46:00 +01:00
Denis Blank
f81f0e649c Initial work on creating the result object 2018-02-19 04:55:02 +01:00
Denis Blank
197a8c4dd1 Add make_ready_continuable and make_exceptional_continuable 2018-02-19 02:43:36 +01:00
Denis Blank
8102c2d841 Some more ideas 2018-02-19 00:58:45 +01:00
Denis Blank
951155bc34 Make it possible to apply transformations through pipes 2018-02-10 05:12:38 +01:00
Denis Blank
e4ef3ccefb Some experiments 2018-02-10 04:51:11 +01:00
Denis Blank
5dfe388f7f Prioritize the mapper if it does accept container and tuple like elements 2018-02-10 04:45:09 +01:00
Denis Blank
20f586376f Some misc improvements 2018-02-10 04:04:43 +01:00
Denis Blank
cd42a7b2ba Promote is_continuable to the public API 2018-02-10 03:40:05 +01:00
Denis Blank
7c0aa6e6ba Move the composition functions to it's own header 2018-02-10 03:30:52 +01:00
Denis Blank
fbd87787aa Add a unit test which was removed in the original version
* Because unfinished traversals weren't allowed there.
2018-02-10 03:13:18 +01:00
Denis Blank
36bd134120 Change the inclusion style of the test header 2018-02-10 03:05:39 +01:00
Denis Blank
7a96a2681f Execute the single tests only once 2018-02-10 03:00:57 +01:00
Denis Blank
d7d10e9448 Fix the remaining unit tests for async traversal 2018-02-10 02:50:24 +01:00
Denis Blank
64f91d8ce0 Remove the 42 in_place_tag workaround 2018-02-10 02:15:02 +01:00
Denis Blank
bac986a23f Convert the async traversal test to gtest 2018-02-10 01:56:50 +01:00
Denis Blank
f935d78c99 Partial conversion of the async traversal unit test 2018-02-10 01:41:14 +01:00
Denis Blank
4b00f5ff8a Fix a missing typename build failure 2018-02-10 01:15:33 +01:00
Denis Blank
04111c0bc3 More work on porting the async traversal 2018-02-10 01:13:01 +01:00
Denis Blank
16dd2b0cb9 Start to port the async traversal 2018-02-09 04:28:04 +01:00
Denis Blank
73927516c6 Set the version to 3.0.0
* There wil be a header split for compositions
  thus the backward compatibility breaks
2018-02-09 04:16:45 +01:00
Denis Blank
bf04c6600f Fix some MSVC warnings 2018-02-09 04:09:58 +01:00
Denis Blank
8df57d6d8b Use std::tuple_size for retrieving the size of a tuple like type 2018-02-09 04:06:40 +01:00
Denis Blank
5904ea06b4 Convert more tests into proper gtest cases 2018-02-09 03:57:12 +01:00
Denis Blank
a5640a5d35 Fix std::array remapping 2018-02-09 03:36:12 +01:00
Denis Blank
0982748ad6 Enable another test 2018-02-07 02:43:25 +01:00
Denis Blank
217aad83ef Fix a MSVC build failure 2018-02-07 02:39:58 +01:00
Denis Blank
bc3e3c6473 Fix some warnings 2018-02-07 02:34:12 +01:00
Denis Blank
45f29c8d00 Enable a failing test 2018-02-07 02:28:49 +01:00
Denis Blank
6dc2b0d0b1 Make the tests pass 2018-02-07 02:15:09 +01:00
Denis Blank
a0a0619953 Fix some SFINAE failures in map_pack
* Make unpack capable of SFINAE
2018-02-07 01:57:22 +01:00
Denis Blank
0da8d1206f Fix a bad conversion 2018-02-07 01:47:42 +01:00
Denis Blank
0e80147343 Readd return type deduction 2018-02-07 01:40:20 +01:00
Denis Blank
7db24574ae Fix a mistake in the tuple remapper 2018-02-07 01:09:33 +01:00
Denis Blank
230b3ec967 Some minor test improvements 2018-02-07 01:01:52 +01:00
Denis Blank
a167261e00 Fix and apply proper version comments 2018-02-06 03:37:57 +01:00
Denis Blank
72974d402e Enable passing parts of the unit test 2018-02-06 03:30:24 +01:00
Denis Blank
78f1c0ae78 More work on the unit test 2018-02-06 03:21:44 +01:00
Denis Blank
8ef86d3b70 Port invoke_fused -> unpack 2018-02-06 03:21:32 +01:00
Denis Blank
f0b25956b9 Implement the missing checks for container categories 2018-02-06 02:52:33 +01:00
Denis Blank
09f9da3e0e Rename CONTINUABLE_WITH_EXCEPTIONS -> CONTINUABLE_HAS_EXCEPTIONS
* Since this is automatically defined
2018-02-06 02:25:27 +01:00
Denis Blank
88aebb422b Improve the feature tests
* Also intend those manually and add ca clang-format (6.0)
  placeholder.
2018-02-06 02:22:00 +01:00
Denis Blank
a02e9bdf89 Enable the feature detection based on __cplusplus 2018-02-06 01:58:07 +01:00
Denis Blank
7e90187e50 Some minor improvements 2018-02-06 01:57:41 +01:00
Denis Blank
c8b4e1ddcd Improve the conditional usage of try catch 2018-02-06 00:54:11 +01:00
Denis Blank
752bee6ea4 Start to port the traversal unit tests 2018-02-06 00:44:51 +01:00
Denis Blank
a107a89991 Convert all invoke_result traits into decltype(...) 2018-02-06 00:18:06 +01:00
Denis Blank
d1e0c1d606 Move is_invokeable to traits and rename it to is_invokeable_from_tuple 2018-02-06 00:12:59 +01:00
Denis Blank
eafbe4b37d Add a C++17 std::disjunction polyfill 2018-02-05 23:47:51 +01:00
Denis Blank
6a8919c63d Add the public headers for both traversal strategies 2018-02-05 23:47:50 +01:00
Denis Blank
7d0e68ad9b Convert util::tuple* to std::tuple* 2018-02-05 23:47:49 +01:00
Denis Blank
cd2af2d49a Only check for wrongly finished async traversals in debug mode 2018-02-05 23:47:49 +01:00
Denis Blank
62823f8f56 More conversion work 2018-02-05 23:47:48 +01:00
Denis Blank
910af18f1e Rename the inclusion guards slightly 2018-02-05 23:47:45 +01:00
Denis Blank
0d3a88c4a1 Take my GSoC code for nested pack traversal over
* See https://naios.github.io/gsoc2017 for details
2018-02-05 23:47:07 +01:00
Denis Blank
17a4e8a8da Remove the continuable-api header 2018-02-02 00:24:38 +01:00
Denis Blank
9ce9884376 Rename the inclusion guards slightly 2018-02-01 22:49:42 +01:00
Denis Blank
49a1e837ac Remove the comparison 2018-02-01 22:48:06 +01:00
Denis Blank
4aaafb57fe Push the version to 2.0.0 2018-01-30 22:09:07 +01:00
Denis Blank
0657a191f6 Clearify promise and callback in the readme 2018-01-30 22:05:44 +01:00
Denis Blank
d3a1ef3005 Some missing readme modernization 2018-01-30 22:01:32 +01:00
Denis Blank
fa273a3eac Add coroutine (await) usage support to the readme 2018-01-30 21:58:17 +01:00
Denis Blank
4b317fb964 Some doxygen additions and improvements 2018-01-30 21:40:09 +01:00
Denis Blank
1daa13b1d6 Some minor improvements to clang-tidy 2018-01-30 05:15:19 +01:00
Denis Blank
1c2cb645ef Initial clang-tidy support 2018-01-30 04:07:42 +01:00
Denis Blank
c4a19efa55 Some const additions 2018-01-30 02:18:31 +01:00
Denis Blank
f35cc355e0 Fix an expression result unused warning 2018-01-30 01:58:53 +01:00
Denis Blank
fb4eb379ca More constexpr conversions 2018-01-30 01:57:41 +01:00
Denis Blank
933d773c4c Improve the constexprness 2018-01-30 01:09:32 +01:00
Denis Blank
24201d5106 Remove the GTest requirement from the amalgamation 2018-01-30 00:30:49 +01:00
Denis Blank
f4ee3ea0d1 Make continuable.hpp include all headers of the library
* Move the old continuable.hpp header into continuable-types.hpp
2018-01-30 00:14:47 +01:00
Denis Blank
7aae524cb1 Remove the alignment tests
* Don't fail the sanitizers when testing with coroutines
2018-01-29 07:45:38 +01:00
Denis Blank
c2ad28d702 More is aligned tests 2018-01-29 07:00:17 +01:00
Denis Blank
0f3ec412fe Move temporarily away from the travis-ci containerized builds
* See travis-ci/travis-ci#9033
2018-01-29 05:19:19 +01:00
Denis Blank
2deaf7a301 Update the ASan and UBSan flags 2018-01-29 04:45:37 +01:00
Denis Blank
8c8f7f98a1 Revert "Revert "Skip asan testing when using await""
* -> "fatal error: error in backend: cannot move instruction since its users are not dominated by CoroBegin"

This reverts commit eed883b0a5bdf8531943ad8afe182fa04e956a3b.
2018-01-29 02:16:24 +01:00
Denis Blank
19941e774b Set the asan verbosity to 1 2018-01-29 02:04:02 +01:00
Denis Blank
701a8ea1a4 Attemt to fix the alignment errors 2018-01-29 01:03:44 +01:00
Denis Blank
eed883b0a5 Revert "Skip asan testing when using await"
This reverts commit 66b662e2a4e949e015c514ce15864f68c4fd98c3.
2018-01-29 00:36:49 +01:00
Denis Blank
9c4b0a3a17 Fix the memory leak tested in 5751152733 2018-01-29 00:33:53 +01:00
Denis Blank
032fc436b9 Fix an unused variable error 2018-01-29 00:20:38 +01:00
Denis Blank
5751152733 Add a memory leak regression test (detected by LSan) 2018-01-28 23:10:39 +01:00
Denis Blank
f976591e94 Remove an unused header 2018-01-28 22:40:07 +01:00
Denis Blank
4c76a11f45 Fix a coroutine MSVC/Clang incompatibility 2018-01-28 22:39:53 +01:00
Denis Blank
3a50657a17 Some test ai examples 2018-01-25 08:13:43 +01:00
Denis Blank
6c209c876a Allows fail to accept plain continuables 2018-01-25 08:11:05 +01:00
Denis Blank
66b662e2a4 Skip asan testing when using await 2018-01-25 06:15:17 +01:00
Denis Blank
444a38f56f Some improvements to the CI script 2018-01-25 05:52:14 +01:00
Denis Blank
5de5de5828 Attempt to fix the current travis issues by using an own shell script 2018-01-25 05:42:47 +01:00
Denis Blank
6e404b6eaa Apply outer and inner size improvements for the callable wrapper
* This will decrease the needed allocations heavily,
  since we don't have to allocate twice if a continuation is type erased
  since the outer type erasure may contain the inner one with zero costs.
2018-01-25 05:00:19 +01:00
Denis Blank
5d6b6116bf Travis test 2018-01-25 04:50:33 +01:00
Denis Blank
2a1949a5b0 Next attempt 2018-01-25 04:14:44 +01:00
Denis Blank
9ee5e38fef Allow the coroutine build to fail 2018-01-25 03:56:52 +01:00
Denis Blank
81f023ba36 Since we are building with libc++ we don't need to install gcc on such builds 2018-01-25 03:56:38 +01:00
Denis Blank
e011ec7071 Attempt to fix travis failures 2018-01-25 03:48:17 +01:00
Denis Blank
9286cfa8cc Feature detection placeholders 2018-01-24 23:05:23 +01:00
Denis Blank
ca5d0a6e29 Some misc changes 2018-01-24 03:05:59 +01:00
Denis Blank
0cfb8a96d2 Prevent unused param warnings 2018-01-24 02:35:54 +01:00
Denis Blank
c81d45c7ea Convert some hardcoded flags to expressions
* Remove the continuable-coroutines interface target
2018-01-24 02:07:11 +01:00
Denis Blank
356e99dba7 Abort ASAN on errors 2018-01-24 02:03:13 +01:00
Denis Blank
b17d2f9c17 Fix a GCC warning:
```cpp
  ../include/continuable/detail/expected.hpp:344:44: error: declaration of ‘using expected = class cti::detail::util::expected<cti::detail::util::detail::void_guard_tag>’ [-fpermissive]
     using expected = expected<void_guard_tag>;
                                              ^
  ../include/continuable/detail/expected.hpp:171:7: error: changes meaning of ‘expected’ from ‘class cti::detail::util::expected<cti::detail::util::detail::void_guard_tag>’ [-fpermissive]
   class expected
  ```
2018-01-24 01:44:30 +01:00
Denis Blank
db8c5b07c9 More work on the await clang build 2018-01-24 01:33:05 +01:00
Denis Blank
b5853c117a Update gtest 2018-01-24 01:18:19 +01:00
Denis Blank
a432c14c57 Update function2 2018-01-24 01:18:11 +01:00
Denis Blank
810d778b2e Attemtp to fix coroutines on clang 2018-01-24 01:13:03 +01:00
Denis Blank
45cd47806b One more missing include 2018-01-24 01:08:18 +01:00
Denis Blank
9b0ad832ff Add a missing include 2018-01-24 01:06:19 +01:00
Denis Blank
efbce1f886 Mark continuation invocations as non exceptional 2018-01-23 19:40:55 +01:00
Denis Blank
ea6b76a044 Some namespace corrections 2018-01-15 05:55:32 +01:00
Denis Blank
cb225835d6 Preparation for making expected available inside the base namespace 2018-01-14 04:26:32 +01:00
Denis Blank
55a59fb7f7 Attempt to fix a gcc warning 2018-01-14 00:18:20 +01:00
Denis Blank
b502ad61be Next attempt 2018-01-04 01:53:45 +01:00
Denis Blank
3626133f8e Happy new Year!
* Update license notices to 2018
2018-01-04 01:45:09 +01:00
Denis Blank
95aadd9273 Attempt to fix the clang travis build 2018-01-04 01:43:03 +01:00
Denis Blank
04613cb005 Use libc++ second try 2017-12-30 05:06:43 +01:00
Denis Blank
140627cfd2 Fix clang and GCC build errors 2017-12-30 03:37:56 +01:00
Denis Blank
19d4bd97dd Travis: Use libc++ for clang based builds
* Required for <experimental/coroutine>
2017-12-30 03:34:11 +01:00
Denis Blank
02965776e3 Additionally test co_await expressions with 1 or more args 2017-12-30 03:19:11 +01:00
Denis Blank
a85040b0c2 Basic skeleton for coroutine_traits 2017-12-28 05:26:29 +01:00
Denis Blank
5d95b5c3e3 Test that coroutines propagate thrown exceptions back to co_await 2017-12-28 04:16:13 +01:00
Denis Blank
6908f22996 Fix an issue with the feature inclusion order 2017-12-27 06:13:38 +01:00
Denis Blank
d774371769 Await unit test 2017-12-27 06:06:25 +01:00
Denis Blank
434db0fc24 First working version of co_await using continuables! 2017-12-26 03:33:44 +01:00
Denis Blank
baf47b129c Fix the remaining copy bug in expected 2017-12-26 03:02:26 +01:00
Denis Blank
71001cecc3 more 2017-12-21 05:04:57 +01:00
Denis Blank
53c878c9c5 build fix 2017-12-10 03:51:08 +01:00
Denis Blank
dbf73b6ac6 more 2017-12-08 21:59:59 +01:00
Denis Blank
73a8da1b86 Support value and error assignments in expected 2017-12-01 19:03:13 +01:00
Denis Blank
95d5f25f84 Rename flow -> next 2017-11-30 07:32:17 +01:00
Denis Blank
b4332b66c5 Make use of expected in await 2017-11-30 07:11:23 +01:00
Denis Blank
3d6bb4d8a7 Add tests for move assignment 2017-11-30 04:20:53 +01:00
Denis Blank
7fda8b9f65 Fix some build errors in expected and add typed tests 2017-11-30 04:17:13 +01:00
Denis Blank
bf89a98d60 Fix the build 2017-11-30 02:16:49 +01:00
Denis Blank
8b99c72071 more 2017-11-30 00:59:52 +01:00
Denis Blank
b18f78c6be More 2017-11-22 23:38:33 +01:00
Denis Blank
0f5dd265fd more 2017-11-22 02:11:27 +01:00
Denis Blank
91b75953a6 more 2017-11-16 04:46:08 +01:00
Denis Blank
b3d350cb11 more 2017-11-14 03:13:54 +01:00
Denis Blank
a6b6148f44 More work on await 2017-11-13 18:23:04 +01:00
Denis Blank
6001e99723 Make continuables usable in await expressions 2017-11-13 15:03:52 +01:00
Denis Blank
d8591d1f13 More work on using operator await 2017-11-13 13:32:34 +01:00
Denis Blank
b54bb80147 Rename and cleanup some CMake options 2017-11-13 13:32:16 +01:00
Denis Blank
2ddc2477bb Make CPack package names platform dependent 2017-11-13 12:47:26 +01:00
Denis Blank
26ff6312ed Initial work on corooutine/await support 2017-11-06 19:21:34 +01:00
Denis Blank
1c0c17f699 Fix a test build error 2017-11-01 23:33:26 +01:00
Denis Blank
3139325440 Describe asynchronous error handling in the readme 2017-11-01 21:56:18 +01:00
Denis Blank
9a2ccb782f More changes to the readme regarding v2.0.0 2017-11-01 21:21:57 +01:00
Denis Blank
20c54f54cb Move transforms in their own toplevel namespace 2017-10-11 17:04:01 +02:00
Denis Blank
5c4d28233c More examples 2017-10-11 15:18:35 +02:00
Denis Blank
d80fe94c0c Update the readme to reflect the recent changes 2017-10-10 13:56:22 +02:00
Denis Blank
3963d34a92 Attempt to upload packages 2017-10-04 23:37:12 +02:00
Denis Blank
e57b9b21ad Add a standalone target test 2017-10-04 23:32:06 +02:00
Denis Blank
4f6686f9c7 Slightly rename the license 2017-10-04 23:31:06 +02:00
Denis Blank
d9cfb743e0 Fix the install target 2017-10-04 23:30:50 +02:00
Denis Blank
e4c146023f Update function2 to naios/function2@8f0d646 2017-10-04 23:30:35 +02:00
Denis Blank
0fb0058f6b Add install targets and CPack support 2017-10-04 22:58:08 +02:00
Denis Blank
c75baaec90 Add a small asio example 2017-10-04 22:23:37 +02:00
Denis Blank
76f3fb0380 Add targets for warnings, flags and noexcept flags 2017-10-04 22:23:25 +02:00
Denis Blank
257c1b0062 more 2017-10-04 20:01:15 +02:00
Denis Blank
79dedef359 Add the sceleton for an asio example 2017-10-04 19:46:30 +02:00
Denis Blank
dcadc77956 Update the doxgen documentation 2017-10-04 19:37:10 +02:00
Denis Blank
f3c33b58c6 Improve the documentation 2017-10-04 19:07:20 +02:00
Denis Blank
2b061a6058 Move the documentation code to doc 2017-10-04 18:52:38 +02:00
Denis Blank
425d922965 Test the flatten transform 2017-10-04 18:45:45 +02:00
Denis Blank
616b68c008 Fix the examples after the r-value qualification in 84ca172caa3 2017-10-04 17:26:07 +02:00
Denis Blank
84ca172caa Qualify continuable callbacks as r-value callable 2017-10-04 17:16:58 +02:00
Denis Blank
5c3ae8223a Materialize the continuable before applying transforms 2017-10-04 16:56:56 +02:00
Denis Blank
92d247f2d6 Fix a build error with the fold emulation 2017-10-04 16:56:38 +02:00
Denis Blank
75b5ecad9d more mocks 2017-10-04 04:52:30 +02:00
Denis Blank
b67ca1c224 Allow basic emulation of C++17 fold expressions 2017-10-04 04:32:49 +02:00
Denis Blank
04539ba638 Implement flatten as transform 2017-10-04 04:11:37 +02:00
Denis Blank
0eb272a46f Move generic transforms out of the main class
* Introduce apply to use generic transforms which are
  defined out of class.
2017-10-04 04:05:11 +02:00
Denis Blank
ec65ce0bf3 Attempt to fix the travis gcc build 2017-10-04 03:39:53 +02:00
Denis Blank
8f83d4d30c Split the sequence tests for an improved memory footprint 2017-10-04 03:33:32 +02:00
Denis Blank
612aeef0c8 Add an operator| (pipe) for future folding and channels 2017-10-04 03:17:26 +02:00
Denis Blank
09bae47e09 Add unit tests targeting error handler and inner handler exceptions 2017-10-04 03:11:10 +02:00
Denis Blank
b80a4e852b Fix a msvc warning 2017-10-04 02:40:53 +02:00
Denis Blank
4afeb141f1 Catch exceptions thrown by the continuation handler
* Forward it to the next error handler
2017-10-04 02:39:02 +02:00
Denis Blank
ddd5b0a0a6 more 2017-10-04 02:23:55 +02:00
Denis Blank
bf1ac9daee Unify the then, fail and flow callback creation 2017-10-04 01:47:04 +02:00
Denis Blank
dc205c8e51 Started on accepting flowing callables which accept all paths 2017-10-04 01:11:25 +02:00
Denis Blank
bac14297e0 Test exceptions on/off in appveyor MSVC builds 2017-10-04 00:10:31 +02:00
Denis Blank
f7fabd8ba3 Attempt to fix the GCC travis build 2017-10-04 00:10:08 +02:00
Denis Blank
1e1f5cdf5b Fix some issues with the no exception build 2017-10-03 21:59:09 +02:00
Denis Blank
1042ddc1f1 Run an exception disabled build inside travis 2017-10-03 21:55:27 +02:00
Denis Blank
4d964b0fe2 Add error handling testing 2017-10-03 21:55:04 +02:00
Denis Blank
60c73a1a48 Add trap and unreachable
* Use both functions to exit on invalid behaviour
2017-10-03 21:03:27 +02:00
Denis Blank
54fb32ae56 Fix the remaining promisify composition GCC errors 2017-10-03 20:26:32 +02:00
Denis Blank
1471e1f610 Use 2 processes in travis-ci 2017-10-03 20:25:13 +02:00
Denis Blank
2dd1b9f361 Promisify all and any callbacks 2017-10-03 20:08:48 +02:00
Denis Blank
40588e4e6e Use ninja for travis-ci builds 2017-10-03 19:58:27 +02:00
Denis Blank
935fd9d32d Initial work on error testing 2017-10-03 19:53:58 +02:00
Denis Blank
d199658ae2 Downgrade GCC in travis to version 6 2017-10-03 19:07:33 +02:00
Denis Blank
ce4ae5204a Fix an expression result unused warning
* Use tuple comparison instead of value per value
2017-10-03 19:00:11 +02:00
Denis Blank
b488d88370 Simplify assert_async_binary_validation 2017-10-03 18:39:35 +02:00
Denis Blank
342d08ab1d Simplify static_for_each_in 2017-10-03 18:33:44 +02:00
Denis Blank
457a9dca00 Simplify some tests in order to reduce heap usage while building 2017-10-03 17:32:03 +02:00
Denis Blank
b8ff4c4c18 Split the chaining tests into multiple ones 2017-10-03 17:15:42 +02:00
Denis Blank
2008f006a0 Test the latest GCC and Clang in travis only 2017-10-03 17:02:23 +02:00
Denis Blank
6f8edf3416 Thin out the tests 2017-10-03 17:01:05 +02:00
Denis Blank
b8b9f31024 Improve the documentation of promise and continuable 2017-10-03 02:42:19 +02:00
Denis Blank
52cf1ab929 Started to implement combined path callables 2017-10-03 02:25:20 +02:00
Denis Blank
7dcd18c964 Expose dispatch_error_tag and error_type 2017-10-03 01:48:22 +02:00
Denis Blank
3d0ce45ae4 Rename unique_callback to promise 2017-10-03 01:37:19 +02:00
Denis Blank
29c61b6b96 Fix an inline function isn't defined warning 2017-10-03 01:36:32 +02:00
Denis Blank
f5e10d9fba Split the base tests intro multiple files 2017-10-03 01:33:04 +02:00
Denis Blank
6b9efad602 Pass promises to type erased continuables instead of the raw callbacks 2017-10-03 01:21:32 +02:00
Denis Blank
a685d9234a Readd unique_continuable 2017-10-03 01:14:53 +02:00
Denis Blank
22c9ee01d4 Fix "all" compositions with error handling 2017-10-02 17:23:06 +02:00
Denis Blank
268245b5e4 Add the possibility to use custom error types 2017-10-02 17:22:25 +02:00
Denis Blank
bc431b4013 Rename catching -> fail 2017-10-01 03:37:34 +02:00
Denis Blank
52fb13387d Update function2 to naios/function2@5e376068 2017-10-01 03:37:16 +02:00
Denis Blank
f8b3c348a4 Update function2 to naios/function2@2.1.0
* Fixes the remaining crash for error handling
2017-10-01 02:18:52 +02:00
Denis Blank
d9546c15bb Allow packed_dispatch to receive an arbitrary count of tail args 2017-10-01 01:49:50 +02:00
Denis Blank
e594989af2 More implementation work on error handling 2017-10-01 01:40:32 +02:00
Denis Blank
8856f85388 Fix more build failures regarding error handling 2017-10-01 01:26:49 +02:00
Denis Blank
5d11d44a44 Enable the failing part 2017-10-01 00:54:18 +02:00
Denis Blank
b9a367ba03 More work on error handling 2017-10-01 00:49:43 +02:00
Denis Blank
64c03b818d Fix a conditional move 2017-09-30 22:21:47 +02:00
Denis Blank
0f242df1a6 Remove an unused lambda capture 2017-09-30 19:49:33 +02:00
Denis Blank
8d7f4e5a1b Fix some unused warnings 2017-09-30 02:40:20 +02:00
Denis Blank
fb8bb4580f Update dependencies 2017-09-30 02:40:11 +02:00
Denis Blank
aa311a378c Update travis to use the containerized trusty and later compiler versions 2017-09-30 02:26:02 +02:00
Denis Blank
7d9198b5cc Rename set_error -> set_exception
* In order to move closer to the standard
2017-09-30 02:19:58 +02:00
Denis Blank
37c70c3365 First building API of promises and error handling
* Currently not functional (yet)
2017-09-30 02:13:27 +02:00
Denis Blank
8e8f5e982d Remove the deprecated composition APIs 2017-09-29 16:16:21 +02:00
Denis Blank
6481b5454d Started on implementing error handler chaining
* Namespace fixups
2017-09-29 15:39:07 +02:00
Denis Blank
98936f6972 minor stuff 2017-09-29 01:24:33 +02:00
Denis Blank
9ed0adbab7 Expose the arguments in promises
* better IDE support
* less instantiations
2017-09-28 05:24:17 +02:00
Denis Blank
eb8528c90e Move unused to util 2017-09-28 05:23:21 +02:00
Denis Blank
7accbdf41c Adapt the promise to the latest tag changes 2017-09-28 05:03:15 +02:00
Denis Blank
172f3561e9 Lift invoke_proxy into chain_continuation 2017-09-28 05:02:02 +02:00
Denis Blank
687133870f Make the multi flow less intrusive 2017-09-28 04:25:29 +02:00
Denis Blank
ff91ff7fc3 More work on supporting multiple flow paths 2017-09-27 06:47:45 +02:00
Denis Blank
0aa42d5b1a Attempt to fix the GCC build 2017-09-27 02:35:53 +02:00
Denis Blank
b65eea8b1a Started to implement promises and multi execution flow 2017-09-27 02:32:46 +02:00
Denis Blank
8d6c9ab895 Split util into traits and util 2017-09-27 01:10:35 +02:00
Denis Blank
7a02148c9d Change inclusions 2017-09-26 23:28:50 +02:00
Denis Blank
bc2d46ff40 Split more functionality into seperate header 2017-09-25 03:28:00 +02:00
Denis Blank
bd68d14b34 Split the header into multiple files 2017-09-24 19:55:58 +02:00
Denis Blank
55252bce4a Add abi inline headers 2017-09-22 17:14:39 +02:00
Denis Blank
08dbd75736 Update the codestyle 2017-09-21 23:36:09 +02:00
Denis Blank
3809af9092 Update editor, formatting and CI files 2017-09-21 23:29:21 +02:00
Denis Blank
bd9cf93f74 Update function2 2017-09-21 23:27:11 +02:00
Denis Blank
447dc123e3 Update gtest 2017-09-21 23:27:03 +02:00
Denis Blank
2392778b11 Bump the version to 1.1.0 2017-04-21 20:04:04 +02:00
Denis Blank
bb70199422 Disable the cxx_function tests 2017-04-21 19:43:57 +02:00
Denis Blank
90dfde84c5 Rename all connection functions to their std counterparts:
- all_of -> when_all
- any_of -> when_any
- seq_of -> when_seq
2017-04-21 19:43:46 +02:00
Denis Blank
db0c20ae3d Update cxx_function to potswa/cxx_function@c12ed6e 2017-04-21 19:14:05 +02:00
Denis Blank
4b570029a9 mock: std::error_code -> std::error_condition 2017-04-21 19:11:36 +02:00
Denis Blank
89559468e7 Update cxx_function to potswa/cxx_function@354f966 2017-03-19 17:50:06 +01:00
Denis Blank
6e04f0d389 Disable the cxx_function tests for now 2017-03-19 00:46:05 +01:00
Denis Blank
a2f9007cf6 Use cxx_function as second testing backend 2017-03-19 00:30:50 +01:00
Denis Blank
e22db05cc3 Add potswa/cxx_function@e7beb8bfe as second test type erasure backend 2017-03-18 23:35:14 +01:00
Denis Blank
c46aa9e7c3 More mocks 2017-03-18 23:33:14 +01:00
Denis Blank
f6151262dc Cleanup 2017-03-18 02:34:09 +01:00
Denis Blank
151dc56297 Update the appveyor badge to display the master branch 2017-03-18 02:26:19 +01:00
Denis Blank
6d9680905a Add a promise mock 2017-03-18 02:10:14 +01:00
Denis Blank
2de2f8d6d9 More mocks 2017-03-17 23:39:51 +01:00
Denis Blank
7c0f6f897b Some minor qualifier and naming improvements 2017-03-17 22:03:21 +01:00
Denis Blank
aa0f0db0cd Also include the exception header 2017-03-17 16:44:21 +01:00
Denis Blank
9822484088 Fix the CI build 2017-03-17 16:33:26 +01:00
Denis Blank
80fb5026fe Add a mock project for experimental research 2017-03-17 13:33:17 +01:00
Denis Blank
8ac5e668c7 Add a missing unit-test for execcutors 2017-03-08 17:20:34 +01:00
Denis Blank
32d3c30703 Beautify the doxygen main page 2017-03-06 21:20:02 +01:00
Denis Blank
4baddcd534 Set the version to 1.0.0 2017-03-04 21:50:41 +01:00
Denis Blank
ccd77dee29 Edit the readme 2017-03-04 21:49:57 +01:00
Denis Blank
7ab7c726b6 Change the expected behaviour of some GTest macros from expect -> assert 2017-03-04 14:11:58 +01:00
Denis Blank
a2fa1c1ae2 Add a slideshow 2017-03-04 14:11:57 +01:00
Denis Blank
05a4ab7e11 Make the function2 dependency available when not building the unit-tests 2017-03-04 14:11:56 +01:00
Denis Blank
22d7404970 Fix the async completion test after bf4335d60. 2017-03-04 14:11:56 +01:00
162 changed files with 18019 additions and 3041 deletions

View File

@ -1,4 +1,38 @@
BasedOnStyle: LLVM
AlignAfterOpenBracket: Align
AllowAllArgumentsOnNextLine: 'true'
AllowAllConstructorInitializersOnNextLine: 'true'
AllowAllParametersOfDeclarationOnNextLine: 'true'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: Empty
AllowShortLambdasOnASingleLine: Empty
AlwaysBreakTemplateDeclarations: 'Yes'
BinPackArguments: 'true'
BinPackParameters: 'true'
BreakConstructorInitializers: BeforeComma
BreakConstructorInitializersBeforeComma: 'true'
ConstructorInitializerIndentWidth: 2
FixNamespaceComments: 'true'
IndentCaseLabels: 'true'
IndentPPDirectives: AfterHash
PenaltyBreakAssignment: 1000
PenaltyBreakBeforeFirstCallParameter: 100
PointerAlignment: Left
IndentCaseLabels: true
MacroBlockBegin: "^CONTINUABLE_BLOCK_.*_BEGIN$"
MacroBlockEnd: "^CONTINUABLE_BLOCK_.*_END$"
IncludeCategories:
- Regex: '^<+[a-z_]+>'
Priority: 1
- Regex: '^<experimental/+[a-z_]+>'
Priority: 2
- Regex: '^<(gtest|function2)/.*\.(h|hpp)>'
Priority: 3
- Regex: '^<continuable/.*\.hpp>'
Priority: 4
- Regex: '^<.*'
Priority: 5
- Regex: '.*'
Priority: 6

19
.clang-tidy Normal file
View File

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

11
.editorconfig Normal file
View File

@ -0,0 +1,11 @@
[*]
charset = utf-8
indent_size = 2
tab_width = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 80
[*.{c,h,cpp,hpp,inl}]
charset = latin1

36
.gitattributes vendored Normal file
View File

@ -0,0 +1,36 @@
#sources
*.c text
*.cc text
*.cxx text
*.cpp text
*.c++ text
*.hpp text
*.h text
*.h++ text
*.hh text
# Compiled Object files
*.slo binary
*.lo binary
*.o binary
*.obj binary
# Precompiled Headers
*.gch binary
*.pch binary
# Compiled Dynamic libraries
*.so binary
*.dylib binary
*.dll binary
# Compiled Static libraries
*.lai binary
*.la binary
*.a binary
*.lib binary
# Executables
*.exe binary
*.out binary
*.app binary

1
.github/CODEOWNERS vendored 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

9
.gitignore vendored
View File

@ -48,9 +48,8 @@ bld/
# Visual Studo 2015 cache/options directory
.vs/
doc/doxygen/
# VSCode
.vscode/
format.sh
push.sh
commit.sh
pull.sh
# TMP files generated from clang-format
*.TMP

10
.gitmodules vendored
View File

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

View File

@ -1,106 +0,0 @@
sudo: true
dist: trusty
language: cpp
git:
depth: 1
matrix:
include:
- os: linux
compiler: gcc
addons:
apt:
packages:
- valgrind
- g++-5
sources:
- ubuntu-toolchain-r-test
env:
- COMPILER=g++-5
- os: linux
compiler: gcc
addons:
apt:
packages:
- valgrind
- g++-6
sources:
- ubuntu-toolchain-r-test
env:
- COMPILER=g++-6
- os: linux
compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.7 main"
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
packages:
- g++-6
- clang-3.7
env:
- COMPILER=clang++-3.7
- os: linux
compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
packages:
- g++-6
- clang-3.9
env:
- COMPILER=clang++-3.9
install:
- export CXX=$COMPILER
- $CXX --version
# Function for creating a new 'build' directory
- |
function renew_build {
echo "Renew build directory..."
cd $TRAVIS_BUILD_DIR
# Remove any existing build directory
[ -e build ] && rm -r -f build
mkdir build
cd build
# Configure the project and build it
cmake -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS -Werror" -DCMAKE_BUILD_TYPE=Debug ..
}
script:
- |
if [[ $COMPILER == *"clang"* ]]; then
# Build the test suite with various sanitizers:
# - ASan (LSan):
echo "Building with address sanitizer..."
CMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer"
renew_build
make -j2
ctest --verbose
# - UBSan:
echo "Building with undefined behaviour sanitizer..."
CMAKE_CXX_FLAGS="-fsanitize=undefined -fno-omit-frame-pointer"
renew_build
make -j2
ctest --verbose
else
# Build an run the tests suite with valgrind
renew_build
make -j2
valgrind --error-exitcode=1 --leak-check=full --show-reachable=yes ctest --verbose
fi
notifications:
email: false

View File

@ -1,24 +1,115 @@
cmake_minimum_required(VERSION 3.2)
project(continuable C CXX)
# 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.
# Dependencies
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
cmake_minimum_required(VERSION 3.11)
project(
continuable
VERSION 4.0.0
LANGUAGES C CXX)
if(CTI_CONTINUABLE_IS_FIND_INCLUDED)
set(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT OFF)
else()
string(COMPARE EQUAL ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}
CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
endif()
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
message(
STATUS
"Building with ${CMAKE_CXX_COMPILER_ID} (${CMAKE_CXX_COMPILER_VERSION})")
endif()
option(CTI_CONTINUABLE_WITH_INSTALL "Add the continuable install targets"
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
option(CTI_CONTINUABLE_WITH_TESTS "Build the continuable unit tests"
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
option(CTI_CONTINUABLE_WITH_EXAMPLES "Build the continuable examples"
${CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT})
option(CTI_CONTINUABLE_WITH_BENCHMARKS "Build the continuable benchmarks" OFF)
option(CTI_CONTINUABLE_WITH_NO_EXCEPTIONS "Disable exception support" OFF)
option(CTI_CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS
"Enable unhandled asynchronous exceptions" OFF)
option(CTI_CONTINUABLE_WITH_COROUTINE "Enable C++20 coroutines" OFF)
option(CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE
"Enable experimental coroutines" OFF)
option(CTI_CONTINUABLE_WITH_CPP_LATEST
"Enable the highest C++ standard available for testing polyfills" OFF)
option(CTI_CONTINUABLE_WITH_LIGHT_TESTS
"Disable some template heavy unit tests (for CI usage)" OFF)
# Top level project settings only
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
set(CTI_CONTINUABLE_WITH_CONCURRENT_JOBS
"0"
CACHE
STRING
"Set the number of concurrent compilation jobs (0 = unlimited, for CI usage)"
)
else()
set(CTI_CONTINUABLE_WITH_CONCURRENT_JOBS "0")
endif()
if(NOT TARGET Threads::Threads)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
endif()
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
include(cmake/CMakeLists.txt)
add_subdirectory(dep)
else()
if(NOT TARGET function2::function2)
find_package(function2 4 REQUIRED)
endif()
endif()
# continuable-base
add_library(continuable-base INTERFACE)
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
add_library(continuable-base INTERFACE)
else()
add_library(continuable-base INTERFACE IMPORTED GLOBAL)
endif()
target_include_directories(continuable-base
INTERFACE
"${CMAKE_CURRENT_LIST_DIR}/include")
add_library(continuable::continuable-base ALIAS continuable-base)
target_link_libraries(continuable-base
INTERFACE
Threads::Threads)
target_include_directories(
continuable-base
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_compile_features(continuable-base
INTERFACE
cxx_alias_templates
target_link_libraries(continuable-base INTERFACE Threads::Threads)
target_compile_features(
continuable-base
INTERFACE cxx_alias_templates
cxx_auto_type
cxx_constexpr
cxx_decltype
@ -32,27 +123,111 @@ target_compile_features(continuable-base
cxx_trailing_return_types
cxx_return_type_deduction)
add_library(continuable INTERFACE)
if(CTI_CONTINUABLE_WITH_CPP_LATEST)
target_compile_features(continuable-base INTERFACE cxx_std_20)
endif()
target_link_libraries(continuable
INTERFACE
if(CTI_CONTINUABLE_WITH_COROUTINE)
if(NOT CTI_CONTINUABLE_WITH_CPP_LATEST)
message(FATAL_ERROR "CTI_CONTINUABLE_WITH_COROUTINE requires "
"CTI_CONTINUABLE_WITH_CPP_LATEST!")
endif()
target_compile_options(
continuable-base
function2)
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()
# Testing
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
if (MSVC)
if(CTI_CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS)
target_compile_definitions(continuable-base
INTERFACE CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS)
endif()
if(CTI_CONTINUABLE_IS_TOP_LEVEL_PROJECT)
add_library(continuable INTERFACE)
else()
add_library(continuable INTERFACE IMPORTED GLOBAL)
endif()
add_library(continuable::continuable ALIAS continuable)
target_link_libraries(continuable INTERFACE continuable::continuable-base
function2::function2)
if(CTI_CONTINUABLE_WITH_INSTALL)
include(ExternalProject)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
# Create an install target: Headers and license files
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/continuable"
DESTINATION "include")
install(FILES "LICENSE.txt" DESTINATION .)
install(FILES "Readme.md" DESTINATION .)
# Config.cmake
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# ConfigVersion.cmake
configure_package_config_file(
"cmake/config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
# PATH_VARS INCLUDE_INSTALL_DIR SYSCONFIG_INSTALL_DIR
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# Targets.cmake
export(
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-base
NAMESPACE ${PROJECT_NAME}::
FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake")
install(
TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-base
EXPORT "${PROJECT_NAME}Targets"
INCLUDES
DESTINATION "include")
install(
EXPORT "${PROJECT_NAME}Targets"
NAMESPACE ${PROJECT_NAME}::
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
# Setup CPack for bundling
set(CPACK_GENERATOR "ZIP")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
include(CPack)
endif()
# Testing and examples
if(CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_EXAMPLES)
if(MSVC)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
endif()
enable_testing()
include(cmake/CMakeLists.txt)
add_subdirectory(dep)
add_subdirectory(examples)
if(CTI_CONTINUABLE_WITH_TESTS)
add_subdirectory(test)
endif ()
endif()
if(CTI_CONTINUABLE_WITH_EXAMPLES)
add_subdirectory(examples)
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
Copyright (c) 2015-2017 Denis Blank
Copyright (c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

481
Readme.md
View File

@ -1,107 +1,98 @@
# continuable->then(make_things_simple());
[![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?svg=true)](https://ci.appveyor.com/project/Naios/continuable) ![](https://img.shields.io/badge/License-MIT-blue.svg) [![](https://img.shields.io/badge/Try-online-green.svg)](http://melpon.org/wandbox/permlink/gRWxSNHtARvRcmSY)
<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>
> Async C++14 platform independent continuation chainer providing light and allocation aware futures
<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>
This library provides full feature support of:
------
* async continuation chaining using **callbacks** (*then*).
* **no enforced type-erasure** which means we need **extremely fewer heap allocations** .
* support for **finite logical connections** between continuables through an **all, any or sequence** strategy.
* **syntactic sugar** for attaching callbacks to a continuation like partial invocation.
#### Continuable is a C++14 library that provides full support for:
* lazy async continuation chaining based on callbacks (**then**) and expression templates, callbacks are wrapped nicely as **promises**.
* **no enforced type-erasure** which means we need **less heap allocations** than comparable libraries, strictly following the **"don't pay for what you don't use"** principle.
* support for *all*, *any* and *sequential* connections between continuables through expressive operator overloads **&&**, **||** and **>>** as well as free functions **when_all**, **when_any** and **when_seq**.
* asynchronous **error handling** through **exceptions**, **error codes** and **user defined types**.
* syntactic sugar for instance: **partial invocation**, **tuple unpacking**, `co_await` support and **executors**.
* **encapsuled from any runtime**, larger framework or executors makes it possible to use continuable even in smaller or esoteric usage scenarios.
------
#### Getting started:
The [documentation](https://naios.github.io/continuable/) offers everything you need:
* [Installation guide](https://naios.github.io/continuable/installation.html)
* [Usage tutorial](https://naios.github.io/continuable/tutorial.html)
* [Configuration explanation](https://naios.github.io/continuable/configuration.html)
* [Changelog](https://naios.github.io/continuable/changelog.html)
#### Issues and contributions
Issue reports and questions are accepted through the Github issue tracker as well as pull requests.
Every contribution is welcome! Don't hesitate to ask for help if you need any support
in improving the implementation or if you have any troubles in using the library
## The library design
#### Quick Tour
The continuable library was designed in order to provide you as much as flexibility as possible:
- **Create a continuable through `make_continuable` which returns a promise on invocation:**
```cpp
auto http_request(std::string url) {
return cti::make_continuable<std::string>([url = std::move(url)](auto&& promise) {
// Perform the actual request through a different library,
// resolve the promise upon completion of the task.
promise.set_value("<html> ... </html>");
// or: promise.set_exception(std::make_exception_ptr(std::exception("Some error")));
// or: promise.set_canceled();
});
}
- There is no enforced type erasure which means there is less memory allocation and thus the callback chains are heavily optimizable by the compiler. That's why the library is well usable in the embedded or gaming field. **Don't pay for what you don't use!**
- The library provides support for **dispatching callbacks on a given executor**, however, it doesn't provide it's own one. You probably will use your own executor like [asio](https://github.com/chriskohlhoff/asio), [libuv](https://github.com/libuv/libuv) or a corresponding [lock-free concurrentqueue](https://github.com/cameron314/concurrentqueue) anyway. In most cases, the executor will do the type erasure for you, so there is no reason to do it twice.
- The library provides as much as **syntactic sugar** as it's possible, in order to make continuation chaining expressive and simple. For instance, it allows you to logical connect continuables through the well-known operators `&&` and `||`.
- The library is header only and has **fewer dependencies**:
- The `continuable-base.hpp` header only depends on the standard library and provides the basic continuation logic.
- The `continuable-test.hpp` header also depends on `gtest` because it adds various test macros for asserting on the result of asynchronous continuations.
- The `continuable.hpp`header depends on my header-only [function2](https://github.com/Naios/function2) library for providing efficient type erasure - non-copyable objects are natively supported without any workaround.
auto 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
});
}
## Installation
auto run_it() {
return async([] {
// Directly start with a handler
});
}
### Inclusion
continuable<> run_it() { // With type erasure
return async([] {
As mentioned earlier the library is header-only. There is a cmake project provided for simple setup:
});
}
```
```sh
# Shell:
git submodule add https://github.com/Naios/continuable.git
```
```cma
# CMake file:
add_subdirectory(continuable)
# continuable provides an interface target which makes it's
# headers available to all projects using the continuable library.
target_link_libraries(my_project continuable)
```
On POSIX you are required to link your application against a corresponding thread library, otherwise `std:: future's` won't work properly, this is done automatically by the provided cmake project.
### Building the unit-tests
In order to build the unit tests clone the repository recursively with all submodules:
```sh
# Shell:
git clone --recursive https://github.com/Naios/continuable.git
```
## Stability and version
Currently, the library is in the incubation state, it provides a stable functionality as the CI unit tests indicate.
The API isn't fixed right now and will be eventually changed into the future.
Also, the unit-test is observed with the Clang sanitizers (asan, ubsan and lsan - when compiling with Clang) or Valgrind (when compiling with GCC).
## Quick reference
### Creating Continuables
Create a continuable from a callback taking function:
```c++
#include "continuable/continuable-base.hpp"
// ...
auto void_continuable = cti::make_continuable<void>([](auto&& callback) {
// ^^^^
// Call the callback later when you have finished your work
callback();
});
auto str_continuable = cti::make_continuable<std::string>([](auto&& callback) {
// ^^^^^^^^^^^
callback("Hello, World!");
});
```
### Chaining Continuables
Chain continuables together in order to build up an asynchronous call hierarchy:
```c++
mysql_query("SELECT `id`, `name` FROM `users`")
.then([](ResultSet users) {
- **Attach your continuations through `then`, supports multiple results and partial handlers:**
```cpp
mysql_query("SELECT `id`, `name` FROM `users`")
.then([](result_set users) {
// Return the next continuable to process ...
return mysql_query("SELECT `id` name FROM `sessions`");
})
.then([](ResultSet sessions) {
.then([](result_set sessions) {
// ... or pass multiple values to the next callback using tuples or pairs ...
return std::make_tuple(std::move(sessions), true);
})
.then([](ResultSet sessions, bool is_ok) {
.then([](result_set sessions, bool is_ok) {
// ... or pass a single value to the next callback ...
return 10;
})
@ -110,257 +101,133 @@ mysql_query("SELECT `id`, `name` FROM `users`")
})
// ... you may even pass continuables to the `then` method directly:
.then(mysql_query("SELECT * `statistics`"))
.then([](ResultSet result) {
.then([](result_set result) {
// ...
return "Hi";
})
.then([] /*(std::string result) */ { // Handlers can accept a partial set of arguments{
// ...
});
```
```
### Providing helper functions
Returning continuables from helper functions makes chaining simple and expressive:
```c++
#include "continuable/continuable-base.hpp"
// ...
auto mysql_query(std::string query) {
return cti::make_continuable<ResultSet>([query = std::move(query)](auto&& callback) mutable {
// Pass the callback to the handler which calls the callback when finished.
// Every function accepting callbacks works with continuables.
mysql_handle_async_query(std::move(query),
std::forward<decltype(callback)>(callback));
- **Handle failures through `fail` or `next`:**
```cpp
http_request("example.com")
.then([] {
throw std::exception("Some error");
})
.fail([] (std::exception_ptr ptr) {
if (ptr) {
try {
std::rethrow_exception(ptr);
} catch(std::exception const& e) {
// Handle the exception or error code here
}
}
});
}
```
// 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");
- **Dispatch continuations through a specific executor** (possibly on a different thread or later)
// Or using chaining to handle the result which is covered in the documentation.
mysql_query("SELECT `id`, `name` FROM users")
.then([](ResultSet result) {
```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);
// ^^^^^^^^
```
### Connecting Continuables {all or any}
Continuables provide the operators **&&** and **||** for logical connection:
* **&&** invokes the final callback with the compound result of all connected continuables.
* **||** invokes the final callback once with the first result available.
```C++
auto http_request(std::string url) {
return cti::make_continuable<std::string>([](auto&& callback) {
callback("<html>...</html>");
});
}
// `all` of connections:
(http_request("github.com") && http_request("travis-ci.org") && http_request("atom.io"))
.then([](std::string github, std::string travis, std::string atom) {
// The callback is called with the response of github, travis and atom.
- **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("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.
// `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.
});
// 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) {
// `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 travis or atom.
// and the second parameter represents the response of example or wikipedia.
});
// There are helper functions for connecting continuables:
auto all = cti::all_of(http_request("github.com"), http_request("travis-ci.org"));
auto any = cti::any_of(http_request("github.com"), http_request("travis-ci.org"));
```
// 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"));
```
Logical connections are ensured to be **thread-safe** and **wait-free** by library design (when assuming that *std::call_once* is wait-free - which depends on the toolchain).
### Type erasure
The library was designed in order to avoid type-erasure until really needed. Thus we provide traits to create an alias to a continuable using the **type-erasure backend of your choice**. All templated functors providing a call operator may be used as a backend (*std::function* for instance).
The library provides aliases for using my [function2 library](https://github.com/Naios/function2) as backend which provides efficient and qualifier correct function wrappers for copyable and non-copyable objects.
```c++
#include "continuable/continuable.hpp"
// ...
cti::unique_continuable<int, std::string> unique =
cti::make_continuable([value = std::make_unique<int>(0)](auto&& callback) {
// The use of non copyable objects is possible by design if
// the type erasure backend supports it.
callback(*value, "Hello, World!");
});
std::move(unique).then([](int i) {
// ...
});
```
However you may still define your own continuation wrapper with the backend of your choice, but keep in mind that the capabilities of your wrapper determine the possible objects, the continuation is capable of carrying. This limits continuations using *std::function* as a backend to copyable types:
```c++
template <typename... Args>
using mycontinuation = cti::continuable_of_t<
cti::continuable_erasure_of_t<std::function, std::function, Args...>,
Args...>;
// ...
mycontinuation<int> myc = cti::make_continuable([](auto&& callback) {
// ^^^^^
// Signatures may be omitted for continuables which are type erased
callback(0);
});
```
We could also think about using `std::future` as backend but this is even worse then using `std::function` because usually there is, even more, type erasure and allocations involved.
### Future conversion
The library is capable of converting (*futurizing*) every continuable into a fitting **std::future** through the `continuable<...>::futurize()` method.
```c++
std::future<std::string> future = http_request("github.com")
.then([](std::string response) {
// Do sth...
return http_request("travis-ci.org") || http_request("atom.io");
- **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();
})
.futurize();
// ^^^^^^^^
.then([] () -> result<> {
// We recovered from the failure and proceeding normally
std::future<std::tuple<std::string, std::string>> future =
(http_request("travis-ci.org") && http_request("atom.io")).futurize();
```
// Will yield a default constructed exception type to signal cancellation
return cancel();
});
```
Continuables returning nothing will evaluate to: `std::future<void>`.
- **`promisify` your existing code or use the (asio) completion token integration:**
```cpp
// Promisification of your existing code that accepts callbacks
auto async_resolve(std::string host, std::string service) {
return cti::promisify<asio::ip::udp::resolver::iterator>::from(
[&](auto&&... args) {
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
},
std::move(host), std::move(service));
}
Continuables returning only one value will evaluate the corresponding future: `std::future<type>`.
// (boost) asio completion token integration
asio::io_context io_context;
asio::steady_timer steady_timer(io_context);
Continuables returning more then one value will evaluate to a future providing a tuple carrying the values : `std::future<std::tuple<...>>`.
steady_timer.expires_after(std::chrono::seconds(5));
steady_timer.async_wait(cti::use_continuable)
.then([] {
// Is called after 5s
});
```
### In Progress (ToDo-List)
- **C++20 Coroutine support:**
Although the library has progressed very far there are still some candies missing:
(`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:
- [x] **Partial application**:
We could allow callbacks to be invoked with fewer arguments than expected:
```C++
http_request("github.com")
.then([]() { // ERROR: Because we expect an object accepting a std::string
// ...
```cpp
int i = co_await cti::make_continuable<int>([](auto&& promise) {
promise.set_value(0);
});
```
- [x] The **sequential operator** which invokes continuables sequentially and calls the callback with all results:
#### Appearances:
```c++
(http_request("github.com") >> http_request("travis-ci.org") >> http_request("atom.io"))
.then([](std::string github, std::string travis, std::string atom) {
// The requests are done sequentially and the callback is called
// with the response of github, travis and atom as soon as atom has responded.
// The responses of github and travis are stored meanwhile.
});
| [MeetingC++ 2018 Talk](https://naios.github.io/talks/2018-11-17-Meeting-C%2B%2B-Berlin/Continuable.pdf) |
| :---: |
| [<img alt="Continuable MeetingC++" width="60%" src="https://img.youtube.com/vi/l6-spMA_x6g/0.jpg">](https://www.youtube.com/watch?v=l6-spMA_x6g)] |
auto seq = cti::seq_of(http_request("github.com"), http_request("travis-ci.org"));
```
This differs from the `all` connection in the way that the continuables are invoked sequentially instead of parallel.
- [ ] **Inplace resolution** (makes it possible to keep the nesting level flat):
```c++
http_request("github.com")
.then([](std::string response) {
// Do something with the response
int response_code = get_code(response);
return std::make_tuple(http_request("atom.io"), response_code);
// ^^^^^^^^^^^^^^^^^^^^^^^
})
.then([](std::string atom, int response_code) {
// - `std::string atom` was resolved from `http_request("atom.io")`
// - `response_code` was passed through the tuple directly
});
```
- [ ] Library support of **infinite logical connections**:
```c++
std::vector<cti::continuable<int, int>> some;
cti::all(std::move(some))
.then([](std::vector<int, int> result) {
// ...
});
```
This library mainly aims to support un-erased continuations, however, sometimes it's required to work with a compile-time unknown amount of continuables.
- [ ] Maybe **Un-erasured fail/rejection handlers** and (possible exception support):
```c++
http_request("github.com")
.rejected([](std::error_code) {
// Is called when the request fails
// Potential difficult to implement with less type erasure
});
```
## Compatibility
Tested & compatible with:
- Visual Studio 2015+ Update 3
- Clang 3.6+
- GCC 5.0+
Every compiler with modern C++14 support should work.
The library only depends on the standard library when using the `continuable/continuable-base.hpp` header only which provides the full un-erasured support.
On Posix: don't forget to **link a corresponding thread library** into your application otherwise `std::future's` won't work `(-pthread)`.
## Similar implementations and alternatives
You already thought it, the idea isn't new and thus it is well known in the JavaScript world already.
There are some existing solutions with similar design thoughts already, which I don't want to hold back from you - you should choose the library fitting your needs best:
#### **[q (C++)](https://github.com/grantila/q)**
Is designed in a similar way, however, it orientates itself more on the corresponding JavaScript libraries which leaves some benefits behind we could reach with modern C++ meta-programming. Like previous approaches, the library uses type erasure excessively (and thus isn't memory allocation aware). What differentiates **q** from the continuable library is that it supports infinite logical connections and ships with built-in exception support as well as it's own executors (thread pools) - thus the library isn't header-only anymore (but the library is still proclaimed to work with other executors). My personal opinion is that a continuation library shouldn't include any other stuff then the continuation logic itself.
### [cpprestsdk](https://github.com/Microsoft/cpprestsdk)
Basically, the same arguments as explained in the q section apply to the cpprestsdk as well, it's major drawbacks is the overwhelming use of type-erasure. Probably you will benefit a lot from the library if you intend to use it's provided asynchronously *http*, *websocket* and *filesystem* functionalities. The *continuable* library was designed with different thoughts in mind - it basically provides the continuation logic without any support methods so you can embed it into your application without depending on a heavy framework. This makes it possible to apply continuation chaning to esoteric domains such as C++ AI scripts with fast or immediately response times. Who knows - maybe someone will provide *continuable* wrappers for open-source libraries like *asio*, libuv or *uWebSockets* in the future too.
### Others
If I forget to mention a library here let me know, so we can add the alternatives.
## License
The continuable library is licensed under the MIT License
.

43
appveyor.yml Normal file
View File

@ -0,0 +1,43 @@
image:
- Visual Studio 2017
- Visual Studio 2019
environment:
matrix:
- WITH_NO_EXCEPTIONS: OFF
WITH_CPP_LATEST: OFF
- WITH_NO_EXCEPTIONS: ON
WITH_CPP_LATEST: OFF
- WITH_NO_EXCEPTIONS: OFF
WITH_CPP_LATEST: ON
- WITH_NO_EXCEPTIONS: ON
WITH_CPP_LATEST: ON
platform:
- x64
clone_script:
- cmd: git clone -q --branch=%APPVEYOR_REPO_BRANCH% https://github.com/%APPVEYOR_REPO_NAME%.git %APPVEYOR_BUILD_FOLDER%
- cmd: cd %APPVEYOR_BUILD_FOLDER%
- cmd: git checkout -qf %APPVEYOR_REPO_COMMIT%
- cmd: git submodule update --init --recursive
before_build:
- cmd: >
cmake -H. -Bbuild -A%PLATFORM%
-DCTI_CONTINUABLE_WITH_NO_EXCEPTIONS=%WITH_NO_EXCEPTIONS%
-DCTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE=ON
-DCTI_CONTINUABLE_WITH_CPP_LATEST=%WITH_CPP_LATEST%
build_script:
- cmd: cmake --build build --config Debug --target ALL_BUILD -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
- cmd: cmake --build build --config Debug --target ALL_BUILD -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
- cmd: cmake --build build --config Release --target PACKAGE -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:minimal /maxcpucount:2 /nologo
test_script:
- cmd: cd build
- cmd: ctest -C Debug -V .
- cmd: ctest -C Release -V .
artifacts:
- path: 'build/*.zip'

View File

@ -1 +1,27 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
add_library(continuable-features-flags INTERFACE)
add_library(continuable-features-warnings INTERFACE)
add_library(continuable-features-noexcept INTERFACE)
include(cmake/configure_compiler.cmake)
include(cmake/configure_macros.cmake)

View File

@ -1,8 +1,35 @@
# Enable full warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra")
if (TESTS_NO_EXCEPTIONS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Enable full warnings
target_compile_options(continuable-features-warnings
INTERFACE
-Wall
-pedantic
-Wextra)
if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
target_compile_options(continuable-features-noexcept
INTERFACE
-fno-exceptions)
message(STATUS "Clang: Disabled exceptions")
endif()

View File

@ -1,7 +1,35 @@
# Enable full warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -Wextra")
if (TESTS_NO_EXCEPTIONS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Enable full warnings
target_compile_options(continuable-features-warnings
INTERFACE
-Wall
-pedantic
-Wextra)
if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
target_compile_options(continuable-features-noexcept
INTERFACE
-fno-exceptions)
message(STATUS "GCC: Disabled exceptions")
endif()

View File

@ -1,8 +1,23 @@
if (${MSVC_VERSION} LESS 1900)
message(FATAL_ERROR "You are using an unsupported version of Visual Studio "
"which doesn't support all required C++11 features. "
"(Visual Studio 2015 (version >= 1900) is required!)")
endif()
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
if(CMAKE_SIZEOF_VOID_P MATCHES 8)
set(PLATFORM 64)
@ -11,13 +26,41 @@ else()
endif()
if (PLATFORM EQUAL 64)
add_definitions("-D_WIN64")
target_compile_definitions(continuable-features-flags
INTERFACE
-D_WIN64)
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP /bigobj")
if (CTI_CONTINUABLE_WITH_CONCURRENT_JOBS)
target_compile_options(continuable-features-flags
INTERFACE
/MP${CTI_CONTINUABLE_WITH_CONCURRENT_JOBS})
else()
target_compile_options(continuable-features-flags
INTERFACE
/MP)
endif()
target_compile_options(continuable-features-flags
INTERFACE
/bigobj
/permissive-)
target_compile_options(continuable-features-warnings
INTERFACE
/W4)
if (NOT CTI_CONTINUABLE_WITH_CPP_LATEST)
target_compile_options(continuable-features-flags
INTERFACE
/std:c++14)
endif()
if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
target_compile_definitions(continuable-features-noexcept
INTERFACE
-D_HAS_EXCEPTIONS=0)
if (TESTS_NO_EXCEPTIONS)
add_definitions(-D_HAS_EXCEPTIONS=0)
string(REGEX REPLACE "/GX" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
message(STATUS "MSVC: Disabled exceptions")

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,11 +1,32 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Select the compiler specific cmake file
set(MSVC_ID "MSVC")
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
include(${CMAKE_SOURCE_DIR}/cmake/compiler/clang.cmake)
if (${CMAKE_CXX_COMPILER_ID} MATCHES "(Apple)?Clang")
include(${PROJECT_SOURCE_DIR}/cmake/compiler/clang.cmake)
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
include(${CMAKE_SOURCE_DIR}/cmake/compiler/gcc.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/compiler/gcc.cmake)
elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL ${MSVC_ID})
include(${CMAKE_SOURCE_DIR}/cmake/compiler/msvc.cmake)
include(${PROJECT_SOURCE_DIR}/cmake/compiler/msvc.cmake)
else()
message(FATAL_ERROR "Unknown compiler!")
endif()

View File

@ -0,0 +1,22 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
include(${PROJECT_SOURCE_DIR}/cmake/macros/group_sources.cmake)

View File

@ -0,0 +1,60 @@
# Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
set(WITH_SOURCE_TREE "hierarchical")
macro(group_sources)
# Skip this if WITH_SOURCE_TREE is not set (empty string).
if (NOT ${WITH_SOURCE_TREE} STREQUAL "")
foreach(dir ${ARGN})
# Include all header and c files
file(GLOB_RECURSE elements RELATIVE ${dir} ${dir}/*)
foreach(element ${elements})
# Extract filename and directory
get_filename_component(element_name ${element} NAME)
get_filename_component(element_dir ${element} DIRECTORY)
if (NOT ${element_dir} STREQUAL "")
# If the file is in a subdirectory use it as source group.
if (${WITH_SOURCE_TREE} STREQUAL "flat")
# Build flat structure by using only the first subdirectory.
string(FIND ${element_dir} "/" delemiter_pos)
if (NOT ${delemiter_pos} EQUAL -1)
string(SUBSTRING ${element_dir} 0 ${delemiter_pos} group_name)
source_group("${group_name}" FILES ${dir}/${element})
else()
# Build hierarchical structure.
# File is in root directory.
source_group("${element_dir}" FILES ${dir}/${element})
endif()
else()
# Use the full hierarchical structure to build source_groups.
string(REPLACE "/" "\\" group_name ${element_dir})
source_group("${group_name}" FILES ${dir}/${element})
endif()
else()
# If the file is in the root directory, place it in the root source_group.
source_group("\\" FILES ${dir}/${element})
endif()
endforeach()
endforeach()
endif()
endmacro()

37
conanfile.py 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,7 +1,25 @@
if(NOT TARGET gtest)
add_subdirectory(googletest)
endif()
if(NOT TARGET function2)
if(NOT TARGET function2::function2)
add_subdirectory(function2)
endif()
if (CTI_CONTINUABLE_WITH_TESTS OR CTI_CONTINUABLE_WITH_BENCHMARKS)
if(NOT TARGET gtest)
add_subdirectory(googletest)
endif()
endif()
if (CTI_CONTINUABLE_WITH_EXAMPLES)
if(NOT TARGET asio)
add_subdirectory(asio)
endif()
endif()
if (CTI_CONTINUABLE_WITH_BENCHMARKS)
if(NOT TARGET benchmark)
add_subdirectory(benchmark)
endif()
if(NOT TARGET boost)
add_subdirectory(boost)
endif()
endif()

40
dep/asio/CMakeLists.txt Normal file
View File

@ -0,0 +1,40 @@
add_library(asio STATIC
${CMAKE_CURRENT_LIST_DIR}/asio/asio/src/asio.cpp
${CMAKE_CURRENT_LIST_DIR}/include/boost/throw_exception.hpp)
target_include_directories(asio
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/asio/asio/include
${CMAKE_CURRENT_LIST_DIR}/include)
target_compile_definitions(asio
PUBLIC
-DASIO_STANDALONE=1
-DASIO_SEPARATE_COMPILATION=1
-DASIO_NO_TYPEID=1)
if (CTI_CONTINUABLE_WITH_NO_EXCEPTIONS)
target_compile_definitions(asio
PUBLIC
-DASIO_NO_EXCEPTIONS=1
-DASIO_HAS_BOOST_THROW_EXCEPTION=1)
message(STATUS "ASIO: Disabled exceptions")
endif()
if(WIN32)
target_compile_definitions(asio
PUBLIC
-D_WIN32_WINNT=0x0501)
endif()
target_compile_features(asio
PUBLIC
cxx_alias_templates
cxx_auto_type
cxx_decltype
cxx_final
cxx_lambdas
cxx_variadic_templates
cxx_defaulted_functions
cxx_nullptr)

1
dep/asio/asio Submodule

@ -0,0 +1 @@
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()

@ -1 +1 @@
Subproject commit dfb91a53cbac35ac4800aac058e0905f0384a431
Subproject commit 3a0746bf5f601dfed05330aefcb6854354fce07d

View File

@ -1,3 +1,4 @@
if(ON)
add_library(gtest STATIC
${CMAKE_CURRENT_LIST_DIR}/googletest/googletest/src/gtest-all.cc)
@ -42,3 +43,10 @@ target_include_directories(gmock
${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/googletest/googlemock/include)
else()
set(BUILD_GTEST ON)
set(BUILD_GMOCK OFF)
set(INSTALL_GTEST OFF)
add_subdirectory(googletest)
endif()

@ -1 +1 @@
Subproject commit 51143d5b62521f71020ada4ba1b6b44f3a6749bb
Subproject commit f2fb48c3b3d79a75a88a99fba6576b25d42ec528

1
doc/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
doxygen/

View File

@ -1,4 +1,4 @@
# Doxyfile 1.8.13
# Doxyfile 1.8.14
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@ -20,8 +20,8 @@
# This tag specifies the encoding used for all characters in the config file
# that follow. The default is UTF-8 which is also the encoding used for all text
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
# for the list of possible encodings.
# built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = continuable
PROJECT_NAME = Continuable
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER =
PROJECT_NUMBER = 4.1.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = "Async C++14 platform independent continuation chainer providing light and allocation aware futures"
PROJECT_BRIEF = "C++14 asynchronous allocation aware futures"
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
@ -337,7 +337,7 @@ BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
@ -623,19 +623,19 @@ STRICT_PROTO_MATCHING = NO
# list. This list is created by putting \todo commands in the documentation.
# The default value is: YES.
GENERATE_TODOLIST = YES
GENERATE_TODOLIST = NO
# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
# list. This list is created by putting \test commands in the documentation.
# The default value is: YES.
GENERATE_TESTLIST = YES
GENERATE_TESTLIST = NO
# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
# list. This list is created by putting \bug commands in the documentation.
# The default value is: YES.
GENERATE_BUGLIST = YES
GENERATE_BUGLIST = NO
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
# the deprecated list. This list is created by putting \deprecated commands in
@ -680,7 +680,7 @@ SHOW_FILES = YES
# Folder Tree View (if specified).
# The default value is: YES.
SHOW_NAMESPACES = NO
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
@ -708,7 +708,7 @@ LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
@ -791,12 +791,12 @@ WARN_LOGFILE =
# Note: If this tag is empty the current directory is searched.
INPUT = ../include \
Index.md
../doc
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: http://www.gnu.org/software/libiconv) for the list of
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
# possible encodings.
# The default value is: UTF-8.
@ -858,6 +858,7 @@ FILE_PATTERNS = *.c \
*.ucf \
*.qsf \
*.as \
*.dox \
*.js
# The RECURSIVE tag can be used to specify whether or not subdirectories should
@ -984,7 +985,7 @@ FILTER_SOURCE_PATTERNS =
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE = Index.md
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
@ -1045,7 +1046,7 @@ SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see http://www.gnu.org/software/global/global.html). You will need version
# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
@ -1091,6 +1092,17 @@ CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
# were built. This is equivalent to specifying the "-p" option to a clang tool,
# such as clang-check. These options will then be pased to the parser.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse-libclang=ON option for CMake.
# The default value is: 0.
CLANG_COMPILATION_DATABASE_PATH= 0
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
@ -1209,7 +1221,7 @@ HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a colorwheel, see
# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
@ -1245,6 +1257,17 @@ HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via Javascript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have Javascript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
@ -1268,12 +1291,12 @@ HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see: http://developer.apple.com/tools/xcode/), introduced with
# environment (see: https://developer.apple.com/tools/xcode/), introduced with
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
@ -1389,7 +1412,7 @@ QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1397,8 +1420,7 @@ QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
# folders).
# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
@ -1406,23 +1428,21 @@ QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
# filters).
# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
# filters).
# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
@ -1515,7 +1535,7 @@ EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
@ -1527,7 +1547,7 @@ FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# http://www.mathjax.org) which uses client side Javascript for the rendering
# https://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
@ -1554,7 +1574,7 @@ MATHJAX_FORMAT = HTML-CSS
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from http://www.mathjax.org before deployment.
# MathJax from https://www.mathjax.org before deployment.
# The default value is: http://cdn.mathjax.org/mathjax/latest.
# This tag requires that the tag USE_MATHJAX is set to YES.
@ -1616,7 +1636,7 @@ SERVER_BASED_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: http://xapian.org/).
# Xapian (see: https://xapian.org/).
#
# See the section "External Indexing and Searching" for details.
# The default value is: NO.
@ -1629,7 +1649,7 @@ EXTERNAL_SEARCH = NO
#
# Doxygen ships with an example indexer (doxyindexer) and search engine
# (doxysearch.cgi) which are based on the open source search engine library
# Xapian (see: http://xapian.org/). See the section "External Indexing and
# Xapian (see: https://xapian.org/). See the section "External Indexing and
# Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES.
@ -1816,7 +1836,7 @@ LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
# The default value is: plain.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@ -1999,9 +2019,9 @@ DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see http://autogen.sf.net) file that captures the
# structure of the code including all documentation. Note that this feature is
# still experimental and incomplete at the moment.
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO

View File

@ -1,20 +0,0 @@
# Documentation of continuable
This documentation covers the continuable library in detail
## Content
- Class cti::continuable_base - main class for continuation chaining.
- \link cti::continuable_base::then then\endlink - adds a callback or cti::continuable_base to the invocation chain.
- \link cti::continuable_base::operator && operator&&\endlink - connects another cti::continuable_base with an *all* logic.
- \link cti::continuable_base::operator|| operator||\endlink - connects another cti::continuable_base with an *any* logic.
- \link cti::continuable_base::release release\endlink - releases the cti::continuable_base and prevents the automatic invocation on destruction.
- Helper functions
- \link cti::make_continuable make_continuable\endlink - creates a cti::continuable_base from a callback tanking function.
- \link cti::all_of all_of\endlink - connects all given cti::continuable_base objects with an *all* logic.
- \link cti::any_of any_of\endlink - connects all given cti::continuable_base objects with an *any* logic.
- GTest macros:
- \link EXPECT_ASYNC_RESULT EXPECT_ASYNC_RESULT\endlink - Expects that the given continuable is finished with the given result.
- \link ASSERT_ASYNC_RESULT ASSERT_ASYNC_RESULT\endlink - Asserts that the given continuable is finished with the given result.
- \link ASSERT_ASYNC_TYPES ASSERT_ASYNC_TYPES\endlink - Asserts that the given continuable is finished with the given types without validating it against equality.

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

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

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

BIN
doc/slideshow.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

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 +1,3 @@
add_subdirectory(documentation)
add_subdirectory(example-asio)
add_subdirectory(example-ai)
add_subdirectory(example-slideshow)

View File

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

View File

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

View File

@ -0,0 +1,13 @@
CheckOptions:
- key: readability-identifier-naming.ClassCase
value: CamelCase
- key: readability-identifier-naming.EnumCase
value: CamelCase
- key: readability-identifier-naming.FunctionCase
value: CamelCase
- key: readability-identifier-naming.MemberCase
value: CamelCase
- key: readability-identifier-naming.UnionCase
value: lower_case
- key: readability-identifier-naming.VariableCase
value: lower_case

View File

@ -0,0 +1,14 @@
add_executable(example-ai
${CMAKE_CURRENT_LIST_DIR}/example-ai.cpp)
target_include_directories(example-ai
PRIVATE
${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(example-ai
PRIVATE
continuable)
target_compile_definitions(example-asio
PUBLIC
-DCONTINUABLE_WITH_NO_EXCEPTIONS)

View File

@ -0,0 +1,77 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#include <chrono>
#include <string>
#include "continuable/continuable.hpp"
using namespace std::chrono_literals;
struct Position {
float x, y, z, o;
};
struct Unit {
virtual ~Unit() = default;
};
struct UnitAI {
virtual ~UnitAI() = default;
virtual void OnEnterCombat() {
}
};
struct CreatureAI : UnitAI {
virtual cti::continuable<> MoveTo(Position pos) = 0;
virtual cti::continuable<> CastSpell(unsigned id, Unit* target) = 0;
virtual cti::continuable<> Say(std::string text,
std::chrono::milliseconds duration) = 0;
};
struct MyCreatureAI : CreatureAI {
void OnEnterCombat() override {
CastSpell(3736, nullptr)
.then(MoveTo({0, 0, 0, 0}) && Say("Walking...!", 5s))
.then(Say("Done!", 5s))
.fail(Say("Interrupted!", 5s));
}
};
int main(int, char**) {
// CreatureAI* ai = nullptr;
// ai->OnEnterCombat();
return 0;
}

View File

@ -0,0 +1,28 @@
add_library(asio-example-deps INTERFACE)
target_include_directories(asio-example-deps
INTERFACE
${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(asio-example-deps
INTERFACE
asio
continuable)
add_executable(example-asio
${CMAKE_CURRENT_LIST_DIR}/example-asio.cpp)
target_link_libraries(example-asio
PRIVATE
asio-example-deps)
target_compile_definitions(example-asio
PUBLIC
-DCONTINUABLE_WITH_NO_EXCEPTIONS)
add_executable(example-asio-integration
${CMAKE_CURRENT_LIST_DIR}/example-asio-integration.cpp)
target_link_libraries(example-asio-integration
PRIVATE
asio-example-deps)

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

@ -0,0 +1,121 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.0.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#include <array>
#include <chrono>
#include <iostream>
#include <string>
#include <system_error>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
#include <exception>
#endif
#include <asio.hpp>
#include <continuable/continuable.hpp>
using namespace std::chrono_literals;
inline auto error_code_remapper() {
return [](auto&& promise, asio::error_code e, auto&&... args) {
if (e) {
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
promise.set_exception(std::make_exception_ptr(e));
#else
promise.set_exception(cti::exception_t(e.value(), e.category()));
#endif
} else {
promise.set_value(std::forward<decltype(args)>(args)...);
}
};
}
struct functional_io_service {
asio::io_context service_;
asio::ip::udp::resolver resolver_;
functional_io_service() : resolver_(service_) {
}
auto trough_post() noexcept {
return [&](auto&& work) mutable {
asio::post(service_,
[work = std::forward<decltype(work)>(work)]() mutable {
std::move(work)();
// .. or: work.set_value();
});
};
}
auto wait_async(std::chrono::milliseconds /*time*/) {
return cti::make_continuable<void>([](auto&& promise) {
// ...
promise.set_value();
});
}
auto async_resolve(std::string host, std::string service) {
return cti::promisify<asio::ip::udp::resolver::iterator>::with(
error_code_remapper(),
[&](auto&&... args) {
resolver_.async_resolve(std::forward<decltype(args)>(args)...);
},
std::move(host), std::move(service));
}
void run() {
service_.run();
}
};
int main(int, char**) {
using asio::ip::udp;
functional_io_service service;
service.async_resolve("127.0.0.1", "daytime")
.then(
[](udp::resolver::iterator iterator) {
// ...
return *iterator;
},
service.trough_post())
.then([](udp::endpoint /*endpoint*/) {
// auto socket = std::make_shared<udp::socket>(service);
// socket->async_send_to()
})
.fail([](cti::exception_t /*error*/) {
// ...
});
service.run();
return 0;
}

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

@ -0,0 +1,85 @@
/*
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#include <string>
#include "continuable/continuable.hpp"
cti::continuable<std::string> http_request(std::string /*url*/) {
return cti::make_ready_continuable<std::string>("<html>...</html>");
}
struct ResultSet {};
struct Buffer {};
cti::continuable<ResultSet> mysql_query(std::string /*url*/) {
return cti::make_ready_continuable(ResultSet{});
}
cti::continuable<Buffer> read_file(std::string /*url*/) {
return cti::make_ready_continuable(Buffer{});
}
struct functional_executor {
auto post() const {
return [](auto&&) {};
}
};
int main(int, char**) {
functional_executor e;
auto executor = &e;
// clang-format off
(http_request("github.com") && http_request("atom.io"))
.then([] (std::string /*github*/, std::string /*atom*/) {
// ...
return mysql_query("select * from `users`");
})
.then([] (ResultSet /*result*/) {
// ...
}, executor->post());
// clang-format on
http_request("github.com") && http_request("atom.io");
http_request("github.com") || http_request("atom.io");
http_request("github.com") >> http_request("atom.io");
// clang-format off
read_file("entries.csv")
.then([] (Buffer /*buffer*/) {
// ...
return std::make_tuple("hey", true, 0);
})
.then([] (std::string /*msg*/) {
// ...
});
// clang-format on
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,301 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_CONNECTIONS_HPP_INCLUDED
#define CONTINUABLE_CONNECTIONS_HPP_INCLUDED
#include <initializer_list>
#include <memory>
#include <utility>
#include <vector>
#include <continuable/detail/connection/connection-all.hpp>
#include <continuable/detail/connection/connection-any.hpp>
#include <continuable/detail/connection/connection-seq.hpp>
#include <continuable/detail/connection/connection.hpp>
#include <continuable/detail/traversal/range.hpp>
namespace cti {
/// \defgroup Connections Connections
/// provides functions to connect \link continuable_base
/// continuable_bases\endlink through various strategies.
/// \{
/// Connects the given arguments with an all logic.
/// All continuables contained inside the given nested pack are
/// invoked at once. On completion the final handler is called
/// with the aggregated result of all continuables.
///
/// \param args Arbitrary arguments which are connected.
/// Every type is allowed as arguments, continuables may be
/// contained inside tuple like types (`std::tuple`)
/// or in homogeneous containers such as `std::vector`.
/// Non continuable arguments are preserved and passed
/// to the final result as shown below:
/// ```cpp
/// cti::when_all(
/// cti::make_ready_continuable(0, 1),
/// 2, //< See this plain value
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
/// cti::make_ready_continuable(4)), // sized container.
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
/// .then([](int r0, int r1, int r2, std::vector<int> r34,
/// std::tuple<std::tuple<int>> r5) {
/// // ...
/// });
/// ```
///
/// \see continuable_base::operator&& for details.
///
/// \since 1.1.0
template <typename... Args>
auto when_all(Args&&... args) {
return detail::connection::apply_connection(
detail::connection::connection_strategy_all_tag{},
std::forward<Args>(args)...);
}
/// Connects the given arguments with an all logic.
/// The content of the iterator is moved out and converted
/// to a temporary `std::vector` which is then passed to when_all.
///
/// ```cpp
/// // cti::populate just creates a std::vector from the two continuables.
/// auto v = cti::populate(cti::make_ready_continuable(0),
/// cti::make_ready_continuable(1));
///
/// cti::when_all(v.begin(), v.end())
/// .then([](std::vector<int> r01) {
/// // ...
/// });
/// ```
///
/// \param begin The begin iterator to the range which will be moved out
/// and used as the arguments to the all connection
///
/// \param end The end iterator to the range which will be moved out
/// and used as the arguments to the all connection
///
/// \see when_all for details.
///
/// \attention Prefer to invoke when_all with the whole container the
/// iterators were taken from, since this saves us
/// the creation of a temporary storage.
///
/// \since 3.0.0
template <
typename Iterator,
std::enable_if_t<detail::range::is_iterator<Iterator>::value>* = nullptr>
auto when_all(Iterator begin, Iterator end) {
return when_all(detail::range::persist_range(begin, end));
}
/// Connects the given arguments with a sequential logic.
/// All continuables contained inside the given nested pack are
/// invoked one after one. On completion the final handler is called
/// with the aggregated result of all continuables.
///
/// \param args Arbitrary arguments which are connected.
/// Every type is allowed as arguments, continuables may be
/// contained inside tuple like types (`std::tuple`)
/// or in homogeneous containers such as `std::vector`.
/// Non continuable arguments are preserved and passed
/// to the final result as shown below:
/// ```cpp
/// cti::when_seq(
/// cti::make_ready_continuable(0, 1),
/// 2, //< See this plain value
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
/// cti::make_ready_continuable(4)), // sized container.
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
/// .then([](int r0, int r1, int r2, std::vector<int> r34,
/// std::tuple<std::tuple<int>> r5) {
/// // ...
/// });
/// ```
///
/// \see continuable_base::operator>> for details.
///
/// \since 1.1.0
template <typename... Args>
auto when_seq(Args&&... args) {
return detail::connection::apply_connection(
detail::connection::connection_strategy_seq_tag{},
std::forward<Args>(args)...);
}
/// Connects the given arguments with a sequential logic.
/// The content of the iterator is moved out and converted
/// to a temporary `std::vector` which is then passed to when_seq.
///
/// ```cpp
/// // cti::populate just creates a std::vector from the two continuables.
/// auto v = cti::populate(cti::make_ready_continuable(0),
/// cti::make_ready_continuable(1));
///
/// cti::when_seq(v.begin(), v.end())
/// .then([](std::vector<int> r01) {
/// // ...
/// });
/// ```
///
/// \param begin The begin iterator to the range which will be moved out
/// and used as the arguments to the sequential connection
///
/// \param end The end iterator to the range which will be moved out
/// and used as the arguments to the sequential connection
///
/// \see when_seq for details.
///
/// \attention Prefer to invoke when_seq with the whole container the
/// iterators were taken from, since this saves us
/// the creation of a temporary storage.
///
/// \since 3.0.0
template <
typename Iterator,
std::enable_if_t<detail::range::is_iterator<Iterator>::value>* = nullptr>
auto when_seq(Iterator begin, Iterator end) {
return when_seq(detail::range::persist_range(begin, end));
}
/// Connects the given arguments with an any logic.
/// All continuables contained inside the given nested pack are
/// invoked at once. On completion of one continuable the final handler
/// is called with the result of the resolved continuable.
///
/// \param args Arbitrary arguments which are connected.
/// Every type is allowed as arguments, continuables may be
/// contained inside tuple like types (`std::tuple`)
/// or in homogeneous containers such as `std::vector`.
/// Non continuable arguments are preserved and passed
/// to the final result as shown below:
/// ```cpp
/// cti::when_any(
/// cti::make_ready_continuable(0, 1),
/// 2, //< See this plain value
/// cti::populate(cti::make_ready_continuable(3), // Creates a runtime
/// cti::make_ready_continuable(4)), // sized container.
/// std::make_tuple(std::make_tuple(cti::make_ready_continuable(5))))
/// .then([](int r0) {
/// // ...
/// });
/// ```
///
/// \see continuable_base::operator|| for details.
///
/// \since 1.1.0
template <typename... Args>
auto when_any(Args&&... args) {
return detail::connection::apply_connection(
detail::connection::connection_strategy_any_tag{},
std::forward<Args>(args)...);
}
/// Connects the given arguments with an any logic.
/// The content of the iterator is moved out and converted
/// to a temporary `std::vector` which is then passed to when_all.
///
/// ```cpp
/// // cti::populate just creates a std::vector from the two continuables.
/// auto v = cti::populate(cti::make_ready_continuable(0),
/// cti::make_ready_continuable(1));
///
/// cti::when_any(v.begin(), v.end())
/// .then([](int r01) {
/// // ...
/// });
/// ```
///
/// \param begin The begin iterator to the range which will be moved out
/// and used as the arguments to the all connection
///
/// \param end The end iterator to the range which will be moved out
/// and used as the arguments to the all connection
///
/// \see when_any for details.
///
/// \attention Prefer to invoke when_any with the whole container the
/// iterators were taken from, since this saves us
/// the creation of a temporary storage.
///
/// \since 3.0.0
template <
typename Iterator,
std::enable_if_t<detail::range::is_iterator<Iterator>::value>* = nullptr>
auto when_any(Iterator begin, Iterator end) {
return when_any(detail::range::persist_range(begin, end));
}
/// Populates a homogeneous container from the given arguments.
/// All arguments need to be convertible to the first one,
/// by default `std::vector` is used as container type.
///
/// This method mainly helps to create a homogeneous container from
/// a runtime known count of continuables which type isn't exactly known.
/// All continuables which are passed to this function should be originating
/// from the same source or a method called with the same types of arguments:
/// ```cpp
/// auto container = cti::populate(cti::make_ready_continuable(0),
/// cti::make_ready_continuable(1)),
///
/// for (int i = 2; i < 5; ++i) {
/// // You may add more continuables to the container afterwards
/// container.emplace_back(cti::make_ready_continuable(i));
/// }
///
/// cti::when_any(std::move(container))
/// .then([](int) {
/// // ...
/// });
/// ```
/// Additionally it is possible to change the targeted container as below:
/// ```cpp
/// auto container = cti::populate<std::list>(cti::make_ready_continuable(0),
/// cti::make_ready_continuable(1)),
/// ```
///
/// \tparam C The container type which is used to store the arguments into.
///
/// \since 3.0.0
template <template <typename, typename> class C = std::vector, typename First,
typename... Args>
C<std::decay_t<First>, std::allocator<std::decay_t<First>>>
populate(First&& first, Args&&... args) {
C<std::decay_t<First>, std::allocator<std::decay_t<First>>> container;
container.reserve(1 + sizeof...(Args));
container.emplace_back(std::forward<First>(first));
(void)std::initializer_list<int>{
0, ((void)container.emplace_back(std::forward<Args>(args)), 0)...};
return container; // RVO
}
/// \}
} // namespace cti
#endif // CONTINUABLE_CONNECTIONS_HPP_INCLUDED

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

@ -0,0 +1,215 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
#define CONTINUABLE_PROMISE_BASE_HPP_INCLUDED
#include <cassert>
#include <type_traits>
#include <utility>
#include <continuable/continuable-primitives.hpp>
#include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/utility/util.hpp>
namespace cti {
/// \defgroup Base Base
/// provides classes and functions to create continuable_base objects.
/// \{
/// The promise_base makes it possible to resolve an asynchronous
/// continuable through it's result or through an error type.
///
/// Use the promise type defined in `continuable/continuable_types.hpp`,
/// in order to use this class.
///
/// If we want to resolve the promise_base trough the call operator,
/// and we want to resolve it through an exception, we must call it with a
/// exception_arg_t as first and the exception as second argument.
/// Additionally the promise is resolveable only through its call
/// operator when invoked as an r-value.
///
/// \since 2.0.0
// clang-format off
template <typename Data, typename Hint>
class promise_base
/// \cond false
;
template <typename Data, typename... Args>
class promise_base<Data, detail::identity<Args...>>
: detail::util::non_copyable
/// \endcond
{ // clang-format on
/// \cond false
// The callback type
Data data_;
/// \endcond
public:
/// Constructor for constructing an empty promise
explicit promise_base() = default;
/// Constructor accepting the data object
explicit promise_base(Data data) : data_(std::move(data)) {
}
/// \cond false
promise_base(promise_base&&) = default;
promise_base(promise_base const&) = delete;
promise_base& operator=(promise_base&&) = default;
promise_base& operator=(promise_base const&) = delete;
/// \endcond
/// Constructor accepting any object convertible to the data object
template <typename OData,
std::enable_if_t<std::is_convertible<
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
/* implicit */ promise_base(OData&& data) : data_(std::forward<OData>(data)) {
}
/// Assignment operator accepting any object convertible to the data object
template <typename OData,
std::enable_if_t<std::is_convertible<
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
promise_base& operator=(OData&& data) {
data_ = std::forward<OData>(data);
return *this;
}
/// Resolves the continuation with the given values.
///
/// \throws This method never throws an exception.
///
/// \attention This method may only be called once,
/// when the promise is valid operator bool() returns true.
/// Calling this method will invalidate the promise such that
/// subsequent calls to operator bool() will return false.
/// This behaviour is only consistent in promise_base and
/// non type erased promises may behave differently.
/// Invoking an invalid promise_base is undefined!
///
/// \since 2.0.0
void operator()(Args... args) && noexcept {
assert(data_);
std::move(data_)(std::move(args)...);
data_ = nullptr;
}
/// Resolves the continuation with the given exception.
///
/// \throws This method never throws an exception.
///
/// \attention This method may only be called once,
/// when the promise is valid operator bool() returns true.
/// Calling this method will invalidate the promise such that
/// subsequent calls to operator bool() will return false.
/// This behaviour is only consistent in promise_base and
/// non type erased promises may behave differently.
/// Invoking an invalid promise_base is undefined!
///
/// \since 2.0.0
void operator()(exception_arg_t tag, exception_t exception) && noexcept {
assert(data_);
std::move(data_)(tag, std::move(exception));
data_ = nullptr;
}
/// Resolves the continuation with the given values.
///
/// \throws This method never throws an exception.
///
/// \attention This method may only be called once,
/// when the promise is valid operator bool() returns true.
/// Calling this method will invalidate the promise such that
/// subsequent calls to operator bool() will return false.
/// This behaviour is only consistent in promise_base and
/// non type erased promises may behave differently.
/// Invoking an invalid promise_base is undefined!
///
/// \since 2.0.0
void set_value(Args... args) noexcept {
// assert(data_);
std::move(data_)(std::move(args)...);
data_ = nullptr;
}
/// Resolves the continuation with the given exception.
///
/// \throws This method never throws an exception.
///
/// \attention This method may only be called once,
/// when the promise is valid operator bool() returns true.
/// Calling this method will invalidate the promise such that
/// subsequent calls to operator bool() will return false.
/// This behaviour is only consistent in promise_base and
/// non type erased promises may behave differently.
/// Invoking an invalid promise_base is undefined!
///
/// \since 2.0.0
void set_exception(exception_t exception) noexcept {
assert(data_);
std::move(data_)(exception_arg_t{}, std::move(exception));
data_ = nullptr;
}
/// Resolves the continuation with the cancellation token which is represented
/// by a default constructed exception_t.
///
/// \throws This method never throws an exception.
///
/// \attention This method may only be called once,
/// when the promise is valid operator bool() returns true.
/// Calling this method will invalidate the promise such that
/// subsequent calls to operator bool() will return false.
/// This behaviour is only consistent in promise_base and
/// non type erased promises may behave differently.
/// Invoking an invalid promise_base is undefined!
///
/// \since 4.0.0
void set_canceled() noexcept {
assert(data_);
std::move(data_)(exception_arg_t{}, exception_t{});
data_ = nullptr;
}
/// Returns true if the continuation is valid (non empty).
///
/// \throws This method never throws an exception.
///
/// \since 4.0.0
explicit operator bool() const noexcept {
return bool(data_);
}
};
/// \}
} // namespace cti
#endif // CONTINUABLE_PROMISE_BASE_HPP_INCLUDED

View File

@ -0,0 +1,120 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_PROMISIFY_HPP_INCLUDED
#define CONTINUABLE_PROMISIFY_HPP_INCLUDED
#include <type_traits>
#include <utility>
#include <continuable/detail/other/promisify.hpp>
namespace cti {
/// \defgroup Promisify Promisify
/// provides helper methods to convert various callback styles to
/// \link continuable_base continuable_bases\endlink.
/// \{
/// Helper class for converting callback taking callable types into a
/// a continuable. Various styles are supported.
/// - `from`: Converts callback taking callable types into continuables
/// which pass an error code as first parameter and the rest of
/// the result afterwards.
///
/// \tparam Result The result of the converted continuable, this should align
/// with the arguments that are passed to the callback.
///
/// \since 3.0.0
template <typename... Result>
class promisify {
using helper = detail::convert::promisify_helper<Result...>;
public:
/// Converts callback taking callable types into a continuable.
/// This applies to calls which pass an error code as first parameter
/// and the rest of the asynchronous result afterwards.
///
/// See an example of how to promisify boost asio's async_resolve below:
/// ```cpp
/// auto async_resolve(std::string host, std::string service) {
/// return cti::promisify<asio::ip::udp::resolver::iterator>::from(
/// [&](auto&&... args) {
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
/// },
/// std::move(host), std::move(service));
/// }
/// ```
///
/// A given error variable is converted to the used error type.
/// If this isn't possible you need to create a custom resolver callable
/// object \see with for details.
///
/// \since 3.0.0
template <typename Callable, typename... Args>
static auto from(Callable&& callable, Args&&... args) {
return helper::template from(detail::convert::default_resolver(),
std::forward<Callable>(callable),
std::forward<Args>(args)...);
}
/// \copybrief from
///
/// This modification of \ref from additionally takes a resolver callable
/// object which is used to resolve the promise from the given result.
///
/// See an example of how to promisify boost asio's async_resolve below:
/// ```cpp
/// auto async_resolve(std::string host, std::string service) {
/// return cti::promisify<asio::ip::udp::resolver::iterator>::with(
/// [](auto&& promise, auto&& e, auto&&... args) {
/// if (e) {
/// promise.set_exception(std::forward<decltype(e)>(e));
/// } else {
/// promise.set_value(std::forward<decltype(args)>(args)...);
/// }
/// },
/// [&](auto&&... args) {
/// resolver_.async_resolve(std::forward<decltype(args)>(args)...);
/// },
/// std::move(host), std::move(service));
/// }
/// ```
///
/// \since 4.0.0
template <typename Resolver, typename Callable, typename... Args>
static auto with(Resolver&& resolver, Callable&& callable, Args&&... args) {
return helper::template from(std::forward<Resolver>(resolver),
std::forward<Callable>(callable),
std::forward<Args>(args)...);
}
};
/// \}
} // namespace cti
#endif // CONTINUABLE_PROMISIFY_HPP_INCLUDED

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,228 +0,0 @@
/**
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v0.8.0
Copyright(c) 2015 - 2017 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_TESTING_HPP_INCLUDED__
#define CONTINUABLE_TESTING_HPP_INCLUDED__
#include "gtest/gtest.h"
#include "continuable/continuable-base.hpp"
namespace cti {
/// \cond false
inline namespace abi_v1 {
/// \endcond
namespace detail {
namespace testing {
template <typename C> void expect_async_completion(C&& continuable) {
auto called = std::make_shared<bool>(false);
std::forward<C>(continuable).then([called](auto&&... args) {
ASSERT_FALSE(*called);
*called = true;
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
});
EXPECT_TRUE(called);
}
template <typename C> void expect_async_incomplete(C&& continuable) {
std::forward<C>(continuable).then([](auto&&... args) {
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
FAIL();
});
}
template <typename C, typename V>
void expect_async_validation(C&& continuable, V&& validator) {
expect_async_completion(
std::forward<C>(continuable)
.then([validator =
std::forward<V>(validator)](auto&&... args) mutable {
validator(std::forward<decltype(args)>(args)...);
}));
}
/// Expects that the continuable is finished with the given arguments
template <typename V, typename C, typename... Args>
void expect_async_binary_validation(V&& validator, C&& continuable,
Args&&... expected) {
expect_async_validation(std::forward<C>(continuable), [
expected_pack = std::make_tuple(std::forward<Args>(expected)...),
validator = std::forward<V>(validator)
](auto&&... args) mutable {
auto actual_pack = std::make_tuple(std::forward<decltype(args)>(args)...);
auto size = util::pack_size_of(util::identity_of(expected_pack));
static_assert(size.value == std::tuple_size<decltype(actual_pack)>::value,
"Async completion handler called with a different count "
"of arguments!");
util::static_for_each_in(
std::make_index_sequence<size.value>{}, [&](auto current) mutable {
auto expected = std::get<current.value>(std::move(expected_pack));
auto actual = std::get<current.value>(std::move(actual_pack));
(void)current;
validator(expected, actual);
});
});
}
inline auto expecting_eq_check() {
return [](auto expected, auto actual) { EXPECT_EQ(expected, actual); };
}
inline auto asserting_eq_check() {
return [](auto expected, auto actual) { ASSERT_EQ(expected, actual); };
}
template <typename C, typename... Args>
void assert_async_types(C&& continuable, util::identity<Args...> expected) {
expect_async_validation(
std::forward<C>(continuable), [&](auto... actualPack) {
auto actual = util::identity<decltype(actualPack)...>{};
util::unused(expected, actual,
std::forward<decltype(actualPack)>(actualPack)...);
static_assert(
std::is_same<std::decay_t<decltype(expected)>,
std::decay_t<decltype(actual)>>::value,
"The called arguments don't match with the expected ones!");
});
}
} // end namespace testing
} // end namespace detail
/// \cond false
} // end inline namespace abi_...
/// \endcond
} // end namespace cti
/// Expects the final callback of the given continuable to be called
/// with any result.
///
/// \since version 1.0.0
#define EXPECT_ASYNC_COMPLETION(CONTINUABLE) \
cti::detail::testing::expect_async_completion(CONTINUABLE);
/// Expects the final callback of the given continuable is never called
/// with any result.
///
/// \since version 1.0.0
#define EXPECT_ASYNC_INCOMPLETE(CONTINUABLE) \
cti::detail::testing::expect_async_incomplete(CONTINUABLE);
/// Expects the continuation to be called and forwards it's arguments to
/// the given validator which can then do assertions on the result.
#define EXPECT_ASYNC_VALIDATION(CONTINUABLE, VALIDATOR) \
cti::detail::testing::expect_async_validation(CONTINUABLE, VALIDATOR);
/// Expects the continuation to be called and forwards it's arguments to
/// the given validator which can then do assertions on the result.
///
/// A validator consists of a binary consumer with a signature as in
/// in the example shown below:
/// ```cpp
/// auto validator = [](auto expected, auto actual) {
/// EXPECT_EQ(expected, actual);
/// };
/// ```
///
/// The macro is usable as shown in the following example:
/// ```cpp
/// continuable<string> async_get(std::string);
/// // ...
/// auto validator = [](auto expected, auto actual) {
/// EXPECT_EQ(expected, actual);
/// };
///
/// EXPECT_ASYNC_BINARY_VALIDATION(validator, async_get("hello"), "hello")
/// ```
///
/// The validator is called for every expecting and actual result.
///
/// \note This macro is mainly present for building other assertions
/// relying on custom validation logic.
///
/// \since version 1.0.0
#define EXPECT_ASYNC_BINARY_VALIDATION(VALIDATOR, ...) \
cti::detail::testing::expect_async_binary_validation(VALIDATOR, __VA_ARGS__);
/// Expects that the continuable is finished with the given result
///
/// ```cpp
/// continuable<string> async_get(std::string);
/// // ...
///
/// EXPECT_ASYNC_RESULT(async_get("hello"), "hello");
/// ```
///
/// \since version 1.0.0
#define EXPECT_ASYNC_RESULT(...) \
EXPECT_ASYNC_BINARY_VALIDATION(cti::detail::testing::expecting_eq_check(), \
__VA_ARGS__)
/// Asserts that the continuable is finished with the given result
///
/// ```cpp
/// continuable<string> async_get(std::string);
/// // ...
///
/// ASSERT_ASYNC_RESULT(async_get("hello"), "hello");
/// ```
///
/// \since version 1.0.0
#define ASSERT_ASYNC_RESULT(...) \
EXPECT_ASYNC_BINARY_VALIDATION(cti::detail::testing::asserting_eq_check(), \
__VA_ARGS__)
/// Asserts that the continuable is finished with the given type of arguments
/// without validating it against equality.
///
/// ```cpp
/// continuable<string> async_get(std::string);
/// // ...
///
/// ASSERT_ASYNC_TYPES(async_get("hello"), std::string);
/// ```
///
/// \note This is a compile-time assertion.
///
/// \since version 1.0.0
#define ASSERT_ASYNC_TYPES(CONTINUABLE, ...) \
cti::detail::testing::assert_async_types( \
CONTINUABLE, cti::detail::util::identity<__VA_ARGS__>{})
#endif // CONTINUABLE_TESTING_HPP_INCLUDED__

View File

@ -0,0 +1,52 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_TRANSFORMS_HPP_INCLUDED
#define CONTINUABLE_TRANSFORMS_HPP_INCLUDED
#include <continuable/transforms/wait.hpp>
#include <continuable/transforms/future.hpp>
namespace cti {
/// \defgroup Transforms Transforms
/// provides utilities to convert
/// \link continuable_base continuable_bases\endlink to other
/// types such as (`std::future`).
/// \{
/// The namespace transforms declares callable objects that transform
/// any continuable_base to an object or to a continuable_base itself.
///
/// Transforms can be applied to continuables through using
/// the cti::continuable_base::apply method accordingly.
namespace transforms {}
} // namespace cti
#endif // CONTINUABLE_TRANSFORMS_HPP_INCLUDED

View File

@ -0,0 +1,130 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED
#define CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED
#include <utility>
#include <continuable/detail/traversal/traverse-async.hpp>
namespace cti {
/// \defgroup Traversal Traversal
/// provides functions to traverse and remap nested packs.
/// \{
/// A tag which is passed to the `operator()` of the visitor
/// if an element is visited synchronously through \ref traverse_pack_async.
///
/// \since 3.0.0
using async_traverse_visit_tag = detail::traversal::async_traverse_visit_tag;
/// A tag which is passed to the `operator()` of the visitor if an element is
/// visited after the traversal was detached through \ref traverse_pack_async.
///
/// \since 3.0.0
using async_traverse_detach_tag = detail::traversal::async_traverse_detach_tag;
/// A tag which is passed to the `operator()` of the visitor if the
/// asynchronous pack traversal was finished through \ref traverse_pack_async.
///
/// \since 3.0.0
using async_traverse_complete_tag =
detail::traversal::async_traverse_complete_tag;
/// A tag to identify that a mapper shall be constructed in-place
/// from the first argument passed to \ref traverse_pack_async.
///
/// \since 3.0.0
template <typename T>
using async_traverse_in_place_tag =
detail::traversal::async_traverse_in_place_tag<T>;
/// Traverses the pack with the given visitor in an asynchronous way.
///
/// This function works in the same way as `traverse_pack`,
/// however, we are able to suspend and continue the traversal at
/// later time.
/// Thus we require a visitor callable object which provides three
/// `operator()` overloads as depicted by the code sample below:
/// ```cpp
/// struct my_async_visitor {
/// /// The synchronous overload is called for each object,
/// /// it may return false to suspend the current control flow.
/// /// In that case the overload below is called.
/// template <typename T>
/// bool operator()(async_traverse_visit_tag, T&& element) {
/// return true;
/// }
///
/// /// The asynchronous overload this is called when the
/// /// synchronous overload returned false.
/// /// In addition to the current visited element the overload is
/// /// called with a contnuation callable object which resumes the
/// /// traversal when it's called later.
/// /// The continuation next may be stored and called later or
/// /// dropped completely to abort the traversal early.
/// template <typename T, typename N>
/// void operator()(async_traverse_detach_tag, T&& element, N&& next) {
/// }
///
/// /// The overload is called when the traversal was finished.
/// /// As argument the whole pack is passed over which we
/// /// traversed asynchrnously.
/// template <typename T>
/// void operator()(async_traverse_complete_tag, T&& pack) {
/// }
/// };
/// ```
///
/// \param visitor A visitor object which provides the three `operator()`
/// overloads that were described above.
/// Additionally the visitor must be compatible
/// for referencing it from a `boost::intrusive_ptr`.
/// The visitor should must have a virtual destructor!
///
/// \param pack The arbitrary parameter pack which is traversed
/// asynchronously. Nested objects inside containers and
/// tuple like types are traversed recursively.
///
/// \returns A std::shared_ptr that references an instance of
/// the given visitor object.
///
/// \since 3.0.0
///
/// See `traverse_pack` for a detailed description about the
/// traversal behaviour and capabilities.
///
template <typename Visitor, typename... T>
auto traverse_pack_async(Visitor&& visitor, T&&... pack) {
return detail::traversal::apply_pack_transform_async(
std::forward<Visitor>(visitor), std::forward<T>(pack)...);
}
/// \}
} // namespace cti
#endif // CONTINUABLE_TRAVERSE_ASYNC_HPP_INCLUDED

View File

@ -0,0 +1,115 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_TRAVERSE_HPP_INCLUDED
#define CONTINUABLE_TRAVERSE_HPP_INCLUDED
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/detail/traversal/traverse.hpp>
namespace cti {
/// \defgroup Traversal Traversal
/// provides functions to traverse and remap nested packs.
/// \{
/// Maps the pack with the given mapper.
///
/// This function tries to visit all plain elements which may be wrapped in:
/// - homogeneous containers (`std::vector`, `std::list`)
/// - heterogenous containers `(std::tuple`, `std::pair`, `std::array`)
/// and re-assembles the pack with the result of the mapper.
/// Mapping from one type to a different one is supported.
///
/// Elements that aren't accepted by the mapper are routed through
/// and preserved through the hierarchy.
///
/// ```cpp
/// // Maps all integers to floats
/// map_pack([](int value) {
/// return float(value);
/// },
/// 1, std::make_tuple(2, std::vector<int>{3, 4}), 5);
/// ```
///
/// \throws std::exception like objects which are thrown by an
/// invocation to the mapper.
///
/// \param mapper A callable object, which accept an arbitrary type
/// and maps it to another type or the same one.
///
/// \param pack An arbitrary variadic pack which may contain any type.
///
/// \returns The mapped element or in case the pack contains
/// multiple elements, the pack is wrapped into
/// a `std::tuple`.
///
/// \since 3.0.0
///
template <typename Mapper, typename... T>
/*keep this inline*/ inline decltype(auto) map_pack(Mapper&& mapper,
T&&... pack) {
return detail::traversal::transform(detail::traversal::strategy_remap_tag{},
std::forward<Mapper>(mapper),
std::forward<T>(pack)...);
}
/// Indicate that the result shall be spread across the parent container
/// if possible. This can be used to create a mapper function used
/// in map_pack that maps one element to an arbitrary count (1:n).
///
/// \since 3.0.0
template <typename... T>
constexpr detail::traversal::spreading::spread_box<std::decay_t<T>...>
spread_this(T&&... args) noexcept(
noexcept(std::make_tuple(std::forward<T>(args)...))) {
using type = detail::traversal::spreading::spread_box<std::decay_t<T>...>;
return type(std::make_tuple(std::forward<T>(args)...));
}
/// Traverses the pack with the given visitor.
///
/// This function works in the same way as `map_pack`,
/// however, the result of the mapper isn't preserved.
///
/// See `map_pack` for a detailed description.
///
/// \since 3.0.0
template <typename Mapper, typename... T>
void traverse_pack(Mapper&& mapper, T&&... pack) {
detail::traversal::transform(detail::traversal::strategy_traverse_tag{},
std::forward<Mapper>(mapper),
std::forward<T>(pack)...);
}
/// \}
} // namespace cti
#endif // CONTINUABLE_TRAVERSE_HPP_INCLUDED

View File

@ -0,0 +1,102 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_TYPES_HPP_INCLUDED
#define CONTINUABLE_TYPES_HPP_INCLUDED
#include <function2/function2.hpp>
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-promise-base.hpp>
#include <continuable/detail/other/erasure.hpp>
namespace cti {
/// \defgroup Types Types
/// provides the \link cti::continuable continuable\endlink and \link
/// cti::promise promise\endlink facility for type erasure.
/// \{
/// Deduces to the preferred continuation capacity for a possible
/// small functor optimization. The given capacity size is always enough to
/// to avoid any allocation when storing a ready continuable_base.
///
/// \since 4.0.0
template <typename... Args>
using continuation_capacity = detail::erasure::continuation_capacity<Args...>;
/// Defines a non-copyable continuation type which uses the
/// function2 backend for type erasure.
///
/// Usable like: `continuable<int, float>`
///
/// \note You can always define your own continuable with a type erasure of
/// choice, the type erasure wrapper just needs to accept a
/// callable object with a continuation signature as specified
/// in the Primitives section.
///
/// \since 1.0.0
template <typename... Args>
using continuable = continuable_base<detail::erasure::continuation<Args...>, //
signature_arg_t<Args...>>;
/// Defines a non-copyable promise type which is using the
/// function2 backend for type erasure.
///
/// Usable like: `promise<int, float>`
///
/// \note You can always define your own promise with a type erasure of
/// choice, the type erasure wrapper just needs to accept a
/// callable object with a callback signature as specified
/// in the Primitives section.
///
/// \since 1.0.0
template <typename... Args>
using promise = promise_base<detail::erasure::callback<Args...>, //
signature_arg_t<Args...>>;
/// Defines a non-copyable type erasure which is capable of carrying
/// callable objects passed to executors.
///
/// The work behaves like a `promise<>` but the work type erasure uses extra
/// stack space for small object optimization.
/// Additionally the outstanding work can be resolved through an exception.
///
/// \note You can always define your own cancelable_work with a type erasure of
/// choice, the type erasure wrapper just needs to accept a
/// callable object which is callable with a `void()` and
/// `void(exception_arg_t, exception_t)` signature.
///
/// \since 4.0.0
using work = promise_base<detail::erasure::work, //
signature_arg_t<>>;
/// \}
} // namespace cti
#endif // CONTINUABLE_TYPES_HPP_INCLUDED

View File

@ -1,13 +1,13 @@
/**
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v0.8.0
v4.2.0
Copyright(c) 2015 - 2017 Denis Blank <denis.blank at outlook dot com>
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
@ -21,79 +21,41 @@
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_HPP_INCLUDED__
#define CONTINUABLE_HPP_INCLUDED__
#ifndef CONTINUABLE_HPP_INCLUDED
#define CONTINUABLE_HPP_INCLUDED
#include "continuable/continuable-base.hpp"
#include "function2/function2.hpp"
namespace cti {
// clang-format off
/// \cond false
inline namespace abi_v1 {
/// \endcond
namespace detail {
template<typename... Args>
using trait_of = continuable_trait<
fu2::function,
fu2::function,
Args...
>;
template<typename... Args>
using unique_trait_of = continuable_trait<
fu2::unique_function,
fu2::unique_function,
Args...
>;
} // end namespace detail
/// Defines a copyable continuation type which uses the
/// function2 backend for type erasure.
/// Declares the continuable library namespace.
///
/// Usable like: `cti::continuable<int, float>`
template <typename... Args>
using continuable = typename detail::trait_of<
Args...
>::continuable;
/// Defines a copyable callback type which uses the
/// function2 backend for the continuable type erasure.
/// The most important class is cti::continuable_base, that provides the
/// whole functionality for continuation chaining.
///
/// Usable like: `callback<int, float>`
template <typename... Args>
using callback = typename detail::trait_of<
Args...
>::callback;
/// Defines a non-copyable continuation type which uses the
/// function2 backend for type erasure.
/// The class cti::continuable_base is created through the
/// cti::make_continuable() function which accepts a callback taking function.
///
/// Usable like: `unique_continuable<int, float>`
template <typename... Args>
using unique_continuable = typename detail::unique_trait_of<
Args...
>::continuable;
/// Also there are following support functions available:
/// - cti::when_all() - connects cti::continuable_base's to an `all` connection.
/// - cti::when_any() - connects cti::continuable_base's to an `any` connection.
/// - cti::when_seq() - connects cti::continuable_base's to a sequence.
namespace cti {}
/// Defines a non-copyable callback type which uses the
/// function2 backend for the continuable type erasure.
///
/// Usable like: `unique_callback<int, float>`
template <typename... Args>
using unique_callback = typename detail::unique_trait_of<
Args...
>::callback;
/// \cond false
} // end inline namespace abi_...
/// \endcond
// clang-format on
} // end namespace cti
#include <continuable/continuable-base.hpp>
#include <continuable/continuable-connections.hpp>
#include <continuable/continuable-coroutine.hpp>
#include <continuable/continuable-operations.hpp>
#include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-promise-base.hpp>
#include <continuable/continuable-promisify.hpp>
#include <continuable/continuable-result.hpp>
#include <continuable/continuable-transforms.hpp>
#include <continuable/continuable-traverse-async.hpp>
#include <continuable/continuable-traverse.hpp>
#include <continuable/continuable-types.hpp>
#endif // CONTINUABLE_HPP_INCLUDED__
#endif // CONTINUABLE_HPP_INCLUDED

View File

@ -0,0 +1,250 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED
#define CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED
#include <cassert>
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/continuable-result.hpp>
#include <continuable/continuable-traverse.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace connection {
/// This namespace provides utilities for performing compound
/// connections between deeply nested continuables and values.
///
/// We create the result pack from the provides values and
/// the async values if those are default constructible,
/// otherwise use a lazy initialization wrapper and unwrap
/// the whole pack when the connection is finished.
/// - value -> value
/// - single async value -> single value
/// - multiple async value -> tuple of async values.
namespace aggregated {
/// Guards a type to be default constructible,
/// and wraps it into an optional type if it isn't default constructible.
template <typename T>
using lazy_value_t = std::conditional_t<std::is_default_constructible<T>::value,
T, result<T>>;
template <typename T>
decltype(auto) unpack_lazy(std::true_type /*is_default_constructible*/,
T&& value) {
return std::forward<T>(value);
}
template <typename T>
T&& unpack_lazy(std::false_type /*is_default_constructible*/,
result<T>&& value) {
assert(value.is_value() &&
"The connection was finalized before all values were present!");
return std::move(value).get_value();
}
template <typename Continuable>
class continuable_box;
template <typename Data>
class continuable_box<continuable_base<Data, identity<>>> {
continuable_base<Data, identity<>> continuable_;
public:
explicit continuable_box(continuable_base<Data, identity<>>&& continuable)
: continuable_(std::move(continuable)) {}
auto const& peek() const {
return continuable_;
}
auto&& fetch() {
return std::move(continuable_);
}
void assign() {}
auto unbox() && {
return spread_this();
}
};
template <typename Data, typename First>
class continuable_box<continuable_base<Data, identity<First>>> {
continuable_base<Data, identity<First>> continuable_;
lazy_value_t<First> first_;
public:
explicit continuable_box(
continuable_base<Data, identity<First>>&& continuable)
: continuable_(std::move(continuable)) {}
auto const& peek() const {
return continuable_;
}
auto&& fetch() {
return std::move(continuable_);
}
void assign(First first) {
first_ = std::move(first);
}
auto unbox() && {
return unpack_lazy(std::is_default_constructible<First>{},
std::move(first_));
}
};
template <typename Data, typename First, typename Second, typename... Rest>
class continuable_box<
continuable_base<Data, identity<First, Second, Rest...>>> {
continuable_base<Data, identity<First, Second, Rest...>> continuable_;
lazy_value_t<std::tuple<First, Second, Rest...>> args_;
public:
explicit continuable_box(
continuable_base<Data, identity<First, Second, Rest...>>&& continuable)
: continuable_(std::move(continuable)) {}
auto const& peek() const {
return continuable_;
}
auto&& fetch() {
return std::move(continuable_);
}
void assign(First first, Second second, Rest... rest) {
args_ = std::make_tuple(std::move(first), std::move(second),
std::move(rest)...);
}
auto unbox() && {
return traits::unpack(
[](auto&&... args) {
return spread_this(std::forward<decltype(args)>(args)...);
},
unpack_lazy(
std::is_default_constructible<std::tuple<First, Second, Rest...>>{},
std::move(args_)));
}
};
template <typename T>
struct is_continuable_box : std::false_type {};
template <typename Continuable>
struct is_continuable_box<continuable_box<Continuable>> : std::true_type {};
namespace detail {
/// Maps a deeply nested pack of continuables to a continuable_box
struct continuable_box_packer {
template <
typename T,
std::enable_if_t<base::is_continuable<std::decay_t<T>>::value>* = nullptr>
auto operator()(T&& continuable) {
return continuable_box<std::decay_t<T>>{std::forward<T>(continuable)};
}
};
/// Maps a deeply nested pack of continuable_boxes to its result
struct continuable_box_unpacker {
template <
typename T,
std::enable_if_t<is_continuable_box<std::decay_t<T>>::value>* = nullptr>
auto operator()(T&& box) {
return std::forward<T>(box).unbox();
}
};
} // namespace detail
/// Returns the boxed pack of the given deeply nested pack.
/// This transforms all continuables into a continuable_box which is
/// capable of caching the result from the corresponding continuable.
template <typename... Args>
constexpr auto box_continuables(Args&&... args) {
return cti::map_pack(detail::continuable_box_packer{},
std::forward<Args>(args)...);
}
/// Returns the unboxed pack of the given deeply nested boxed pack.
/// This transforms all continuable_boxes into its result.
template <typename... Args>
constexpr auto unbox_continuables(Args&&... args) {
return cti::map_pack(detail::continuable_box_unpacker{},
std::forward<Args>(args)...);
}
namespace detail {
template <typename Callback, typename Data>
constexpr auto finalize_impl(identity<void>, Callback&& callback, Data&&) {
return std::forward<Callback>(callback)();
}
template <typename... Args, typename Callback, typename Data>
constexpr auto finalize_impl(identity<std::tuple<Args...>>, Callback&& callback,
Data&& data) {
// Call the final callback with the cleaned result
return traits::unpack(std::forward<Callback>(callback),
unbox_continuables(std::forward<Data>(data)));
}
struct hint_mapper {
template <typename... T>
constexpr auto operator()(T...) -> identity<T...> {
return {};
}
};
} // namespace detail
template <typename Callback, typename Data>
constexpr auto finalize_data(Callback&& callback, Data&& data) {
using result_t = decltype(unbox_continuables(std::forward<Data>(data)));
// Guard the final result against void
return detail::finalize_impl(identity<std::decay_t<result_t>>{},
std::forward<Callback>(callback),
std::forward<Data>(data));
}
template <typename Data>
constexpr auto hint_of_data() {
return decltype(finalize_data(detail::hint_mapper{}, std::declval<Data>())){};
}
} // namespace aggregated
} // namespace connection
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_CONNECTION_REMAPPING_HPP_INCLUDED

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

@ -0,0 +1,204 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED
#define CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED
#include <atomic>
#include <memory>
#include <mutex>
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-promise-base.hpp>
#include <continuable/continuable-traverse.hpp>
#include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/traversal/container-category.hpp>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace connection {
namespace any {
/// Invokes the callback with the first arriving result
template <typename T>
class any_result_submitter
: public std::enable_shared_from_this<any_result_submitter<T>>,
public util::non_movable {
T callback_;
std::once_flag flag_;
struct any_callback {
std::shared_ptr<any_result_submitter> me_;
template <typename... PartialArgs>
void operator()(PartialArgs&&... args) && {
me_->invoke(std::forward<decltype(args)>(args)...);
}
};
public:
explicit any_result_submitter(T callback) : callback_(std::move(callback)) {
}
/// Creates a submitter which submits it's result to the callback
auto create_callback() {
return any_callback{this->shared_from_this()};
}
private:
// Invokes the callback with the given arguments
template <typename... ActualArgs>
void invoke(ActualArgs&&... args) {
std::call_once(flag_, std::move(callback_),
std::forward<ActualArgs>(args)...);
}
};
struct result_deducer {
template <typename T>
static auto deduce_one(std::false_type, identity<T>) {
static_assert(traits::fail<T>::value,
"Non continuable types except tuple like and homogeneous "
"containers aren't allowed inside an any expression!");
}
template <typename T>
static auto deduce_one(std::true_type, identity<T> id) {
return base::annotation_of(id);
}
template <typename T>
static auto deduce(traversal::container_category_tag<false, false>,
identity<T> id) {
return deduce_one<T>(base::is_continuable<T>{}, id);
}
/// Deduce a homogeneous container
template <bool IsTupleLike, typename T>
static auto deduce(traversal::container_category_tag<true, IsTupleLike>,
identity<T>) {
// Deduce the containing type
using element_t = std::decay_t<decltype(*std::declval<T>().begin())>;
return deduce(traversal::container_category_of_t<element_t>{},
identity<element_t>{});
}
template <typename First, typename... T>
static auto deduce_same_hints(First first, T...) {
static_assert(traits::conjunction<std::is_same<First, T>...>::value,
"The continuables inside the given pack must have the "
"same signature hint!");
return first;
}
template <std::size_t... I, typename T>
static auto deduce_tuple_like(std::integer_sequence<std::size_t, I...>,
identity<T>) {
return deduce_same_hints(deduce(
traversal::container_category_of_t<
std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{},
identity<std::decay_t<decltype(std::get<I>(std::declval<T>()))>>{})...);
}
/// Traverse tuple like container
template <typename T>
static auto deduce(traversal::container_category_tag<false, true>,
identity<T> id) {
constexpr auto const size = std::tuple_size<T>::value;
return deduce_tuple_like(std::make_index_sequence<size>{}, id);
}
};
template <typename Submitter>
struct continuable_dispatcher {
std::shared_ptr<Submitter>& submitter;
template <typename Continuable,
std::enable_if_t<base::is_continuable<
std::decay_t<Continuable>>::value>* = nullptr>
void operator()(Continuable&& continuable) {
// Retrieve a callback from the submitter and attach it to the continuable
std::forward<Continuable>(continuable)
.next(submitter->create_callback())
.done();
}
};
} // namespace any
struct connection_strategy_any_tag {};
template <>
struct is_connection_strategy<connection_strategy_any_tag> // ...
: std::true_type {};
/// Finalizes the any logic of a given connection
template <>
struct connection_finalizer<connection_strategy_any_tag> {
template <typename Connection>
static auto finalize(Connection&& connection, util::ownership ownership) {
constexpr auto const signature = decltype(any::result_deducer::deduce(
traversal::container_category_of_t<std::decay_t<Connection>>{},
identity<std::decay_t<Connection>>{})){};
return base::attorney::create_from(
[connection =
std::forward<Connection>(connection)](auto&& callback) mutable {
using submitter_t =
any::any_result_submitter<std::decay_t<decltype(callback)>>;
// Create the submitter which calls the given callback once at the
// first callback invocation.
auto submitter = std::make_shared<submitter_t>(
std::forward<decltype(callback)>(callback));
traverse_pack(any::continuable_dispatcher<submitter_t>{submitter},
std::move(connection));
},
signature, std::move(ownership));
}
};
} // namespace connection
/// Specialization for a connection annotation
template <>
struct annotation_trait<connection::connection_strategy_any_tag>
: connection::connection_annotation_trait<
connection::connection_strategy_any_tag> {};
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_CONNECTION_ANY_HPP_INCLUDED

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

@ -0,0 +1,197 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED
#define CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED
#include <cassert>
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/continuable-traverse.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/utility/util.hpp>
namespace cti {
namespace detail {
/// The namespace `connection` offers methods to chain continuations together
/// with `all`, `any` or `seq` logic.
namespace connection {
template <typename T>
struct is_connection_strategy // ...
: std::false_type {};
/// Adds the given continuation tuple to the left connection
template <typename... LeftArgs, typename... RightArgs>
auto chain_connection(std::tuple<LeftArgs...> leftPack,
std::tuple<RightArgs...> rightPack) {
return std::tuple_cat(std::move(leftPack), std::move(rightPack));
}
/// Normalizes a continuation to a tuple holding an arbitrary count of
/// continuations matching the given strategy.
///
/// Basically we can encounter 3 cases:
/// - The continuable isn't in any strategy:
/// -> make a tuple containing the continuable as only element
template <
typename Strategy, typename Data, typename Annotation,
std::enable_if_t<!is_connection_strategy<Annotation>::value>* = nullptr>
auto normalize(Strategy /*strategy*/,
continuable_base<Data, Annotation>&& continuation) {
// If the continuation isn't a strategy initialize the strategy
return std::make_tuple(std::move(continuation));
}
/// - The continuable is in a different strategy then the current one:
/// -> materialize it
template <
typename Strategy, typename Data, typename Annotation,
std::enable_if_t<is_connection_strategy<Annotation>::value>* = nullptr>
auto normalize(Strategy /*strategy*/,
continuable_base<Data, Annotation>&& continuation) {
// If the right continuation is a different strategy materialize it
// in order to keep the precedence in cases where: `c1 && (c2 || c3)`.
return std::make_tuple(std::move(continuation).finish());
}
/// - The continuable is inside the current strategy state:
/// -> return the data of the tuple
template <typename Strategy, typename Data>
auto normalize(Strategy /*strategy*/,
continuable_base<Data, Strategy>&& continuation) {
// If we are in the given strategy we can just use the data of the continuable
return base::attorney::consume(std::move(continuation));
}
/// Entry function for connecting two continuables with a given strategy.
template <typename Strategy, typename LData, typename LAnnotation,
typename RData, typename RAnnotation>
auto connect(Strategy strategy, continuable_base<LData, LAnnotation>&& left,
continuable_base<RData, RAnnotation>&& right) {
auto ownership_ =
base::attorney::ownership_of(left) | base::attorney::ownership_of(right);
left.freeze();
right.freeze();
// Make the new data which consists of a tuple containing
// all connected continuables.
auto data = chain_connection(normalize(strategy, std::move(left)),
normalize(strategy, std::move(right)));
// Return a new continuable containing the tuple and holding
// the current strategy as annotation.
return base::attorney::create_from_raw(std::move(data), strategy, ownership_);
}
/// All strategies should specialize this class in order to provide:
/// - A finalize static method that creates the callable object which
/// is invoked with the callback to call when the connection is finished.
/// - A static method hint that returns the new signature hint.
template <typename Strategy>
struct connection_finalizer;
template <typename Strategy>
struct connection_annotation_trait {
/// Finalizes the connection logic of a given connection
template <typename Continuable>
static auto finish(Continuable&& continuable) {
using finalizer = connection_finalizer<Strategy>;
util::ownership ownership = base::attorney::ownership_of(continuable);
auto connection =
base::attorney::consume(std::forward<Continuable>(continuable));
// Return a new continuable which
return finalizer::finalize(std::move(connection), std::move(ownership));
}
template <typename Continuable>
static bool is_ready(Continuable const& /*continuable*/) noexcept {
return false;
}
};
class prepare_continuables {
util::ownership& ownership_;
public:
explicit constexpr prepare_continuables(util::ownership& ownership)
: ownership_(ownership) {
}
template <typename Continuable,
std::enable_if_t<base::is_continuable<
std::decay_t<Continuable>>::value>* = nullptr>
auto operator()(Continuable&& continuable) noexcept {
util::ownership current = base::attorney::ownership_of(continuable);
assert(current.is_acquired() &&
"Only valid continuables should be passed!");
// Propagate a frozen state to the new continuable
if (!ownership_.is_frozen() && current.is_frozen()) {
ownership_.freeze();
}
// Freeze the continuable since it is stored for later usage
continuable.freeze();
// Materialize every continuable
// TODO Actually we would just need to consume the data here
return std::forward<Continuable>(continuable).finish();
}
};
template <typename Strategy, typename... Args>
auto apply_connection(Strategy, Args&&... args) {
using finalizer = connection_finalizer<Strategy>;
// Freeze every continuable inside the given arguments,
// and freeze the ownership if one of the continuables
// is frozen already.
// Additionally test whether every continuable is acquired.
// Also materialize every continuable.
util::ownership ownership;
auto connection = map_pack(prepare_continuables{ownership},
std::make_tuple(std::forward<Args>(args)...));
return finalizer::finalize(std::move(connection), std::move(ownership));
}
} // namespace connection
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_CONNECTION_HPP_INCLUDED

View File

@ -0,0 +1,53 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED
#define CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED
#include <type_traits>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace hints {
/// Extracts the signature we pass to the internal continuable
/// from an argument pack as specified by make_continuable.
///
/// This is the overload taking an arbitrary amount of args
template <typename... HintArgs>
struct from_args : std::common_type<signature_arg_t<HintArgs...>> {};
template <>
struct from_args<void> : std::common_type<signature_arg_t<>> {};
} // namespace hints
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_ANNOTATION_HPP_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED
#include <type_traits>
#include <utility>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/identity.hpp>
#ifndef CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
#include <exception>
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
#include <system_error>
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
#endif // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
namespace cti {
template <typename Data, typename Annotation>
class continuable_base;
namespace detail {
/// Contains types used globally across the library
namespace types {
#ifdef CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
using exception_t = CONTINUABLE_WITH_CUSTOM_ERROR_TYPE;
#else // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
/// Represents the exception type when exceptions are enabled
using exception_t = std::exception_ptr;
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
/// Represents the error type when exceptions are disabled
using exception_t = std::error_condition;
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
#endif // CONTINUABLE_WITH_CUSTOM_ERROR_TYPE
/// A tag which is used to execute the continuation inside the current thread
struct this_thread_executor_tag {};
/// Marks a given callable object as transformation
template <typename T>
class plain_tag {
T value_;
public:
template <typename O, std::enable_if_t<std::is_constructible<
T, std::decay_t<O>>::value>* = nullptr>
/* implicit */ plain_tag(O&& value) : value_(std::forward<O>(value)) {
}
explicit plain_tag(T value) : value_(std::move(value)) {
}
T&& consume() && {
return std::move(value_);
}
};
} // namespace types
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TYPES_HPP_INCLUDED

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

@ -0,0 +1,147 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED
#define CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED
// Defines CONTINUABLE_WITH_NO_EXCEPTIONS when exception support is disabled
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
# if defined(_MSC_VER)
# if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0)
# define CONTINUABLE_WITH_NO_EXCEPTIONS
# endif
# elif defined(__clang__)
# if !(__EXCEPTIONS && __has_feature(cxx_exceptions))
# define CONTINUABLE_WITH_NO_EXCEPTIONS
# endif
# elif defined(__GNUC__)
# if !__EXCEPTIONS
# define CONTINUABLE_WITH_NO_EXCEPTIONS
# endif
# endif
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
// clang-format off
// Detect if the whole standard is available
#if (defined(_MSC_VER) && defined(_HAS_CXX17) && _HAS_CXX17) || \
(__cplusplus >= 201703L)
#define CONTINUABLE_HAS_CXX17_CONSTEXPR_IF
#define CONTINUABLE_HAS_CXX17_DISJUNCTION
#define CONTINUABLE_HAS_CXX17_CONJUNCTION
#define CONTINUABLE_HAS_CXX17_VOID_T
#else
// Generic feature detection based on __has_feature
// and other preprocessor definitions based on:
// http://en.cppreference.com/w/User:D41D8CD98F/feature_testing_macros
#if defined(__has_feature)
#if !defined(CONTINUABLE_HAS_CXX17_CONSTEXPR_IF) && \
__has_feature(cxx_if_constexpr)
#define CONTINUABLE_HAS_CXX17_CONSTEXPR_IF
#endif
#endif
#if !defined(CONTINUABLE_HAS_CXX17_DISJUNCTION) && \
defined(__cpp_lib_experimental_logical_traits) && \
(__cpp_lib_experimental_logical_traits >= 201511)
#define CONTINUABLE_HAS_CXX17_DISJUNCTION
#endif
#if !defined(CONTINUABLE_HAS_CXX17_CONJUNCTION) && \
defined(__cpp_lib_experimental_logical_traits) && \
(__cpp_lib_experimental_logical_traits >= 201511)
#define CONTINUABLE_HAS_CXX17_CONJUNCTION
#endif
#if !defined(CONTINUABLE_HAS_CXX17_VOID_T) && \
defined(__cpp_lib_void_t) && \
(__cpp_lib_void_t >= 201411)
#define CONTINUABLE_HAS_CXX17_VOID_T
#endif
#endif
// Automatically detects support for coroutines.
// Parts of this detection mechanism were adapted from boost::asio,
// with support added for experimental coroutines.
#if !defined(CONTINUABLE_HAS_DISABLED_COROUTINE) \
&& !defined(CONTINUABLE_HAS_COROUTINE)
/// Define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE when
/// CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE is defined.
#if defined(CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE)
#define CONTINUABLE_HAS_COROUTINE 1
#elif defined(CONTINUABLE_WITH_COROUTINE)
#define CONTINUABLE_HAS_COROUTINE 1
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
#elif defined(_MSC_VER) // Visual Studio
#if (_MSC_VER >= 1928) && (_MSVC_LANG >= 201705)
#define CONTINUABLE_HAS_COROUTINE 1
#elif _MSC_FULL_VER >= 190023506
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
#define CONTINUABLE_HAS_COROUTINE 1
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
#endif // defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
#endif // _MSC_FULL_VER >= 190023506
#elif defined(__clang__) // Clang
#if defined(__cpp_coroutines) && (__cpp_coroutines >= 201703L)
#define CONTINUABLE_HAS_COROUTINE 1
#if defined(_LIBCPP_EXPERIMENTAL_COROUTINE)
#define CONTINUABLE_HAS_EXPERIMENTAL_COROUTINE 1
#endif
#endif // defined(__cpp_coroutines) && (__cpp_coroutines >= 201703L)
#elif defined(__GNUC__) // GCC
#if (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
#if __has_include(<coroutine>)
#define CONTINUABLE_HAS_COROUTINE 1
#endif // __has_include(<coroutine>)
#endif // (__cplusplus >= 201709) && (__cpp_impl_coroutine >= 201902)
#endif
#endif
/// Define CONTINUABLE_HAS_EXCEPTIONS when exceptions are used
#if !defined(CONTINUABLE_WITH_CUSTOM_ERROR_TYPE) && \
!defined(CONTINUABLE_WITH_NO_EXCEPTIONS)
#define CONTINUABLE_HAS_EXCEPTIONS 1
#else
#undef CONTINUABLE_HAS_EXCEPTIONS
#endif
/// Define CONTINUABLE_HAS_IMMEDIATE_TYPES when either
/// - CONTINUABLE_WITH_IMMEDIATE_TYPES is defined
/// - Building in release mode (NDEBUG is defined)
///
/// Build error messages will become more readable in debug mode while
/// we don't suffer any runtime penalty in release.
#if defined(CONTINUABLE_WITH_IMMEDIATE_TYPES) || defined(NDEBUG)
#define CONTINUABLE_HAS_IMMEDIATE_TYPES 1
#else
#undef CONTINUABLE_HAS_IMMEDIATE_TYPES
#endif
// clang-format on
#endif // CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED

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

@ -0,0 +1,218 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_TESTING_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TESTING_HPP_INCLUDED
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include <continuable/continuable-primitives.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/traits.hpp>
#include <continuable/detail/utility/util.hpp>
namespace cti {
namespace detail {
namespace testing {
template <typename C>
void assert_async_completion(C&& continuable) {
auto called = std::make_shared<bool>(false);
std::forward<C>(continuable)
.then([called](auto&&... args) {
ASSERT_FALSE(*called);
*called = true;
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
})
.fail([](cti::exception_t /*error*/) {
// ...
FAIL();
});
ASSERT_TRUE(*called);
}
template <typename C>
void assert_async_exception_completion(C&& continuable) {
auto called = std::make_shared<bool>(false);
std::forward<C>(continuable)
.then([](auto&&... args) {
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
// ...
FAIL();
})
.fail([called](cti::exception_t error) {
ASSERT_TRUE(bool(error));
ASSERT_FALSE(*called);
*called = true;
});
ASSERT_TRUE(*called);
}
template <typename C>
void assert_async_cancellation(C&& continuable) {
auto called = std::make_shared<bool>(false);
std::forward<C>(continuable)
.then([](auto&&... args) {
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
// ...
FAIL();
})
.fail([called](cti::exception_t error) {
ASSERT_FALSE(bool(error));
ASSERT_FALSE(*called);
*called = true;
});
ASSERT_TRUE(*called);
}
template <typename C>
void assert_async_never_completed(C&& continuable) {
std::forward<C>(continuable)
.then([](auto&&... args) {
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
FAIL();
})
.fail([](cti::exception_t) {
// ...
FAIL();
});
}
template <typename C, typename V>
void assert_async_validation(C&& continuable, V&& validator) {
assert_async_completion(
std::forward<C>(continuable)
.then(
[validator = std::forward<V>(validator)](auto&&... args) mutable {
validator(std::forward<decltype(args)>(args)...);
}));
}
/// Expects that the continuable is finished with the given arguments
template <typename V, typename C, typename... Args>
void assert_async_binary_validation(V&& validator, C&& continuable,
Args&&... expected) {
using size = std::integral_constant<std::size_t, sizeof...(expected)>;
assert_async_validation(
std::forward<C>(continuable),
[expected_pack = std::make_tuple(std::forward<Args>(expected)...),
validator = std::forward<V>(validator)](auto&&... args) mutable {
static_assert(size::value == sizeof...(args),
"Async completion handler called with a different count "
"of arguments!");
validator(std::make_tuple(std::forward<decltype(args)>(args)...),
expected_pack);
});
}
/// Expects that the continuable is finished with the given arguments
template <typename V, typename C, typename Args>
void assert_async_binary_exception_validation(V&& validator, C&& continuable,
Args&& expected) {
auto called = std::make_shared<bool>(false);
std::forward<C>(continuable)
.then([](auto&&... args) {
// Workaround for our known GCC bug.
util::unused(std::forward<decltype(args)>(args)...);
// The exception was not thrown!
FAIL();
})
.fail([called, validator = std::forward<decltype(validator)>(validator),
expected = std::forward<decltype(expected)>(expected)](
exception_t error) {
ASSERT_FALSE(*called);
*called = true;
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
try {
std::rethrow_exception(error);
} catch (std::decay_t<decltype(expected)> const& exception) {
validator(exception, expected);
} catch (...) {
FAIL();
}
#else
validator(error, expected);
#endif
});
ASSERT_TRUE(*called);
}
inline auto expecting_eq_check() {
return [](auto&& expected, auto&& actual) {
EXPECT_EQ(std::forward<decltype(expected)>(expected),
std::forward<decltype(actual)>(actual));
};
}
inline auto asserting_eq_check() {
return [](auto&& expected, auto&& actual) {
ASSERT_EQ(std::forward<decltype(expected)>(expected),
std::forward<decltype(actual)>(actual));
};
}
template <typename... Expected>
struct assert_async_types_validator {
template <typename... Actual>
void operator()(Actual...) {
static_assert(
std::is_same<identity<Actual...>, identity<Expected...>>::value,
"The called arguments don't match with the expected ones!");
}
};
template <typename C, typename... Args>
void assert_async_types(C&& continuable, identity<Args...> /*expected*/) {
assert_async_validation(std::forward<C>(continuable),
assert_async_types_validator<Args...>{});
}
} // namespace testing
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TESTING_HPP_INCLUDED

View File

@ -0,0 +1,138 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_TRANSFORMS_FUTURE_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TRANSFORMS_FUTURE_HPP_INCLUDED
#include <future>
#include <continuable/continuable-primitives.hpp>
#include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/features.hpp>
#include <continuable/detail/utility/util.hpp>
namespace cti {
namespace detail {
/// Provides helper functions to transform continuations to other types
namespace transforms {
/// Provides helper functions and typedefs for converting callback arguments
/// to their types a promise can accept.
template <typename... Args>
struct future_trait {
/// The promise type used to create the future
using promise_t = std::promise<std::tuple<Args...>>;
/// Boxes the argument pack into a tuple
static void resolve(promise_t& promise, Args... args) {
promise.set_value(std::make_tuple(std::move(args)...));
}
};
template <>
struct future_trait<> {
/// The promise type used to create the future
using promise_t = std::promise<void>;
/// Boxes the argument pack into void
static void resolve(promise_t& promise) {
promise.set_value();
}
};
template <typename First>
struct future_trait<First> {
/// The promise type used to create the future
using promise_t = std::promise<First>;
/// Boxes the argument pack into nothing
static void resolve(promise_t& promise, First first) {
promise.set_value(std::move(first));
}
};
template <typename Hint>
class promise_callback;
template <typename... Args>
class promise_callback<identity<Args...>> : public future_trait<Args...> {
typename future_trait<Args...>::promise_t promise_;
public:
constexpr promise_callback() = default;
promise_callback(promise_callback const&) = delete;
constexpr promise_callback(promise_callback&&) = default;
promise_callback& operator=(promise_callback const&) = delete;
promise_callback& operator=(promise_callback&&) = delete;
/// Resolves the promise
void operator()(Args... args) {
this->resolve(promise_, std::move(args)...);
}
/// Resolves the promise through the exception
void operator()(exception_arg_t, exception_t error) {
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
promise_.set_exception(error);
#else
(void)error;
// Can't forward a std::error_condition or custom error type
// to a std::promise. Handle the error first in order
// to prevent this trap!
CTI_DETAIL_TRAP();
#endif // CONTINUABLE_HAS_EXCEPTIONS
}
/// Returns the future from the promise
auto get_future() {
return promise_.get_future();
}
};
/// Transforms the continuation to a future
template <typename Data, typename Annotation>
auto to_future(continuable_base<Data, Annotation>&& continuable) {
// Create the promise which is able to supply the current arguments
constexpr auto const hint =
base::annotation_of(identify<decltype(continuable)>{});
promise_callback<std::decay_t<decltype(hint)>> callback;
(void)hint;
// Receive the future
auto future = callback.get_future();
// Dispatch the continuation with the promise resolving callback
std::move(continuable).next(std::move(callback)).done();
return future;
}
} // namespace transforms
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TRANSFORMS_FUTURE_HPP_INCLUDED

View File

@ -0,0 +1,264 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_TRANSFORMS_WAIT_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TRANSFORMS_WAIT_HPP_INCLUDED
#include <atomic>
#include <cassert>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <continuable/continuable-primitives.hpp>
#include <continuable/continuable-result.hpp>
#include <continuable/detail/core/annotation.hpp>
#include <continuable/detail/core/base.hpp>
#include <continuable/detail/core/types.hpp>
#include <continuable/detail/features.hpp>
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
# include <exception>
#endif
namespace cti {
namespace detail {
namespace transforms {
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
class wait_transform_canceled_exception : public std::exception {
public:
wait_transform_canceled_exception() noexcept = default;
char const* what() const noexcept override {
return "cti::transforms::wait canceled due to cancellation of the "
"continuation";
}
};
#endif // CONTINUABLE_HAS_EXCEPTIONS
template <typename Hint>
struct sync_trait;
template <typename... Args>
struct sync_trait<identity<Args...>> {
using result_t = result<Args...>;
};
using lock_t = std::unique_lock<std::mutex>;
using condition_variable_t = std::condition_variable;
template <typename Result>
struct unsafe_unlocker {
explicit unsafe_unlocker(std::atomic_bool* ready, condition_variable_t* cv,
std::mutex* mutex, Result* result)
: ready_(ready)
, cv_(cv)
, mutex_(mutex)
, result_(result) {}
unsafe_unlocker(unsafe_unlocker const&) = delete;
unsafe_unlocker(unsafe_unlocker&&) = default;
unsafe_unlocker& operator=(unsafe_unlocker const&) = delete;
unsafe_unlocker& operator=(unsafe_unlocker&&) = default;
~unsafe_unlocker() {
unlock(Result::empty());
}
template <typename... Args>
void operator()(Args&&... args) {
unlock(Result::from(std::forward<Args>(args)...));
}
void unlock(Result&& result) {
if (!ownership_.is_acquired()) {
return;
}
ownership_.release();
lock_t lock(*mutex_);
*result_ = std::move(result);
assert(!ready_->load(std::memory_order_acquire));
ready_->store(true, std::memory_order_release);
cv_->notify_all();
}
std::atomic_bool* ready_;
condition_variable_t* cv_;
std::mutex* mutex_;
Result* result_;
util::ownership ownership_;
};
template <typename Data, typename Annotation,
typename Result = typename sync_trait<Annotation>::result_t>
Result wait_relaxed(continuable_base<Data, Annotation>&& continuable) {
// Do an immediate unpack if the continuable is ready
if (continuable.is_ready()) {
return std::move(continuable).unpack();
}
condition_variable_t cv;
std::mutex cv_mutex;
std::atomic_bool ready{false};
Result sync_result;
std::move(continuable)
.next(unsafe_unlocker<Result>{
&ready,
&cv,
&cv_mutex,
&sync_result,
})
.done();
lock_t lock(cv_mutex);
if (!ready.load(std::memory_order_acquire)) {
cv.wait(lock, [&] {
return ready.load(std::memory_order_acquire);
});
}
return sync_result;
}
/// Transforms the continuation to sync execution and unpacks the result the if
/// possible
template <typename Data, typename Annotation>
auto wait_and_unpack(continuable_base<Data, Annotation>&& continuable) {
auto sync_result = wait_relaxed(std::move(continuable));
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
if (sync_result.is_value()) {
return std::move(sync_result).get_value();
} else if (sync_result.is_exception()) {
if (sync_result.is_exception()) {
if (exception_t e = sync_result.get_exception()) {
std::rethrow_exception(e);
}
}
}
throw wait_transform_canceled_exception();
#else
return sync_result;
#endif // CONTINUABLE_HAS_EXCEPTIONS
}
template <typename Result>
struct wait_frame {
std::mutex cv_mutex;
std::mutex rw_mutex;
condition_variable_t cv;
std::atomic_bool ready{false};
Result sync_result;
};
template <typename Result>
struct unlocker {
unlocker(unlocker const&) = delete;
unlocker(unlocker&&) = default;
unlocker& operator=(unlocker const&) = delete;
unlocker& operator=(unlocker&&) = default;
explicit unlocker(std::weak_ptr<wait_frame<Result>> frame)
: frame_(std::move(frame)) {}
~unlocker() {
unlock(Result::empty());
}
template <typename... Args>
void operator()(Args&&... args) {
unlock(Result::from(std::forward<decltype(args)>(args)...));
}
void unlock(Result&& result) {
if (!ownership_.is_acquired()) {
return;
}
ownership_.release();
if (auto locked = frame_.lock()) {
{
std::lock_guard<std::mutex> rw_lock(locked->rw_mutex);
assert(!locked->ready.load(std::memory_order_acquire));
locked->sync_result = std::move(result);
}
locked->ready.store(true, std::memory_order_release);
locked->cv.notify_all();
}
}
std::weak_ptr<wait_frame<Result>> frame_;
util::ownership ownership_;
};
template <typename Data, typename Annotation, typename Waiter,
typename Result = typename sync_trait<Annotation>::result_t>
Result wait_unsafe(continuable_base<Data, Annotation>&& continuable,
Waiter&& waiter) {
// Do an immediate unpack if the continuable is ready
if (continuable.is_ready()) {
return std::move(continuable).unpack();
}
using frame_t = wait_frame<Result>;
auto frame = std::make_shared<frame_t>();
std::move(continuable)
.next(unlocker<Result>{std::weak_ptr<frame_t>(frame)})
.done();
if (!frame->ready.load(std::memory_order_acquire)) {
lock_t lock(frame->cv_mutex);
std::forward<Waiter>(waiter)(frame->cv, lock, [&] {
return frame->ready.load(std::memory_order_acquire);
});
}
return ([&] {
std::lock_guard<std::mutex> rw_lock(frame->rw_mutex);
Result cached = std::move(frame->sync_result);
return cached;
})();
}
} // namespace transforms
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TRANSFORMS_WAIT_HPP_INCLUDED

View File

@ -0,0 +1,76 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_CONTAINER_CATEGORY_HPP_INCLUDED
#define CONTINUABLE_DETAIL_CONTAINER_CATEGORY_HPP_INCLUDED
#include <tuple>
#include <type_traits>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace traversal {
/// Deduces to a true type if the given parameter T
/// has a begin() and end() method.
// TODO Find out whether we should use std::begin and std::end instead, which
// could cause issues with plain arrays.
template <typename T, typename = void>
struct is_range : std::false_type {};
template <typename T>
struct is_range<T, traits::void_t<decltype(std::declval<T>().begin() ==
std::declval<T>().end())>>
: std::true_type {};
/// Deduces to a true type if the given parameter T
/// is accessible through std::tuple_size.
template <typename T, typename = void>
struct is_tuple_like : std::false_type {};
template <typename T>
struct is_tuple_like<T, traits::void_t<decltype(std::tuple_size<T>::value)>>
: std::true_type {};
/// A tag for dispatching based on the tuple like
/// or container properties of a type.
///
/// This type deduces to a true_type if it has any category.
template <bool IsContainer, bool IsTupleLike>
struct container_category_tag
: std::integral_constant<bool, IsContainer || IsTupleLike> {};
/// Deduces to the container_category_tag of the given type T.
template <typename T>
using container_category_of_t =
container_category_tag<is_range<T>::value, is_tuple_like<T>::value>;
} // namespace traversal
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_CONTAINER_CATEGORY_HPP_INCLUDED

View File

@ -0,0 +1,65 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_RANGE_HPP_INCLUDED
#define CONTINUABLE_DETAIL_RANGE_HPP_INCLUDED
#include <iterator>
#include <type_traits>
#include <utility>
#include <vector>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace range {
/// Deduces to a true_type if the given type is an interator
template <typename T, typename = void>
struct is_iterator : std::false_type {};
template <typename T>
struct is_iterator<T,
traits::void_t<typename std::iterator_traits<T>::value_type>>
: std::true_type {};
/// Moves the content of the given iterators to a persistent storage
template <typename Iterator>
auto persist_range(Iterator begin, Iterator end) {
std::vector<typename std::iterator_traits<Iterator>::value_type> storage;
// TODO Find out why the superior idiom below has issues with move only types:
// storage.insert(storage.end(), std::make_move_iterator(begin),
// std::make_move_iterator(end));
std::move(begin, end, std::back_inserter(storage));
return storage;
}
} // namespace range
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_RANGE_HPP_INCLUDED

View File

@ -0,0 +1,580 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_TRAVERSE_ASYNC_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TRAVERSE_ASYNC_HPP_INCLUDED
#include <atomic>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/detail/traversal/container-category.hpp>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace traversal {
/// A tag which is passed to the `operator()` of the visitor
/// if an element is visited synchronously.
struct async_traverse_visit_tag {};
/// A tag which is passed to the `operator()` of the visitor
/// if an element is visited after the traversal was detached.
struct async_traverse_detach_tag {};
/// A tag which is passed to the `operator()` of the visitor
/// if the asynchronous pack traversal was finished.
struct async_traverse_complete_tag {};
/// A tag to identify that a mapper shall be constructed in-place
/// from the first argument passed.
template <typename T>
struct async_traverse_in_place_tag {};
/// Relocates the given pack with the given offset
template <std::size_t Offset, typename Pack>
struct relocate_index_pack;
template <std::size_t Offset, std::size_t... Sequence>
struct relocate_index_pack<Offset,
std::integer_sequence<std::size_t, Sequence...>>
: std::common_type<
std::integer_sequence<std::size_t, (Sequence + Offset)...>> {};
/// Creates a sequence from begin to end explicitly
template <std::size_t Begin, std::size_t End>
using explicit_range_sequence_of_t =
typename relocate_index_pack<Begin,
std::make_index_sequence<End - Begin>>::type;
/// Continues the traversal when the object is called
template <typename Frame, typename State>
class resume_traversal_callable {
Frame frame_;
State state_;
public:
explicit resume_traversal_callable(Frame frame, State state)
: frame_(std::move(frame)), state_(std::move(state)) {
}
/// The callable operator for resuming
/// the asynchronous pack traversal
void operator()();
};
/// Creates a resume_traversal_callable from the given frame and the
/// given iterator tuple.
template <typename Frame, typename State>
auto make_resume_traversal_callable(Frame&& frame, State&& state)
-> resume_traversal_callable<std::decay_t<Frame>, std::decay_t<State>> {
return resume_traversal_callable<std::decay_t<Frame>, std::decay_t<State>>(
std::forward<Frame>(frame), std::forward<State>(state));
}
template <typename T, typename = void>
struct has_head : std::false_type {};
template <typename T>
struct has_head<T, traits::void_t<decltype(std::declval<T>().head())>>
: std::true_type {};
template <typename Visitor, typename... Args>
class async_traversal_frame_data : public Visitor {
std::tuple<Args...> args_;
public:
explicit async_traversal_frame_data(Visitor visitor, Args... args)
: Visitor(std::move(visitor)),
args_(std::make_tuple(std::move(args)...)) {
}
template <typename MapperArg>
explicit async_traversal_frame_data(async_traverse_in_place_tag<Visitor>,
MapperArg&& mapper_arg, Args... args)
: Visitor(std::forward<MapperArg>(mapper_arg)),
args_(std::make_tuple(std::move(args)...)) {
}
/// Returns the arguments of the frame
std::tuple<Args...>& head() noexcept {
return args_;
}
};
template <typename Visitor>
class async_traversal_frame_no_data : public Visitor {
public:
explicit async_traversal_frame_no_data(Visitor visitor)
: Visitor(std::move(visitor)) {
}
template <typename MapperArg>
explicit async_traversal_frame_no_data(async_traverse_in_place_tag<Visitor>,
MapperArg&& mapper_arg)
: Visitor(std::forward<MapperArg>(mapper_arg)) {
}
};
template <typename Visitor, typename... Args>
using data_layout_t =
std::conditional_t<has_head<Visitor>::value,
async_traversal_frame_no_data<Visitor>,
async_traversal_frame_data<Visitor, Args...>>;
/// Stores the visitor and the arguments to traverse
template <typename Visitor, typename... Args>
class async_traversal_frame : public data_layout_t<Visitor, Args...> {
#ifndef NDEBUG
std::atomic<bool> finished_;
#endif // NDEBUG
Visitor& visitor() noexcept {
return *static_cast<Visitor*>(this);
}
Visitor const& visitor() const noexcept {
return *static_cast<Visitor const*>(this);
}
public:
template <typename... T>
explicit async_traversal_frame(T&&... args)
: data_layout_t<Visitor, Args...>(std::forward<T>(args)...)
#ifndef NDEBUG
,
finished_(false)
#endif // NDEBUG
{
}
/// We require a virtual base
virtual ~async_traversal_frame() override = default;
/// Calls the visitor with the given element
template <typename T>
auto traverse(T&& value) -> decltype(visitor()(async_traverse_visit_tag{},
std::forward<T>(value))) {
return visitor()(async_traverse_visit_tag{}, std::forward<T>(value));
}
/// Calls the visitor with the given element and a continuation
/// which is capable of continuing the asynchronous traversal
/// when it's called later.
template <typename T, typename Hierarchy>
void async_continue(T&& value, Hierarchy&& hierarchy) {
// Cast the frame up
auto frame = std::static_pointer_cast<async_traversal_frame>(
this->shared_from_this());
// Create a callable object which resumes the current
// traversal when it's called.
auto resumable = make_resume_traversal_callable(
std::move(frame), std::forward<Hierarchy>(hierarchy));
// Invoke the visitor with the current value and the
// callable object to resume the control flow.
visitor()(async_traverse_detach_tag{}, std::forward<T>(value),
std::move(resumable));
}
/// Calls the visitor with no arguments to signalize that the
/// asynchronous traversal was finished.
void async_complete() {
#ifndef NDEBUG
{
bool expected = false;
assert(finished_.compare_exchange_strong(expected, true));
}
#endif // NDEBUG
visitor()(async_traverse_complete_tag{}, std::move(this->head()));
}
};
template <typename Target, std::size_t Begin, std::size_t End>
struct static_async_range {
Target* target_;
constexpr decltype(auto) operator*() const noexcept {
return std::get<Begin>(*target_);
}
template <std::size_t Position>
constexpr auto relocate(std::integral_constant<std::size_t, Position>) const
noexcept {
return static_async_range<Target, Position, End>{target_};
}
constexpr auto next() const noexcept {
return static_async_range<Target, Begin + 1, End>{target_};
}
constexpr bool is_finished() const noexcept {
return false;
}
};
/// Specialization for the end marker which doesn't provide
/// a particular element dereference
template <typename Target, std::size_t Begin>
struct static_async_range<Target, Begin, Begin> {
explicit static_async_range(Target*) {
}
constexpr bool is_finished() const noexcept {
return true;
}
};
/// Returns a static range for the given type
template <typename T>
auto make_static_range(T&& element) {
using range_t = static_async_range<std::decay_t<T>, 0U,
std::tuple_size<std::decay_t<T>>::value>;
return range_t{std::addressof(element)};
}
template <typename Begin, typename Sentinel>
struct dynamic_async_range {
Begin begin_;
Sentinel sentinel_;
dynamic_async_range& operator++() noexcept {
++begin_;
return *this;
}
auto operator*() const noexcept -> decltype(*std::declval<Begin const&>()) {
return *begin_;
}
dynamic_async_range next() const {
dynamic_async_range other = *this;
++other;
return other;
}
bool is_finished() const {
return begin_ == sentinel_;
}
};
template <typename T>
using dynamic_async_range_of_t =
dynamic_async_range<std::decay_t<decltype(std::begin(std::declval<T>()))>,
std::decay_t<decltype(std::end(std::declval<T>()))>>;
/// Returns a dynamic range for the given type
template <typename T>
auto make_dynamic_async_range(T&& element) {
using range_t = dynamic_async_range_of_t<T>;
return range_t{std::begin(element), std::end(element)};
}
/// Represents a particular point in a asynchronous traversal hierarchy
template <typename Frame, typename... Hierarchy>
class async_traversal_point {
Frame frame_;
std::tuple<Hierarchy...> hierarchy_;
bool& detached_;
public:
explicit async_traversal_point(Frame frame,
std::tuple<Hierarchy...> hierarchy,
bool& detached)
: frame_(std::move(frame)), hierarchy_(std::move(hierarchy)),
detached_(detached) {
}
// Abort the current control flow
void detach() noexcept {
assert(!detached_);
detached_ = true;
}
/// Returns true when we should abort the current control flow
bool is_detached() const noexcept {
return detached_;
}
/// Creates a new traversal point which
template <typename Parent>
auto push(Parent&& parent)
-> async_traversal_point<Frame, std::decay_t<Parent>, Hierarchy...> {
// Create a new hierarchy which contains the
// the parent (the last traversed element).
auto hierarchy = std::tuple_cat(
std::make_tuple(std::forward<Parent>(parent)), hierarchy_);
return async_traversal_point<Frame, std::decay_t<Parent>, Hierarchy...>(
frame_, std::move(hierarchy), detached_);
}
/// Forks the current traversal point and continues the child
/// of the given parent.
template <typename Child, typename Parent>
void fork(Child&& child, Parent&& parent) {
// Push the parent on top of the hierarchy
auto point = push(std::forward<Parent>(parent));
// Continue the traversal with the current element
point.async_traverse(std::forward<Child>(child));
}
/// Async traverse a single element, and do nothing.
/// This function is matched last.
template <typename Matcher, typename Current>
void async_traverse_one_impl(Matcher, Current&& /*current*/) {
// Do nothing if the visitor doesn't accept the type
}
/// Async traverse a single element which isn't a container or
/// tuple like type. This function is SFINAEd out if the element
/// isn't accepted by the visitor.
template <typename Current>
auto async_traverse_one_impl(container_category_tag<false, false>,
Current&& current)
/// SFINAE this out if the visitor doesn't accept
/// the given element
-> traits::void_t<decltype(std::declval<Frame>()->traverse(*current))> {
if (!frame_->traverse(*current)) {
// Store the current call hierarchy into a tuple for
// later re-entrance.
auto hierarchy =
std::tuple_cat(std::make_tuple(current.next()), hierarchy_);
// First detach the current execution context
detach();
// If the traversal method returns false, we detach the
// current execution context and call the visitor with the
// element and a continue callable object again.
frame_->async_continue(*current, std::move(hierarchy));
}
}
/// Async traverse a single element which is a container or
/// tuple like type.
template <bool IsTupleLike, typename Current>
void async_traverse_one_impl(container_category_tag<true, IsTupleLike>,
Current&& current) {
auto range = make_dynamic_async_range(*current);
fork(std::move(range), std::forward<Current>(current));
}
/// Async traverse a single element which is a tuple like type only.
template <typename Current>
void async_traverse_one_impl(container_category_tag<false, true>,
Current&& current) {
auto range = make_static_range(*current);
fork(std::move(range), std::forward<Current>(current));
}
/// Async traverse the current iterator
template <typename Current>
void async_traverse_one(Current&& current) {
using ElementType = std::decay_t<decltype(*current)>;
return async_traverse_one_impl(container_category_of_t<ElementType>{},
std::forward<Current>(current));
}
/// Async traverse the current iterator but don't traverse
/// if the control flow was detached.
template <typename Current>
void async_traverse_one_checked(Current&& current) {
if (!is_detached()) {
async_traverse_one(std::forward<Current>(current));
}
}
template <std::size_t... Sequence, typename Current>
void async_traverse_static_async_range(
std::integer_sequence<std::size_t, Sequence...>, Current&& current) {
int dummy[] = {0, (async_traverse_one_checked(current.relocate(
std::integral_constant<std::size_t, Sequence>{})),
0)...};
(void)dummy;
(void)current;
}
/// Traverse a static range
template <typename Target, std::size_t Begin, std::size_t End>
void async_traverse(static_async_range<Target, Begin, End> current) {
async_traverse_static_async_range(
explicit_range_sequence_of_t<Begin, End>{}, current);
}
/// Traverse a dynamic range
template <typename Begin, typename Sentinel>
void async_traverse(dynamic_async_range<Begin, Sentinel> range) {
if (!is_detached()) {
for (/**/; !range.is_finished(); ++range) {
async_traverse_one(range);
if (is_detached()) // test before increment
break;
}
}
}
};
/// Deduces to the traversal point class of the
/// given frame and hierarchy
template <typename Frame, typename... Hierarchy>
using traversal_point_of_t =
async_traversal_point<std::decay_t<Frame>, std::decay_t<Hierarchy>...>;
/// A callable object which is capable of resuming an asynchronous
/// pack traversal.
struct resume_state_callable {
/// Reenter an asynchronous iterator pack and continue
/// its traversal.
template <typename Frame, typename Current, typename... Hierarchy>
void operator()(Frame&& frame, Current&& current,
Hierarchy&&... hierarchy) const {
bool detached = false;
next(detached, std::forward<Frame>(frame), std::forward<Current>(current),
std::forward<Hierarchy>(hierarchy)...);
}
template <typename Frame, typename Current>
void next(bool& detached, Frame&& frame, Current&& current) const {
// Only process the next element if the current iterator
// hasn't reached its end.
if (!current.is_finished()) {
traversal_point_of_t<Frame> point(frame, std::make_tuple(), detached);
point.async_traverse(std::forward<Current>(current));
// Don't continue the frame when the execution was detached
if (detached) {
return;
}
}
frame->async_complete();
}
/// Reenter an asynchronous iterator pack and continue
/// its traversal.
template <typename Frame, typename Current, typename Parent,
typename... Hierarchy>
void next(bool& detached, Frame&& frame, Current&& current, Parent&& parent,
Hierarchy&&... hierarchy) const {
// Only process the element if the current iterator
// hasn't reached its end.
if (!current.is_finished()) {
// Don't forward the arguments here, since we still need
// the objects in a valid state later.
traversal_point_of_t<Frame, Parent, Hierarchy...> point(
frame, std::make_tuple(parent, hierarchy...), detached);
point.async_traverse(std::forward<Current>(current));
// Don't continue the frame when the execution was detached
if (detached) {
return;
}
}
// Pop the top element from the hierarchy, and shift the
// parent element one to the right
next(detached, std::forward<Frame>(frame),
std::forward<Parent>(parent).next(),
std::forward<Hierarchy>(hierarchy)...);
}
};
template <typename Frame, typename State>
void resume_traversal_callable<Frame, State>::operator()() {
auto hierarchy = std::tuple_cat(std::make_tuple(frame_), state_);
traits::unpack(resume_state_callable{}, std::move(hierarchy));
}
/// Gives access to types related to the traversal frame
template <typename Visitor, typename... Args>
struct async_traversal_types {
/// Deduces to the async traversal frame type of the given
/// traversal arguments and mapper
using frame_t =
async_traversal_frame<std::decay_t<Visitor>, std::decay_t<Args>...>;
/// The type of the demoted visitor type
using visitor_t = Visitor;
};
template <typename Visitor, typename VisitorArg, typename... Args>
struct async_traversal_types<async_traverse_in_place_tag<Visitor>, VisitorArg,
Args...>
: async_traversal_types<Visitor, Args...> {};
/// Traverses the given pack with the given mapper
template <typename Visitor, typename... Args>
auto apply_pack_transform_async(Visitor&& visitor, Args&&... args) {
// Provide the frame and visitor type
using types = async_traversal_types<Visitor, Args...>;
using frame_t = typename types::frame_t;
using visitor_t = typename types::visitor_t;
// Check whether the visitor inherits enable_shared_from_this
static_assert(std::is_base_of<std::enable_shared_from_this<visitor_t>,
visitor_t>::value,
"The visitor must inherit std::enable_shared_from_this!");
// Check whether the visitor is virtual destructible
static_assert(std::has_virtual_destructor<visitor_t>::value,
"The visitor must have a virtual destructor!");
// Create the frame on the heap which stores the arguments
// to traverse asynchronous. It persists until the
// traversal frame isn't referenced anymore.
auto frame = std::make_shared<frame_t>(std::forward<Visitor>(visitor),
std::forward<Args>(args)...);
// Create a static range for the top level tuple
auto range = std::make_tuple(make_static_range(frame->head()));
// Create a resumer to start the asynchronous traversal
auto resumer = make_resume_traversal_callable(frame, std::move(range));
// Start the asynchronous traversal
resumer();
// Cast the shared_ptr down to the given visitor type
// for implementation invisibility
return std::static_pointer_cast<visitor_t>(std::move(frame));
}
} // namespace traversal
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TRAVERSE_ASYNC_HPP_INCLUDED

View File

@ -0,0 +1,868 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_TRAVERSE_HPP_INCLUDED
#define CONTINUABLE_DETAIL_TRAVERSE_HPP_INCLUDED
#include <cstddef>
#include <iterator>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <continuable/detail/traversal/container-category.hpp>
#include <continuable/detail/utility/traits.hpp>
namespace cti {
namespace detail {
namespace traversal {
/// Exposes useful facilities for dealing with 1:n mappings
namespace spreading {
/// \cond false
/// A struct to mark a tuple to be unpacked into the parent context
template <typename... T>
class spread_box {
std::tuple<T...> boxed_;
public:
explicit constexpr spread_box(std::tuple<T...> boxed)
: boxed_(std::move(boxed)) {
}
std::tuple<T...> unbox() {
return std::move(boxed_);
}
};
template <>
class spread_box<> {
public:
explicit constexpr spread_box() noexcept {
}
explicit constexpr spread_box(std::tuple<>) noexcept {
}
constexpr std::tuple<> unbox() const noexcept {
return std::tuple<>{};
}
};
/// Returns an empty spread box which represents an empty
/// mapped object.
constexpr spread_box<> empty_spread() noexcept {
return spread_box<>{};
}
/// Deduces to a true_type if the given type is a spread marker
template <typename T>
struct is_spread : std::false_type {};
template <typename... T>
struct is_spread<spread_box<T...>> : std::true_type {};
/// Deduces to a true_type if the given type is an empty
/// spread marker
template <typename T>
struct is_empty_spread : std::false_type {};
template <>
struct is_empty_spread<spread_box<>> : std::true_type {};
/// Converts types to the type and spread_box objects to its
/// underlying tuple.
template <typename T>
constexpr T unpack(T&& type) {
return std::forward<T>(type);
}
template <typename... T>
constexpr auto unpack(spread_box<T...> type) -> decltype(type.unbox()) {
return type.unbox();
}
/// Deduces to the type unpack is returning when called with the
/// the given type T.
template <typename T>
using unpacked_of_t = decltype(unpack(std::declval<T>()));
/// Converts types to the type and spread_box objects to its
/// underlying tuple. If the type is mapped to zero elements,
/// the return type will be void.
template <typename T>
constexpr auto unpack_or_void(T&& type)
-> decltype(unpack(std::forward<T>(type))) {
return unpack(std::forward<T>(type));
}
inline void unpack_or_void(spread_box<>) noexcept {
}
/// Converts types to the a tuple carrying the single type and
/// spread_box objects to its underlying tuple.
template <typename T>
constexpr std::tuple<T> undecorate(T&& type) {
return std::tuple<T>{std::forward<T>(type)};
}
template <typename... T>
constexpr auto undecorate(spread_box<T...> type) -> decltype(type.unbox()) {
return type.unbox();
}
/// A callable object which maps its content back to a
/// tuple like type.
template <typename EmptyType, template <typename...> class Type>
struct tupelizer_base {
// We overload with one argument here so Clang and GCC don't
// have any issues with overloading against zero arguments.
template <typename First, typename... T>
constexpr Type<First, T...> operator()(First&& first, T&&... args) const {
return Type<First, T...>{std::forward<First>(first),
std::forward<T>(args)...};
}
// Specifically return the empty object which can be different
// from a tuple.
constexpr EmptyType operator()() const noexcept(noexcept(EmptyType{})) {
return EmptyType{};
}
};
/// A callable object which maps its content back to a tuple.
template <template <typename...> class Type = std::tuple>
using tupelizer_of_t = tupelizer_base<std::tuple<>, Type>;
/// A callable object which maps its content back to a tuple like
/// type if it wasn't empty. For empty types arguments an empty
/// spread box is returned instead. This is useful to propagate
/// empty mappings back to the caller.
template <template <typename...> class Type = std::tuple>
using flat_tupelizer_of_t = tupelizer_base<spread_box<>, Type>;
/// A callable object which maps its content back to an
/// array like type.
/// This transform can only be used for (flat) mappings which
/// return an empty mapping back to the caller.
template <template <typename, std::size_t> class Type>
struct flat_arraylizer {
/// Deduces to the array type when the array is instantiated
/// with the given arguments.
template <typename First, typename... Rest>
using array_type_of_t = Type<std::decay_t<First>, 1 + sizeof...(Rest)>;
// We overload with one argument here so Clang and GCC don't
// have any issues with overloading against zero arguments.
template <typename First, typename... T>
constexpr auto operator()(First&& first, T&&... args) const
-> array_type_of_t<First, T...> {
return array_type_of_t<First, T...>{
{std::forward<First>(first), std::forward<T>(args)...}};
}
constexpr auto operator()() const noexcept -> decltype(empty_spread()) {
return empty_spread();
}
};
/// Use the recursive instantiation for a variadic pack which
/// may contain spread types
template <typename C, typename... T>
constexpr auto apply_spread_impl(std::true_type, C&& callable, T&&... args)
-> decltype(
traits::unpack(std::forward<C>(callable),
std::tuple_cat(undecorate(std::forward<T>(args))...))) {
return traits::unpack(std::forward<C>(callable),
std::tuple_cat(undecorate(std::forward<T>(args))...));
}
/// Use the linear instantiation for variadic packs which don't
/// contain spread types.
template <typename C, typename... T>
constexpr auto apply_spread_impl(std::false_type, C&& callable, T&&... args)
-> decltype(std::forward<C>(callable)(std::forward<T>(args)...)) {
return std::forward<C>(callable)(std::forward<T>(args)...);
}
/// Deduces to a true_type if any of the given types marks
/// the underlying type to be spread into the current context.
template <typename... T>
using is_any_spread_t = traits::disjunction<is_spread<T>...>;
template <typename C, typename... T>
constexpr auto map_spread(C&& callable, T&&... args)
-> decltype(apply_spread_impl(is_any_spread_t<T...>{},
std::forward<C>(callable),
std::forward<T>(args)...)) {
// Check whether any of the args is a detail::flatted_tuple_t,
// if not, use the linear called version for better
// compilation speed.
return apply_spread_impl(is_any_spread_t<T...>{}, std::forward<C>(callable),
std::forward<T>(args)...);
}
/// Converts the given variadic arguments into a tuple in a way
/// that spread return values are inserted into the current pack.
template <typename... T>
constexpr auto tupelize(T&&... args)
-> decltype(map_spread(tupelizer_of_t<>{}, std::forward<T>(args)...)) {
return map_spread(tupelizer_of_t<>{}, std::forward<T>(args)...);
}
/// Converts the given variadic arguments into a tuple in a way
/// that spread return values are inserted into the current pack.
/// If the arguments were mapped to zero arguments, the empty
/// mapping is propagated backwards to the caller.
template <template <typename...> class Type, typename... T>
constexpr auto flat_tupelize_to(T&&... args)
-> decltype(map_spread(flat_tupelizer_of_t<Type>{},
std::forward<T>(args)...)) {
return map_spread(flat_tupelizer_of_t<Type>{}, std::forward<T>(args)...);
}
/// Converts the given variadic arguments into an array in a way
/// that spread return values are inserted into the current pack.
/// Through this the size of the array like type might change.
/// If the arguments were mapped to zero arguments, the empty
/// mapping is propagated backwards to the caller.
template <template <typename, std::size_t> class Type, typename... T>
constexpr auto flat_arraylize_to(T&&... args)
-> decltype(map_spread(flat_arraylizer<Type>{}, std::forward<T>(args)...)) {
return map_spread(flat_arraylizer<Type>{}, std::forward<T>(args)...);
}
/// Converts an empty tuple to void
template <typename First, typename... Rest>
constexpr std::tuple<First, Rest...>
voidify_empty_tuple(std::tuple<First, Rest...> val) {
return std::move(val);
}
inline void voidify_empty_tuple(std::tuple<>) noexcept {
}
/// Converts the given variadic arguments into a tuple in a way
/// that spread return values are inserted into the current pack.
///
/// If the returned tuple is empty, voidis returned instead.
template <typename... T>
constexpr decltype(auto) tupelize_or_void(T&&... args) {
return voidify_empty_tuple(tupelize(std::forward<T>(args)...));
}
/// \endcond
} // namespace spreading
/// Just traverses the pack with the given callable object,
/// no result is returned or preserved.
struct strategy_traverse_tag {};
/// Remaps the variadic pack with the return values from the mapper.
struct strategy_remap_tag {};
/// Deduces to a true type if the type leads to at least one effective
/// call to the mapper.
template <typename Mapper, typename T>
using is_effective_t = traits::is_invocable<typename Mapper::traversor_type, T>;
// TODO find out whether the linear compile-time instantiation is faster:
// template <typename Mapper, typename... T>
// struct is_effective_any_of_t
// : traits::disjunction<is_effective_t<Mapper, T>...> {};
// template <typename Mapper>
// struct is_effective_any_of_t<Mapper> : std::false_type {};
/// Deduces to a true type if any type leads to at least one effective
/// call to the mapper.
template <typename Mapper, typename... T>
struct is_effective_any_of_t;
template <typename Mapper, typename First, typename... Rest>
struct is_effective_any_of_t<Mapper, First, Rest...>
: std::conditional<is_effective_t<Mapper, First>::value, std::true_type,
is_effective_any_of_t<Mapper, Rest...>>::type {};
template <typename Mapper>
struct is_effective_any_of_t<Mapper> : std::false_type {};
/// Provides utilities for remapping the whole content of a
/// container like type to the same container holding different types.
namespace container_remapping {
/// Deduces to a true type if the given parameter T
/// has a push_back method that accepts a type of E.
template <typename T, typename E, typename = void>
struct has_push_back : std::false_type {};
template <typename T, typename E>
struct has_push_back<
T, E,
traits::void_t<decltype(std::declval<T>().push_back(std::declval<E>()))>>
: std::true_type {};
/// Specialization for a container with a single type T
template <typename NewType, template <class> class Base, typename OldType>
auto rebind_container(Base<OldType> const & /*container*/) -> Base<NewType> {
return Base<NewType>();
}
/// Specialization for a container with a single type T and
/// a particular allocator,
/// which is preserved across the remap.
/// -> We remap the allocator through std::allocator_traits.
template <
typename NewType, template <class, class> class Base, typename OldType,
typename OldAllocator,
// Check whether the second argument of the container was
// the used allocator.
typename std::enable_if<std::uses_allocator<
Base<OldType, OldAllocator>, OldAllocator>::value>::type* = nullptr,
typename NewAllocator = typename std::allocator_traits<
OldAllocator>::template rebind_alloc<NewType>>
auto rebind_container(Base<OldType, OldAllocator> const& container)
-> Base<NewType, NewAllocator> {
// Create a new version of the allocator, that is capable of
// allocating the mapped type.
return Base<NewType, NewAllocator>(NewAllocator(container.get_allocator()));
}
/// Returns the default iterators of the container in case
/// the container was passed as an l-value reference.
/// Otherwise move iterators of the container are returned.
template <typename C, typename = void>
class container_accessor {
static_assert(std::is_lvalue_reference<C>::value,
"This should be a lvalue reference here!");
C container_;
public:
container_accessor(C container) : container_(container) {
}
auto begin() -> decltype(container_.begin()) {
return container_.begin();
}
auto end() -> decltype(container_.end()) {
return container_.end();
}
};
template <typename C>
class container_accessor<
C, typename std::enable_if<std::is_rvalue_reference<C&&>::value>::type> {
C&& container_;
public:
container_accessor(C&& container) : container_(std::move(container)) {
}
auto begin() -> decltype(std::make_move_iterator(container_.begin())) {
return std::make_move_iterator(container_.begin());
}
auto end() -> decltype(std::make_move_iterator(container_.end())) {
return std::make_move_iterator(container_.end());
}
};
template <typename T>
container_accessor<T> container_accessor_of(T&& container) {
// Don't use any decay here
return container_accessor<T>(std::forward<T>(container));
}
/// Deduces to the type the homogeneous container is containing
///
/// This alias deduces to the same type on which
/// container_accessor<T> is iterating.
///
/// The basic idea is that we deduce to the type the homogeneous
/// container T is carrying as reference while preserving the
/// original reference type of the container:
/// - If the container was passed as l-value its containing
/// values are referenced through l-values.
/// - If the container was passed as r-value its containing
/// values are referenced through r-values.
template <typename Container>
using element_of_t = typename std::conditional<
std::is_rvalue_reference<Container&&>::value,
decltype(std::move(*(std::declval<Container>().begin()))),
decltype(*(std::declval<Container>().begin()))>::type;
/// Removes all qualifier and references from the given type
/// if the type is a l-value or r-value reference.
template <typename T>
using dereferenced_of_t = typename std::conditional<std::is_reference<T>::value,
std::decay_t<T>, T>::type;
/// Returns the type which is resulting if the mapping is applied to
/// an element in the container.
///
/// Since standard containers don't allow to be instantiated with
/// references we try to construct the container from a copied
/// version.
template <typename Container, typename Mapping>
using mapped_type_from_t = dereferenced_of_t<spreading::unpacked_of_t<decltype(
std::declval<Mapping>()(std::declval<element_of_t<Container>>()))>>;
/// Deduces to a true_type if the mapping maps to zero elements.
template <typename T, typename M>
using is_empty_mapped = spreading::is_empty_spread<
std::decay_t<decltype(std::declval<M>()(std::declval<element_of_t<T>>()))>>;
/// We are allowed to reuse the container if we map to the same
/// type we are accepting and when we have
/// the full ownership of the container.
template <typename T, typename M>
using can_reuse = std::integral_constant<
bool, std::is_same<element_of_t<T>, mapped_type_from_t<T, M>>::value &&
std::is_rvalue_reference<T&&>::value>;
/// Categorizes a mapping of a homogeneous container
///
/// \tparam IsEmptyMapped Identifies whether the mapping maps to
/// to zero arguments.
/// \tparam CanReuse Identifies whether the container can be
/// re-used through the mapping.
template <bool IsEmptyMapped, bool CanReuse>
struct container_mapping_tag {};
/// Categorizes the given container through a container_mapping_tag
template <typename T, typename M>
using container_mapping_tag_of_t =
container_mapping_tag<is_empty_mapped<T, M>::value, can_reuse<T, M>::value>;
/// Deduces to a true type if the given parameter T supports a `reserve` method
template <typename From, typename To, typename = void>
struct is_reservable_from : std::false_type {};
template <typename From, typename To>
struct is_reservable_from<From, To,
traits::void_t<decltype(std::declval<To>().reserve(
std::declval<From>().size()))>> : std::true_type {
};
template <typename Dest, typename Source>
void reserve_if(std::true_type, Dest&& dest, Source&& source) {
// Reserve the mapped size
dest.reserve(source.size());
}
template <typename Dest, typename Source>
void reserve_if(std::false_type, Dest&&, Source&&) noexcept {
// We do nothing here, since the container doesn't support reserving
}
/// We create a new container, which may hold the resulting type
template <typename M, typename T>
auto remap_container(container_mapping_tag<false, false>, M&& mapper,
T&& container)
-> decltype(rebind_container<mapped_type_from_t<T, M>>(container)) {
static_assert(has_push_back<std::decay_t<T>, element_of_t<T>>::value,
"Can only remap containers that provide a push_back "
"method!");
// Create the new container, which is capable of holding
// the remappped types.
auto remapped = rebind_container<mapped_type_from_t<T, M>>(container);
// We try to reserve the original size from the source
// container inside the destination container.
reserve_if(
is_reservable_from<std::decay_t<T>, std::decay_t<decltype(remapped)>>{},
remapped, container);
// Perform the actual value remapping from the source to
// the destination.
// We could have used std::transform for this, however,
// I didn't want to pull a whole header for it in.
for (auto&& val : container_accessor_of(std::forward<T>(container))) {
remapped.push_back(spreading::unpack(
std::forward<M>(mapper)(std::forward<decltype(val)>(val))));
}
return remapped; // RVO
}
/// The remapper optimized for the case that we map to the same
/// type we accepted such as int -> int.
template <typename M, typename T>
auto remap_container(container_mapping_tag<false, true>, M&& mapper,
T&& container) -> std::decay_t<T> {
for (auto&& val : container_accessor_of(std::forward<T>(container))) {
val = spreading::unpack(
std::forward<M>(mapper)(std::forward<decltype(val)>(val)));
}
return std::forward<T>(container);
}
/// Remap the container to zero arguments
template <typename M, typename T>
auto remap_container(container_mapping_tag<true, false>, M&& mapper,
T&& container) -> decltype(spreading::empty_spread()) {
for (auto&& val : container_accessor_of(std::forward<T>(container))) {
// Don't save the empty mapping for each invocation
// of the mapper.
std::forward<M>(mapper)(std::forward<decltype(val)>(val));
}
// Return one instance of an empty mapping for the container
return spreading::empty_spread();
}
/// \cond false
/// Remaps the content of the given container with type T,
/// to a container of the same type which may contain
/// different types.
template <typename T, typename M>
auto remap(
strategy_remap_tag, T&& container, M&& mapper,
typename std::enable_if<is_effective_t<M, element_of_t<T>>::value>::type* =
nullptr) -> decltype(remap_container(container_mapping_tag_of_t<T, M>{},
std::forward<M>(mapper),
std::forward<T>(container))) {
return remap_container(container_mapping_tag_of_t<T, M>{},
std::forward<M>(mapper), std::forward<T>(container));
}
/// \endcond
/// Just call the visitor with the content of the container
template <typename T, typename M>
void remap(
strategy_traverse_tag, T&& container, M&& mapper,
typename std::enable_if<is_effective_t<M, element_of_t<T>>::value>::type* =
nullptr) {
for (auto&& element : container_accessor_of(std::forward<T>(container))) {
std::forward<M>(mapper)(std::forward<decltype(element)>(element));
}
}
} // end namespace container_remapping
/// Provides utilities for remapping the whole content of a
/// tuple like type to the same type holding different types.
namespace tuple_like_remapping {
template <typename Strategy, typename Mapper, typename T,
typename Enable = void>
struct tuple_like_remapper;
/// Specialization for std::tuple like types which contain
/// an arbitrary amount of heterogenous arguments.
template <typename M, template <typename...> class Base, typename... OldArgs>
struct tuple_like_remapper<strategy_remap_tag, M, Base<OldArgs...>,
// Support for skipping completely untouched types
typename std::enable_if<is_effective_any_of_t<
M, OldArgs...>::value>::type> {
M mapper_;
template <typename... Args>
auto operator()(Args&&... args) -> decltype(spreading::flat_tupelize_to<Base>(
std::declval<M>()(std::forward<Args>(args))...)) {
return spreading::flat_tupelize_to<Base>(
mapper_(std::forward<Args>(args))...);
}
};
template <typename M, template <typename...> class Base, typename... OldArgs>
struct tuple_like_remapper<strategy_traverse_tag, M, Base<OldArgs...>,
// Support for skipping completely untouched types
typename std::enable_if<is_effective_any_of_t<
M, OldArgs...>::value>::type> {
M mapper_;
template <typename... Args>
auto operator()(Args&&... args) -> traits::void_t<
decltype(std::declval<M>()(std::declval<OldArgs>()))...> {
int dummy[] = {0, ((void)mapper_(std::forward<Args>(args)), 0)...};
(void)dummy;
}
};
/// Specialization for std::array like types, which contains a
/// compile-time known amount of homogeneous types.
template <typename M, template <typename, std::size_t> class Base,
typename OldArg, std::size_t Size>
struct tuple_like_remapper<
strategy_remap_tag, M, Base<OldArg, Size>,
// Support for skipping completely untouched types
typename std::enable_if<is_effective_t<M, OldArg>::value>::type> {
M mapper_;
template <typename... Args>
auto operator()(Args&&... args)
-> decltype(spreading::flat_arraylize_to<Base>(
mapper_(std::forward<Args>(args))...)) {
return spreading::flat_arraylize_to<Base>(
mapper_(std::forward<Args>(args))...);
}
};
template <typename M, template <typename, std::size_t> class Base,
typename OldArg, std::size_t Size>
struct tuple_like_remapper<
strategy_traverse_tag, M, Base<OldArg, Size>,
// Support for skipping completely untouched types
typename std::enable_if<is_effective_t<M, OldArg>::value>::type> {
M mapper_;
template <typename... Args>
auto operator()(Args&&... args)
-> decltype((std::declval<M>()(std::declval<OldArg>()))()) {
int dummy[] = {0, ((void)mapper_(std::forward<Args>(args)), 0)...};
(void)dummy;
}
};
/// Remaps the content of the given tuple like type T,
/// to a container of the same type which may contain
/// different types.
template <typename Strategy, typename T, typename M>
auto remap(Strategy, T&& container, M&& mapper) -> decltype(traits::unpack(
std::declval<
tuple_like_remapper<Strategy, std::decay_t<M>, std::decay_t<T>>>(),
std::forward<T>(container))) {
return traits::unpack(
tuple_like_remapper<Strategy, std::decay_t<M>, std::decay_t<T>>{
std::forward<M>(mapper)},
std::forward<T>(container));
}
} // end namespace tuple_like_remapping
/// Base class for making strategy dependent behaviour available
/// to the mapping_helper class.
template <typename Strategy>
struct mapping_strategy_base {
template <typename T>
auto may_void(T&& element) const -> std::decay_t<T> {
return std::forward<T>(element);
}
};
template <>
struct mapping_strategy_base<strategy_traverse_tag> {
template <typename T>
void may_void(T&& /*element*/) const noexcept {
}
};
/// A helper class which applies the mapping or
/// routes the element through
template <typename Strategy, typename M>
class mapping_helper : protected mapping_strategy_base<Strategy> {
M mapper_;
class traversal_callable_base {
mapping_helper* helper_;
public:
explicit traversal_callable_base(mapping_helper* helper) : helper_(helper) {
}
protected:
mapping_helper* get_helper() noexcept {
return helper_;
}
};
/// A callable object which forwards its invocations
/// to mapping_helper::traverse.
class traversor : public traversal_callable_base {
public:
using traversal_callable_base::traversal_callable_base;
/// SFINAE helper
template <typename T>
auto operator()(T&& element)
-> decltype(std::declval<traversor>().get_helper()->traverse(
Strategy{}, std::forward<T>(element)));
/// An alias to this type
using traversor_type = traversor;
};
/// A callable object which forwards its invocations
/// to mapping_helper::try_traverse.
///
/// This callable object will accept any input,
/// since elements passed to it are passed through,
/// if the provided mapper doesn't accept it.
class try_traversor : public traversal_callable_base {
public:
using traversal_callable_base::traversal_callable_base;
template <typename T>
auto operator()(T&& element)
-> decltype(std::declval<try_traversor>().get_helper()->try_traverse(
Strategy{}, std::forward<T>(element))) {
return this->get_helper()->try_traverse(Strategy{},
std::forward<T>(element));
}
/// An alias to the traversor type
using traversor_type = traversor;
};
/// Invokes the real mapper with the given element
template <typename T>
auto invoke_mapper(T&& element) -> decltype(
std::declval<mapping_helper>().mapper_(std::forward<T>(element))) {
return mapper_(std::forward<T>(element));
}
/// SFINAE helper for plain elements not satisfying the tuple like
/// or container requirements.
///
/// We use the proxy function invoke_mapper here,
/// because some compilers (MSVC) tend to instantiate the invocation
/// before matching the tag, which leads to build failures.
template <typename T>
auto match(container_category_tag<false, false>, T&& element) -> decltype(
std::declval<mapping_helper>().invoke_mapper(std::forward<T>(element)));
/// SFINAE helper for elements satisfying the container
/// requirements, which are not tuple like.
template <typename T>
auto match(container_category_tag<true, false>, T&& container)
-> decltype(container_remapping::remap(Strategy{},
std::forward<T>(container),
std::declval<traversor>()));
/// SFINAE helper for elements which are tuple like and
/// that also may satisfy the container requirements
template <bool IsContainer, typename T>
auto match(container_category_tag<IsContainer, true>, T&& tuple_like)
-> decltype(tuple_like_remapping::remap(Strategy{},
std::forward<T>(tuple_like),
std::declval<traversor>()));
/// This method implements the functionality for routing
/// elements through, that aren't accepted by the mapper.
/// Since the real matcher methods below are failing through SFINAE,
/// the compiler will try to specialize this function last,
/// since it's the least concrete one.
/// This works recursively, so we only call the mapper
/// with the minimal needed set of accepted arguments.
template <typename MatcherTag, typename T>
auto try_match(MatcherTag, T&& element) -> decltype(
std::declval<mapping_helper>().may_void(std::forward<T>(element))) {
return this->may_void(std::forward<T>(element));
}
/// Match plain elements not satisfying the tuple like or
/// container requirements.
///
/// We use the proxy function invoke_mapper here,
/// because some compilers (MSVC) tend to instantiate the invocation
/// before matching the tag, which leads to build failures.
template <typename T>
auto try_match(container_category_tag<false, false>, T&& element) -> decltype(
std::declval<mapping_helper>().invoke_mapper(std::forward<T>(element))) {
// T could be any non container or non tuple like type here,
// take int or hpx::future<int> as an example.
return invoke_mapper(std::forward<T>(element));
}
/// Match elements satisfying the container requirements,
/// which are not tuple like.
template <typename T>
auto try_match(container_category_tag<true, false>, T&& container)
-> decltype(container_remapping::remap(Strategy{},
std::forward<T>(container),
std::declval<try_traversor>())) {
return container_remapping::remap(Strategy{}, std::forward<T>(container),
try_traversor{this});
}
/// Match elements which are tuple like and that also may
/// satisfy the container requirements
/// -> We match tuple like types over container like ones
template <bool IsContainer, typename T>
auto try_match(container_category_tag<IsContainer, true>, T&& tuple_like)
-> decltype(tuple_like_remapping::remap(Strategy{},
std::forward<T>(tuple_like),
std::declval<try_traversor>())) {
return tuple_like_remapping::remap(Strategy{}, std::forward<T>(tuple_like),
try_traversor{this});
}
/// Traverses a single element.
///
/// SFINAE helper: Doesn't allow routing through elements,
/// that aren't accepted by the mapper
template <typename T>
auto traverse(Strategy, T&& element)
-> decltype(std::declval<mapping_helper>().match(
std::declval<container_category_of_t<std::decay_t<T>>>(),
std::declval<T>()));
/// \copybrief traverse
template <typename T>
auto try_traverse(Strategy, T&& element)
-> decltype(std::declval<mapping_helper>().try_match(
std::declval<container_category_of_t<std::decay_t<T>>>(),
std::declval<T>())) {
// We use tag dispatching here, to categorize the type T whether
// it satisfies the container or tuple like requirements.
// Then we can choose the underlying implementation accordingly.
return try_match(container_category_of_t<std::decay_t<T>>{},
std::forward<T>(element));
}
public:
explicit mapping_helper(M mapper) : mapper_(std::move(mapper)) {
}
/// \copybrief try_traverse
template <typename T>
decltype(auto) init_traverse(strategy_remap_tag, T&& element) {
return spreading::unpack_or_void(
try_traverse(strategy_remap_tag{}, std::forward<T>(element)));
}
template <typename T>
void init_traverse(strategy_traverse_tag, T&& element) {
try_traverse(strategy_traverse_tag{}, std::forward<T>(element));
}
/// Calls the traversal method for every element in the pack,
/// and returns a tuple containing the remapped content.
template <typename First, typename Second, typename... T>
decltype(auto) init_traverse(strategy_remap_tag strategy, First&& first,
Second&& second, T&&... rest) {
return spreading::tupelize_or_void(
try_traverse(strategy, std::forward<First>(first)),
try_traverse(strategy, std::forward<Second>(second)),
try_traverse(strategy, std::forward<T>(rest))...);
}
/// Calls the traversal method for every element in the pack,
/// without preserving the return values of the mapper.
template <typename First, typename Second, typename... T>
void init_traverse(strategy_traverse_tag strategy, First&& first,
Second&& second, T&&... rest) {
try_traverse(strategy, std::forward<First>(first));
try_traverse(strategy, std::forward<Second>(second));
int dummy[] = {0,
((void)try_traverse(strategy, std::forward<T>(rest)), 0)...};
(void)dummy;
}
};
/// Traverses the given pack with the given mapper and strategy
template <typename Strategy, typename Mapper, typename... T>
decltype(auto) transform(Strategy strategy, Mapper&& mapper, T&&... pack) {
mapping_helper<Strategy, std::decay_t<Mapper>> helper(
std::forward<Mapper>(mapper));
return helper.init_traverse(strategy, std::forward<T>(pack)...);
}
} // namespace traversal
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_TRAVERSE_HPP_INCLUDED

View File

@ -0,0 +1,54 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v4.2.0
Copyright(c) 2015 - 2022 Denis Blank <denis.blank at outlook dot com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**/
#ifndef CONTINUABLE_DETAIL_IDENTITY_HPP_INCLUDED
#define CONTINUABLE_DETAIL_IDENTITY_HPP_INCLUDED
#include <type_traits>
#include <continuable/detail/features.hpp>
namespace cti {
namespace detail {
/// A tagging type for wrapping other types
template <typename... T>
struct identity {};
template <typename>
struct is_identity : std::false_type {};
template <typename... Args>
struct is_identity<identity<Args...>> : std::true_type {};
template <typename T>
using identify = std::conditional_t<is_identity<std::decay_t<T>>::value, T,
identity<std::decay_t<T>>>;
} // namespace detail
} // namespace cti
#endif // CONTINUABLE_DETAIL_IDENTITY_HPP_INCLUDED

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