Started the connection tutorial

This commit is contained in:
Denis Blank 2018-03-11 04:32:12 +01:00
parent b031417aa8
commit 71e219cbe0
5 changed files with 431 additions and 4 deletions

View File

@ -135,7 +135,7 @@ following definitions is defined:
In order to prevent this and to allow unhandled errors
define `CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS`.
\section tutorial-chaining-continuables-next Using next for everything
\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
@ -143,7 +143,7 @@ method \ref continuable_base::next provided.
The exception path overload is marked through the \ref dispatch_error_tag :
\code{.cpp}
struct handle_everything {
struct handle_all_paths {
void operator() (std::string result) {
// ...
}
@ -155,7 +155,7 @@ struct handle_everything {
// ...
http_request("github.com")
.next(handle_everything{});
.next(handle_all_paths{});
\endcode
*/

View File

@ -0,0 +1,118 @@
/*
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. But work similar however
Currently there are following strategies available:
\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.
\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 .
\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.
\section tutorial-connecting-continuables-any Using the any connection
\section tutorial-connecting-continuables-nested Nested continuables and plain types
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"));
\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

@ -119,9 +119,15 @@ like when using \ref promise_base::set_value or
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 abstractions such as `std::future` which execute the
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

View File

@ -32,5 +32,6 @@ 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
*/
}

View File

@ -0,0 +1,302 @@
/*
/~` _ _ _|_. _ _ |_ | _
\_,(_)| | | || ||_|(_||_)|(/_
https://github.com/Naios/continuable
v3.0.0
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.
**/
#ifndef CONTINUABLE_CONNECTIONS_HPP_INCLUDED
#define CONTINUABLE_CONNECTIONS_HPP_INCLUDED
#include <initializer_list>
#include <memory>
#include <utility>
#include <vector>
#include <continuable/detail/composition-all.hpp>
#include <continuable/detail/composition-any.hpp>
#include <continuable/detail/composition-seq.hpp>
#include <continuable/detail/composition.hpp>
#include <continuable/detail/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::composition::apply_composition(
detail::composition::composition_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::composition::apply_composition(
detail::composition::composition_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::composition::apply_composition(
detail::composition::composition_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