Fix a materialization error on futurization

* Add more documentation and tests
This commit is contained in:
Denis Blank 2017-02-27 14:40:38 +01:00
parent 4448eb7351
commit 43a2c47a91
8 changed files with 206 additions and 132 deletions

View File

@ -4,7 +4,7 @@ This documentation covers the continuable library in detail
## Content
- Class cti::continuable_base - main class for continuation chaining
- Class cti::continuable_base - main class for continuation chaining.
- \link cti::continuable_base::then then\endlink - adds a callback or cti::continuable_base to the invocation chain.
- \link cti::continuable_base::operator && operator&&\endlink - connects another cti::continuable_base with an *all* logic.
- \link cti::continuable_base::operator|| operator||\endlink - connects another cti::continuable_base with an *any* logic.
@ -13,4 +13,8 @@ This documentation covers the continuable library in detail
- \link cti::make_continuable make_continuable\endlink - creates a cti::continuable_base from a callback tanking function.
- \link cti::all_of all_of\endlink - connects all given cti::continuable_base objects with an *all* logic.
- \link cti::any_of any_of\endlink - connects all given cti::continuable_base objects with an *any* logic.
- GTest macros:
- \link EXPECT_ASYNC_RESULT EXPECT_ASYNC_RESULT\endlink - Expects that the given continuable is finished with the given result.
- \link ASSERT_ASYNC_RESULT ASSERT_ASYNC_RESULT\endlink - Asserts that the given continuable is finished with the given result.
- \link ASSERT_ASYNC_TYPES ASSERT_ASYNC_TYPES\endlink - Asserts that the given continuable is finished with the given types without validating it against equality.

View File

@ -47,6 +47,7 @@ namespace detail {
/// Utility namespace which provides useful meta-programming support
namespace util {
/// \cond false
#define CTI__FOR_EACH_BOOLEAN_BIN_OP(CTI__OP__) \
CTI__OP__(==) \
CTI__OP__(!=) CTI__OP__(<=) CTI__OP__(>=) CTI__OP__(<) CTI__OP__(>)
@ -56,9 +57,11 @@ namespace util {
CTI__OP__(/) CTI__OP__(+) CTI__OP__(-) CTI__FOR_EACH_BOOLEAN_BIN_OP(CTI__OP__)
#define CTI__FOR_EACH_INTEGRAL_UNA_OP(CTI__OP__) \
CTI__OP__(~) CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__OP__)
/// \endcond
template <typename T, T Value>
struct constant : std::integral_constant<T, Value> {
/// \cond false
#define CTI__INST(CTI__OP) \
template <typename OT, OT OValue> \
/*constexpr*/ auto operator CTI__OP(std::integral_constant<OT, OValue>) \
@ -74,10 +77,12 @@ struct constant : std::integral_constant<T, Value> {
}
CTI__FOR_EACH_INTEGRAL_UNA_OP(CTI__INST)
#undef CTI__INST
/// \endcond
};
template <bool Value>
struct constant<bool, Value> : std::integral_constant<bool, Value> {
/// \cond false
#define CTI__INST(CTI__OP) \
template <typename OT, OT OValue> \
/*constexpr*/ auto operator CTI__OP(std::integral_constant<bool, OValue>) \
@ -92,6 +97,7 @@ struct constant<bool, Value> : std::integral_constant<bool, Value> {
}
CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__INST)
#undef CTI__INST
/// \endcond
};
template <bool Value> using bool_constant = constant<bool, Value>;
@ -277,19 +283,6 @@ auto static_while(Value&& value, Predicate&& predicate, Handler&& handler) {
});
}
/// Iterates from the begin to the end, the handler is invoked
/// with a constant representing the current position.
/// The handler shall return a constant representing the next position.
// template <std::size_t Begin, std::size_t End, typename Handler>
// void static_for(size_constant<Begin> begin, size_constant<End> end, Handler&&
// handler) {
// auto delta = begin - end;
// static_for_each_in(std::make_index_sequence<delta.value>{}, [&](auto pos)
// mutable {
// handler(pos + begin);
// });
//}
/// Returns a validator which checks whether the given sequenceable is empty
inline auto is_empty() {
return [](auto const& checkable) {
@ -966,13 +959,6 @@ private:
};
} // end namespace detail
/// Adds the given continuation to the left composition
/*template <typename... LeftArgs, typename Continuation>
auto chain_composition(std::tuple<LeftArgs...> leftPack,
Continuation&& continuation) {
return util::push(std::move(leftPack),
std::forward<Continuation>(continuation));
}*/
/// Adds the given continuation tuple to the left composition
template <typename... LeftArgs, typename... RightArgs>
auto chain_composition(std::tuple<LeftArgs...> leftPack,
@ -1304,7 +1290,9 @@ public:
std::move(*this), std::move(right));
}
auto futurize() && { return detail::transforms::as_future(std::move(*this)); }
auto futurize() && {
return detail::transforms::as_future(std::move(*this).materialize());
}
void done() && {
assert(ownership_.has_ownership() &&

View File

@ -58,8 +58,8 @@ void expect_async_validation(C&& continuable, V&& validator) {
}
/// Expects that the continuable is finished with the given arguments
template <typename C, typename V, typename... Args>
void expect_async_binary_validation(C&& continuable, V&& validator,
template <typename V, typename C, typename... Args>
void expect_async_binary_validation(V&& validator, C&& continuable,
Args&&... expected) {
expect_async_validation(std::forward<C>(continuable), [
expected_pack = std::make_tuple(std::forward<Args>(expected)...),
@ -85,16 +85,16 @@ void expect_async_binary_validation(C&& continuable, V&& validator,
});
}
template <typename C, typename... Args>
void expect_async_result(C&& continuable, Args&&... expected) {
expect_async_binary_validation(
std::forward<C>(continuable),
[](auto&& expected, auto&& actual) { EXPECT_EQ(expected, actual); },
std::forward<Args>(expected)...);
inline auto expecting_eq_check() {
return [](auto expected, auto actual) { EXPECT_EQ(expected, actual); };
}
inline auto asserting_eq_check() {
return [](auto expected, auto actual) { ASSERT_EQ(expected, actual); };
}
template <typename C, typename... Args>
void expect_async_types(C&& continuable, util::identity<Args...> expected) {
void assert_async_types(C&& continuable, util::identity<Args...> expected) {
expect_async_validation(
std::forward<C>(continuable), [&](auto... actualPack) {
auto actual = util::identity<decltype(actualPack)...>{};
@ -116,6 +116,8 @@ void expect_async_types(C&& continuable, util::identity<Args...> expected) {
/// Expects the final callback of the given continuable to be called
/// with any result.
///
/// \since version 1.0.0
#define EXPECT_ASYNC_COMPLETION(CONTINUABLE) \
cti::detail::testing::expect_async_completion(CONTINUABLE);
@ -125,25 +127,79 @@ void expect_async_types(C&& continuable, util::identity<Args...> expected) {
cti::detail::testing::expect_async_validation(CONTINUABLE, VALIDATOR);
/// Expects the continuation to be called and forwards it's arguments to
/// the given validator which can then do assertions on the result:
/// the given validator which can then do assertions on the result.
///
/// auto validator = [](auto expected, auto actual) {
/// // ...
/// };
/// A validator consists of a binary consumer with a signature as in
/// in the example shown below:
/// ```cpp
/// auto validator = [](auto expected, auto actual) {
/// EXPECT_EQ(expected, actual);
/// };
/// ```
///
/// The macro is usable as shown in the following example:
/// ```cpp
/// continuable<string> async_get(std::string);
/// // ...
/// auto validator = [](auto expected, auto actual) {
/// EXPECT_EQ(expected, actual);
/// };
///
/// EXPECT_ASYNC_BINARY_VALIDATION(validator, async_get("hello"), "hello")
/// ```
///
/// The validator is called for every expecting and actual result.
#define EXPECT_ASYNC_BINARY_VALIDATION(CONTINUABLE, ...) \
cti::detail::testing::expect_async_binary_validation(CONTINUABLE, \
__VA_ARGS__);
///
/// \note This macro is mainly present for building other assertions
/// relying on custom validation logic.
///
/// \since version 1.0.0
#define EXPECT_ASYNC_BINARY_VALIDATION(VALIDATOR, ...) \
cti::detail::testing::expect_async_binary_validation(VALIDATOR, __VA_ARGS__);
/// Expects that the continuable is finished with the given result
///
/// ```cpp
/// continuable<string> async_get(std::string);
/// // ...
///
/// EXPECT_ASYNC_RESULT(async_get("hello"), "hello");
/// ```
///
/// \since version 1.0.0
#define EXPECT_ASYNC_RESULT(...) \
cti::detail::testing::expect_async_result(__VA_ARGS__);
EXPECT_ASYNC_BINARY_VALIDATION(cti::detail::testing::expecting_eq_check(), \
__VA_ARGS__)
/// Expects that the continuable is finished with the given type of arguments
/// Asserts that the continuable is finished with the given result
///
/// ```cpp
/// continuable<string> async_get(std::string);
/// // ...
///
/// ASSERT_ASYNC_RESULT(async_get("hello"), "hello");
/// ```
///
/// \since version 1.0.0
#define ASSERT_ASYNC_RESULT(...) \
EXPECT_ASYNC_BINARY_VALIDATION(cti::detail::testing::asserting_eq_check(), \
__VA_ARGS__)
/// Asserts that the continuable is finished with the given type of arguments
/// without validating it against equality.
#define EXPECT_ASYNC_TYPES(CONTINUABLE, ...) \
cti::detail::testing::expect_async_types( \
///
/// ```cpp
/// continuable<string> async_get(std::string);
/// // ...
///
/// ASSERT_ASYNC_TYPES(async_get("hello"), std::string);
/// ```
///
/// \note This is a compile-time assertion.
///
/// \since version 1.0.0
#define ASSERT_ASYNC_TYPES(CONTINUABLE, ...) \
cti::detail::testing::assert_async_types( \
CONTINUABLE, cti::detail::util::identity<__VA_ARGS__>{})
#endif // CONTINUABLE_TESTING_HPP_INCLUDED__

View File

@ -5,7 +5,7 @@ add_executable(test-continuable
${CMAKE_CURRENT_LIST_DIR}/test-continuable.hpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-base.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-connection.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-transforms.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-erasure.cpp
${CMAKE_CURRENT_LIST_DIR}/test-continuable-recursion.cpp)
target_link_libraries(test-continuable

View File

@ -42,7 +42,7 @@ TYPED_TEST(single_dimension_tests, are_supplyd_on_destruct) {
EXPECT_ASYNC_RESULT(this->supply(0xDA), 0xDA);
EXPECT_ASYNC_TYPES(this->supply(tag1{}), tag1);
ASSERT_ASYNC_TYPES(this->supply(tag1{}), tag1);
}
TYPED_TEST(single_dimension_tests, are_chainable) {
@ -53,7 +53,7 @@ TYPED_TEST(single_dimension_tests, are_chainable) {
// Type chain
{
auto chain = this->supply().then([] { return tag1{}; });
EXPECT_ASYNC_TYPES(std::move(chain), tag1);
ASSERT_ASYNC_TYPES(std::move(chain), tag1);
}
// Pair chain
@ -62,7 +62,7 @@ TYPED_TEST(single_dimension_tests, are_chainable) {
// ...
return std::make_pair(tag1{}, tag2{});
});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2);
ASSERT_ASYNC_TYPES(std::move(chain), tag1, tag2);
}
// Tuple chain
@ -71,20 +71,20 @@ TYPED_TEST(single_dimension_tests, are_chainable) {
// ...
return std::make_tuple(tag1{}, tag2{}, tag3{});
});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3);
ASSERT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3);
}
// Erasing chain
{
auto chain = this->supply().then(this->supply(tag1{}));
EXPECT_ASYNC_TYPES(std::move(chain), tag1);
ASSERT_ASYNC_TYPES(std::move(chain), tag1);
}
// Continuing chain
{
auto chain = this->supply().then([&] { return this->supply(tag1{}); });
EXPECT_ASYNC_TYPES(std::move(chain), tag1);
ASSERT_ASYNC_TYPES(std::move(chain), tag1);
}
}

View File

@ -23,6 +23,13 @@
#include "test-continuable.hpp"
template <typename T> auto make_step(T* me, unsigned& current, unsigned step) {
return me->invoke([=]() mutable {
ASSERT_EQ(step, current);
++current;
});
}
TYPED_TEST(single_dimension_tests, is_logical_and_connectable) {
{
@ -42,8 +49,16 @@ TYPED_TEST(single_dimension_tests, is_logical_and_connectable) {
{
auto chain = this->supply(tag1{}) && this->supply(tag2{}, tag3{});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3);
ASSERT_ASYNC_TYPES(std::move(chain), 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));
}*/
}
TYPED_TEST(single_dimension_tests, is_logical_or_connectable) {
@ -65,18 +80,18 @@ TYPED_TEST(single_dimension_tests, is_logical_or_connectable) {
{
auto chain = this->supply(tag1{}, tag2{}) || this->supply(tag1{}, tag2{});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2);
ASSERT_ASYNC_TYPES(std::move(chain), tag1, tag2);
}
{
auto chain = this->supply(tag1{}, tag2{}, tag3{}) ||
this->supply(tag1{}, tag2{}, tag3{});
EXPECT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3);
ASSERT_ASYNC_TYPES(std::move(chain), tag1, tag2, tag3);
}
{
using common = std::common_type_t<char, int>;
auto chain = this->supply(char(0), int(0)) || this->supply(int(0), char(0));
EXPECT_ASYNC_TYPES(std::move(chain), common, common);
ASSERT_ASYNC_TYPES(std::move(chain), common, common);
}
}

View File

@ -23,12 +23,18 @@
#include "test-continuable.hpp"
using namespace cti;
TYPED_TEST(single_dimension_tests, is_eraseable) {
TEST(ContinuableErasureTests, FunctionsAreUnwrappable) {
// ...
{
cti::unique_continuable<int> erasure =
cti::make_continuable(supplier_of(0xDF));
// continuable<int> ss = supply(0);
EXPECT_ASYNC_RESULT(std::move(erasure), 0xDF);
}
// auto itm = std::move(ss).then(supply(2));
{
cti::unique_continuable<int> erasure = this->supply(0xDF);
EXPECT_ASYNC_RESULT(std::move(erasure), 0xDF);
}
}

View File

@ -31,26 +31,40 @@
#include "continuable/continuable.hpp"
#include "gtest/gtest.h"
using cti::detail::util::identity;
inline auto to_hint(identity<> /*hint*/) { return identity<void>{}; }
template <typename... Args> auto to_hint(identity<Args...> hint) {
return hint;
}
template <typename... Args> auto supplier_of(Args&&... args) {
return [values = std::make_tuple(std::forward<Args>(args)...)](
auto&& callback) mutable {
cti::detail::util::unpack(std::move(values), [&](auto&&... passed) {
// ...
std::forward<decltype(callback)>(callback)(
std::forward<decltype(passed)>(passed)...);
});
};
}
template <typename Provider>
class continuation_provider : public ::testing::Test, public Provider {
public:
auto supply() {
return this->makeVoid([](auto&& callback) mutable {
// ...
std::forward<decltype(callback)>(callback)();
});
};
template <typename T> auto invoke(T&& type) {
return this->make(identity<>{}, identity<void>{},
[type = std::forward<T>(type)](auto&& callback) mutable {
std::forward<decltype(callback)>(callback)();
});
}
template <typename... Args> auto supply(Args&&... args) {
return this->template make<std::decay_t<Args>...>([values = std::make_tuple(
std::forward<Args>(
args)...)](
auto&& callback) mutable {
cti::detail::util::unpack(std::move(values), [&](auto&&... passed) {
// ...
std::forward<decltype(callback)>(callback)(
std::forward<decltype(passed)>(passed)...);
});
});
identity<std::decay_t<Args>...> arg_types;
auto hint_types = to_hint(arg_types);
return this->make(arg_types, hint_types,
supplier_of(std::forward<Args>(args)...));
}
};
@ -61,21 +75,21 @@ inline auto empty_caller() {
};
}
inline auto empty_continuable() {
return cti::make_continuable<void>(empty_caller());
}
struct provide_copyable {
template <typename T> auto makeVoid(T&& callback) {
return make<void>(std::forward<T>(callback));
}
template <typename... Args, typename T> auto make(T&& callback) {
return cti::make_continuable<Args...>(std::forward<T>(callback));
template <typename... Args, typename... Hint, typename T>
auto make(identity<Args...>, identity<Hint...>, T&& callback) {
return cti::make_continuable<Hint...>(std::forward<T>(callback));
}
};
struct provide_unique {
template <typename T> auto makeVoid(T&& callback) {
return make<void>(std::forward<T>(callback));
}
template <typename... Args, typename T> auto make(T&& callback) {
return cti::make_continuable<Args...>([
template <typename... Args, typename... Hint, typename T>
auto make(identity<Args...>, identity<Hint...>, T&& callback) {
return cti::make_continuable<Hint...>([
callback = std::forward<T>(callback), guard = std::make_unique<int>(0)
](auto&&... args) mutable {
(void)(*guard);
@ -85,68 +99,59 @@ struct provide_unique {
};
struct provide_copyable_erasure {
template <typename T> auto makeVoid(T&& callback) {
return make(std::forward<T>(callback));
}
template <typename... Args, typename T>
cti::continuable<Args...> make(T&& callback) {
return cti::make_continuable(std::forward<T>(callback));
template <typename... Args, typename... Hint, typename T>
cti::continuable<Args...> make(identity<Args...>, identity<Hint...>,
T&& callback) {
return cti::make_continuable<Hint...>(std::forward<T>(callback));
}
};
struct provide_unique_erasure {
template <typename T> auto makeVoid(T&& callback) {
return make(std::forward<T>(callback));
}
template <typename... Args, typename T>
cti::unique_continuable<Args...> make(T&& callback) {
return cti::make_continuable(std::forward<T>(callback));
template <typename... Args, typename... Hint, typename T>
cti::unique_continuable<Args...> make(identity<Args...>, identity<Hint...>,
T&& callback) {
return cti::make_continuable<Hint...>(std::forward<T>(callback));
}
};
/*
template <typename Left, typename Right> struct provide_continuation_or_left {
Left left_;
Right right_;
template <typename Provider> struct provide_continuation_and_left {
Provider provider_;
template <typename T> auto makeVoid(T&& callback) {
return left_.template make<void>(std::forward<T>(callback)) ||
right_.template make<void>(empty_caller());
}
template <typename... Args, typename T> auto make(T&& callback) {
return left_.template make<Args...>(std::forward<T>(callback)) ||
right_.template make<void>(empty_caller());
template <typename... Args, typename... Hint, typename T>
auto make(identity<Args...> args, identity<Hint...> hint, T&& callback) {
return empty_continuable() &&
provider_.make(args, hint, std::forward<T>(callback));
}
};
*/
template <typename... Args> struct type_chainer {
template <typename First> auto add() {
return type_chainer<Args..., First>{};
}
template <template <typename> class T, typename First> auto add() {
return type_chainer<Args..., T<First>>{};
}
template <template <typename, typename> class T, typename First,
typename Second>
auto add() {
return type_chainer<Args..., T<First, Second>, T<First, Second>,
T<Second, First>, T<Second, Second>>{};
}
template <typename Provider> struct provide_continuation_and_right {
Provider provider_;
using type = testing::Types<Args...>;
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();
}
};
inline auto make_type() {
type_chainer<> chainer{};
return chainer // ...
.add<provide_copyable>()
.add<provide_unique>()
.add<provide_copyable_erasure>()
.add<provide_unique_erasure>();
}
using single_types = decltype(make_type())::type;
// clang-format off
using single_types = ::testing::Types<
provide_copyable,
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_right<provide_copyable>,
provide_continuation_and_right<provide_unique>
// provide_continuation_and_left<provide_copyable_erasure>,
// provide_continuation_and_left<provide_unique_erasure>
>;
// clang-format on
struct tag1 {};
struct tag2 {};