From bd68d14b34470abd56bbb1bfd6722827e0e728e1 Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Sun, 24 Sep 2017 19:48:12 +0200 Subject: [PATCH] Split the header into multiple files --- .editorconfig | 2 +- include/continuable/continuable-base.hpp | 671 +----------------- include/continuable/continuable-testing.hpp | 8 +- include/continuable/continuable.hpp | 8 +- include/continuable/detail/api.hpp | 80 +++ .../{continuable-abi-begin.inl => base.hpp} | 15 +- include/continuable/detail/composition.hpp | 42 ++ include/continuable/detail/features.hpp | 42 ++ .../{continuable-abi-end.inl => hints.hpp} | 15 +- include/continuable/detail/traits.hpp | 42 ++ include/continuable/detail/util.hpp | 648 +++++++++++++++++ test/playground/CMakeLists.txt | 22 +- 12 files changed, 914 insertions(+), 681 deletions(-) create mode 100644 include/continuable/detail/api.hpp rename include/continuable/detail/{continuable-abi-begin.inl => base.hpp} (81%) create mode 100644 include/continuable/detail/composition.hpp create mode 100644 include/continuable/detail/features.hpp rename include/continuable/detail/{continuable-abi-end.inl => hints.hpp} (80%) create mode 100644 include/continuable/detail/traits.hpp create mode 100644 include/continuable/detail/util.hpp diff --git a/.editorconfig b/.editorconfig index a148dfe..7153da4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,5 +7,5 @@ insert_final_newline = true trim_trailing_whitespace = true max_line_length = 80 -[*.{cpp,hpp}] +[*.{c,h,cpp,hpp,inl}] charset = latin1 diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index 3891e67..f22d353 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -5,7 +5,7 @@ \_,(_)| | | || ||_|(_||_)|(/_ https://github.com/Naios/continuable - v1.1.0 + v2.0.0 Copyright(c) 2015 - 2017 Denis Blank @@ -41,658 +41,12 @@ #include #include -/// Declares the continuable library namespace. -/// -/// The most important class is cti::continuable_base, that provides the -/// whole functionality for continuation chaining. -/// -/// The class cti::continuable_base is created through the -/// cti::make_continuable() function which accepts a callback taking function. -/// -/// Also there are following support functions available: -/// - cti::when_all() - connects cti::continuable_base's to an `all` connection. -/// - cti::when_any() - connects cti::continuable_base's to an `any` connection. -/// - cti::when_seq() - connects cti::continuable_base's to a sequence. -/// +#include "continuable/detail/api.hpp" +#include "continuable/detail/traits.hpp" +#include "continuable/detail/util.hpp" + namespace cti { -/// \cond false -inline namespace abi_v1 { -/// \endcond - -/// The main class of the continuable library, it provides the functionality -/// for chaining callbacks and continuations together to a unified hierarchy. -/// -/// The most important method is the cti::continuable_base::then() method, -/// which allows to attach a callback to the continuable. -/// -/// \tparam Data The internal data which is used to store the current -/// continuation and intermediate lazy connection result. -/// -/// \tparam Annotation The internal data used to store the current signature -/// hint or strategy used for combining lazy connections. -/// -/// \note Nearly all methods of the cti::continuable_base are required to be -/// called as r-value. This is required because the continuable carries -/// variables which are consumed when the object is transformed as part -/// of a method call. You may copy a continuable which underlying -/// storages are copyable to split the call hierarchy into multiple parts. -/// -/// \attention The continuable_base objects aren't intended to be stored. -/// If you want to store a continuble_base you should always -/// call the continuable_base::freeze method for disabling the -/// invocation on destruction. -/// -/// \since version 1.0.0 -template -class continuable_base; - -/// Declares the internal private namespace of the continuable library -/// which isn't intended to be used by users of the library. 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 sequenceOf(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 isLessThen(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 validatorOf(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), - sequenceOf(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), - sequenceOf(identity_of(firstSequenceable)), - sequenceOf(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 > -struct is_invokable_impl : std::common_type {}; - -template -struct is_invokable_impl< - T, std::tuple, - void_t()(std::declval()...))>> - : std::common_type {}; -} // end namespace detail - -/// Deduces to a std::true_type if the given type is callable with the arguments -/// inside the given tuple. -/// The main reason for implementing it with the detection idiom instead of -/// hana like detection is that MSVC has issues with capturing raw template -/// arguments inside lambda closures. -/// -/// ```cpp -/// util::is_invokable_t> -/// ``` -template -using is_invokable_t = typename detail::is_invokable_impl::type; - -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 sequence = std::make_index_sequence(); - - return 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 std::forward(callable)(std::forward(arg)...); - }); -} - -/// We were unable to call the callable with the arguments in the tuple. -/// Remove the last argument from the tuple and try it again. -template -auto partial_invoke_impl(std::false_type, T&& callable, - std::tuple args) { - - // If you are encountering this assertion you tried to attach a callback - // which can't accept the arguments of the continuation. - // - // ```cpp - // continuable c; - // std::move(c).then([](std::vector v) { /*...*/ }) - // ``` - static_assert( - sizeof...(Args) > 0, - "There is no way to call the given object with these arguments!"); - - // Remove the last argument from the tuple - auto next = forward_except_last(std::move(args)); - - // Test whether we are able to call the function with the given tuple - is_invokable_t is_invokable; - - return partial_invoke_impl(is_invokable, std::forward(callable), - std::move(next)); -} - -/// Shortcut - we can call the callable directly -template -auto partial_invoke_impl_shortcut(std::true_type, T&& callable, - Args&&... args) { - return std::forward(callable)(std::forward(args)...); -} - -/// Failed shortcut - we were unable to invoke the callable with the -/// original arguments. -template -auto partial_invoke_impl_shortcut(std::false_type failed, T&& callable, - Args&&... args) { - - // Our shortcut failed, convert the arguments into a forwarding tuple - return partial_invoke_impl( - failed, std::forward(callable), - std::forward_as_tuple(std::forward(args)...)); -} -} // end namespace detail - -/// Partially invokes the given callable with the given arguments. -/// -/// \note This function will assert statically if there is no way to call the -/// given object with less arguments. -template -auto partial_invoke(T&& callable, Args&&... args) { - // Test whether we are able to call the function with the given arguments. - is_invokable_t> is_invokable; - - // The implementation is done in a shortcut way so there are less - // type instantiations needed to call the callable with its full signature. - return detail::partial_invoke_impl_shortcut( - is_invokable, std::forward(callable), std::forward(args)...); -} - -// Class for making child classes non copyable -struct non_copyable { - constexpr non_copyable() = default; - non_copyable(non_copyable const&) = delete; - constexpr non_copyable(non_copyable&&) = default; - non_copyable& operator=(non_copyable const&) = delete; - non_copyable& operator=(non_copyable&&) = default; -}; - -// Class for making child classes non copyable and movable -struct non_movable { - constexpr non_movable() = default; - non_movable(non_movable const&) = delete; - constexpr non_movable(non_movable&&) = delete; - non_movable& operator=(non_movable const&) = delete; - non_movable& operator=(non_movable&&) = delete; -}; -} // end namespace util - -/// This class is responsible for holding an abstract copy- and -/// move-able ownership that is invalidated when the object -/// is moved to another instance. -class ownership { - explicit constexpr ownership(bool acquired, bool frozen) - : acquired_(acquired), frozen_(frozen) { - } - -public: - constexpr ownership() : acquired_(true), frozen_(false) { - } - constexpr ownership(ownership const&) = default; - ownership(ownership&& right) noexcept - : acquired_(right.consume()), frozen_(right.is_frozen()) { - } - ownership& operator=(ownership const&) = default; - ownership& operator=(ownership&& right) noexcept { - acquired_ = right.consume(); - frozen_ = right.is_frozen(); - return *this; - } - - // Merges both ownerships together - ownership operator|(ownership const& right) const noexcept { - return ownership(is_acquired() && right.is_acquired(), - is_frozen() || right.is_frozen()); - } - - constexpr bool is_acquired() const noexcept { - return acquired_; - } - constexpr bool is_frozen() const noexcept { - return frozen_; - } - - void release() noexcept { - assert(is_acquired() && "Tried to release the ownership twice!"); - acquired_ = false; - } - void freeze(bool enabled = true) noexcept { - assert(is_acquired() && "Tried to freeze a released object!"); - frozen_ = enabled; - } - -private: - bool consume() noexcept { - if (is_acquired()) { - release(); - return true; - } - return false; - } - - /// Is true when the object is in a valid state - bool acquired_ : 1; - /// Is true when the automatic invocation on destruction is disabled - bool frozen_ : 1; -}; - /// Represents a present signature hint template using signature_hint_tag = util::identity; @@ -745,7 +99,7 @@ struct is_continuation> : std::true_type {}; struct attorney { /// Makes a continuation wrapper from the given argument template - static auto create(T&& continuation, A /*hint*/, ownership ownership_) { + static auto create(T&& continuation, A /*hint*/, util::ownership ownership_) { return continuable_base, std::decay_t>( std::forward(continuation), ownership_); } @@ -772,7 +126,7 @@ struct attorney { } template - static ownership ownership_of(Continuable&& continuation) { + static util::ownership ownership_of(Continuable&& continuation) { return continuation.ownership_; } }; @@ -1533,11 +887,11 @@ class continuable_base { // The continuation type or intermediate result Data data_; // The transferable state which represents the validity of the object - detail::ownership ownership_; + detail::util::ownership ownership_; /// \endcond /// Constructor accepting the data object while erasing the annotation - explicit continuable_base(Data data, detail::ownership ownership) + explicit continuable_base(Data data, detail::util::ownership ownership) : data_(std::move(data)), ownership_(std::move(ownership)) { } @@ -2007,7 +1361,8 @@ auto make_continuable(Continuation&& continuation) { detail::util::identity{}); return detail::base::attorney::create( - std::forward(continuation), hint, detail::ownership{}); + std::forward(continuation), hint, + detail::util::ownership{}); } /// Connects the given continuables with an *all* logic. @@ -2116,10 +1471,6 @@ struct continuable_trait { using continuable = continuable_base>; }; - -/// \cond false -} // namespace abi_v1 -/// \endcond } // end namespace cti #endif // CONTINUABLE_BASE_HPP_INCLUDED__ diff --git a/include/continuable/continuable-testing.hpp b/include/continuable/continuable-testing.hpp index c51e25b..066c48c 100644 --- a/include/continuable/continuable-testing.hpp +++ b/include/continuable/continuable-testing.hpp @@ -5,7 +5,7 @@ \_,(_)| | | || ||_|(_||_)|(/_ https://github.com/Naios/continuable - v1.1.0 + v2.0.0 Copyright(c) 2015 - 2017 Denis Blank @@ -36,9 +36,6 @@ #include "continuable/continuable-base.hpp" namespace cti { -/// \cond false -inline namespace abi_v1 { -/// \endcond namespace detail { namespace testing { template void assert_async_completion(C&& continuable) { @@ -125,9 +122,6 @@ void assert_async_types(C&& continuable, util::identity expected) { } } // end namespace testing } // end namespace detail -/// \cond false -} // end inline namespace abi_... -/// \endcond } // end namespace cti /// Asserts that the final callback of the given continuable was called diff --git a/include/continuable/continuable.hpp b/include/continuable/continuable.hpp index 6c7c397..a5bb049 100644 --- a/include/continuable/continuable.hpp +++ b/include/continuable/continuable.hpp @@ -5,7 +5,7 @@ \_,(_)| | | || ||_|(_||_)|(/_ https://github.com/Naios/continuable - v1.1.0 + v2.0.0 Copyright(c) 2015 - 2017 Denis Blank @@ -36,9 +36,6 @@ namespace cti { // clang-format off -/// \cond false -inline namespace abi_v1 { -/// \endcond namespace detail { template using trait_of = continuable_trait< @@ -90,9 +87,6 @@ template using unique_callback = typename detail::unique_trait_of< Args... >::callback; -/// \cond false -} // end inline namespace abi_... -/// \endcond // clang-format on } // end namespace cti diff --git a/include/continuable/detail/api.hpp b/include/continuable/detail/api.hpp new file mode 100644 index 0000000..0933d2e --- /dev/null +++ b/include/continuable/detail/api.hpp @@ -0,0 +1,80 @@ + +/** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v2.0.0 + + Copyright(c) 2015 - 2017 Denis Blank + + 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_DETAIL_API_HPP_INCLUDED__ +#define CONTINUABLE_DETAIL_API_HPP_INCLUDED__ + +/// Declares the continuable library namespace. +/// +/// The most important class is cti::continuable_base, that provides the +/// whole functionality for continuation chaining. +/// +/// The class cti::continuable_base is created through the +/// cti::make_continuable() function which accepts a callback taking function. +/// +/// Also there are following support functions available: +/// - cti::when_all() - connects cti::continuable_base's to an `all` connection. +/// - cti::when_any() - connects cti::continuable_base's to an `any` connection. +/// - cti::when_seq() - connects cti::continuable_base's to a sequence. +/// +namespace cti { +/// The main class of the continuable library, it provides the functionality +/// for chaining callbacks and continuations together to a unified hierarchy. +/// +/// The most important method is the cti::continuable_base::then() method, +/// which allows to attach a callback to the continuable. +/// +/// \tparam Data The internal data which is used to store the current +/// continuation and intermediate lazy connection result. +/// +/// \tparam Annotation The internal data used to store the current signature +/// hint or strategy used for combining lazy connections. +/// +/// \note Nearly all methods of the cti::continuable_base are required to be +/// called as r-value. This is required because the continuable carries +/// variables which are consumed when the object is transformed as part +/// of a method call. You may copy a continuable which underlying +/// storages are copyable to split the call hierarchy into multiple parts. +/// +/// \attention The continuable_base objects aren't intended to be stored. +/// If you want to store a continuble_base you should always +/// call the continuable_base::freeze method for disabling the +/// invocation on destruction. +/// +/// \since version 1.0.0 +template +class continuable_base; + +/// Declares the internal private namespace of the continuable library +/// which isn't intended to be used by users of the library. +namespace detail {} +} // namespace cti + +#endif // CONTINUABLE_DETAIL_API_HPP_INCLUDED__ diff --git a/include/continuable/detail/continuable-abi-begin.inl b/include/continuable/detail/base.hpp similarity index 81% rename from include/continuable/detail/continuable-abi-begin.inl rename to include/continuable/detail/base.hpp index b43401a..9faef4d 100644 --- a/include/continuable/detail/continuable-abi-begin.inl +++ b/include/continuable/detail/base.hpp @@ -5,7 +5,7 @@ \_,(_)| | | || ||_|(_||_)|(/_ https://github.com/Naios/continuable - v1.1.0 + v2.0.0 Copyright(c) 2015 - 2017 Denis Blank @@ -28,4 +28,15 @@ SOFTWARE. **/ -inline namespace abi_v7 { +#ifndef CONTINUABLE_DETAIL_BASE_HPP_INCLUDED__ +#define CONTINUABLE_DETAIL_BASE_HPP_INCLUDED__ + +#include "continuable/detail/api.hpp" + +namespace cti { +namespace detail { +// +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_BASE_HPP_INCLUDED__ diff --git a/include/continuable/detail/composition.hpp b/include/continuable/detail/composition.hpp new file mode 100644 index 0000000..7e32f7e --- /dev/null +++ b/include/continuable/detail/composition.hpp @@ -0,0 +1,42 @@ + +/** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v2.0.0 + + Copyright(c) 2015 - 2017 Denis Blank + + 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_DETAIL_COMPOSITION_HPP_INCLUDED__ +#define CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED__ + +#include "continuable/detail/api.hpp" + +namespace cti { +namespace detail { +// +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_COMPOSITION_HPP_INCLUDED__ diff --git a/include/continuable/detail/features.hpp b/include/continuable/detail/features.hpp new file mode 100644 index 0000000..040772c --- /dev/null +++ b/include/continuable/detail/features.hpp @@ -0,0 +1,42 @@ + +/** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v2.0.0 + + Copyright(c) 2015 - 2017 Denis Blank + + 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_DETAIL_FEATURES_HPP_INCLUDED__ +#define CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED__ + +#include "continuable/detail/api.hpp" + +namespace cti { +namespace detail { +// +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_FEATURES_HPP_INCLUDED__ diff --git a/include/continuable/detail/continuable-abi-end.inl b/include/continuable/detail/hints.hpp similarity index 80% rename from include/continuable/detail/continuable-abi-end.inl rename to include/continuable/detail/hints.hpp index d76758a..1fbf513 100644 --- a/include/continuable/detail/continuable-abi-end.inl +++ b/include/continuable/detail/hints.hpp @@ -5,7 +5,7 @@ \_,(_)| | | || ||_|(_||_)|(/_ https://github.com/Naios/continuable - v1.1.0 + v2.0.0 Copyright(c) 2015 - 2017 Denis Blank @@ -28,4 +28,15 @@ SOFTWARE. **/ -} +#ifndef CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED__ +#define CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED__ + +#include "continuable/detail/api.hpp" + +namespace cti { +namespace detail { +// +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_HINTS_HPP_INCLUDED__ diff --git a/include/continuable/detail/traits.hpp b/include/continuable/detail/traits.hpp new file mode 100644 index 0000000..aaeb3f3 --- /dev/null +++ b/include/continuable/detail/traits.hpp @@ -0,0 +1,42 @@ + +/** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v2.0.0 + + Copyright(c) 2015 - 2017 Denis Blank + + 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_DETAIL_TRAITS_HPP_INCLUDED__ +#define CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED__ + +#include "continuable/detail/api.hpp" + +namespace cti { +namespace detail { +// +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED__ diff --git a/include/continuable/detail/util.hpp b/include/continuable/detail/util.hpp new file mode 100644 index 0000000..dc9e79d --- /dev/null +++ b/include/continuable/detail/util.hpp @@ -0,0 +1,648 @@ + +/** + + /~` _ _ _|_. _ _ |_ | _ + \_,(_)| | | || ||_|(_||_)|(/_ + + https://github.com/Naios/continuable + v2.0.0 + + Copyright(c) 2015 - 2017 Denis Blank + + 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_DETAIL_UTIL_HPP_INCLUDED__ +#define CONTINUABLE_DETAIL_UTIL_HPP_INCLUDED__ + +#include +#include +#include + +#include "continuable/detail/api.hpp" + +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 sequenceOf(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 isLessThen(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 validatorOf(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), + sequenceOf(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), + sequenceOf(identity_of(firstSequenceable)), + sequenceOf(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 > +struct is_invokable_impl : std::common_type {}; + +template +struct is_invokable_impl< + T, std::tuple, + void_t()(std::declval()...))>> + : std::common_type {}; +} // end namespace detail + +/// Deduces to a std::true_type if the given type is callable with the arguments +/// inside the given tuple. +/// The main reason for implementing it with the detection idiom instead of +/// hana like detection is that MSVC has issues with capturing raw template +/// arguments inside lambda closures. +/// +/// ```cpp +/// util::is_invokable_t> +/// ``` +template +using is_invokable_t = typename detail::is_invokable_impl::type; + +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 sequence = std::make_index_sequence(); + + return 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 std::forward(callable)(std::forward(arg)...); + }); +} + +/// We were unable to call the callable with the arguments in the tuple. +/// Remove the last argument from the tuple and try it again. +template +auto partial_invoke_impl(std::false_type, T&& callable, + std::tuple args) { + + // If you are encountering this assertion you tried to attach a callback + // which can't accept the arguments of the continuation. + // + // ```cpp + // continuable c; + // std::move(c).then([](std::vector v) { /*...*/ }) + // ``` + static_assert( + sizeof...(Args) > 0, + "There is no way to call the given object with these arguments!"); + + // Remove the last argument from the tuple + auto next = forward_except_last(std::move(args)); + + // Test whether we are able to call the function with the given tuple + is_invokable_t is_invokable; + + return partial_invoke_impl(is_invokable, std::forward(callable), + std::move(next)); +} + +/// Shortcut - we can call the callable directly +template +auto partial_invoke_impl_shortcut(std::true_type, T&& callable, + Args&&... args) { + return std::forward(callable)(std::forward(args)...); +} + +/// Failed shortcut - we were unable to invoke the callable with the +/// original arguments. +template +auto partial_invoke_impl_shortcut(std::false_type failed, T&& callable, + Args&&... args) { + + // Our shortcut failed, convert the arguments into a forwarding tuple + return partial_invoke_impl( + failed, std::forward(callable), + std::forward_as_tuple(std::forward(args)...)); +} +} // end namespace detail + +/// Partially invokes the given callable with the given arguments. +/// +/// \note This function will assert statically if there is no way to call the +/// given object with less arguments. +template +auto partial_invoke(T&& callable, Args&&... args) { + // Test whether we are able to call the function with the given arguments. + is_invokable_t> is_invokable; + + // The implementation is done in a shortcut way so there are less + // type instantiations needed to call the callable with its full signature. + return detail::partial_invoke_impl_shortcut( + is_invokable, std::forward(callable), std::forward(args)...); +} + +// Class for making child classes non copyable +struct non_copyable { + constexpr non_copyable() = default; + non_copyable(non_copyable const&) = delete; + constexpr non_copyable(non_copyable&&) = default; + non_copyable& operator=(non_copyable const&) = delete; + non_copyable& operator=(non_copyable&&) = default; +}; + +// Class for making child classes non copyable and movable +struct non_movable { + constexpr non_movable() = default; + non_movable(non_movable const&) = delete; + constexpr non_movable(non_movable&&) = delete; + non_movable& operator=(non_movable const&) = delete; + non_movable& operator=(non_movable&&) = delete; +}; + +/// This class is responsible for holding an abstract copy- and +/// move-able ownership that is invalidated when the object +/// is moved to another instance. +class ownership { + explicit constexpr ownership(bool acquired, bool frozen) + : acquired_(acquired), frozen_(frozen) { + } + +public: + constexpr ownership() : acquired_(true), frozen_(false) { + } + constexpr ownership(ownership const&) = default; + ownership(ownership&& right) noexcept + : acquired_(right.consume()), frozen_(right.is_frozen()) { + } + ownership& operator=(ownership const&) = default; + ownership& operator=(ownership&& right) noexcept { + acquired_ = right.consume(); + frozen_ = right.is_frozen(); + return *this; + } + + // Merges both ownerships together + ownership operator|(ownership const& right) const noexcept { + return ownership(is_acquired() && right.is_acquired(), + is_frozen() || right.is_frozen()); + } + + constexpr bool is_acquired() const noexcept { + return acquired_; + } + constexpr bool is_frozen() const noexcept { + return frozen_; + } + + void release() noexcept { + assert(is_acquired() && "Tried to release the ownership twice!"); + acquired_ = false; + } + void freeze(bool enabled = true) noexcept { + assert(is_acquired() && "Tried to freeze a released object!"); + frozen_ = enabled; + } + +private: + bool consume() noexcept { + if (is_acquired()) { + release(); + return true; + } + return false; + } + + /// Is true when the object is in a valid state + bool acquired_ : 1; + /// Is true when the automatic invocation on destruction is disabled + bool frozen_ : 1; +}; +} // namespace util +} // namespace detail +} // namespace cti + +#endif // CONTINUABLE_DETAIL_UTIL_HPP_INCLUDED__ diff --git a/test/playground/CMakeLists.txt b/test/playground/CMakeLists.txt index 725be13..0eeb826 100644 --- a/test/playground/CMakeLists.txt +++ b/test/playground/CMakeLists.txt @@ -1,9 +1,27 @@ -add_executable(test-playground +set(LIB_SOURCES ${CMAKE_SOURCE_DIR}/include/continuable/continuable.hpp ${CMAKE_SOURCE_DIR}/include/continuable/continuable-base.hpp - ${CMAKE_SOURCE_DIR}/include/continuable/continuable-testing.hpp + ${CMAKE_SOURCE_DIR}/include/continuable/continuable-testing.hpp) +set(LIB_SOURCES_DETAIL + ${CMAKE_SOURCE_DIR}/include/continuable/detail/base.hpp + ${CMAKE_SOURCE_DIR}/include/continuable/detail/composition.hpp + ${CMAKE_SOURCE_DIR}/include/continuable/detail/api.hpp + ${CMAKE_SOURCE_DIR}/include/continuable/detail/hints.hpp + ${CMAKE_SOURCE_DIR}/include/continuable/detail/features.hpp + ${CMAKE_SOURCE_DIR}/include/continuable/detail/traits.hpp + ${CMAKE_SOURCE_DIR}/include/continuable/detail/util.hpp) +set(TEST ${CMAKE_CURRENT_LIST_DIR}/test-playground.cpp) +add_executable(test-playground + ${LIB_SOURCES} + ${LIB_SOURCES_DETAIL} + ${TEST}) + +source_group(src FILES ${LIB_SOURCES}) +source_group(src\\detail FILES ${LIB_SOURCES_DETAIL}) +source_group(test FILES ${TEST}) + target_link_libraries(test-playground PRIVATE continuable)