diff --git a/doc/configuration.dox b/doc/configuration.dox index b57a79b..64e588d 100644 --- a/doc/configuration.dox +++ b/doc/configuration.dox @@ -33,7 +33,7 @@ in order to change the libraries behaviour: | `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_EXPERIMENTAL_COROUTINE` | Enables support for experimental coroutines and `co_await` expressions. | +| `CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE` | Enables support for experimental coroutines and `co_await` expressions. See \ref continuable_base::operator co_await() for details. | */ } diff --git a/doc/index.dox b/doc/index.dox index 9e0ebef..0a8a300 100644 --- a/doc/index.dox +++ b/doc/index.dox @@ -26,18 +26,23 @@ namespace cti { \section mainpage-overview Overview 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. + - 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 less heap allocations than comparable libraries, strictly following the "don't pay for what you don't use" principle. - - support for **connections** between continuables through an **all, any or - sequential** strategy through expressive operator overloads **&&**, - || and >>. - - asynchronous **error handling** through exceptions, error coded or - user defined types. + - 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_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** - and **executors**. + and \link continuable_base::then executors\endlink. - **encapsuled from any runtime**, larger framework or executor making it possible to use continuable even in smaller or esoteric usage scenarios. @@ -47,7 +52,7 @@ Continuable is a header-only library with zero compilation dependencies. The \ref installation is 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. +API. Beside of this, there is a detailed in-source documentation provided. \section mainpage-contact Contributing and Questions @@ -56,10 +61,13 @@ 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. -\note - If you like the library I would be glad if you star it on Github, - because it helps other users to find this library. +\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: diff --git a/doc/tutorial-connecting-continuables.dox b/doc/tutorial-connecting-continuables.dox index cc96d8f..6483957 100644 --- a/doc/tutorial-connecting-continuables.dox +++ b/doc/tutorial-connecting-continuables.dox @@ -34,15 +34,31 @@ count of \ref continuable_base objects in order resolve a returned 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. But work similar however +\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. -Currently there are following strategies available: +\note Connections between continuable_base objects 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). \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 <>` | `` | `` | +| `continuable_base with ` | `Arg` | `Arg` | +| `continuable_base with ` | `Args...` | `std::tuple` | + \subsection tutorial-connecting-continuables-aggregated-all Using the all connection The *all* strategy invokes all connected continuable at once, it tries to resolve @@ -53,6 +69,16 @@ through the *all* strategy by using \ref continuable_base::operator && or 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, @@ -62,57 +88,125 @@ 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: - -Continuables provide the operators **&&** and **||** for logical connection: - -* **&&** invokes the final callback with the compound result of all connected continuables, the continuables were invoked in parallel. -* **||** invokes the final callback once with the first result which becomes available. -* **>\>** invokes the final callback with the compound result of all connected continuables but the continuations were invokes sequentially. - -\endcodeC++ -auto http_request(std::string url) { - return cti::make_continuable([](auto&& promise) { - promise.set_value("..."); - }); -} - -// `all` of connections: -(http_request("github.com") && http_request("travis-ci.org") && http_request("atom.io")) - .then([](std::string github, std::string travis, std::string atom) { - // The callback is called with the response of github, travis and atom. - }); - -// `any` of connections: -(http_request("github.com") || http_request("travis-ci.org") || http_request("atom.io")) - .then([](std::string github_or_travis_or_atom) { - // The callback is called with the first response of either github, travis or atom. - }); - -// `sequence` of connections: -(http_request("github.com") >> http_request("travis-ci.org") >> http_request("atom.io")) - .then([](std::string github, std::string travis, std::string atom) { - // The requests are invoked sequentially - }); - -// mixed logical connections: -(http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io"))) - .then([](std::string github, std::string travis_or_atom) { - // The callback is called with the response of github for sure - // and the second parameter represents the response of travis or atom. - }); - -// There are helper functions for connecting continuables: -auto all = cti::when_all(http_request("github.com"), http_request("travis-ci.org")); -auto any = cti::when_any(http_request("github.com"), http_request("travis-ci.org")); -auto seq = cti::when_seq(http_request("github.com"), http_request("travis-ci.org")); +\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>> outstanding; +// ... + +cti::when_all(std::make_tuple(std::move(outstanding), + http_request("github.com"))) + .then([](std::tuple>, + 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 r34, + std::tuple> 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(v) + .then([](std::vector resolved) { + // ... + }); \endcode -> **Note:** Logical connections are ensured to be **thread-safe** and **wait-free** by library design (when assuming that *std::call_once* is wait-free - which depends on the toolchain). */ } diff --git a/doc/tutorial-creating-continuables.dox b/doc/tutorial-creating-continuables.dox index 2a57765..d79ce96 100644 --- a/doc/tutorial-creating-continuables.dox +++ b/doc/tutorial-creating-continuables.dox @@ -101,7 +101,7 @@ auto resolve_sth() { return cti::make_continuable( [](auto&& promise) { promise.set_value(0, 1, 2.f, '3'); - }); + }); \endcode \warning A \ref promise_base is only usable once and thus invalidated diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index c0c4978..970e286 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -476,8 +476,8 @@ public: /// }); /// ``` /// - /// \note The continuable_base objects are invoked parallel on the - /// current thread, because the `all` strategy tries to resolve + /// \note The continuable_base objects are invoked all at onve, + /// because the `all` strategy tries to resolve /// the continuations as fast as possible. /// Sequential invocation is also supported through the /// continuable_base::operator>> method. @@ -516,9 +516,9 @@ public: /// }); /// ``` /// - /// \note The continuable_base objects are invoked parallel on the - /// current thread, however, the callback is only called once with - /// the first result which becomes available. + /// \note The continuable_base objects are invoked all at once, + /// however, the callback is only called once with + /// the first result or exception which becomes available. /// /// \since 1.0.0 template @@ -545,9 +545,9 @@ public: /// }); /// ``` /// - /// \note The continuable_base objects are invoked sequential on the - /// current thread. Parallel invocation is also supported through the - /// continuable_base::operator&& method. + /// \note The continuable_base objects are invoked sequential one after + /// the previous one was finished. Parallel invocation is also + /// supported through the continuable_base::operator && method. /// /// \since 1.0.0 template @@ -855,6 +855,13 @@ constexpr auto make_ready_continuable(FirstResult&& first_result, /// Returns a continuable with the parameterized result which instantly /// resolves the promise with the given error type. /// +/// See an example below: +/// ```cpp +/// std::logic_error exception("Some issue!"); +/// auto ptr = std::make_exception_ptr(exception); +/// auto ct = cti::make_exceptional_continuable(ptr); +/// ``` +/// /// \tparam Signature The fake signature of the returned continuable. /// /// \since 3.0.0 diff --git a/include/continuable/continuable-testing.hpp b/include/continuable/continuable-testing.hpp index fee5ebb..861fce8 100644 --- a/include/continuable/continuable-testing.hpp +++ b/include/continuable/continuable-testing.hpp @@ -167,6 +167,6 @@ ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION( \ cti::detail::testing::asserting_eq_check(), __VA_ARGS__) -//// \} +/// \} #endif // CONTINUABLE_TESTING_HPP_INCLUDED