mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Finish the connection tutorial
This commit is contained in:
parent
71e219cbe0
commit
60f40415c3
@ -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_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_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_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. |
|
||||||
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,18 +26,23 @@ namespace cti {
|
|||||||
\section mainpage-overview Overview
|
\section mainpage-overview Overview
|
||||||
|
|
||||||
<b>Continuable is a C++14 library that provides full support for:</b>
|
<b>Continuable is a C++14 library that provides full support for:</b>
|
||||||
- lazy async continuation chaining based on **callbacks** (`then`) and
|
- lazy async continuation chaining based on **callbacks**
|
||||||
expression templates, callbacks are wrapped nicely as promises.
|
(\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
|
- **no enforced type-erasure** which means we need <b>less heap
|
||||||
allocations</b> than comparable libraries, strictly following the <b>"don't
|
allocations</b> than comparable libraries, strictly following the <b>"don't
|
||||||
pay for what you don't use"</b> principle.
|
pay for what you don't use"</b> principle.
|
||||||
- support for **connections** between continuables through an **all, any or
|
- support for **all, any and sequential connections** between continuables
|
||||||
sequential** strategy through expressive operator overloads **&&**,
|
through expressive operator overloads \link continuable_base::operator && &&\endlink,
|
||||||
<b>||</b> and <b>>></b>.
|
\link continuable_base::operator || ||\endlink and
|
||||||
- asynchronous **error handling** through exceptions, error coded or
|
\link continuable_base::operator>> >>\endlink as well as free functions
|
||||||
user defined types.
|
\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**
|
- **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
|
- **encapsuled from any runtime**, larger framework or executor making
|
||||||
it possible to use continuable even in smaller or esoteric usage scenarios.
|
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 installation is explained in its own chapter.
|
||||||
|
|
||||||
The \ref tutorial is everything you need in order to get to know the libraries
|
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
|
\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
|
Also I would like to hear your personal opinion about the library design or
|
||||||
your personal experience in using the library to improve it.
|
your personal experience in using the library to improve it.
|
||||||
|
|
||||||
\note
|
\attention If you like the library I would be glad if you star it on Github,
|
||||||
If you like the library I would be glad if you star it on Github,
|
because it helps other users to find this library.
|
||||||
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
|
\section mainpage-license License
|
||||||
|
|
||||||
Continuable is licensed under the MIT license:
|
Continuable is licensed under the MIT license:
|
||||||
|
|||||||
@ -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 each connection strategy \ref continuable_base provides an operator for
|
||||||
for instance \ref continuable_base::operator && and a free function,
|
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
|
\section tutorial-connecting-continuables-aggregated Using aggregated strategies
|
||||||
|
|
||||||
Aggregated strategies will call the result handler with the compound result of
|
Aggregated strategies will call the result handler with the compound result of
|
||||||
all connected \ref continuable_base objects.
|
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
|
\subsection tutorial-connecting-continuables-aggregated-all Using the all connection
|
||||||
|
|
||||||
The *all* strategy invokes all connected continuable at once, it tries to resolve
|
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
|
workin with plain types and deeply nested \ref continuable_base objects as
|
||||||
described in \ref tutorial-connecting-continuables-nested .
|
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
|
\subsection tutorial-connecting-continuables-aggregated-seq Using the sequential connection
|
||||||
|
|
||||||
The *sequential* strategy invokes all connected continuable one after each other,
|
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
|
through the *sequential* strategy by using \ref continuable_base::operator>> or
|
||||||
\ref when_seq.
|
\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
|
\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
|
\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}
|
||||||
Continuables provide the operators **&&** and **||** for logical connection:
|
cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
|
||||||
|
cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
|
||||||
* **&&** invokes the final callback with the compound result of all connected continuables, the continuables were invoked in parallel.
|
cti::when_seq(http_request("github.com"), http_request("travis-ci.org"));
|
||||||
* **||** invokes the final callback once with the first result which becomes available.
|
\endcode
|
||||||
* **>\>** invokes the final callback with the compound result of all connected continuables but the continuations were invokes sequentially.
|
|
||||||
|
Additionally the free functions are capable of working with continuables deeply
|
||||||
\endcodeC++
|
nested inside tuple like objects (`std::tuple`, `std::pair` and `std::array`)
|
||||||
auto http_request(std::string url) {
|
as well as homogeneous containers (`std::vector`, `std::list` etc.).
|
||||||
return cti::make_continuable<std::string>([](auto&& promise) {
|
|
||||||
promise.set_value("<html>...</html>");
|
\code{.cpp}
|
||||||
});
|
std::tuple<std::vector<cti::continuable<int>>> outstanding;
|
||||||
}
|
// ...
|
||||||
|
|
||||||
// `all` of connections:
|
cti::when_all(std::make_tuple(std::move(outstanding),
|
||||||
(http_request("github.com") && http_request("travis-ci.org") && http_request("atom.io"))
|
http_request("github.com")))
|
||||||
.then([](std::string github, std::string travis, std::string atom) {
|
.then([](std::tuple<std::tuple<std::vector<int>>,
|
||||||
// The callback is called with the response of github, travis and atom.
|
std::string> result) {
|
||||||
});
|
// ...
|
||||||
|
});
|
||||||
// `any` of connections:
|
\endcode
|
||||||
(http_request("github.com") || http_request("travis-ci.org") || http_request("atom.io"))
|
|
||||||
.then([](std::string github_or_travis_or_atom) {
|
Values which are given to such a free function are preserved and
|
||||||
// The callback is called with the first response of either github, travis or atom.
|
later passed to the result handler:
|
||||||
});
|
|
||||||
|
\code{.cpp}
|
||||||
// `sequence` of connections:
|
cti::when_seq(0, 1,
|
||||||
(http_request("github.com") >> http_request("travis-ci.org") >> http_request("atom.io"))
|
cti::make_ready_continuable(2, 3),
|
||||||
.then([](std::string github, std::string travis, std::string atom) {
|
4, 5)
|
||||||
// The requests are invoked sequentially
|
.then([](int r0, int r1, int r2,
|
||||||
});
|
int r3, int r4) {
|
||||||
|
// ...
|
||||||
// mixed logical connections:
|
});
|
||||||
(http_request("github.com") && (http_request("travis-ci.org") || http_request("atom.io")))
|
\endcode
|
||||||
.then([](std::string github, std::string travis_or_atom) {
|
|
||||||
// The callback is called with the response of github for sure
|
When combining both capabilities it's even possible do something like this:
|
||||||
// and the second parameter represents the response of travis or atom.
|
|
||||||
});
|
\code{.cpp}
|
||||||
|
cti::when_all(
|
||||||
// There are helper functions for connecting continuables:
|
cti::make_ready_continuable(0, 1),
|
||||||
auto all = cti::when_all(http_request("github.com"), http_request("travis-ci.org"));
|
2, //< See this plain value
|
||||||
auto any = cti::when_any(http_request("github.com"), http_request("travis-ci.org"));
|
cti::populate(cti::make_ready_continuable(3), // Creates a runtime
|
||||||
auto seq = cti::when_seq(http_request("github.com"), http_request("travis-ci.org"));
|
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
|
\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).
|
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,7 +101,7 @@ auto resolve_sth() {
|
|||||||
return cti::make_continuable<int, int, float, char>(
|
return cti::make_continuable<int, int, float, char>(
|
||||||
[](auto&& promise) {
|
[](auto&& promise) {
|
||||||
promise.set_value(0, 1, 2.f, '3');
|
promise.set_value(0, 1, 2.f, '3');
|
||||||
});
|
});
|
||||||
\endcode
|
\endcode
|
||||||
|
|
||||||
\warning A \ref promise_base is only usable once and thus invalidated
|
\warning A \ref promise_base is only usable once and thus invalidated
|
||||||
|
|||||||
@ -476,8 +476,8 @@ public:
|
|||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// \note The continuable_base objects are invoked parallel on the
|
/// \note The continuable_base objects are invoked all at onve,
|
||||||
/// current thread, because the `all` strategy tries to resolve
|
/// because the `all` strategy tries to resolve
|
||||||
/// the continuations as fast as possible.
|
/// the continuations as fast as possible.
|
||||||
/// Sequential invocation is also supported through the
|
/// Sequential invocation is also supported through the
|
||||||
/// continuable_base::operator>> method.
|
/// continuable_base::operator>> method.
|
||||||
@ -516,9 +516,9 @@ public:
|
|||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// \note The continuable_base objects are invoked parallel on the
|
/// \note The continuable_base objects are invoked all at once,
|
||||||
/// current thread, however, the callback is only called once with
|
/// however, the callback is only called once with
|
||||||
/// the first result which becomes available.
|
/// the first result or exception which becomes available.
|
||||||
///
|
///
|
||||||
/// \since 1.0.0
|
/// \since 1.0.0
|
||||||
template <typename OData, typename OAnnotation>
|
template <typename OData, typename OAnnotation>
|
||||||
@ -545,9 +545,9 @@ public:
|
|||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// \note The continuable_base objects are invoked sequential on the
|
/// \note The continuable_base objects are invoked sequential one after
|
||||||
/// current thread. Parallel invocation is also supported through the
|
/// the previous one was finished. Parallel invocation is also
|
||||||
/// continuable_base::operator&& method.
|
/// supported through the continuable_base::operator && method.
|
||||||
///
|
///
|
||||||
/// \since 1.0.0
|
/// \since 1.0.0
|
||||||
template <typename OData, typename OAnnotation>
|
template <typename OData, typename OAnnotation>
|
||||||
@ -855,6 +855,13 @@ constexpr auto make_ready_continuable(FirstResult&& first_result,
|
|||||||
/// Returns a continuable with the parameterized result which instantly
|
/// Returns a continuable with the parameterized result which instantly
|
||||||
/// resolves the promise with the given error type.
|
/// resolves the promise with the given error type.
|
||||||
///
|
///
|
||||||
|
/// See an example below:
|
||||||
|
/// ```cpp
|
||||||
|
/// std::logic_error exception("Some issue!");
|
||||||
|
/// auto ptr = std::make_exception_ptr(exception);
|
||||||
|
/// auto ct = cti::make_exceptional_continuable<int>(ptr);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
/// \tparam Signature The fake signature of the returned continuable.
|
/// \tparam Signature The fake signature of the returned continuable.
|
||||||
///
|
///
|
||||||
/// \since 3.0.0
|
/// \since 3.0.0
|
||||||
|
|||||||
@ -167,6 +167,6 @@
|
|||||||
ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION( \
|
ASSERT_ASYNC_BINARY_EXCEPTION_VALIDATION( \
|
||||||
cti::detail::testing::asserting_eq_check(), __VA_ARGS__)
|
cti::detail::testing::asserting_eq_check(), __VA_ARGS__)
|
||||||
|
|
||||||
//// \}
|
/// \}
|
||||||
|
|
||||||
#endif // CONTINUABLE_TESTING_HPP_INCLUDED
|
#endif // CONTINUABLE_TESTING_HPP_INCLUDED
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user