mirror of
https://github.com/Naios/continuable.git
synced 2025-12-07 17:26:47 +08:00
Split util into traits and util
This commit is contained in:
parent
7a02148c9d
commit
8d6c9ab895
@ -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
|
||||
$<$<COMPILE_FEATURES:cxx_std_17>:cxx_std_17>)
|
||||
|
||||
add_library(continuable INTERFACE)
|
||||
|
||||
|
||||
@ -527,8 +527,8 @@ private:
|
||||
template <typename... Args, typename Continuation>
|
||||
auto make_continuable(Continuation&& continuation) {
|
||||
auto hint = detail::composition::annotating::extract(
|
||||
detail::util::identity_of(continuation),
|
||||
detail::util::identity<Args...>{});
|
||||
detail::traits::identity_of(continuation),
|
||||
detail::traits::identity<Args...>{});
|
||||
|
||||
return detail::base::attorney::create(
|
||||
std::forward<Continuation>(continuation), hint,
|
||||
@ -547,8 +547,8 @@ template <typename... Continuables>
|
||||
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>(continuables)...);
|
||||
return detail::traits::fold(detail::traits::and_folding(),
|
||||
std::forward<Continuables>(continuables)...);
|
||||
}
|
||||
|
||||
/// \copydoc when_all
|
||||
@ -574,8 +574,8 @@ template <typename... Continuables>
|
||||
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>(continuables)...);
|
||||
return detail::traits::fold(detail::traits::or_folding(),
|
||||
std::forward<Continuables>(continuables)...);
|
||||
}
|
||||
|
||||
/// \copydoc when_any
|
||||
@ -601,8 +601,8 @@ template <typename... Continuables>
|
||||
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>(continuables)...);
|
||||
return detail::traits::fold(detail::traits::seq_folding(),
|
||||
std::forward<Continuables>(continuables)...);
|
||||
}
|
||||
|
||||
/// \copydoc when_seq
|
||||
|
||||
@ -45,7 +45,7 @@ template <typename C> void assert_async_completion(C&& continuable) {
|
||||
*called = true;
|
||||
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
traits::unused(std::forward<decltype(args)>(args)...);
|
||||
});
|
||||
ASSERT_TRUE(*called);
|
||||
}
|
||||
@ -53,7 +53,7 @@ template <typename C> void assert_async_completion(C&& continuable) {
|
||||
template <typename C> void assert_async_never_completed(C&& continuable) {
|
||||
std::forward<C>(continuable).then([](auto&&... args) {
|
||||
// Workaround for our known GCC bug.
|
||||
util::unused(std::forward<decltype(args)>(args)...);
|
||||
traits::unused(std::forward<decltype(args)>(args)...);
|
||||
|
||||
FAIL();
|
||||
});
|
||||
@ -81,13 +81,13 @@ void assert_async_binary_validation(V&& validator, C&& continuable,
|
||||
|
||||
auto actual_pack = std::make_tuple(std::forward<decltype(args)>(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<decltype(actual_pack)>::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<size.value>{}, [&](auto current) mutable {
|
||||
auto expected = std::get<current.value>(std::move(expected_pack));
|
||||
auto actual = std::get<current.value>(std::move(actual_pack));
|
||||
@ -107,11 +107,11 @@ inline auto asserting_eq_check() {
|
||||
}
|
||||
|
||||
template <typename C, typename... Args>
|
||||
void assert_async_types(C&& continuable, util::identity<Args...> expected) {
|
||||
void assert_async_types(C&& continuable, traits::identity<Args...> expected) {
|
||||
assert_async_validation(
|
||||
std::forward<C>(continuable), [&](auto... actualPack) {
|
||||
auto actual = util::identity<decltype(actualPack)...>{};
|
||||
util::unused(expected, actual,
|
||||
auto actual = traits::identity<decltype(actualPack)...>{};
|
||||
traits::unused(expected, actual,
|
||||
std::forward<decltype(actualPack)>(actualPack)...);
|
||||
|
||||
static_assert(
|
||||
@ -217,6 +217,6 @@ void assert_async_types(C&& continuable, util::identity<Args...> 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__
|
||||
|
||||
@ -61,15 +61,15 @@ struct this_thread_executor_tag {};
|
||||
|
||||
/// Returns the signature hint of the given continuable
|
||||
template <typename T>
|
||||
constexpr auto hint_of(util::identity<T>) {
|
||||
static_assert(util::fail<T>::value,
|
||||
constexpr auto hint_of(traits::identity<T>) {
|
||||
static_assert(traits::fail<T>::value,
|
||||
"Expected a continuation with an existing signature hint!");
|
||||
return util::identity_of<void>();
|
||||
return traits::identity_of<void>();
|
||||
}
|
||||
/// Returns the signature hint of the given continuable
|
||||
template <typename Data, typename... Args>
|
||||
constexpr auto
|
||||
hint_of(util::identity<
|
||||
hint_of(traits::identity<
|
||||
continuable_base<Data, hints::signature_hint_tag<Args...>>>) {
|
||||
return hints::signature_hint_tag<Args...>{};
|
||||
}
|
||||
@ -155,7 +155,8 @@ constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag<Args...>) {
|
||||
|
||||
/// - continuable<?...> -> result(nextCallback);
|
||||
template <typename Data, typename Annotation>
|
||||
constexpr auto invoker_of(util::identity<continuable_base<Data, Annotation>>) {
|
||||
constexpr auto
|
||||
invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
|
||||
/// Get the hint of the unwrapped returned continuable
|
||||
using Type = decltype(attorney::materialize(
|
||||
std::declval<continuable_base<Data, Annotation>>()));
|
||||
@ -169,12 +170,12 @@ constexpr auto invoker_of(util::identity<continuable_base<Data, Annotation>>) {
|
||||
std::move(continuation_),
|
||||
std::forward<decltype(nextCallback)>(nextCallback));
|
||||
},
|
||||
hint_of(util::identity_of<Type>()));
|
||||
hint_of(traits::identity_of<Type>()));
|
||||
}
|
||||
|
||||
/// - ? -> nextCallback(?)
|
||||
template <typename T>
|
||||
auto invoker_of(util::identity<T>) {
|
||||
auto invoker_of(traits::identity<T>) {
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& nextCallback, auto&&... args) {
|
||||
auto result = std::forward<decltype(callback)>(callback)(
|
||||
@ -182,11 +183,11 @@ auto invoker_of(util::identity<T>) {
|
||||
|
||||
std::forward<decltype(nextCallback)>(nextCallback)(std::move(result));
|
||||
},
|
||||
util::identity_of<T>());
|
||||
traits::identity_of<T>());
|
||||
}
|
||||
|
||||
/// - void -> nextCallback()
|
||||
inline auto invoker_of(util::identity<void>) {
|
||||
inline auto invoker_of(traits::identity<void>) {
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& nextCallback, auto&&... args) {
|
||||
std::forward<decltype(callback)>(callback)(
|
||||
@ -194,7 +195,7 @@ inline auto invoker_of(util::identity<void>) {
|
||||
|
||||
std::forward<decltype(nextCallback)>(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<decltype(callback)>(callback)(
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
util::unpack(std::move(result), [&](auto&&... types) {
|
||||
traits::unpack(std::move(result), [&](auto&&... types) {
|
||||
/// TODO Add inplace resolution here
|
||||
|
||||
std::forward<decltype(nextCallback)>(nextCallback)(
|
||||
@ -215,15 +216,15 @@ inline auto sequenced_unpack_invoker() {
|
||||
|
||||
// - std::pair<?, ?> -> nextCallback(?, ?)
|
||||
template <typename First, typename Second>
|
||||
constexpr auto invoker_of(util::identity<std::pair<First, Second>>) {
|
||||
constexpr auto invoker_of(traits::identity<std::pair<First, Second>>) {
|
||||
return make_invoker(sequenced_unpack_invoker(),
|
||||
util::identity<First, Second>{});
|
||||
traits::identity<First, Second>{});
|
||||
}
|
||||
|
||||
// - std::tuple<?...> -> nextCallback(?...)
|
||||
template <typename... Args>
|
||||
constexpr auto invoker_of(util::identity<std::tuple<Args...>>) {
|
||||
return make_invoker(sequenced_unpack_invoker(), util::identity<Args...>{});
|
||||
constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
|
||||
return make_invoker(sequenced_unpack_invoker(), traits::identity<Args...>{});
|
||||
}
|
||||
} // end namespace decoration
|
||||
|
||||
@ -255,7 +256,7 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker,
|
||||
nextCallback = std::forward<NextCallback>(nextCallback),
|
||||
args = std::make_tuple(std::forward<Args>(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<Args...>,
|
||||
|
||||
// In order to retrieve the correct decorator we must know what the
|
||||
// result type is.
|
||||
auto result =
|
||||
util::identity_of<decltype(std::move(callback)(std::move(args)...))>();
|
||||
auto result = traits::identity_of<decltype(
|
||||
std::move(callback)(std::move(args)...))>();
|
||||
|
||||
// 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<Args...>,
|
||||
|
||||
/// Returns the next hint when the callback is invoked with the given hint
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto next_hint_of(util::identity<T> /*callback*/,
|
||||
constexpr auto next_hint_of(traits::identity<T> /*callback*/,
|
||||
hints::signature_hint_tag<Args...> /*current*/) {
|
||||
return decoration::invoker_of(util::identity_of<decltype(std::declval<T>()(
|
||||
return decoration::invoker_of(traits::identity_of<decltype(std::declval<T>()(
|
||||
std::declval<Args>()...))>())
|
||||
.hint();
|
||||
}
|
||||
@ -338,8 +339,8 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback,
|
||||
std::forward<decltype(args)>(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>(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<decltype(nextCallback)>(nextCallback));
|
||||
|
||||
@ -41,7 +41,6 @@
|
||||
#include <continuable/detail/api.hpp>
|
||||
#include <continuable/detail/base.hpp>
|
||||
#include <continuable/detail/traits.hpp>
|
||||
#include <continuable/detail/util.hpp>
|
||||
|
||||
namespace cti {
|
||||
namespace detail {
|
||||
@ -63,12 +62,12 @@ struct is_strategy<strategy_any_tag> : std::true_type {};
|
||||
namespace annotating {
|
||||
namespace detail {
|
||||
/// Void hints are equal to an empty signature
|
||||
constexpr auto make_hint_of(util::identity<void>) noexcept {
|
||||
constexpr auto make_hint_of(traits::identity<void>) noexcept {
|
||||
return hints::signature_hint_tag<>{};
|
||||
}
|
||||
/// All other hints are the obvious hints...
|
||||
template <typename... HintArgs>
|
||||
constexpr auto make_hint_of(util::identity<HintArgs...> args) noexcept {
|
||||
constexpr auto make_hint_of(traits::identity<HintArgs...> 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<HintArgs...> args) noexcept {
|
||||
/// - absent_signature_hint_tag
|
||||
///
|
||||
template <typename T, typename... HintArgs>
|
||||
constexpr auto extract(util::identity<T> /*type*/,
|
||||
util::identity<HintArgs...> 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<T> /*type*/,
|
||||
traits::identity<HintArgs...> 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 <std::size_t Pos, typename T>
|
||||
constexpr void assign(util::size_constant<Pos> /*pos*/, T& /*storage*/) {
|
||||
constexpr void assign(traits::size_constant<Pos> /*pos*/, T& /*storage*/) {
|
||||
// ...
|
||||
}
|
||||
template <std::size_t Pos, typename T, typename Current, typename... Args>
|
||||
void assign(util::size_constant<Pos> pos, T& storage, Current&& current,
|
||||
void assign(traits::size_constant<Pos> pos, T& storage, Current&& current,
|
||||
Args&&... args) {
|
||||
std::get<Pos>(storage) = std::forward<Current>(current);
|
||||
assign(pos + util::size_constant_of<1>(), storage,
|
||||
assign(pos + traits::size_constant_of<1>(), storage,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@ -134,8 +134,8 @@ public:
|
||||
|
||||
/// Creates a submitter which submits it's result into the tuple
|
||||
template <std::size_t From, std::size_t To>
|
||||
auto create_callback(util::size_constant<From> from,
|
||||
util::size_constant<To> to) {
|
||||
auto create_callback(traits::size_constant<From> from,
|
||||
traits::size_constant<To> 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<decltype(args)>(args)...);
|
||||
});
|
||||
}
|
||||
@ -205,7 +205,7 @@ template <typename... LeftArgs, typename... RightArgs>
|
||||
auto chain_composition(std::tuple<LeftArgs...> leftPack,
|
||||
std::tuple<RightArgs...> 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<LData, LAnnotation>&& left,
|
||||
/// Creates a submitter which caches the intermediate results of `all` chains
|
||||
template <typename Callback, std::size_t Submissions, typename... Args>
|
||||
auto make_all_result_submitter(Callback&& callback,
|
||||
util::size_constant<Submissions>,
|
||||
util::identity<Args...>) {
|
||||
traits::size_constant<Submissions>,
|
||||
traits::identity<Args...>) {
|
||||
return std::make_shared<detail::all_result_submitter<
|
||||
std::decay_t<decltype(callback)>, Submissions, Args...>>(
|
||||
std::forward<decltype(callback)>(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<?>, 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<decltype(callback)>(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<decltype(current.first)::value>(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 <typename T, typename... Args>
|
||||
constexpr T first_of(util::identity<T, Args...>) noexcept;
|
||||
constexpr T first_of(traits::identity<T, Args...>) noexcept;
|
||||
|
||||
template <typename Signature, typename... Args>
|
||||
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<Args...>{}, [&](auto rest) {
|
||||
auto is_empty = (util::pack_size_of(rest) == util::size_constant_of<0>());
|
||||
traits::static_for_each_in(traits::identity<Args...>{}, [&](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 <typename Signature, typename First, typename... Args>
|
||||
constexpr auto common_result_of(Signature signature, First first,
|
||||
Args... args) {
|
||||
using Common =
|
||||
util::identity<std::common_type_t<decltype(first_of(first)),
|
||||
decltype(first_of(args))...>>;
|
||||
traits::identity<std::common_type_t<decltype(first_of(first)),
|
||||
decltype(first_of(args))...>>;
|
||||
|
||||
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<decltype(callback)>(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<decltype(entry)>(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<decltype(entry)>(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<decltype(args)>(args)...)](
|
||||
auto&&... args) mutable {
|
||||
return util::merge(
|
||||
return traits::merge(
|
||||
std::move(previous),
|
||||
std::make_tuple(std::forward<decltype(args)>(args)...));
|
||||
});
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
|
||||
/~` _ _ _|_. _ _ |_ | _
|
||||
@ -33,10 +32,7 @@
|
||||
|
||||
#include <continuable/detail/api.hpp>
|
||||
|
||||
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__
|
||||
|
||||
@ -41,7 +41,7 @@ namespace detail {
|
||||
namespace hints {
|
||||
/// Represents a present signature hint
|
||||
template <typename... Args>
|
||||
using signature_hint_tag = util::identity<Args...>;
|
||||
using signature_hint_tag = traits::identity<Args...>;
|
||||
/// Represents an absent signature hint
|
||||
struct absent_signature_hint_tag {};
|
||||
|
||||
|
||||
@ -31,12 +31,435 @@
|
||||
#ifndef CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED__
|
||||
#define CONTINUABLE_DETAIL_TRAITS_HPP_INCLUDED__
|
||||
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/api.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
|
||||
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 <typename T, T Value>
|
||||
struct constant : std::integral_constant<T, Value> {
|
||||
/// \cond false
|
||||
#define CTI__INST(CTI__OP) \
|
||||
template <typename OT, OT OValue> \
|
||||
/*constexpr*/ auto operator CTI__OP(std::integral_constant<OT, OValue>) \
|
||||
const noexcept { \
|
||||
return constant<decltype((Value CTI__OP OValue)), \
|
||||
(Value CTI__OP OValue)>{}; \
|
||||
}
|
||||
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<decltype((CTI__OP Value)), (CTI__OP Value)>{}; \
|
||||
}
|
||||
CTI__FOR_EACH_INTEGRAL_UNA_OP(CTI__INST)
|
||||
#undef CTI__INST
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
template <bool Value>
|
||||
struct constant<bool, Value> : std::integral_constant<bool, Value> {
|
||||
/// \cond false
|
||||
#define CTI__INST(CTI__OP) \
|
||||
template <typename OT, OT OValue> \
|
||||
/*constexpr*/ auto operator CTI__OP(std::integral_constant<bool, OValue>) \
|
||||
const noexcept { \
|
||||
return constant<bool, (Value CTI__OP OValue)>{}; \
|
||||
}
|
||||
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<bool, CTI__OP Value>{}; \
|
||||
}
|
||||
CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__INST)
|
||||
#undef CTI__INST
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
template <bool Value>
|
||||
using bool_constant = constant<bool, Value>;
|
||||
template <std::size_t Value>
|
||||
using size_constant = constant<std::size_t, Value>;
|
||||
|
||||
template <typename T, bool Value>
|
||||
constexpr auto constant_of(std::integral_constant<T, Value> /*value*/ = {}) {
|
||||
return constant<T, Value>{};
|
||||
}
|
||||
template <std::size_t Value>
|
||||
constexpr auto
|
||||
size_constant_of(std::integral_constant<std::size_t, Value> /*value*/ = {}) {
|
||||
return size_constant<Value>{};
|
||||
}
|
||||
template <bool Value>
|
||||
constexpr auto
|
||||
bool_constant_of(std::integral_constant<bool, Value> /*value*/ = {}) {
|
||||
return bool_constant<Value>{};
|
||||
}
|
||||
|
||||
#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 <std::size_t I, typename... Args>
|
||||
using at_t = decltype(std::get<I>(std::declval<std::tuple<Args...>>()));
|
||||
|
||||
/// Evaluates to an integral constant which represents the size
|
||||
/// of the given pack.
|
||||
template <typename... Args>
|
||||
using size_of_t = size_constant<sizeof...(Args)>;
|
||||
|
||||
/// A tagging type for wrapping other types
|
||||
template <typename... T>
|
||||
struct identity {};
|
||||
template <typename T>
|
||||
struct identity<T> : std::common_type<T> {};
|
||||
|
||||
template <typename>
|
||||
struct is_identity : std::false_type {};
|
||||
template <typename... Args>
|
||||
struct is_identity<identity<Args...>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
identity<std::decay_t<T>> constexpr identity_of(T const& /*type*/) noexcept {
|
||||
return {};
|
||||
}
|
||||
template <typename... Args>
|
||||
constexpr identity<Args...> identity_of(identity<Args...> /*type*/) noexcept {
|
||||
return {};
|
||||
}
|
||||
template <typename T>
|
||||
constexpr auto identity_of() noexcept {
|
||||
return std::conditional_t<is_identity<std::decay_t<T>>::value, T,
|
||||
identity<std::decay_t<T>>>{};
|
||||
}
|
||||
|
||||
template <std::size_t I, typename... T>
|
||||
constexpr auto get(identity<T...>) noexcept {
|
||||
return identity_of<at_t<I, T...>>();
|
||||
}
|
||||
|
||||
/// Helper to trick compilers about that a parameter pack is used
|
||||
template <typename... T>
|
||||
void unused(T&&... args) {
|
||||
auto use = [](auto&& type) mutable {
|
||||
(void)type;
|
||||
return 0;
|
||||
};
|
||||
auto deduce = {0, use(std::forward<decltype(args)>(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 <typename...>
|
||||
struct deduce_to_void : std::common_type<void> {};
|
||||
} // end namespace detail
|
||||
|
||||
/// C++17 like void_t type
|
||||
template <typename... T>
|
||||
using void_t = typename detail::deduce_to_void<T...>::type;
|
||||
|
||||
namespace detail {
|
||||
template <typename T, typename Check, typename = void_t<>>
|
||||
struct is_valid_impl : std::common_type<std::false_type> {};
|
||||
|
||||
template <typename T, typename Check>
|
||||
struct is_valid_impl<T, Check,
|
||||
void_t<decltype(std::declval<Check>()(std::declval<T>()))>>
|
||||
: std::common_type<std::true_type> {};
|
||||
|
||||
template <typename Type, typename TrueCallback>
|
||||
constexpr void static_if_impl(std::true_type, Type&& type,
|
||||
TrueCallback&& trueCallback) {
|
||||
std::forward<TrueCallback>(trueCallback)(std::forward<Type>(type));
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback>
|
||||
constexpr void static_if_impl(std::false_type, Type&& /*type*/,
|
||||
TrueCallback&& /*trueCallback*/) {
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback, typename FalseCallback>
|
||||
constexpr auto static_if_impl(std::true_type, Type&& type,
|
||||
TrueCallback&& trueCallback,
|
||||
FalseCallback&& /*falseCallback*/) {
|
||||
return std::forward<TrueCallback>(trueCallback)(std::forward<Type>(type));
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback, typename FalseCallback>
|
||||
constexpr auto static_if_impl(std::false_type, Type&& type,
|
||||
TrueCallback&& /*trueCallback*/,
|
||||
FalseCallback&& falseCallback) {
|
||||
return std::forward<FalseCallback>(falseCallback)(std::forward<Type>(type));
|
||||
}
|
||||
} // end namespace detail
|
||||
|
||||
/// Returns the pack size of the given type
|
||||
template <typename... Args>
|
||||
constexpr auto pack_size_of(identity<std::tuple<Args...>>) noexcept {
|
||||
return size_of_t<Args...>{};
|
||||
}
|
||||
/// Returns the pack size of the given type
|
||||
template <typename First, typename Second>
|
||||
constexpr auto pack_size_of(identity<std::pair<First, Second>>) noexcept {
|
||||
return size_of_t<First, Second>{};
|
||||
}
|
||||
/// Returns the pack size of the given type
|
||||
template <typename... Args>
|
||||
constexpr auto pack_size_of(identity<Args...>) noexcept {
|
||||
return size_of_t<Args...>{};
|
||||
}
|
||||
|
||||
/// Returns an index sequence of the given type
|
||||
template <typename T>
|
||||
constexpr auto sequence_of(T&& /*sequenceable*/) noexcept {
|
||||
return std::make_index_sequence<decltype(
|
||||
pack_size_of(std::declval<T>()))::value>();
|
||||
}
|
||||
|
||||
/// Returns a check which returns a true type if the current value
|
||||
/// is below the
|
||||
template <std::size_t End>
|
||||
constexpr auto is_less_than(size_constant<End> end) noexcept {
|
||||
return [=](auto current) { return end > current; };
|
||||
}
|
||||
|
||||
/// Compile-time check for validating a certain expression
|
||||
template <typename T, typename Check>
|
||||
constexpr auto is_valid(T&& /*type*/, Check&& /*check*/) noexcept {
|
||||
return typename detail::is_valid_impl<T, Check>::type{};
|
||||
}
|
||||
|
||||
/// Creates a static functional validator object.
|
||||
template <typename Check>
|
||||
constexpr auto validator_of(Check&& check) noexcept(
|
||||
std::is_nothrow_move_constructible<std::decay_t<Check>>::value) {
|
||||
return [check = std::forward<Check>(check)](auto&& matchable) {
|
||||
return is_valid(std::forward<decltype(matchable)>(matchable), check);
|
||||
};
|
||||
}
|
||||
|
||||
/// Invokes the callback only if the given type matches the check
|
||||
template <typename Type, typename Check, typename TrueCallback>
|
||||
constexpr void static_if(Type&& type, Check&& check,
|
||||
TrueCallback&& trueCallback) {
|
||||
detail::static_if_impl(std::forward<Check>(check)(type),
|
||||
std::forward<Type>(type),
|
||||
std::forward<TrueCallback>(trueCallback));
|
||||
}
|
||||
|
||||
/// Invokes the callback only if the given type matches the check
|
||||
template <typename Type, typename Check, typename TrueCallback,
|
||||
typename FalseCallback>
|
||||
constexpr auto static_if(Type&& type, Check&& check,
|
||||
TrueCallback&& trueCallback,
|
||||
FalseCallback&& falseCallback) {
|
||||
return detail::static_if_impl(std::forward<Check>(check)(type),
|
||||
std::forward<Type>(type),
|
||||
std::forward<TrueCallback>(trueCallback),
|
||||
std::forward<FalseCallback>(falseCallback));
|
||||
}
|
||||
|
||||
/// A compile-time while loop, which loops as long the value matches
|
||||
/// the predicate. The handler shall return the next value.
|
||||
template <typename Value, typename Predicate, typename Handler>
|
||||
constexpr auto static_while(Value&& value, Predicate&& predicate,
|
||||
Handler&& handler) {
|
||||
return static_if(std::forward<Value>(value), predicate,
|
||||
[&](auto&& result) mutable {
|
||||
return static_while(
|
||||
handler(std::forward<decltype(result)>(result)),
|
||||
std::forward<Predicate>(predicate),
|
||||
std::forward<Handler>(handler));
|
||||
},
|
||||
[&](auto&& result) mutable {
|
||||
return std::forward<decltype(result)>(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 <typename U, std::size_t... I>
|
||||
constexpr auto unpack(std::integer_sequence<std::size_t, I...>, U&& unpacker) {
|
||||
return std::forward<U>(unpacker)(size_constant_of<I>()...);
|
||||
}
|
||||
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename U, std::size_t... I>
|
||||
constexpr auto unpack(F&& firstSequenceable, U&& unpacker,
|
||||
std::integer_sequence<std::size_t, I...>) {
|
||||
using std::get;
|
||||
(void)firstSequenceable;
|
||||
return std::forward<U>(unpacker)(
|
||||
get<I>(std::forward<F>(firstSequenceable))...);
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename S, typename U, std::size_t... IF,
|
||||
std::size_t... IS>
|
||||
constexpr auto unpack(F&& firstSequenceable, S&& secondSequenceable,
|
||||
U&& unpacker, std::integer_sequence<std::size_t, IF...>,
|
||||
std::integer_sequence<std::size_t, IS...>) {
|
||||
using std::get;
|
||||
(void)firstSequenceable;
|
||||
(void)secondSequenceable;
|
||||
return std::forward<U>(unpacker)(
|
||||
get<IF>(std::forward<F>(firstSequenceable))...,
|
||||
get<IS>(std::forward<S>(secondSequenceable))...);
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename U>
|
||||
auto unpack(F&& firstSequenceable, U&& unpacker) {
|
||||
return unpack(std::forward<F>(firstSequenceable), std::forward<U>(unpacker),
|
||||
sequence_of(identity_of(firstSequenceable)));
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceables
|
||||
template <typename F, typename S, typename U>
|
||||
constexpr auto unpack(F&& firstSequenceable, S&& secondSequenceable,
|
||||
U&& unpacker) {
|
||||
return unpack(std::forward<F>(firstSequenceable),
|
||||
std::forward<S>(secondSequenceable), std::forward<U>(unpacker),
|
||||
sequence_of(identity_of(firstSequenceable)),
|
||||
sequence_of(identity_of(secondSequenceable)));
|
||||
}
|
||||
|
||||
/// Applies the handler function to each element contained in the sequenceable
|
||||
template <typename Sequenceable, typename Handler>
|
||||
constexpr void static_for_each_in(Sequenceable&& sequenceable,
|
||||
Handler&& handler) {
|
||||
unpack(
|
||||
std::forward<Sequenceable>(sequenceable), [&](auto&&... entries) mutable {
|
||||
auto consume = [&](auto&& entry) mutable {
|
||||
handler(std::forward<decltype(entry)>(entry));
|
||||
return 0;
|
||||
};
|
||||
// Apply the consume function to every entry inside the pack
|
||||
auto deduce = {0, consume(std::forward<decltype(entries)>(entries))...};
|
||||
(void)deduce;
|
||||
(void)consume;
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds the given type at the back of the left sequenceable
|
||||
template <typename Left, typename Element>
|
||||
constexpr auto push(Left&& left, Element&& element) {
|
||||
return unpack(std::forward<Left>(left), [&](auto&&... leftArgs) {
|
||||
return std::make_tuple(std::forward<decltype(leftArgs)>(leftArgs)...,
|
||||
std::forward<Element>(element));
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds the element to the back of the identity
|
||||
template <typename... Args, typename Element>
|
||||
constexpr auto push(identity<Args...>, identity<Element>) noexcept {
|
||||
return identity<Args..., Element>{};
|
||||
}
|
||||
|
||||
/// Removes the first element from the identity
|
||||
template <typename First, typename... Rest>
|
||||
constexpr auto pop_first(identity<First, Rest...>) noexcept {
|
||||
return identity<Rest...>{};
|
||||
}
|
||||
|
||||
/// Returns the merged sequence
|
||||
template <typename Left>
|
||||
constexpr auto merge(Left&& left) {
|
||||
return std::forward<Left>(left);
|
||||
}
|
||||
/// Merges the left sequenceable with the right ones
|
||||
template <typename Left, typename Right, typename... Rest>
|
||||
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>(left), std::forward<Right>(right),
|
||||
[&](auto&&... args) {
|
||||
// Maybe use: template <template<typename...> class T,
|
||||
// typename... Args>
|
||||
return std::make_tuple(
|
||||
std::forward<decltype(args)>(args)...);
|
||||
}),
|
||||
std::forward<Rest>(rest)...);
|
||||
}
|
||||
/// Merges the left identity with the right ones
|
||||
template <typename... LeftArgs, typename... RightArgs, typename... Rest>
|
||||
constexpr auto merge(identity<LeftArgs...> /*left*/,
|
||||
identity<RightArgs...> /*right*/, Rest&&... rest) {
|
||||
return merge(identity<LeftArgs..., RightArgs...>{},
|
||||
std::forward<Rest>(rest)...);
|
||||
}
|
||||
|
||||
/// Combines the given arguments with the given folding function
|
||||
template <typename F, typename First>
|
||||
constexpr auto fold(F&& /*folder*/, First&& first) {
|
||||
return std::forward<First>(first);
|
||||
}
|
||||
/// Combines the given arguments with the given folding function
|
||||
template <typename F, typename First, typename Second, typename... Rest>
|
||||
auto fold(F&& folder, First&& first, Second&& second, Rest&&... rest) {
|
||||
auto res = folder(std::forward<First>(first), std::forward<Second>(second));
|
||||
return fold(std::forward<F>(folder), std::move(res),
|
||||
std::forward<Rest>(rest)...);
|
||||
}
|
||||
|
||||
/// Returns a folding function using operator `&&`.
|
||||
inline auto and_folding() noexcept {
|
||||
return [](auto&& left, auto&& right) {
|
||||
return std::forward<decltype(left)>(left) &&
|
||||
std::forward<decltype(right)>(right);
|
||||
};
|
||||
}
|
||||
/// Returns a folding function using operator `||`.
|
||||
inline auto or_folding() noexcept {
|
||||
return [](auto&& left, auto&& right) {
|
||||
return std::forward<decltype(left)>(left) ||
|
||||
std::forward<decltype(right)>(right);
|
||||
};
|
||||
}
|
||||
/// Returns a folding function using operator `>>`.
|
||||
inline auto seq_folding() noexcept {
|
||||
return [](auto&& left, auto&& right) {
|
||||
return std::forward<decltype(left)>(left) >>
|
||||
std::forward<decltype(right)>(right);
|
||||
};
|
||||
}
|
||||
|
||||
/// Deduces to a std::false_type
|
||||
template <typename T>
|
||||
using fail = std::integral_constant<bool, !std::is_same<T, T>::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__
|
||||
|
||||
@ -100,7 +100,7 @@ public:
|
||||
template <typename Data, typename Annotation>
|
||||
auto as_future(continuable_base<Data, Annotation>&& 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<std::decay_t<decltype(hint)>> callback;
|
||||
(void)hint;
|
||||
|
||||
@ -36,434 +36,21 @@
|
||||
#include <utility>
|
||||
|
||||
#include <continuable/detail/api.hpp>
|
||||
#include <continuable/detail/features.hpp>
|
||||
#include <continuable/detail/traits.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 <typename T, T Value>
|
||||
struct constant : std::integral_constant<T, Value> {
|
||||
/// \cond false
|
||||
#define CTI__INST(CTI__OP) \
|
||||
template <typename OT, OT OValue> \
|
||||
/*constexpr*/ auto operator CTI__OP(std::integral_constant<OT, OValue>) \
|
||||
const noexcept { \
|
||||
return constant<decltype((Value CTI__OP OValue)), \
|
||||
(Value CTI__OP OValue)>{}; \
|
||||
}
|
||||
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<decltype((CTI__OP Value)), (CTI__OP Value)>{}; \
|
||||
}
|
||||
CTI__FOR_EACH_INTEGRAL_UNA_OP(CTI__INST)
|
||||
#undef CTI__INST
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
template <bool Value>
|
||||
struct constant<bool, Value> : std::integral_constant<bool, Value> {
|
||||
/// \cond false
|
||||
#define CTI__INST(CTI__OP) \
|
||||
template <typename OT, OT OValue> \
|
||||
/*constexpr*/ auto operator CTI__OP(std::integral_constant<bool, OValue>) \
|
||||
const noexcept { \
|
||||
return constant<bool, (Value CTI__OP OValue)>{}; \
|
||||
}
|
||||
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<bool, CTI__OP Value>{}; \
|
||||
}
|
||||
CTI__FOR_EACH_BOOLEAN_UNA_OP(CTI__INST)
|
||||
#undef CTI__INST
|
||||
/// \endcond
|
||||
};
|
||||
|
||||
template <bool Value>
|
||||
using bool_constant = constant<bool, Value>;
|
||||
template <std::size_t Value>
|
||||
using size_constant = constant<std::size_t, Value>;
|
||||
|
||||
template <typename T, bool Value>
|
||||
constexpr auto constant_of(std::integral_constant<T, Value> /*value*/ = {}) {
|
||||
return constant<T, Value>{};
|
||||
}
|
||||
template <std::size_t Value>
|
||||
constexpr auto
|
||||
size_constant_of(std::integral_constant<std::size_t, Value> /*value*/ = {}) {
|
||||
return size_constant<Value>{};
|
||||
}
|
||||
template <bool Value>
|
||||
constexpr auto
|
||||
bool_constant_of(std::integral_constant<bool, Value> /*value*/ = {}) {
|
||||
return bool_constant<Value>{};
|
||||
}
|
||||
|
||||
#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 <std::size_t I, typename... Args>
|
||||
using at_t = decltype(std::get<I>(std::declval<std::tuple<Args...>>()));
|
||||
|
||||
/// Evaluates to an integral constant which represents the size
|
||||
/// of the given pack.
|
||||
template <typename... Args>
|
||||
using size_of_t = size_constant<sizeof...(Args)>;
|
||||
|
||||
/// A tagging type for wrapping other types
|
||||
template <typename... T>
|
||||
struct identity {};
|
||||
template <typename T>
|
||||
struct identity<T> : std::common_type<T> {};
|
||||
|
||||
template <typename>
|
||||
struct is_identity : std::false_type {};
|
||||
template <typename... Args>
|
||||
struct is_identity<identity<Args...>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
identity<std::decay_t<T>> constexpr identity_of(T const& /*type*/) noexcept {
|
||||
return {};
|
||||
}
|
||||
template <typename... Args>
|
||||
constexpr identity<Args...> identity_of(identity<Args...> /*type*/) noexcept {
|
||||
return {};
|
||||
}
|
||||
template <typename T>
|
||||
constexpr auto identity_of() noexcept {
|
||||
return std::conditional_t<is_identity<std::decay_t<T>>::value, T,
|
||||
identity<std::decay_t<T>>>{};
|
||||
}
|
||||
|
||||
template <std::size_t I, typename... T>
|
||||
constexpr auto get(identity<T...>) noexcept {
|
||||
return identity_of<at_t<I, T...>>();
|
||||
}
|
||||
|
||||
/// Helper to trick compilers about that a parameter pack is used
|
||||
template <typename... T>
|
||||
void unused(T&&... args) {
|
||||
auto use = [](auto&& type) mutable {
|
||||
(void)type;
|
||||
return 0;
|
||||
};
|
||||
auto deduce = {0, use(std::forward<decltype(args)>(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 <typename...>
|
||||
struct deduce_to_void : std::common_type<void> {};
|
||||
} // end namespace detail
|
||||
|
||||
/// C++17 like void_t type
|
||||
template <typename... T>
|
||||
using void_t = typename detail::deduce_to_void<T...>::type;
|
||||
|
||||
namespace detail {
|
||||
template <typename T, typename Check, typename = void_t<>>
|
||||
struct is_valid_impl : std::common_type<std::false_type> {};
|
||||
|
||||
template <typename T, typename Check>
|
||||
struct is_valid_impl<T, Check,
|
||||
void_t<decltype(std::declval<Check>()(std::declval<T>()))>>
|
||||
: std::common_type<std::true_type> {};
|
||||
|
||||
template <typename Type, typename TrueCallback>
|
||||
constexpr void static_if_impl(std::true_type, Type&& type,
|
||||
TrueCallback&& trueCallback) {
|
||||
std::forward<TrueCallback>(trueCallback)(std::forward<Type>(type));
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback>
|
||||
constexpr void static_if_impl(std::false_type, Type&& /*type*/,
|
||||
TrueCallback&& /*trueCallback*/) {
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback, typename FalseCallback>
|
||||
constexpr auto static_if_impl(std::true_type, Type&& type,
|
||||
TrueCallback&& trueCallback,
|
||||
FalseCallback&& /*falseCallback*/) {
|
||||
return std::forward<TrueCallback>(trueCallback)(std::forward<Type>(type));
|
||||
}
|
||||
|
||||
template <typename Type, typename TrueCallback, typename FalseCallback>
|
||||
constexpr auto static_if_impl(std::false_type, Type&& type,
|
||||
TrueCallback&& /*trueCallback*/,
|
||||
FalseCallback&& falseCallback) {
|
||||
return std::forward<FalseCallback>(falseCallback)(std::forward<Type>(type));
|
||||
}
|
||||
} // end namespace detail
|
||||
|
||||
/// Returns the pack size of the given type
|
||||
template <typename... Args>
|
||||
constexpr auto pack_size_of(identity<std::tuple<Args...>>) noexcept {
|
||||
return size_of_t<Args...>{};
|
||||
}
|
||||
/// Returns the pack size of the given type
|
||||
template <typename First, typename Second>
|
||||
constexpr auto pack_size_of(identity<std::pair<First, Second>>) noexcept {
|
||||
return size_of_t<First, Second>{};
|
||||
}
|
||||
/// Returns the pack size of the given type
|
||||
template <typename... Args>
|
||||
constexpr auto pack_size_of(identity<Args...>) noexcept {
|
||||
return size_of_t<Args...>{};
|
||||
}
|
||||
|
||||
/// Returns an index sequence of the given type
|
||||
template <typename T>
|
||||
constexpr auto sequence_of(T&& /*sequenceable*/) noexcept {
|
||||
return std::make_index_sequence<decltype(
|
||||
pack_size_of(std::declval<T>()))::value>();
|
||||
}
|
||||
|
||||
/// Returns a check which returns a true type if the current value
|
||||
/// is below the
|
||||
template <std::size_t End>
|
||||
constexpr auto is_less_than(size_constant<End> end) noexcept {
|
||||
return [=](auto current) { return end > current; };
|
||||
}
|
||||
|
||||
/// Compile-time check for validating a certain expression
|
||||
template <typename T, typename Check>
|
||||
constexpr auto is_valid(T&& /*type*/, Check&& /*check*/) noexcept {
|
||||
return typename detail::is_valid_impl<T, Check>::type{};
|
||||
}
|
||||
|
||||
/// Creates a static functional validator object.
|
||||
template <typename Check>
|
||||
constexpr auto validator_of(Check&& check) noexcept(
|
||||
std::is_nothrow_move_constructible<std::decay_t<Check>>::value) {
|
||||
return [check = std::forward<Check>(check)](auto&& matchable) {
|
||||
return is_valid(std::forward<decltype(matchable)>(matchable), check);
|
||||
};
|
||||
}
|
||||
|
||||
/// Invokes the callback only if the given type matches the check
|
||||
template <typename Type, typename Check, typename TrueCallback>
|
||||
constexpr void static_if(Type&& type, Check&& check,
|
||||
TrueCallback&& trueCallback) {
|
||||
detail::static_if_impl(std::forward<Check>(check)(type),
|
||||
std::forward<Type>(type),
|
||||
std::forward<TrueCallback>(trueCallback));
|
||||
}
|
||||
|
||||
/// Invokes the callback only if the given type matches the check
|
||||
template <typename Type, typename Check, typename TrueCallback,
|
||||
typename FalseCallback>
|
||||
constexpr auto static_if(Type&& type, Check&& check,
|
||||
TrueCallback&& trueCallback,
|
||||
FalseCallback&& falseCallback) {
|
||||
return detail::static_if_impl(std::forward<Check>(check)(type),
|
||||
std::forward<Type>(type),
|
||||
std::forward<TrueCallback>(trueCallback),
|
||||
std::forward<FalseCallback>(falseCallback));
|
||||
}
|
||||
|
||||
/// A compile-time while loop, which loops as long the value matches
|
||||
/// the predicate. The handler shall return the next value.
|
||||
template <typename Value, typename Predicate, typename Handler>
|
||||
constexpr auto static_while(Value&& value, Predicate&& predicate,
|
||||
Handler&& handler) {
|
||||
return static_if(std::forward<Value>(value), predicate,
|
||||
[&](auto&& result) mutable {
|
||||
return static_while(
|
||||
handler(std::forward<decltype(result)>(result)),
|
||||
std::forward<Predicate>(predicate),
|
||||
std::forward<Handler>(handler));
|
||||
},
|
||||
[&](auto&& result) mutable {
|
||||
return std::forward<decltype(result)>(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 <typename U, std::size_t... I>
|
||||
constexpr auto unpack(std::integer_sequence<std::size_t, I...>, U&& unpacker) {
|
||||
return std::forward<U>(unpacker)(size_constant_of<I>()...);
|
||||
}
|
||||
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename U, std::size_t... I>
|
||||
constexpr auto unpack(F&& firstSequenceable, U&& unpacker,
|
||||
std::integer_sequence<std::size_t, I...>) {
|
||||
using std::get;
|
||||
(void)firstSequenceable;
|
||||
return std::forward<U>(unpacker)(
|
||||
get<I>(std::forward<F>(firstSequenceable))...);
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename S, typename U, std::size_t... IF,
|
||||
std::size_t... IS>
|
||||
constexpr auto unpack(F&& firstSequenceable, S&& secondSequenceable,
|
||||
U&& unpacker, std::integer_sequence<std::size_t, IF...>,
|
||||
std::integer_sequence<std::size_t, IS...>) {
|
||||
using std::get;
|
||||
(void)firstSequenceable;
|
||||
(void)secondSequenceable;
|
||||
return std::forward<U>(unpacker)(
|
||||
get<IF>(std::forward<F>(firstSequenceable))...,
|
||||
get<IS>(std::forward<S>(secondSequenceable))...);
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename F, typename U>
|
||||
auto unpack(F&& firstSequenceable, U&& unpacker) {
|
||||
return unpack(std::forward<F>(firstSequenceable), std::forward<U>(unpacker),
|
||||
sequence_of(identity_of(firstSequenceable)));
|
||||
}
|
||||
/// Calls the given unpacker with the content of the given sequenceables
|
||||
template <typename F, typename S, typename U>
|
||||
constexpr auto unpack(F&& firstSequenceable, S&& secondSequenceable,
|
||||
U&& unpacker) {
|
||||
return unpack(std::forward<F>(firstSequenceable),
|
||||
std::forward<S>(secondSequenceable), std::forward<U>(unpacker),
|
||||
sequence_of(identity_of(firstSequenceable)),
|
||||
sequence_of(identity_of(secondSequenceable)));
|
||||
}
|
||||
|
||||
/// Applies the handler function to each element contained in the sequenceable
|
||||
template <typename Sequenceable, typename Handler>
|
||||
constexpr void static_for_each_in(Sequenceable&& sequenceable,
|
||||
Handler&& handler) {
|
||||
unpack(
|
||||
std::forward<Sequenceable>(sequenceable), [&](auto&&... entries) mutable {
|
||||
auto consume = [&](auto&& entry) mutable {
|
||||
handler(std::forward<decltype(entry)>(entry));
|
||||
return 0;
|
||||
};
|
||||
// Apply the consume function to every entry inside the pack
|
||||
auto deduce = {0, consume(std::forward<decltype(entries)>(entries))...};
|
||||
(void)deduce;
|
||||
(void)consume;
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds the given type at the back of the left sequenceable
|
||||
template <typename Left, typename Element>
|
||||
constexpr auto push(Left&& left, Element&& element) {
|
||||
return unpack(std::forward<Left>(left), [&](auto&&... leftArgs) {
|
||||
return std::make_tuple(std::forward<decltype(leftArgs)>(leftArgs)...,
|
||||
std::forward<Element>(element));
|
||||
});
|
||||
}
|
||||
|
||||
/// Adds the element to the back of the identity
|
||||
template <typename... Args, typename Element>
|
||||
constexpr auto push(identity<Args...>, identity<Element>) noexcept {
|
||||
return identity<Args..., Element>{};
|
||||
}
|
||||
|
||||
/// Removes the first element from the identity
|
||||
template <typename First, typename... Rest>
|
||||
constexpr auto pop_first(identity<First, Rest...>) noexcept {
|
||||
return identity<Rest...>{};
|
||||
}
|
||||
|
||||
/// Returns the merged sequence
|
||||
template <typename Left>
|
||||
constexpr auto merge(Left&& left) {
|
||||
return std::forward<Left>(left);
|
||||
}
|
||||
/// Merges the left sequenceable with the right ones
|
||||
template <typename Left, typename Right, typename... Rest>
|
||||
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>(left), std::forward<Right>(right),
|
||||
[&](auto&&... args) {
|
||||
// Maybe use: template <template<typename...> class T,
|
||||
// typename... Args>
|
||||
return std::make_tuple(
|
||||
std::forward<decltype(args)>(args)...);
|
||||
}),
|
||||
std::forward<Rest>(rest)...);
|
||||
}
|
||||
/// Merges the left identity with the right ones
|
||||
template <typename... LeftArgs, typename... RightArgs, typename... Rest>
|
||||
constexpr auto merge(identity<LeftArgs...> /*left*/,
|
||||
identity<RightArgs...> /*right*/, Rest&&... rest) {
|
||||
return merge(identity<LeftArgs..., RightArgs...>{},
|
||||
std::forward<Rest>(rest)...);
|
||||
}
|
||||
|
||||
/// Combines the given arguments with the given folding function
|
||||
template <typename F, typename First>
|
||||
constexpr auto fold(F&& /*folder*/, First&& first) {
|
||||
return std::forward<First>(first);
|
||||
}
|
||||
/// Combines the given arguments with the given folding function
|
||||
template <typename F, typename First, typename Second, typename... Rest>
|
||||
auto fold(F&& folder, First&& first, Second&& second, Rest&&... rest) {
|
||||
auto res = folder(std::forward<First>(first), std::forward<Second>(second));
|
||||
return fold(std::forward<F>(folder), std::move(res),
|
||||
std::forward<Rest>(rest)...);
|
||||
}
|
||||
|
||||
/// Returns a folding function using operator `&&`.
|
||||
inline auto and_folding() noexcept {
|
||||
return [](auto&& left, auto&& right) {
|
||||
return std::forward<decltype(left)>(left) &&
|
||||
std::forward<decltype(right)>(right);
|
||||
};
|
||||
}
|
||||
/// Returns a folding function using operator `||`.
|
||||
inline auto or_folding() noexcept {
|
||||
return [](auto&& left, auto&& right) {
|
||||
return std::forward<decltype(left)>(left) ||
|
||||
std::forward<decltype(right)>(right);
|
||||
};
|
||||
}
|
||||
/// Returns a folding function using operator `>>`.
|
||||
inline auto seq_folding() noexcept {
|
||||
return [](auto&& left, auto&& right) {
|
||||
return std::forward<decltype(left)>(left) >>
|
||||
std::forward<decltype(right)>(right);
|
||||
};
|
||||
}
|
||||
|
||||
/// Deduces to a std::false_type
|
||||
template <typename T>
|
||||
using fail = std::integral_constant<bool, !std::is_same<T, T>::value>;
|
||||
|
||||
namespace detail {
|
||||
template <typename T, typename Args, typename = void_t<>>
|
||||
template <typename T, typename Args, typename = traits::void_t<>>
|
||||
struct is_invokable_impl : std::common_type<std::false_type> {};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
struct is_invokable_impl<
|
||||
T, std::tuple<Args...>,
|
||||
void_t<decltype(std::declval<T>()(std::declval<Args>()...))>>
|
||||
traits::void_t<decltype(std::declval<T>()(std::declval<Args>()...))>>
|
||||
: std::common_type<std::true_type> {};
|
||||
} // end namespace detail
|
||||
|
||||
@ -474,7 +61,7 @@ struct is_invokable_impl<
|
||||
/// arguments inside lambda closures.
|
||||
///
|
||||
/// ```cpp
|
||||
/// util::is_invokable_t<object, std::tuple<Args...>>
|
||||
/// traits::is_invokable_t<object, std::tuple<Args...>>
|
||||
/// ```
|
||||
template <typename T, typename Args>
|
||||
using is_invokable_t = typename detail::is_invokable_impl<T, Args>::type;
|
||||
@ -483,22 +70,23 @@ namespace detail {
|
||||
/// Forwards every element in the tuple except the last one
|
||||
template <typename T>
|
||||
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<size.value>();
|
||||
|
||||
return unpack(std::forward<T>(sequenceable),
|
||||
[](auto&&... args) {
|
||||
return std::forward_as_tuple(
|
||||
std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
sequence);
|
||||
return traits::unpack(std::forward<T>(sequenceable),
|
||||
[](auto&&... args) {
|
||||
return std::forward_as_tuple(
|
||||
std::forward<decltype(args)>(args)...);
|
||||
},
|
||||
sequence);
|
||||
}
|
||||
|
||||
/// We are able to call the callable with the arguments given in the tuple
|
||||
template <typename T, typename... Args>
|
||||
auto partial_invoke_impl(std::true_type, T&& callable,
|
||||
std::tuple<Args...> args) {
|
||||
return unpack(std::move(args), [&](auto&&... arg) {
|
||||
return traits::unpack(std::move(args), [&](auto&&... arg) {
|
||||
return std::forward<T>(callable)(std::forward<decltype(arg)>(arg)...);
|
||||
});
|
||||
}
|
||||
|
||||
@ -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<int>{}, args...); });
|
||||
auto count = traits::unpack(tp2, [](auto... args) {
|
||||
return traits::fold(std::plus<int>{}, args...);
|
||||
});
|
||||
EXPECT_EQ(count, 20);
|
||||
}
|
||||
|
||||
TEST(recursion_tests, are_noncopyable_mergeable) {
|
||||
std::tuple<util::non_copyable> 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));
|
||||
}
|
||||
|
||||
@ -46,7 +46,8 @@
|
||||
|
||||
#include "cxx_function/cxx_function.hpp"
|
||||
|
||||
template <typename T> using cxx_function_fn = cxx_function::function<T>;
|
||||
template <typename T>
|
||||
using cxx_function_fn = cxx_function::function<T>;
|
||||
|
||||
template <typename... Args>
|
||||
using cxx_trait_of =
|
||||
@ -75,17 +76,21 @@ using std_trait_of =
|
||||
template <typename... Args>
|
||||
using std_continuable = typename std_trait_of<Args...>::continuable;
|
||||
|
||||
using cti::detail::util::identity;
|
||||
using cti::detail::traits::identity;
|
||||
|
||||
inline auto to_hint(identity<> /*hint*/) { return identity<void>{}; }
|
||||
template <typename... Args> auto to_hint(identity<Args...> hint) {
|
||||
inline auto to_hint(identity<> /*hint*/) {
|
||||
return identity<void>{};
|
||||
}
|
||||
template <typename... Args>
|
||||
auto to_hint(identity<Args...> hint) {
|
||||
return hint;
|
||||
}
|
||||
|
||||
template <typename... Args> auto supplier_of(Args&&... args) {
|
||||
template <typename... Args>
|
||||
auto supplier_of(Args&&... args) {
|
||||
return [values = std::make_tuple(std::forward<Args>(args)...)](
|
||||
auto&& callback) mutable {
|
||||
cti::detail::util::unpack(std::move(values), [&](auto&&... passed) {
|
||||
cti::detail::traits::unpack(std::move(values), [&](auto&&... passed) {
|
||||
// ...
|
||||
std::forward<decltype(callback)>(callback)(
|
||||
std::forward<decltype(passed)>(passed)...);
|
||||
@ -96,14 +101,16 @@ template <typename... Args> auto supplier_of(Args&&... args) {
|
||||
template <typename Provider>
|
||||
class continuation_provider : public ::testing::Test, public Provider {
|
||||
public:
|
||||
template <typename T> auto invoke(T&& type) {
|
||||
template <typename T>
|
||||
auto invoke(T&& type) {
|
||||
return this->make(identity<>{}, identity<void>{},
|
||||
[type = std::forward<T>(type)](auto&& callback) mutable {
|
||||
std::forward<decltype(callback)>(callback)();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename... Args> auto supply(Args&&... args) {
|
||||
template <typename... Args>
|
||||
auto supply(Args&&... args) {
|
||||
identity<std::decay_t<Args>...> arg_types;
|
||||
auto hint_types = to_hint(arg_types);
|
||||
|
||||
@ -142,14 +149,16 @@ struct provide_unique {
|
||||
}
|
||||
};
|
||||
|
||||
template <template <typename...> class Erasure> struct provide_erasure {
|
||||
template <template <typename...> class Erasure>
|
||||
struct provide_erasure {
|
||||
template <typename... Args, typename... Hint, typename T>
|
||||
Erasure<Args...> make(identity<Args...>, identity<Hint...>, T&& callback) {
|
||||
return cti::make_continuable<Hint...>(std::forward<T>(callback));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Provider> struct provide_continuation_and_left {
|
||||
template <typename Provider>
|
||||
struct provide_continuation_and_left {
|
||||
Provider provider_;
|
||||
|
||||
template <typename... Args, typename... Hint, typename T>
|
||||
@ -159,7 +168,8 @@ template <typename Provider> struct provide_continuation_and_left {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Provider> struct provide_continuation_and_right {
|
||||
template <typename Provider>
|
||||
struct provide_continuation_and_right {
|
||||
Provider provider_;
|
||||
|
||||
template <typename... Args, typename... Hint, typename T>
|
||||
@ -169,7 +179,8 @@ template <typename Provider> struct provide_continuation_and_right {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Provider> struct provide_continuation_seq_right {
|
||||
template <typename Provider>
|
||||
struct provide_continuation_seq_right {
|
||||
Provider provider_;
|
||||
|
||||
template <typename... Args, typename... Hint, typename T>
|
||||
@ -222,7 +233,8 @@ struct single_dimension_tests : continuation_provider<Provider> {};
|
||||
|
||||
TYPED_TEST_CASE(single_dimension_tests, single_types);
|
||||
|
||||
template <typename T> auto make_step(T* me, unsigned& current, unsigned step) {
|
||||
template <typename T>
|
||||
auto make_step(T* me, unsigned& current, unsigned step) {
|
||||
return me->invoke([=]() mutable {
|
||||
ASSERT_EQ(step, current);
|
||||
++current;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user