Add the sequence operator feature

This commit is contained in:
Denis Blank 2017-02-28 23:59:46 +01:00
parent cd265f991b
commit 22d13997df
5 changed files with 111 additions and 11 deletions

View File

@ -265,10 +265,10 @@ Although the library has progressed very far there are still some candies missin
```
- [ ] The **sequential/pipe operator** which invokes continuables sequentially and calls the callback with all results:
- [x] The **sequential operator** which invokes continuables sequentially and calls the callback with all results:
```c++
(http_request("github.com") | http_request("travis-ci.org") | http_request("atom.io"))
(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 done sequentially and the callback is called
// with the response of github, travis and atom as soon as atom has responded.

View File

@ -14,8 +14,7 @@ if (PLATFORM EQUAL 64)
add_definitions("-D_WIN64")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP /bigobj")
if (TESTS_NO_EXCEPTIONS)
add_definitions(-D_HAS_EXCEPTIONS=0)

View File

@ -422,6 +422,13 @@ inline auto or_folding() {
std::forward<decltype(right)>(right);
};
}
/// Returns a folding function using operator `>>`.
inline auto seq_folding() {
return [](auto&& left, auto&& right) {
return std::forward<decltype(left)>(left) >>
std::forward<decltype(right)>(right);
};
}
/// Deduces to a std::false_type
template <typename T>
@ -927,7 +934,7 @@ auto wrap_continuation(Continuation&& continuation) {
} // end namespace base
/// The namespace `compose` offers methods to chain continuations together
/// with `all` and `any` logic.
/// with `all`, `any` or `seq` logic.
namespace compose {
struct strategy_all_tag {};
struct strategy_any_tag {};
@ -1262,6 +1269,24 @@ auto finalize_composition(strategy_any_tag, Composition&& composition) {
},
signature);
}
/// Connects the left and the right continuable to a sequence
///
/// \note This is implemented in an eager way because we would not gain
/// any profit from chaining sequences lazily.
template <typename Left, typename Right>
auto sequential_connect(Left&& left, Right&& right) {
return std::forward<Left>(left).then([right = std::forward<Right>(right)](
auto&&... args) mutable {
return std::move(right).then([previous = std::make_tuple(
std::forward<decltype(args)>(args)...)](
auto&&... args) mutable {
return util::merge(
std::move(previous),
std::make_tuple(std::forward<decltype(args)>(args)...));
});
});
}
} // end namespace compose
/// Provides helper functions to transform continuations to other types
@ -1405,6 +1430,13 @@ public:
std::move(*this), std::move(right));
}
template <typename OData, typename OAnnotation>
auto operator>>(continuable_base<OData, OAnnotation>&& right) && {
right.assert_owning();
return detail::compose::sequential_connect(std::move(*this),
std::move(right));
}
auto futurize() && {
return detail::transforms::as_future(std::move(*this).materialize());
}
@ -1573,6 +1605,22 @@ auto any_of(Continuables&&... continuables) {
std::forward<Continuables>(continuables)...);
}
/// Connects the given continuables with a *seq* logic.
///
/// \param continuables The continuable_base objects to connect.
/// Requires at least 2 objects to connect.
///
/// \see continuable_base::operator>> for details.
///
/// \since version 1.0.0
template <typename... Continuables>
auto seq_of(Continuables&&... continuables) {
static_assert(sizeof...(continuables) >= 2,
"Requires at least 2 continuables!");
return detail::util::fold(detail::util::seq_folding(),
std::forward<Continuables>(continuables)...);
}
template <template <typename> class CallbackWrapper,
template <typename> class ContinuationWrapper, typename... Args>
using continuable_erasure_of_t =

View File

@ -103,4 +103,45 @@ TYPED_TEST(single_dimension_tests, is_logical_or_connectable) {
EXPECT_ASYNC_RESULT(std::move(composed));
}
}
TYPED_TEST(single_dimension_tests, is_logical_seq_connectable) {
{
auto chain = this->supply() >> this->supply();
EXPECT_ASYNC_RESULT(std::move(chain));
}
{
auto chain = this->supply(1) >> this->supply(2);
EXPECT_ASYNC_RESULT(std::move(chain), 1, 2);
}
{
auto chain = this->supply(1, 2) >> this->supply(3, 4);
EXPECT_ASYNC_RESULT(std::move(chain), 1, 2, 3, 4);
}
{
auto chain = this->supply(tag1{}, tag2{}) >> this->supply(tag1{}, tag2{});
ASSERT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag1, tag2);
}
{
auto chain = this->supply(tag1{}, tag2{}, tag3{}) >>
this->supply(tag1{}, tag2{}, tag3{});
ASSERT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3, tag1, tag2, tag3);
}
{
// Check the evaluation order
unsigned i = 0;
auto composed =
make_step(this, i, 0) >> make_step(this, i, 1) >> make_step(this, i, 2);
EXPECT_ASYNC_RESULT(std::move(composed));
}
{
auto chain = this->supply('a') >> this->supply('b') >> this->supply('c');
EXPECT_ASYNC_RESULT(std::move(chain), 'a', 'b', 'c');
}
}

View File

@ -134,22 +134,34 @@ template <typename Provider> struct provide_continuation_and_right {
}
};
template <typename Provider> struct provide_continuation_seq_right {
Provider provider_;
template <typename... Args, typename... Hint, typename T>
auto make(identity<Args...> args, identity<Hint...> hint, T&& callback) {
return provider_.make(args, hint, std::forward<T>(callback)) >>
empty_continuable();
}
};
// clang-format off
// Feel free to uncomment more tests, however this will increase the
// build time significantly.
using single_types = ::testing::Types<
provide_copyable,
provide_unique,
provide_copyable_erasure,
// provide_unique,
// provide_copyable_erasure,
provide_unique_erasure,
// Some instantiations out commented for compilation speed reasons
// provide_continuation_and_left<provide_copyable>,
provide_continuation_and_left<provide_unique>,
// provide_continuation_and_left<provide_copyable_erasure>,
// provide_continuation_and_left<provide_unique_erasure>,
// Some instantiations out commented for compilation speed reasons
// provide_continuation_and_left<provide_copyable_erasure>,
// provide_continuation_and_left<provide_unique_erasure>,
// provide_continuation_and_right<provide_copyable>,
provide_continuation_and_right<provide_unique>
provide_continuation_and_right<provide_unique>,
// provide_continuation_and_left<provide_copyable_erasure>,
// provide_continuation_and_left<provide_unique_erasure>
provide_continuation_seq_right<provide_unique>
>;
// clang-format on