Require make_continuable to be called with a valid signature

This commit is contained in:
Denis Blank 2018-02-26 18:55:06 +01:00
parent 331d642e5d
commit 05b9223da8
7 changed files with 55 additions and 91 deletions

View File

@ -25,27 +25,27 @@
#include "continuable/continuable.hpp" #include "continuable/continuable.hpp"
cti::continuable<std::string> http_request(std::string /*url*/) { cti::continuable<std::string> http_request(std::string /*url*/) {
return cti::make_continuable<std::string>([](auto&& callback) { return [](auto&& callback) {
// ... // ...
callback.set_value("<html>...</html>"); callback.set_value("<html>...</html>");
}); };
} }
struct ResultSet {}; struct ResultSet {};
struct Buffer {}; struct Buffer {};
cti::continuable<ResultSet> mysql_query(std::string /*url*/) { cti::continuable<ResultSet> mysql_query(std::string /*url*/) {
return cti::make_continuable([](auto&& callback) { return [](auto&& callback) {
// ... // ...
callback.set_value(ResultSet{}); callback.set_value(ResultSet{});
}); };
} }
cti::continuable<Buffer> read_file(std::string /*url*/) { cti::continuable<Buffer> read_file(std::string /*url*/) {
return cti::make_continuable([](auto&& callback) { return [](auto&& callback) {
// ... // ...
callback.set_value(Buffer{}); callback.set_value(Buffer{});
}); };
} }
struct a { struct a {

View File

@ -167,7 +167,7 @@ public:
/// ``` /// ```
/// ///
/// \param executor The optional executor which is used to dispatch /// \param executor The optional executor which is used to dispatch
/// the callback. The executor needs to accept functional objects /// the callback. The executor needs to accept callable objects
/// callable through an `operator()` through its operator() itself. /// callable through an `operator()` through its operator() itself.
/// The executor can be move-only, but it's not required to. /// The executor can be move-only, but it's not required to.
/// The default executor which is used when omitting the argument /// The default executor which is used when omitting the argument
@ -634,29 +634,30 @@ private:
/// std::forward<decltype(callback)>(callback)(); /// std::forward<decltype(callback)>(callback)();
/// }); /// });
/// ``` /// ```
/// * **No arguments** indicate that the types are unknown. /// * **No arguments** Since version 3.0.0 make_continuable always requires
/// to be given valid arguments!
/// You should always give the type hint a callback is called with because /// You should always give the type hint a callback is called with because
/// it's required for intermediate actions like connecting continuables. /// it's required for intermediate actions like connecting continuables.
/// You may omit the signature hint if you are erasing the type of /// You may omit the signature hint if you are erasing the type of
/// the continuable right after creation. /// the continuable right after creation.
/// ```cpp /// ```cpp
/// // Never do this: /// // This won't work because the arguments are missing:
/// auto ct = cti::make_continuable([](auto&& callback) { /// auto ct = cti::make_continuable([](auto&& callback) {
/// std::forward<decltype(callback)>(callback)(0.f, 'c'); /// std::forward<decltype(callback)>(callback)(0.f, 'c');
/// }); /// });
/// ///
/// // However, you may do this: /// // However, you are allowed to do this:
/// continuable<float, char> ct = cti::make_continuable([](auto&& callback) { /// continuable<float, char> ct = [](auto&& callback) {
/// std::forward<decltype(callback)>(callback)(0.f, 'c'); /// std::forward<decltype(callback)>(callback)(0.f, 'c');
/// }); /// };
/// ``` /// ```
/// ///
/// \param continuation The continuation the continuable is created from. /// \param continuation The continuation the continuable is created from.
/// The continuation must be a functional type accepting a callback parameter /// The continuation must be a callable type accepting a callback parameter
/// which represents the object invokable with the asynchronous result of this /// which represents the object invokable with the asynchronous result of this
/// continuable. /// continuable.
/// ```cpp /// ```cpp
/// auto ct = cti::make_continuable([](auto&& promise) { /// auto ct = cti::make_continuable<std::string>([](auto&& promise) {
/// promise.set_value("result"); /// promise.set_value("result");
/// }); /// });
/// ``` /// ```
@ -666,11 +667,11 @@ private:
/// It's recommended to accept any callback instead of erasing it. /// It's recommended to accept any callback instead of erasing it.
/// ```cpp /// ```cpp
/// // Good practice: /// // Good practice:
/// auto ct = cti::make_continuable([](auto&& promise) { /// auto ct = cti::make_continuable<std::string>([](auto&& promise) {
/// promise.set_value("result"); /// promise.set_value("result");
/// }); /// });
/// ///
/// // Good practice using a functional object: /// // Good practice using a callable object:
/// struct Continuation { /// struct Continuation {
/// template<typename T> /// template<typename T>
/// void operator() (T&& continuation) && { /// void operator() (T&& continuation) && {
@ -678,10 +679,10 @@ private:
/// } /// }
/// } /// }
/// ///
/// auto ct = cti::make_continuable(Continuation{}); /// auto ct = cti::make_continuable<std::string>(Continuation{});
/// ///
/// // Bad practice (because of unnecessary type erasure): /// // Bad practice (because of unnecessary type erasure):
/// auto ct = cti::make_continuable( /// auto ct = cti::make_continuable<std::string>(
/// [](cti::promise<std::string> promise) { /// [](cti::promise<std::string> promise) {
/// promise.set_value("result"); /// promise.set_value("result");
/// }); /// });
@ -698,12 +699,15 @@ private:
/// \since 1.0.0 /// \since 1.0.0
template <typename... Args, typename Continuation> template <typename... Args, typename Continuation>
auto make_continuable(Continuation&& continuation) { auto make_continuable(Continuation&& continuation) {
auto hint = detail::composition::annotating::extract( static_assert(sizeof...(Args) > 0,
detail::traits::identity_of(continuation), "Since version 3.0.0 make_continuable requires an exact "
detail::traits::identity<Args...>{}); "signature! If you did intend to create a void continuable "
"use make_continuable<void>(...). Continuables with an exact "
"signature may be created through make_continuable<Args...>.");
return detail::base::attorney::create( return detail::base::attorney::create(
std::forward<Continuation>(continuation), hint, std::forward<Continuation>(continuation),
detail::hints::extract(detail::traits::identity<Args...>{}),
detail::util::ownership{}); detail::util::ownership{});
} }

View File

@ -71,7 +71,7 @@ struct is_continuable<continuable_base<Data, Annotation>> : std::true_type {};
/// the continuable_base class. /// the continuable_base class.
struct attorney { struct attorney {
/// Makes a continuation wrapper from the given argument /// Makes a continuation wrapper from the given argument
template <typename T, typename A = hints::absent_signature_hint_tag> template <typename T, typename A>
static auto create(T&& continuation, A /*hint*/, util::ownership ownership_) { static auto create(T&& continuation, A /*hint*/, util::ownership ownership_) {
return continuable_base<std::decay_t<T>, std::decay_t<A>>( return continuable_base<std::decay_t<T>, std::decay_t<A>>(
std::forward<T>(continuation), ownership_); std::forward<T>(continuation), ownership_);
@ -107,7 +107,7 @@ struct attorney {
// Returns the invoker of a callback, the next callback // Returns the invoker of a callback, the next callback
// and the arguments of the previous continuation. // and the arguments of the previous continuation.
// //
// The return type of the invokerOf function matches a functional of: // The return type of the invokerOf function matches a callable of:
// void(auto&& callback, auto&& next_callback, auto&&... args) // void(auto&& callback, auto&& next_callback, auto&&... args)
// //
// The invoker decorates the result type in the following way // The invoker decorates the result type in the following way
@ -285,7 +285,7 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) {
}); });
}; };
// Pass the work functional object to the executor // Pass the work callable object to the executor
std::forward<Executor>(executor)(std::move(work)); std::forward<Executor>(executor)(std::move(work));
} }
@ -568,7 +568,7 @@ public:
} }
}; };
/// Returns a continuable into a functional object returning the continuable /// Returns a continuable into a callable object returning the continuable
template <typename Continuation> template <typename Continuation>
auto wrap_continuation(Continuation&& continuation) { auto wrap_continuation(Continuation&& continuation) {
continuation.freeze(); continuation.freeze();

View File

@ -59,52 +59,6 @@ struct is_strategy<strategy_all_tag> : std::true_type {};
template <> template <>
struct is_strategy<strategy_any_tag> : std::true_type {}; struct is_strategy<strategy_any_tag> : std::true_type {};
/// Provides support for extracting the signature hint out
/// of given types and parameters.
namespace annotating {
namespace detail {
/// Void hints are equal to an empty signature
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(traits::identity<HintArgs...> args) noexcept {
return args; // Identity is equal to signature_hint_tag
}
} // namespace detail
/// Extracts the signature hint of a given continuation and it's optional
/// present hint arguments.
///
/// There are 3 cases:
/// - Any argument is given:
/// -> The hint is of the argument type where void is equal to no args
/// - An unwrappable type is given which first arguments signature is known
/// -> The hint is of the mentioned signature
/// - An object which signature isn't known
/// -> The hint is unknown
///
/// In any cases the return type is a:
/// - signature_hint_tag<?...> or a
/// - absent_signature_hint_tag
///
template <typename T, typename... HintArgs>
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);
});
}
} // namespace annotating
namespace detail { namespace detail {
template <std::size_t Pos, typename T> template <std::size_t Pos, typename T>
constexpr void assign(traits::size_constant<Pos> /*pos*/, T& /*storage*/) { constexpr void assign(traits::size_constant<Pos> /*pos*/, T& /*storage*/) {

View File

@ -42,28 +42,28 @@ namespace hints {
/// Represents a present signature hint /// Represents a present signature hint
template <typename... Args> template <typename... Args>
using signature_hint_tag = traits::identity<Args...>; using signature_hint_tag = traits::identity<Args...>;
/// Represents an absent signature hint
struct absent_signature_hint_tag {};
template <typename>
struct is_absent_hint : std::false_type {};
template <>
struct is_absent_hint<absent_signature_hint_tag> : std::true_type {};
/// Returns the signature hint of the given continuable
template <typename T>
constexpr auto hint_of(traits::identity<T>) {
static_assert(traits::fail<T>::value,
"Expected a continuation with an existing signature hint!");
return traits::identity<void>{};
}
/// Returns the signature hint of the given continuable /// Returns the signature hint of the given continuable
template <typename Data, typename... Args> template <typename Data, typename... Args>
constexpr auto constexpr auto
hint_of(traits::identity< hint_of(traits::identity<continuable_base<Data, signature_hint_tag<Args...>>>) {
continuable_base<Data, hints::signature_hint_tag<Args...>>>) {
return hints::signature_hint_tag<Args...>{}; return hints::signature_hint_tag<Args...>{};
} }
/// Extracts the signature we pass to the internal continuable
/// from an argument pack as specified by make_continuable.
///
/// This is the overload taking an arbitrary amount of args
template <typename... HintArgs>
constexpr auto extract(traits::identity<HintArgs...> hint) {
return hint;
}
/// \copybrief extract
///
/// This is the overload taking a void arg.
constexpr auto extract(traits::identity<void> /*hint*/) {
return traits::identity<>{};
}
} // namespace hints } // namespace hints
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti

View File

@ -254,7 +254,7 @@ constexpr auto is_valid(T&& /*type*/, Check&& /*check*/) noexcept {
return typename detail::is_valid_impl<T, Check>::type{}; return typename detail::is_valid_impl<T, Check>::type{};
} }
/// Creates a static functional validator object. /// Creates a static callable validation object.
template <typename Check> template <typename Check>
constexpr auto validator_of(Check&& check) noexcept( constexpr auto validator_of(Check&& check) noexcept(
std::is_nothrow_move_constructible<std::decay_t<Check>>::value) { std::is_nothrow_move_constructible<std::decay_t<Check>>::value) {

View File

@ -29,7 +29,13 @@ TYPED_TEST(single_dimension_tests, is_eraseable) {
{ {
cti::unique_continuable<int> erasure = cti::unique_continuable<int> erasure =
cti::make_continuable(supplier_of(0xDF)); cti::make_continuable<int>(supplier_of(0xDF));
EXPECT_ASYNC_RESULT(std::move(erasure), 0xDF);
}
{
cti::unique_continuable<int> erasure = supplier_of(0xDF);
EXPECT_ASYNC_RESULT(std::move(erasure), 0xDF); EXPECT_ASYNC_RESULT(std::move(erasure), 0xDF);
} }