mirror of
https://github.com/Naios/continuable.git
synced 2026-02-09 11:16:40 +08:00
Base implementation of partial callback calls
This commit is contained in:
parent
43a2c47a91
commit
3b4fd82039
@ -427,6 +427,114 @@ inline auto or_folding() {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
using fail = std::integral_constant<bool, !std::is_same<T, T>::value>;
|
using fail = std::integral_constant<bool, !std::is_same<T, T>::value>;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename T, typename Args, typename = 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>()...))>>
|
||||||
|
: std::common_type<std::true_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<object, std::tuple<Args...>>
|
||||||
|
/// ```
|
||||||
|
template <typename T, typename Args>
|
||||||
|
using is_invokable_t = typename detail::is_invokable_impl<T, Args>::type;
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 std::forward<T>(callable)(std::forward<decltype(arg)>(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 <typename T, typename... Args>
|
||||||
|
auto partial_invoke_impl(std::false_type, T&& callable,
|
||||||
|
std::tuple<Args...> args) {
|
||||||
|
|
||||||
|
// If you are encountering this assertion you tried to attach a callback
|
||||||
|
// which can't accept the arguments of the continuation.
|
||||||
|
//
|
||||||
|
// ```cpp
|
||||||
|
// continuable<int, int> c;
|
||||||
|
// std::move(c).then([](std::vector<int> 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<decltype(callable), decltype(next)> is_invokable;
|
||||||
|
|
||||||
|
return partial_invoke_impl(is_invokable, std::forward<T>(callable),
|
||||||
|
std::move(next));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut - we can call the callable directly
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
auto partial_invoke_impl_shortcut(std::true_type, T&& callable,
|
||||||
|
Args&&... args) {
|
||||||
|
return std::forward<T>(callable)(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Failed shortcut - we were unable to invoke the callable with the
|
||||||
|
/// original arguments.
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
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<T>(callable),
|
||||||
|
std::forward_as_tuple(std::forward<Args>(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 <typename T, typename... Args>
|
||||||
|
auto partial_invoke(T&& callable, Args&&... args) {
|
||||||
|
// Test whether we are able to call the function with the given arguments.
|
||||||
|
is_invokable_t<decltype(callable), std::tuple<Args...>> 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<T>(callable), std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
// Class for making child classes non copyable
|
// Class for making child classes non copyable
|
||||||
struct non_copyable {
|
struct non_copyable {
|
||||||
non_copyable() = default;
|
non_copyable() = default;
|
||||||
|
|||||||
@ -4,3 +4,6 @@ add_executable(test-playground
|
|||||||
target_link_libraries(test-playground
|
target_link_libraries(test-playground
|
||||||
PRIVATE
|
PRIVATE
|
||||||
continuable)
|
continuable)
|
||||||
|
|
||||||
|
add_test(NAME continuable-playground-tests
|
||||||
|
COMMAND test-playground)
|
||||||
|
|||||||
@ -22,16 +22,31 @@
|
|||||||
|
|
||||||
#include "continuable/continuable.hpp"
|
#include "continuable/continuable.hpp"
|
||||||
|
|
||||||
auto invoke() {
|
using namespace cti::detail;
|
||||||
return cti::make_continuable<void>([](auto&& callback) { callback(); });
|
using namespace cti::detail::util;
|
||||||
|
|
||||||
|
/// Predicate to check whether an object is callable with the given arguments
|
||||||
|
template <typename... Args> auto is_invokable_with(identity<Args...>) {
|
||||||
|
return [](auto&& callable) {
|
||||||
|
(void)callable;
|
||||||
|
return is_invokable_t<decltype(callable), identity<Args...>>{};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void trythestuff() {
|
template <typename... Right>
|
||||||
auto future = invoke().futurize();
|
auto reverse(identity<>, identity<Right...> right = identity<>{}) {
|
||||||
future.get();
|
return right;
|
||||||
|
}
|
||||||
|
template <typename First, typename... Left, typename... Right>
|
||||||
|
auto reverse(identity<First, Left...>, identity<Right...> = identity<>{}) {
|
||||||
|
return reverse(identity<Left...>{}, identity<First, Right...>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int, char**) {
|
int main(int, char**) {
|
||||||
trythestuff();
|
|
||||||
|
auto cb = [](int, int) { return 0; };
|
||||||
|
|
||||||
|
partial_invoke(cb, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user