diff --git a/CMakeLists.txt b/CMakeLists.txt index 70b0923..7246005 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,8 @@ target_compile_features(continuable-base cxx_defaulted_functions cxx_nullptr cxx_trailing_return_types - cxx_return_type_deduction) + cxx_return_type_deduction + $<$:cxx_std_17>) add_library(continuable INTERFACE) diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index c288c9c..938bb77 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -527,8 +527,8 @@ private: template auto make_continuable(Continuation&& continuation) { auto hint = detail::composition::annotating::extract( - detail::util::identity_of(continuation), - detail::util::identity{}); + detail::traits::identity_of(continuation), + detail::traits::identity{}); return detail::base::attorney::create( std::forward(continuation), hint, @@ -547,8 +547,8 @@ template auto when_all(Continuables&&... continuables) { static_assert(sizeof...(continuables) >= 2, "Requires at least 2 continuables!"); - return detail::util::fold(detail::util::and_folding(), - std::forward(continuables)...); + return detail::traits::fold(detail::traits::and_folding(), + std::forward(continuables)...); } /// \copydoc when_all @@ -574,8 +574,8 @@ template auto when_any(Continuables&&... continuables) { static_assert(sizeof...(continuables) >= 2, "Requires at least 2 continuables!"); - return detail::util::fold(detail::util::or_folding(), - std::forward(continuables)...); + return detail::traits::fold(detail::traits::or_folding(), + std::forward(continuables)...); } /// \copydoc when_any @@ -601,8 +601,8 @@ template auto when_seq(Continuables&&... continuables) { static_assert(sizeof...(continuables) >= 2, "Requires at least 2 continuables!"); - return detail::util::fold(detail::util::seq_folding(), - std::forward(continuables)...); + return detail::traits::fold(detail::traits::seq_folding(), + std::forward(continuables)...); } /// \copydoc when_seq diff --git a/include/continuable/continuable-testing.hpp b/include/continuable/continuable-testing.hpp index bc0c0bd..79cc283 100644 --- a/include/continuable/continuable-testing.hpp +++ b/include/continuable/continuable-testing.hpp @@ -45,7 +45,7 @@ template void assert_async_completion(C&& continuable) { *called = true; // Workaround for our known GCC bug. - util::unused(std::forward(args)...); + traits::unused(std::forward(args)...); }); ASSERT_TRUE(*called); } @@ -53,7 +53,7 @@ template void assert_async_completion(C&& continuable) { template void assert_async_never_completed(C&& continuable) { std::forward(continuable).then([](auto&&... args) { // Workaround for our known GCC bug. - util::unused(std::forward(args)...); + traits::unused(std::forward(args)...); FAIL(); }); @@ -81,13 +81,13 @@ void assert_async_binary_validation(V&& validator, C&& continuable, auto actual_pack = std::make_tuple(std::forward(args)...); - auto size = util::pack_size_of(util::identity_of(expected_pack)); + auto size = traits::pack_size_of(traits::identity_of(expected_pack)); static_assert(size.value == std::tuple_size::value, "Async completion handler called with a different count " "of arguments!"); - util::static_for_each_in( + traits::static_for_each_in( std::make_index_sequence{}, [&](auto current) mutable { auto expected = std::get(std::move(expected_pack)); auto actual = std::get(std::move(actual_pack)); @@ -107,11 +107,11 @@ inline auto asserting_eq_check() { } template -void assert_async_types(C&& continuable, util::identity expected) { +void assert_async_types(C&& continuable, traits::identity expected) { assert_async_validation( std::forward(continuable), [&](auto... actualPack) { - auto actual = util::identity{}; - util::unused(expected, actual, + auto actual = traits::identity{}; + traits::unused(expected, actual, std::forward(actualPack)...); static_assert( @@ -217,6 +217,6 @@ void assert_async_types(C&& continuable, util::identity expected) { /// \since version 1.0.0 #define ASSERT_ASYNC_TYPES(CONTINUABLE, ...) \ cti::detail::testing::assert_async_types( \ - CONTINUABLE, cti::detail::util::identity<__VA_ARGS__>{}) + CONTINUABLE, cti::detail::traits::identity<__VA_ARGS__>{}) #endif // CONTINUABLE_TESTING_HPP_INCLUDED__ diff --git a/include/continuable/detail/base.hpp b/include/continuable/detail/base.hpp index 52bc3f5..906ab83 100644 --- a/include/continuable/detail/base.hpp +++ b/include/continuable/detail/base.hpp @@ -61,15 +61,15 @@ struct this_thread_executor_tag {}; /// Returns the signature hint of the given continuable template -constexpr auto hint_of(util::identity) { - static_assert(util::fail::value, +constexpr auto hint_of(traits::identity) { + static_assert(traits::fail::value, "Expected a continuation with an existing signature hint!"); - return util::identity_of(); + return traits::identity_of(); } /// Returns the signature hint of the given continuable template constexpr auto -hint_of(util::identity< +hint_of(traits::identity< continuable_base>>) { return hints::signature_hint_tag{}; } @@ -155,7 +155,8 @@ constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag) { /// - continuable -> result(nextCallback); template -constexpr auto invoker_of(util::identity>) { +constexpr auto +invoker_of(traits::identity>) { /// Get the hint of the unwrapped returned continuable using Type = decltype(attorney::materialize( std::declval>())); @@ -169,12 +170,12 @@ constexpr auto invoker_of(util::identity>) { std::move(continuation_), std::forward(nextCallback)); }, - hint_of(util::identity_of())); + hint_of(traits::identity_of())); } /// - ? -> nextCallback(?) template -auto invoker_of(util::identity) { +auto invoker_of(traits::identity) { return make_invoker( [](auto&& callback, auto&& nextCallback, auto&&... args) { auto result = std::forward(callback)( @@ -182,11 +183,11 @@ auto invoker_of(util::identity) { std::forward(nextCallback)(std::move(result)); }, - util::identity_of()); + traits::identity_of()); } /// - void -> nextCallback() -inline auto invoker_of(util::identity) { +inline auto invoker_of(traits::identity) { return make_invoker( [](auto&& callback, auto&& nextCallback, auto&&... args) { std::forward(callback)( @@ -194,7 +195,7 @@ inline auto invoker_of(util::identity) { std::forward(nextCallback)(); }, - util::identity<>{}); + traits::identity<>{}); } /// Returns a sequenced invoker which is able to invoke @@ -204,7 +205,7 @@ inline auto sequenced_unpack_invoker() { auto result = std::forward(callback)( std::forward(args)...); - util::unpack(std::move(result), [&](auto&&... types) { + traits::unpack(std::move(result), [&](auto&&... types) { /// TODO Add inplace resolution here std::forward(nextCallback)( @@ -215,15 +216,15 @@ inline auto sequenced_unpack_invoker() { // - std::pair -> nextCallback(?, ?) template -constexpr auto invoker_of(util::identity>) { +constexpr auto invoker_of(traits::identity>) { return make_invoker(sequenced_unpack_invoker(), - util::identity{}); + traits::identity{}); } // - std::tuple -> nextCallback(?...) template -constexpr auto invoker_of(util::identity>) { - return make_invoker(sequenced_unpack_invoker(), util::identity{}); +constexpr auto invoker_of(traits::identity>) { + return make_invoker(sequenced_unpack_invoker(), traits::identity{}); } } // end namespace decoration @@ -255,7 +256,7 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, nextCallback = std::forward(nextCallback), args = std::make_tuple(std::forward(args)...) ]() mutable { - util::unpack(std::move(args), [&](auto&&... captured_args) { + traits::unpack(std::move(args), [&](auto&&... captured_args) { // Just use the packed dispatch method which dispatches the work on // the current thread. packed_dispatch(this_thread_executor_tag{}, std::move(invoker), @@ -294,8 +295,8 @@ void invoke_proxy(hints::signature_hint_tag, // In order to retrieve the correct decorator we must know what the // result type is. - auto result = - util::identity_of(); + auto result = traits::identity_of(); // Pick the correct invoker that handles decorating of the result auto invoker = decoration::invoker_of(result); @@ -309,9 +310,9 @@ void invoke_proxy(hints::signature_hint_tag, /// Returns the next hint when the callback is invoked with the given hint template -constexpr auto next_hint_of(util::identity /*callback*/, +constexpr auto next_hint_of(traits::identity /*callback*/, hints::signature_hint_tag /*current*/) { - return decoration::invoker_of(util::identity_of()( + return decoration::invoker_of(traits::identity_of()( std::declval()...))>()) .hint(); } @@ -338,8 +339,8 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback, std::forward(args)...); }; - auto hint = hint_of(util::identity_of(continuation)); - auto next_hint = next_hint_of(util::identity_of(partial_callable), hint); + auto hint = hint_of(traits::identity_of(continuation)); + auto next_hint = next_hint_of(traits::identity_of(partial_callable), hint); auto ownership_ = attorney::ownership_of(continuation); continuation.freeze(); @@ -351,7 +352,7 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback, partial_callable = std::move(partial_callable), executor = std::forward(executor) ](auto&& nextCallback) mutable { - invoke_proxy(hint_of(util::identity_of(continuation)), + invoke_proxy(hint_of(traits::identity_of(continuation)), std::move(continuation), std::move(partial_callable), std::move(executor), std::forward(nextCallback)); diff --git a/include/continuable/detail/composition.hpp b/include/continuable/detail/composition.hpp index 8f13ab8..5995ffd 100644 --- a/include/continuable/detail/composition.hpp +++ b/include/continuable/detail/composition.hpp @@ -41,7 +41,6 @@ #include #include #include -#include namespace cti { namespace detail { @@ -63,12 +62,12 @@ struct is_strategy : std::true_type {}; namespace annotating { namespace detail { /// Void hints are equal to an empty signature -constexpr auto make_hint_of(util::identity) noexcept { +constexpr auto make_hint_of(traits::identity) noexcept { return hints::signature_hint_tag<>{}; } /// All other hints are the obvious hints... template -constexpr auto make_hint_of(util::identity args) noexcept { +constexpr auto make_hint_of(traits::identity args) noexcept { return args; // Identity is equal to signature_hint_tag } } // end namespace detail @@ -89,30 +88,31 @@ constexpr auto make_hint_of(util::identity args) noexcept { /// - absent_signature_hint_tag /// template -constexpr auto extract(util::identity /*type*/, - util::identity hint) { - return util::static_if(hint, util::is_empty(), - [=](auto /*hint*/) { - /// When the arguments are the hint is absent - return hints::absent_signature_hint_tag{}; - }, - [](auto hint) { - // When hint arguments are given just take it as hint - return detail::make_hint_of(hint); - }); +constexpr auto extract(traits::identity /*type*/, + traits::identity hint) { + return traits::static_if(hint, traits::is_empty(), + [=](auto /*hint*/) { + /// When the arguments are the hint is absent + return hints::absent_signature_hint_tag{}; + }, + [](auto hint) { + // When hint arguments are given just take it as + // hint + return detail::make_hint_of(hint); + }); } } // end namespace annotating namespace detail { template -constexpr void assign(util::size_constant /*pos*/, T& /*storage*/) { +constexpr void assign(traits::size_constant /*pos*/, T& /*storage*/) { // ... } template -void assign(util::size_constant pos, T& storage, Current&& current, +void assign(traits::size_constant pos, T& storage, Current&& current, Args&&... args) { std::get(storage) = std::forward(current); - assign(pos + util::size_constant_of<1>(), storage, + assign(pos + traits::size_constant_of<1>(), storage, std::forward(args)...); } @@ -134,8 +134,8 @@ public: /// Creates a submitter which submits it's result into the tuple template - auto create_callback(util::size_constant from, - util::size_constant to) { + auto create_callback(traits::size_constant from, + traits::size_constant to) { return [ me = this->shared_from_this(), from, to ](auto&&... args) { static_assert(sizeof...(args) == (To - From), @@ -156,7 +156,7 @@ private: void invoke() { assert((left_ == 0U) && "Expected that the submitter is finished!"); std::atomic_thread_fence(std::memory_order_acquire); - util::unpack(std::move(result_), [&](auto&&... args) { + traits::unpack(std::move(result_), [&](auto&&... args) { std::move(callback_)(std::forward(args)...); }); } @@ -205,7 +205,7 @@ template auto chain_composition(std::tuple leftPack, std::tuple rightPack) { - return util::merge(std::move(leftPack), std::move(rightPack)); + return traits::merge(std::move(leftPack), std::move(rightPack)); } /// Normalizes a continuation to a tuple holding an arbitrary count of @@ -268,8 +268,8 @@ auto connect(Strategy strategy, continuable_base&& left, /// Creates a submitter which caches the intermediate results of `all` chains template auto make_all_result_submitter(Callback&& callback, - util::size_constant, - util::identity) { + traits::size_constant, + traits::identity) { return std::make_shared, Submissions, Args...>>( std::forward(callback)); @@ -285,8 +285,8 @@ auto finalize_composition( auto composition = base::attorney::consume_data(std::move(continuation)); // Merge all signature hints together - auto signature = util::unpack(composition, [](auto&... entries) { - return util::merge(base::hint_of(util::identity_of(entries))...); + auto signature = traits::unpack(composition, [](auto&... entries) { + return traits::merge(base::hint_of(traits::identity_of(entries))...); }); return base::attorney::create( @@ -296,10 +296,10 @@ auto finalize_composition( // std::pair, size_constant> // ~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ // Continuation pos Result pos - auto begin = std::make_pair(util::size_constant_of<0>(), - util::size_constant_of<0>()); - auto pack = util::identity_of(composition); - auto end = util::pack_size_of(pack); + auto begin = std::make_pair(traits::size_constant_of<0>(), + traits::size_constant_of<0>()); + auto pack = traits::identity_of(composition); + auto end = traits::pack_size_of(pack); auto condition = [=](auto pos) { return pos.first < end; }; // Create the result submitter which caches all results and invokes @@ -308,13 +308,13 @@ auto finalize_composition( std::forward(callback), end, signature); // Invoke every continuation with it's callback of the submitter - util::static_while(begin, condition, [&](auto current) mutable { + traits::static_while(begin, condition, [&](auto current) mutable { auto entry = std::move(std::get(composition)); // This is the length of the arguments of the current continuable auto arg_size = - util::pack_size_of(base::hint_of(util::identity_of(entry))); + traits::pack_size_of(base::hint_of(traits::identity_of(entry))); // The next position in the result tuple auto next = current.second + arg_size; @@ -324,7 +324,7 @@ auto finalize_composition( std::move(entry), submitter->create_callback(current.second, next)); - return std::make_pair(current.first + util::size_constant_of<1>(), + return std::make_pair(current.first + traits::size_constant_of<1>(), next); }); }, @@ -340,15 +340,16 @@ auto make_any_result_submitter(Callback&& callback) { } template -constexpr T first_of(util::identity) noexcept; +constexpr T first_of(traits::identity) noexcept; template constexpr auto common_result_of(Signature signature, hints::signature_hint_tag<>, Args... /*args*/) { /// Assert that the other signatures are empty too which means all signatures /// had the same size. - util::static_for_each_in(util::identity{}, [&](auto rest) { - auto is_empty = (util::pack_size_of(rest) == util::size_constant_of<0>()); + traits::static_for_each_in(traits::identity{}, [&](auto rest) { + auto is_empty = + (traits::pack_size_of(rest) == traits::size_constant_of<0>()); static_assert(is_empty.value, "Expected all continuations to have the same" "count of arguments!"); }); @@ -363,11 +364,11 @@ template constexpr auto common_result_of(Signature signature, First first, Args... args) { using Common = - util::identity>; + traits::identity>; - return common_result_of(util::push(signature, Common{}), - util::pop_first(first), util::pop_first(args)...); + return common_result_of(traits::push(signature, Common{}), + traits::pop_first(first), traits::pop_first(args)...); } /// Finalizes the any logic of a given composition @@ -380,9 +381,9 @@ auto finalize_composition( auto composition = base::attorney::consume_data(std::move(continuation)); // Determine the shared result between all continuations - auto signature = util::unpack(composition, [](auto const&... args) { + auto signature = traits::unpack(composition, [](auto const&... args) { return common_result_of(hints::signature_hint_tag<>{}, - base::hint_of(util::identity_of(args))...); + base::hint_of(traits::identity_of(args))...); }); return base::attorney::create( @@ -393,14 +394,14 @@ auto finalize_composition( auto submitter = make_any_result_submitter( std::forward(callback)); - util::static_for_each_in(std::move(composition), - [&](auto&& entry) mutable { - // Invoke the continuation with a submission - // callback - base::attorney::invoke_continuation( - std::forward(entry), - submitter->create_callback()); - }); + traits::static_for_each_in(std::move(composition), + [&](auto&& entry) mutable { + // Invoke the continuation with a + // submission callback + base::attorney::invoke_continuation( + std::forward(entry), + submitter->create_callback()); + }); }, signature, std::move(ownership_)); } @@ -419,7 +420,7 @@ auto sequential_connect(Left&& left, Right&& right) { return std::move(right).then([previous = std::make_tuple( std::forward(args)...)]( auto&&... args) mutable { - return util::merge( + return traits::merge( std::move(previous), std::make_tuple(std::forward(args)...)); }); diff --git a/include/continuable/detail/features.hpp b/include/continuable/detail/features.hpp index 8263608..5294a23 100644 --- a/include/continuable/detail/features.hpp +++ b/include/continuable/detail/features.hpp @@ -1,4 +1,3 @@ - /** /~` _ _ _|_. _ _ |_ | _ @@ -33,10 +32,7 @@ #include -namespace cti { -namespace detail { -// -} // namespace detail -} // namespace cti +#undef CONTINUABLE_HAS_CXX17_CONSTEXPR_IF +#undef CONTINUABLE_HAS_CXX17_FOLD_EXPRESSION #endif // CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED__ diff --git a/include/continuable/detail/hints.hpp b/include/continuable/detail/hints.hpp index 6805ebd..cb5a200 100644 --- a/include/continuable/detail/hints.hpp +++ b/include/continuable/detail/hints.hpp @@ -41,7 +41,7 @@ namespace detail { namespace hints { /// Represents a present signature hint template -using signature_hint_tag = util::identity; +using signature_hint_tag = traits::identity; /// Represents an absent signature hint struct absent_signature_hint_tag {}; diff --git a/include/continuable/detail/traits.hpp b/include/continuable/detail/traits.hpp index c84d5f4..2adba4f 100644 --- a/include/continuable/detail/traits.hpp +++ b/include/continuable/detail/traits.hpp @@ -31,12 +31,435 @@ #ifndef CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED__ #define CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED__ +#include +#include +#include +#include + #include +#include namespace cti { namespace detail { -// +namespace traits { +/// \cond false +#define CTI__FOR_EACH_BOOLEAN_BIN_OP(CTI__OP__) \ + CTI__OP__(==) \ + CTI__OP__(!=) CTI__OP__(<=) CTI__OP__(>=) CTI__OP__(<) CTI__OP__(>) +#define CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__OP__) CTI__OP__(!) +#define CTI__FOR_EACH_INTEGRAL_BIN_OP(CTI__OP__) \ + CTI__OP__(*) \ + 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) \ + const noexcept { \ + return constant{}; \ + } + CTI__FOR_EACH_INTEGRAL_BIN_OP(CTI__INST) +#undef CTI__INST +#define CTI__INST(CTI__OP) \ + /*constexpr*/ auto operator CTI__OP() const noexcept { \ + return 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) \ + const noexcept { \ + return constant{}; \ + } + CTI__FOR_EACH_BOOLEAN_BIN_OP(CTI__INST) +#undef CTI__INST +#define CTI__INST(CTI__OP) \ + /*constexpr*/ auto operator CTI__OP() const noexcept { \ + return constant{}; \ + } + CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__INST) +#undef CTI__INST + /// \endcond +}; + +template +using bool_constant = constant; +template +using size_constant = constant; + +template +constexpr auto constant_of(std::integral_constant /*value*/ = {}) { + return constant{}; +} +template +constexpr auto +size_constant_of(std::integral_constant /*value*/ = {}) { + return size_constant{}; +} +template +constexpr auto +bool_constant_of(std::integral_constant /*value*/ = {}) { + return bool_constant{}; +} + +#undef CTI__FOR_EACH_BOOLEAN_BIN_OP +#undef CTI__FOR_EACH_BOOLEAN_UNA_OP +#undef CTI__FOR_EACH_INTEGRAL_BIN_OP +#undef CTI__FOR_EACH_INTEGRAL_UNA_OP + +/// Evaluates to the element at position I. +template +using at_t = decltype(std::get(std::declval>())); + +/// Evaluates to an integral constant which represents the size +/// of the given pack. +template +using size_of_t = size_constant; + +/// A tagging type for wrapping other types +template +struct identity {}; +template +struct identity : std::common_type {}; + +template +struct is_identity : std::false_type {}; +template +struct is_identity> : std::true_type {}; + +template +identity> constexpr identity_of(T const& /*type*/) noexcept { + return {}; +} +template +constexpr identity identity_of(identity /*type*/) noexcept { + return {}; +} +template +constexpr auto identity_of() noexcept { + return std::conditional_t>::value, T, + identity>>{}; +} + +template +constexpr auto get(identity) noexcept { + return identity_of>(); +} + +/// Helper to trick compilers about that a parameter pack is used +template +void unused(T&&... args) { + auto use = [](auto&& type) mutable { + (void)type; + return 0; + }; + auto deduce = {0, use(std::forward(args))...}; + (void)deduce; + (void)use; +} + +namespace detail { +// Equivalent to C++17's std::void_t which targets a bug in GCC, +// that prevents correct SFINAE behavior. +// See http://stackoverflow.com/questions/35753920 for details. +template +struct deduce_to_void : std::common_type {}; +} // end namespace detail + +/// C++17 like void_t type +template +using void_t = typename detail::deduce_to_void::type; + +namespace detail { +template > +struct is_valid_impl : std::common_type {}; + +template +struct is_valid_impl()(std::declval()))>> + : std::common_type {}; + +template +constexpr void static_if_impl(std::true_type, Type&& type, + TrueCallback&& trueCallback) { + std::forward(trueCallback)(std::forward(type)); +} + +template +constexpr void static_if_impl(std::false_type, Type&& /*type*/, + TrueCallback&& /*trueCallback*/) { +} + +template +constexpr auto static_if_impl(std::true_type, Type&& type, + TrueCallback&& trueCallback, + FalseCallback&& /*falseCallback*/) { + return std::forward(trueCallback)(std::forward(type)); +} + +template +constexpr auto static_if_impl(std::false_type, Type&& type, + TrueCallback&& /*trueCallback*/, + FalseCallback&& falseCallback) { + return std::forward(falseCallback)(std::forward(type)); +} +} // end namespace detail + +/// Returns the pack size of the given type +template +constexpr auto pack_size_of(identity>) noexcept { + return size_of_t{}; +} +/// Returns the pack size of the given type +template +constexpr auto pack_size_of(identity>) noexcept { + return size_of_t{}; +} +/// Returns the pack size of the given type +template +constexpr auto pack_size_of(identity) noexcept { + return size_of_t{}; +} + +/// Returns an index sequence of the given type +template +constexpr auto sequence_of(T&& /*sequenceable*/) noexcept { + return std::make_index_sequence()))::value>(); +} + +/// Returns a check which returns a true type if the current value +/// is below the +template +constexpr auto is_less_than(size_constant end) noexcept { + return [=](auto current) { return end > current; }; +} + +/// Compile-time check for validating a certain expression +template +constexpr auto is_valid(T&& /*type*/, Check&& /*check*/) noexcept { + return typename detail::is_valid_impl::type{}; +} + +/// Creates a static functional validator object. +template +constexpr auto validator_of(Check&& check) noexcept( + std::is_nothrow_move_constructible>::value) { + return [check = std::forward(check)](auto&& matchable) { + return is_valid(std::forward(matchable), check); + }; +} + +/// Invokes the callback only if the given type matches the check +template +constexpr void static_if(Type&& type, Check&& check, + TrueCallback&& trueCallback) { + detail::static_if_impl(std::forward(check)(type), + std::forward(type), + std::forward(trueCallback)); +} + +/// Invokes the callback only if the given type matches the check +template +constexpr auto static_if(Type&& type, Check&& check, + TrueCallback&& trueCallback, + FalseCallback&& falseCallback) { + return detail::static_if_impl(std::forward(check)(type), + std::forward(type), + std::forward(trueCallback), + std::forward(falseCallback)); +} + +/// A compile-time while loop, which loops as long the value matches +/// the predicate. The handler shall return the next value. +template +constexpr auto static_while(Value&& value, Predicate&& predicate, + Handler&& handler) { + return static_if(std::forward(value), predicate, + [&](auto&& result) mutable { + return static_while( + handler(std::forward(result)), + std::forward(predicate), + std::forward(handler)); + }, + [&](auto&& result) mutable { + return std::forward(result); + }); +} + +/// Returns a validator which checks whether the given sequenceable is empty +inline auto is_empty() noexcept { + return [](auto const& checkable) { + return pack_size_of(checkable) == size_constant_of<0>(); + }; +} + +/// Calls the given unpacker with the content of the given sequence +template +constexpr auto unpack(std::integer_sequence, U&& unpacker) { + return std::forward(unpacker)(size_constant_of()...); +} + +/// Calls the given unpacker with the content of the given sequenceable +template +constexpr auto unpack(F&& firstSequenceable, U&& unpacker, + std::integer_sequence) { + using std::get; + (void)firstSequenceable; + return std::forward(unpacker)( + get(std::forward(firstSequenceable))...); +} +/// Calls the given unpacker with the content of the given sequenceable +template +constexpr auto unpack(F&& firstSequenceable, S&& secondSequenceable, + U&& unpacker, std::integer_sequence, + std::integer_sequence) { + using std::get; + (void)firstSequenceable; + (void)secondSequenceable; + return std::forward(unpacker)( + get(std::forward(firstSequenceable))..., + get(std::forward(secondSequenceable))...); +} +/// Calls the given unpacker with the content of the given sequenceable +template +auto unpack(F&& firstSequenceable, U&& unpacker) { + return unpack(std::forward(firstSequenceable), std::forward(unpacker), + sequence_of(identity_of(firstSequenceable))); +} +/// Calls the given unpacker with the content of the given sequenceables +template +constexpr auto unpack(F&& firstSequenceable, S&& secondSequenceable, + U&& unpacker) { + return unpack(std::forward(firstSequenceable), + std::forward(secondSequenceable), std::forward(unpacker), + sequence_of(identity_of(firstSequenceable)), + sequence_of(identity_of(secondSequenceable))); +} + +/// Applies the handler function to each element contained in the sequenceable +template +constexpr void static_for_each_in(Sequenceable&& sequenceable, + Handler&& handler) { + unpack( + std::forward(sequenceable), [&](auto&&... entries) mutable { + auto consume = [&](auto&& entry) mutable { + handler(std::forward(entry)); + return 0; + }; + // Apply the consume function to every entry inside the pack + auto deduce = {0, consume(std::forward(entries))...}; + (void)deduce; + (void)consume; + }); +} + +/// Adds the given type at the back of the left sequenceable +template +constexpr auto push(Left&& left, Element&& element) { + return unpack(std::forward(left), [&](auto&&... leftArgs) { + return std::make_tuple(std::forward(leftArgs)..., + std::forward(element)); + }); +} + +/// Adds the element to the back of the identity +template +constexpr auto push(identity, identity) noexcept { + return identity{}; +} + +/// Removes the first element from the identity +template +constexpr auto pop_first(identity) noexcept { + return identity{}; +} + +/// Returns the merged sequence +template +constexpr auto merge(Left&& left) { + return std::forward(left); +} +/// Merges the left sequenceable with the right ones +template +constexpr auto merge(Left&& left, Right&& right, Rest&&... rest) { + // Merge the left with the right sequenceable and + // merge the result with the rest. + return merge(unpack(std::forward(left), std::forward(right), + [&](auto&&... args) { + // Maybe use: template class T, + // typename... Args> + return std::make_tuple( + std::forward(args)...); + }), + std::forward(rest)...); +} +/// Merges the left identity with the right ones +template +constexpr auto merge(identity /*left*/, + identity /*right*/, Rest&&... rest) { + return merge(identity{}, + std::forward(rest)...); +} + +/// Combines the given arguments with the given folding function +template +constexpr auto fold(F&& /*folder*/, First&& first) { + return std::forward(first); +} +/// Combines the given arguments with the given folding function +template +auto fold(F&& folder, First&& first, Second&& second, Rest&&... rest) { + auto res = folder(std::forward(first), std::forward(second)); + return fold(std::forward(folder), std::move(res), + std::forward(rest)...); +} + +/// Returns a folding function using operator `&&`. +inline auto and_folding() noexcept { + return [](auto&& left, auto&& right) { + return std::forward(left) && + std::forward(right); + }; +} +/// Returns a folding function using operator `||`. +inline auto or_folding() noexcept { + return [](auto&& left, auto&& right) { + return std::forward(left) || + std::forward(right); + }; +} +/// Returns a folding function using operator `>>`. +inline auto seq_folding() noexcept { + return [](auto&& left, auto&& right) { + return std::forward(left) >> + std::forward(right); + }; +} + +/// Deduces to a std::false_type +template +using fail = std::integral_constant::value>; +} // namespace traits } // namespace detail } // namespace cti +#define CONTINUABLE_CONSTEXPR_IF(EXPR, TRUE_BRANCH, FALSE_BRANCH) +#define CONTINUABLE_FOLD_EXPRESSION(OPERATOR, SEQUENCE) + #endif // CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED__ diff --git a/include/continuable/detail/transforms.hpp b/include/continuable/detail/transforms.hpp index d4687d3..4cb7bc3 100644 --- a/include/continuable/detail/transforms.hpp +++ b/include/continuable/detail/transforms.hpp @@ -100,7 +100,7 @@ public: template auto as_future(continuable_base&& continuable) { // Create the promise which is able to supply the current arguments - auto hint = base::hint_of(util::identity_of(continuable)); + auto hint = base::hint_of(traits::identity_of(continuable)); promise_callback> callback; (void)hint; diff --git a/include/continuable/detail/util.hpp b/include/continuable/detail/util.hpp index 9507de9..79d4407 100644 --- a/include/continuable/detail/util.hpp +++ b/include/continuable/detail/util.hpp @@ -36,434 +36,21 @@ #include #include +#include +#include namespace cti { 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__(>) -#define CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__OP__) CTI__OP__(!) -#define CTI__FOR_EACH_INTEGRAL_BIN_OP(CTI__OP__) \ - CTI__OP__(*) \ - 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) \ - const noexcept { \ - return constant{}; \ - } - CTI__FOR_EACH_INTEGRAL_BIN_OP(CTI__INST) -#undef CTI__INST -#define CTI__INST(CTI__OP) \ - /*constexpr*/ auto operator CTI__OP() const noexcept { \ - return 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) \ - const noexcept { \ - return constant{}; \ - } - CTI__FOR_EACH_BOOLEAN_BIN_OP(CTI__INST) -#undef CTI__INST -#define CTI__INST(CTI__OP) \ - /*constexpr*/ auto operator CTI__OP() const noexcept { \ - return constant{}; \ - } - CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__INST) -#undef CTI__INST - /// \endcond -}; - -template -using bool_constant = constant; -template -using size_constant = constant; - -template -constexpr auto constant_of(std::integral_constant /*value*/ = {}) { - return constant{}; -} -template -constexpr auto -size_constant_of(std::integral_constant /*value*/ = {}) { - return size_constant{}; -} -template -constexpr auto -bool_constant_of(std::integral_constant /*value*/ = {}) { - return bool_constant{}; -} - -#undef CTI__FOR_EACH_BOOLEAN_BIN_OP -#undef CTI__FOR_EACH_BOOLEAN_UNA_OP -#undef CTI__FOR_EACH_INTEGRAL_BIN_OP -#undef CTI__FOR_EACH_INTEGRAL_UNA_OP - -/// Evaluates to the element at position I. -template -using at_t = decltype(std::get(std::declval>())); - -/// Evaluates to an integral constant which represents the size -/// of the given pack. -template -using size_of_t = size_constant; - -/// A tagging type for wrapping other types -template -struct identity {}; -template -struct identity : std::common_type {}; - -template -struct is_identity : std::false_type {}; -template -struct is_identity> : std::true_type {}; - -template -identity> constexpr identity_of(T const& /*type*/) noexcept { - return {}; -} -template -constexpr identity identity_of(identity /*type*/) noexcept { - return {}; -} -template -constexpr auto identity_of() noexcept { - return std::conditional_t>::value, T, - identity>>{}; -} - -template -constexpr auto get(identity) noexcept { - return identity_of>(); -} - -/// Helper to trick compilers about that a parameter pack is used -template -void unused(T&&... args) { - auto use = [](auto&& type) mutable { - (void)type; - return 0; - }; - auto deduce = {0, use(std::forward(args))...}; - (void)deduce; - (void)use; -} - namespace detail { -// Equivalent to C++17's std::void_t which targets a bug in GCC, -// that prevents correct SFINAE behavior. -// See http://stackoverflow.com/questions/35753920 for details. -template -struct deduce_to_void : std::common_type {}; -} // end namespace detail - -/// C++17 like void_t type -template -using void_t = typename detail::deduce_to_void::type; - -namespace detail { -template > -struct is_valid_impl : std::common_type {}; - -template -struct is_valid_impl()(std::declval()))>> - : std::common_type {}; - -template -constexpr void static_if_impl(std::true_type, Type&& type, - TrueCallback&& trueCallback) { - std::forward(trueCallback)(std::forward(type)); -} - -template -constexpr void static_if_impl(std::false_type, Type&& /*type*/, - TrueCallback&& /*trueCallback*/) { -} - -template -constexpr auto static_if_impl(std::true_type, Type&& type, - TrueCallback&& trueCallback, - FalseCallback&& /*falseCallback*/) { - return std::forward(trueCallback)(std::forward(type)); -} - -template -constexpr auto static_if_impl(std::false_type, Type&& type, - TrueCallback&& /*trueCallback*/, - FalseCallback&& falseCallback) { - return std::forward(falseCallback)(std::forward(type)); -} -} // end namespace detail - -/// Returns the pack size of the given type -template -constexpr auto pack_size_of(identity>) noexcept { - return size_of_t{}; -} -/// Returns the pack size of the given type -template -constexpr auto pack_size_of(identity>) noexcept { - return size_of_t{}; -} -/// Returns the pack size of the given type -template -constexpr auto pack_size_of(identity) noexcept { - return size_of_t{}; -} - -/// Returns an index sequence of the given type -template -constexpr auto sequence_of(T&& /*sequenceable*/) noexcept { - return std::make_index_sequence()))::value>(); -} - -/// Returns a check which returns a true type if the current value -/// is below the -template -constexpr auto is_less_than(size_constant end) noexcept { - return [=](auto current) { return end > current; }; -} - -/// Compile-time check for validating a certain expression -template -constexpr auto is_valid(T&& /*type*/, Check&& /*check*/) noexcept { - return typename detail::is_valid_impl::type{}; -} - -/// Creates a static functional validator object. -template -constexpr auto validator_of(Check&& check) noexcept( - std::is_nothrow_move_constructible>::value) { - return [check = std::forward(check)](auto&& matchable) { - return is_valid(std::forward(matchable), check); - }; -} - -/// Invokes the callback only if the given type matches the check -template -constexpr void static_if(Type&& type, Check&& check, - TrueCallback&& trueCallback) { - detail::static_if_impl(std::forward(check)(type), - std::forward(type), - std::forward(trueCallback)); -} - -/// Invokes the callback only if the given type matches the check -template -constexpr auto static_if(Type&& type, Check&& check, - TrueCallback&& trueCallback, - FalseCallback&& falseCallback) { - return detail::static_if_impl(std::forward(check)(type), - std::forward(type), - std::forward(trueCallback), - std::forward(falseCallback)); -} - -/// A compile-time while loop, which loops as long the value matches -/// the predicate. The handler shall return the next value. -template -constexpr auto static_while(Value&& value, Predicate&& predicate, - Handler&& handler) { - return static_if(std::forward(value), predicate, - [&](auto&& result) mutable { - return static_while( - handler(std::forward(result)), - std::forward(predicate), - std::forward(handler)); - }, - [&](auto&& result) mutable { - return std::forward(result); - }); -} - -/// Returns a validator which checks whether the given sequenceable is empty -inline auto is_empty() noexcept { - return [](auto const& checkable) { - return pack_size_of(checkable) == size_constant_of<0>(); - }; -} - -/// Calls the given unpacker with the content of the given sequence -template -constexpr auto unpack(std::integer_sequence, U&& unpacker) { - return std::forward(unpacker)(size_constant_of()...); -} - -/// Calls the given unpacker with the content of the given sequenceable -template -constexpr auto unpack(F&& firstSequenceable, U&& unpacker, - std::integer_sequence) { - using std::get; - (void)firstSequenceable; - return std::forward(unpacker)( - get(std::forward(firstSequenceable))...); -} -/// Calls the given unpacker with the content of the given sequenceable -template -constexpr auto unpack(F&& firstSequenceable, S&& secondSequenceable, - U&& unpacker, std::integer_sequence, - std::integer_sequence) { - using std::get; - (void)firstSequenceable; - (void)secondSequenceable; - return std::forward(unpacker)( - get(std::forward(firstSequenceable))..., - get(std::forward(secondSequenceable))...); -} -/// Calls the given unpacker with the content of the given sequenceable -template -auto unpack(F&& firstSequenceable, U&& unpacker) { - return unpack(std::forward(firstSequenceable), std::forward(unpacker), - sequence_of(identity_of(firstSequenceable))); -} -/// Calls the given unpacker with the content of the given sequenceables -template -constexpr auto unpack(F&& firstSequenceable, S&& secondSequenceable, - U&& unpacker) { - return unpack(std::forward(firstSequenceable), - std::forward(secondSequenceable), std::forward(unpacker), - sequence_of(identity_of(firstSequenceable)), - sequence_of(identity_of(secondSequenceable))); -} - -/// Applies the handler function to each element contained in the sequenceable -template -constexpr void static_for_each_in(Sequenceable&& sequenceable, - Handler&& handler) { - unpack( - std::forward(sequenceable), [&](auto&&... entries) mutable { - auto consume = [&](auto&& entry) mutable { - handler(std::forward(entry)); - return 0; - }; - // Apply the consume function to every entry inside the pack - auto deduce = {0, consume(std::forward(entries))...}; - (void)deduce; - (void)consume; - }); -} - -/// Adds the given type at the back of the left sequenceable -template -constexpr auto push(Left&& left, Element&& element) { - return unpack(std::forward(left), [&](auto&&... leftArgs) { - return std::make_tuple(std::forward(leftArgs)..., - std::forward(element)); - }); -} - -/// Adds the element to the back of the identity -template -constexpr auto push(identity, identity) noexcept { - return identity{}; -} - -/// Removes the first element from the identity -template -constexpr auto pop_first(identity) noexcept { - return identity{}; -} - -/// Returns the merged sequence -template -constexpr auto merge(Left&& left) { - return std::forward(left); -} -/// Merges the left sequenceable with the right ones -template -constexpr auto merge(Left&& left, Right&& right, Rest&&... rest) { - // Merge the left with the right sequenceable and - // merge the result with the rest. - return merge(unpack(std::forward(left), std::forward(right), - [&](auto&&... args) { - // Maybe use: template class T, - // typename... Args> - return std::make_tuple( - std::forward(args)...); - }), - std::forward(rest)...); -} -/// Merges the left identity with the right ones -template -constexpr auto merge(identity /*left*/, - identity /*right*/, Rest&&... rest) { - return merge(identity{}, - std::forward(rest)...); -} - -/// Combines the given arguments with the given folding function -template -constexpr auto fold(F&& /*folder*/, First&& first) { - return std::forward(first); -} -/// Combines the given arguments with the given folding function -template -auto fold(F&& folder, First&& first, Second&& second, Rest&&... rest) { - auto res = folder(std::forward(first), std::forward(second)); - return fold(std::forward(folder), std::move(res), - std::forward(rest)...); -} - -/// Returns a folding function using operator `&&`. -inline auto and_folding() noexcept { - return [](auto&& left, auto&& right) { - return std::forward(left) && - std::forward(right); - }; -} -/// Returns a folding function using operator `||`. -inline auto or_folding() noexcept { - return [](auto&& left, auto&& right) { - return std::forward(left) || - std::forward(right); - }; -} -/// Returns a folding function using operator `>>`. -inline auto seq_folding() noexcept { - return [](auto&& left, auto&& right) { - return std::forward(left) >> - std::forward(right); - }; -} - -/// Deduces to a std::false_type -template -using fail = std::integral_constant::value>; - -namespace detail { -template > +template > struct is_invokable_impl : std::common_type {}; template struct is_invokable_impl< T, std::tuple, - void_t()(std::declval()...))>> + traits::void_t()(std::declval()...))>> : std::common_type {}; } // end namespace detail @@ -474,7 +61,7 @@ struct is_invokable_impl< /// arguments inside lambda closures. /// /// ```cpp -/// util::is_invokable_t> +/// traits::is_invokable_t> /// ``` template using is_invokable_t = typename detail::is_invokable_impl::type; @@ -483,22 +70,23 @@ namespace detail { /// Forwards every element in the tuple except the last one template auto forward_except_last(T&& sequenceable) { - auto size = pack_size_of(identity_of(sequenceable)) - size_constant_of<1>(); + auto size = pack_size_of(traits::identity_of(sequenceable)) - + traits::size_constant_of<1>(); auto sequence = std::make_index_sequence(); - return unpack(std::forward(sequenceable), - [](auto&&... args) { - return std::forward_as_tuple( - std::forward(args)...); - }, - sequence); + return traits::unpack(std::forward(sequenceable), + [](auto&&... args) { + return std::forward_as_tuple( + std::forward(args)...); + }, + sequence); } /// We are able to call the callable with the arguments given in the tuple template auto partial_invoke_impl(std::true_type, T&& callable, std::tuple args) { - return unpack(std::move(args), [&](auto&&... arg) { + return traits::unpack(std::move(args), [&](auto&&... arg) { return std::forward(callable)(std::forward(arg)...); }); } diff --git a/test/unit-test/test-continuable-regression.cpp b/test/unit-test/test-continuable-regression.cpp index ce39766..a801ab3 100644 --- a/test/unit-test/test-continuable-regression.cpp +++ b/test/unit-test/test-continuable-regression.cpp @@ -31,19 +31,20 @@ using namespace cti::detail; TEST(regression_tests, are_multiple_args_mergeable) { { auto tp = std::make_tuple(1, 2, 3); - util::merge(tp, tp, tp, tp, tp); + traits::merge(tp, tp, tp, tp, tp); } - auto tp2 = - util::merge(std::make_tuple(), std::make_tuple(1), std::make_tuple(1, 2), - std::make_tuple(1, 2, 3), std::make_tuple(1, 2, 3, 4)); + auto tp2 = traits::merge(std::make_tuple(), std::make_tuple(1), + std::make_tuple(1, 2), std::make_tuple(1, 2, 3), + std::make_tuple(1, 2, 3, 4)); - auto count = util::unpack( - tp2, [](auto... args) { return util::fold(std::plus{}, args...); }); + auto count = traits::unpack(tp2, [](auto... args) { + return traits::fold(std::plus{}, args...); + }); EXPECT_EQ(count, 20); } TEST(recursion_tests, are_noncopyable_mergeable) { std::tuple nc1, nc2, nc3; - util::merge(std::move(nc1), std::move(nc2), std::move(nc3)); + traits::merge(std::move(nc1), std::move(nc2), std::move(nc3)); } diff --git a/test/unit-test/test-continuable.hpp b/test/unit-test/test-continuable.hpp index 1f70417..9143cdc 100644 --- a/test/unit-test/test-continuable.hpp +++ b/test/unit-test/test-continuable.hpp @@ -46,7 +46,8 @@ #include "cxx_function/cxx_function.hpp" -template using cxx_function_fn = cxx_function::function; +template +using cxx_function_fn = cxx_function::function; template using cxx_trait_of = @@ -75,17 +76,21 @@ using std_trait_of = template using std_continuable = typename std_trait_of::continuable; -using cti::detail::util::identity; +using cti::detail::traits::identity; -inline auto to_hint(identity<> /*hint*/) { return identity{}; } -template auto to_hint(identity hint) { +inline auto to_hint(identity<> /*hint*/) { + return identity{}; +} +template +auto to_hint(identity hint) { return hint; } -template auto supplier_of(Args&&... args) { +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) { + cti::detail::traits::unpack(std::move(values), [&](auto&&... passed) { // ... std::forward(callback)( std::forward(passed)...); @@ -96,14 +101,16 @@ template auto supplier_of(Args&&... args) { template class continuation_provider : public ::testing::Test, public Provider { public: - template auto invoke(T&& type) { + 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) { + template + auto supply(Args&&... args) { identity...> arg_types; auto hint_types = to_hint(arg_types); @@ -142,14 +149,16 @@ struct provide_unique { } }; -template