diff --git a/doc/Index.md b/doc/Index.md index 808881d..f3b7b18 100644 --- a/doc/Index.md +++ b/doc/Index.md @@ -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. diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index 1777799..6436c2a 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -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 struct constant : std::integral_constant { +/// \cond false #define CTI__INST(CTI__OP) \ template \ /*constexpr*/ auto operator CTI__OP(std::integral_constant) \ @@ -74,10 +77,12 @@ struct constant : std::integral_constant { } CTI__FOR_EACH_INTEGRAL_UNA_OP(CTI__INST) #undef CTI__INST + /// \endcond }; template struct constant : std::integral_constant { +/// \cond false #define CTI__INST(CTI__OP) \ template \ /*constexpr*/ auto operator CTI__OP(std::integral_constant) \ @@ -92,6 +97,7 @@ struct constant : std::integral_constant { } CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__INST) #undef CTI__INST + /// \endcond }; template using bool_constant = constant; @@ -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 -// void static_for(size_constant begin, size_constant end, Handler&& -// handler) { -// auto delta = begin - end; -// static_for_each_in(std::make_index_sequence{}, [&](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 -auto chain_composition(std::tuple leftPack, - Continuation&& continuation) { - return util::push(std::move(leftPack), - std::forward(continuation)); -}*/ /// Adds the given continuation tuple to the left composition template auto chain_composition(std::tuple 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() && diff --git a/include/continuable/continuable-testing.hpp b/include/continuable/continuable-testing.hpp index 9b92081..8338f9c 100644 --- a/include/continuable/continuable-testing.hpp +++ b/include/continuable/continuable-testing.hpp @@ -58,8 +58,8 @@ void expect_async_validation(C&& continuable, V&& validator) { } /// Expects that the continuable is finished with the given arguments -template -void expect_async_binary_validation(C&& continuable, V&& validator, +template +void expect_async_binary_validation(V&& validator, C&& continuable, Args&&... expected) { expect_async_validation(std::forward(continuable), [ expected_pack = std::make_tuple(std::forward(expected)...), @@ -85,16 +85,16 @@ void expect_async_binary_validation(C&& continuable, V&& validator, }); } -template -void expect_async_result(C&& continuable, Args&&... expected) { - expect_async_binary_validation( - std::forward(continuable), - [](auto&& expected, auto&& actual) { EXPECT_EQ(expected, actual); }, - std::forward(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 -void expect_async_types(C&& continuable, util::identity expected) { +void assert_async_types(C&& continuable, util::identity expected) { expect_async_validation( std::forward(continuable), [&](auto... actualPack) { auto actual = util::identity{}; @@ -116,6 +116,8 @@ void expect_async_types(C&& continuable, util::identity 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 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 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 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 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 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__ diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index 66cbc8f..85b6e9d 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -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 diff --git a/test/unit-test/test-continuable-base.cpp b/test/unit-test/test-continuable-base.cpp index 1a523e9..90b2bef 100644 --- a/test/unit-test/test-continuable-base.cpp +++ b/test/unit-test/test-continuable-base.cpp @@ -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); } } diff --git a/test/unit-test/test-continuable-connection.cpp b/test/unit-test/test-continuable-connection.cpp index b031936..862d46a 100644 --- a/test/unit-test/test-continuable-connection.cpp +++ b/test/unit-test/test-continuable-connection.cpp @@ -23,6 +23,13 @@ #include "test-continuable.hpp" +template 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; 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); } } diff --git a/test/unit-test/test-continuable-transforms.cpp b/test/unit-test/test-continuable-erasure.cpp similarity index 78% rename from test/unit-test/test-continuable-transforms.cpp rename to test/unit-test/test-continuable-erasure.cpp index aa33215..f61d708 100644 --- a/test/unit-test/test-continuable-transforms.cpp +++ b/test/unit-test/test-continuable-erasure.cpp @@ -23,12 +23,18 @@ #include "test-continuable.hpp" -using namespace cti; +TYPED_TEST(single_dimension_tests, is_eraseable) { -TEST(ContinuableErasureTests, FunctionsAreUnwrappable) { - // ... + { + cti::unique_continuable erasure = + cti::make_continuable(supplier_of(0xDF)); - // continuable ss = supply(0); + EXPECT_ASYNC_RESULT(std::move(erasure), 0xDF); + } - // auto itm = std::move(ss).then(supply(2)); + { + cti::unique_continuable erasure = this->supply(0xDF); + + EXPECT_ASYNC_RESULT(std::move(erasure), 0xDF); + } } diff --git a/test/unit-test/test-continuable.hpp b/test/unit-test/test-continuable.hpp index d615f2b..e378c56 100644 --- a/test/unit-test/test-continuable.hpp +++ b/test/unit-test/test-continuable.hpp @@ -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{}; } +template auto to_hint(identity hint) { + return hint; +} + +template auto supplier_of(Args&&... args) { + return [values = std::make_tuple(std::forward(args)...)]( + auto&& callback) mutable { + cti::detail::util::unpack(std::move(values), [&](auto&&... passed) { + // ... + std::forward(callback)( + std::forward(passed)...); + }); + }; +} + template class continuation_provider : public ::testing::Test, public Provider { public: - auto supply() { - return this->makeVoid([](auto&& callback) mutable { - // ... - std::forward(callback)(); - }); - }; + template auto invoke(T&& type) { + return this->make(identity<>{}, identity{}, + [type = std::forward(type)](auto&& callback) mutable { + std::forward(callback)(); + }); + } + template auto supply(Args&&... args) { - return this->template make...>([values = std::make_tuple( - std::forward( - args)...)]( - auto&& callback) mutable { - cti::detail::util::unpack(std::move(values), [&](auto&&... passed) { - // ... - std::forward(callback)( - std::forward(passed)...); - }); - }); + identity...> arg_types; + auto hint_types = to_hint(arg_types); + + return this->make(arg_types, hint_types, + supplier_of(std::forward(args)...)); } }; @@ -61,21 +75,21 @@ inline auto empty_caller() { }; } +inline auto empty_continuable() { + return cti::make_continuable(empty_caller()); +} + struct provide_copyable { - template auto makeVoid(T&& callback) { - return make(std::forward(callback)); - } - template auto make(T&& callback) { - return cti::make_continuable(std::forward(callback)); + template + auto make(identity, identity, T&& callback) { + return cti::make_continuable(std::forward(callback)); } }; struct provide_unique { - template auto makeVoid(T&& callback) { - return make(std::forward(callback)); - } - template auto make(T&& callback) { - return cti::make_continuable([ + template + auto make(identity, identity, T&& callback) { + return cti::make_continuable([ callback = std::forward(callback), guard = std::make_unique(0) ](auto&&... args) mutable { (void)(*guard); @@ -85,68 +99,59 @@ struct provide_unique { }; struct provide_copyable_erasure { - template auto makeVoid(T&& callback) { - return make(std::forward(callback)); - } - template - cti::continuable make(T&& callback) { - return cti::make_continuable(std::forward(callback)); + template + cti::continuable make(identity, identity, + T&& callback) { + return cti::make_continuable(std::forward(callback)); } }; struct provide_unique_erasure { - template auto makeVoid(T&& callback) { - return make(std::forward(callback)); - } - template - cti::unique_continuable make(T&& callback) { - return cti::make_continuable(std::forward(callback)); + template + cti::unique_continuable make(identity, identity, + T&& callback) { + return cti::make_continuable(std::forward(callback)); } }; -/* -template struct provide_continuation_or_left { - Left left_; - Right right_; +template struct provide_continuation_and_left { + Provider provider_; - template auto makeVoid(T&& callback) { - return left_.template make(std::forward(callback)) || - right_.template make(empty_caller()); - } - template auto make(T&& callback) { - return left_.template make(std::forward(callback)) || - right_.template make(empty_caller()); + template + auto make(identity args, identity hint, T&& callback) { + return empty_continuable() && + provider_.make(args, hint, std::forward(callback)); } }; -*/ -template struct type_chainer { - template auto add() { - return type_chainer{}; - } - template