Finish the connection tutorial

This commit is contained in:
Denis Blank 2018-03-11 06:40:11 +01:00
parent 71e219cbe0
commit 60f40415c3
6 changed files with 178 additions and 69 deletions

View File

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

View File

@ -26,18 +26,23 @@ namespace cti {
\section mainpage-overview Overview
<b>Continuable is a C++14 library that provides full support for:</b>
- 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 <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 **connections** between continuables through an **all, any or
sequential** strategy through expressive operator overloads **&&**,
<b>||</b> and <b>>></b>.
- 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:

View File

@ -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
<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
@ -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<std::string>([](auto&& promise) {
promise.set_value("<html>...</html>");
});
}
// `all` of connections:
(http_request("github.com") && http_request("travis-ci.org") && http_request("atom.io"))
.then([](std::string github, std::string travis, std::string atom) {
// The callback is called with the response of github, travis and atom.
});
// `any` of connections:
(http_request("github.com") || http_request("travis-ci.org") || http_request("atom.io"))
.then([](std::string github_or_travis_or_atom) {
// The callback is called with the first response of either github, travis or atom.
});
// `sequence` of connections:
(http_request("github.com") >> http_request("travis-ci.org") >> http_request("atom.io"))
.then([](std::string github, std::string travis, std::string atom) {
// The requests are invoked sequentially
});
// mixed logical connections:
(http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io")))
.then([](std::string github, std::string travis_or_atom) {
// The callback is called with the response of github for sure
// and the second parameter represents the response of travis or atom.
});
// There are helper functions for connecting continuables:
auto all = cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
auto any = cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
auto seq = cti::when_seq(http_request("github.com"), http_request("travis-ci.org"));
\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(v)
.then([](std::vector<int> 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).
*/
}

View File

@ -101,7 +101,7 @@ 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

View File

@ -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 <typename OData, typename OAnnotation>
@ -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 <typename OData, typename OAnnotation>
@ -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<int>(ptr);
/// ```
///
/// \tparam Signature The fake signature of the returned continuable.
///
/// \since 3.0.0

View File

@ -167,6 +167,6 @@
ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION( \
cti::detail::testing::asserting_eq_check(), __VA_ARGS__)
//// \}
/// \}
#endif // CONTINUABLE_TESTING_HPP_INCLUDED