mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 08:46:44 +08:00
213 lines
8.3 KiB
Plaintext
213 lines
8.3 KiB
Plaintext
/*
|
|
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files(the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions :
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
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(v)
|
|
.then([](std::vector<int> resolved) {
|
|
// ...
|
|
});
|
|
\endcode
|
|
|
|
*/
|
|
}
|