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"
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>");
});
};
}
struct ResultSet {};
struct Buffer {};
cti::continuable<ResultSet> mysql_query(std::string /*url*/) {
return cti::make_continuable([](auto&& callback) {
return [](auto&& callback) {
// ...
callback.set_value(ResultSet{});
});
};
}
cti::continuable<Buffer> read_file(std::string /*url*/) {
return cti::make_continuable([](auto&& callback) {
return [](auto&& callback) {
// ...
callback.set_value(Buffer{});
});
};
}
struct a {

View File

@ -167,7 +167,7 @@ public:
/// ```
///
/// \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.
/// The executor can be move-only, but it's not required to.
/// The default executor which is used when omitting the argument
@ -634,29 +634,30 @@ private:
/// 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
/// it's required for intermediate actions like connecting continuables.
/// You may omit the signature hint if you are erasing the type of
/// the continuable right after creation.
/// ```cpp
/// // Never do this:
/// // This won't work because the arguments are missing:
/// auto ct = cti::make_continuable([](auto&& callback) {
/// std::forward<decltype(callback)>(callback)(0.f, 'c');
/// });
///
/// // However, you may do this:
/// continuable<float, char> ct = cti::make_continuable([](auto&& callback) {
/// // However, you are allowed to do this:
/// continuable<float, char> ct = [](auto&& callback) {
/// std::forward<decltype(callback)>(callback)(0.f, 'c');
/// });
/// };
/// ```
///
/// \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
/// continuable.
/// ```cpp
/// auto ct = cti::make_continuable([](auto&& promise) {
/// auto ct = cti::make_continuable<std::string>([](auto&& promise) {
/// promise.set_value("result");
/// });
/// ```
@ -666,11 +667,11 @@ private:
/// It's recommended to accept any callback instead of erasing it.
/// ```cpp
/// // Good practice:
/// auto ct = cti::make_continuable([](auto&& promise) {
/// auto ct = cti::make_continuable<std::string>([](auto&& promise) {
/// promise.set_value("result");
/// });
///
/// // Good practice using a functional object:
/// // Good practice using a callable object:
/// struct Continuation {
/// template<typename T>
/// 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):
/// auto ct = cti::make_continuable(
/// auto ct = cti::make_continuable<std::string>(
/// [](cti::promise<std::string> promise) {
/// promise.set_value("result");
/// });
@ -698,12 +699,15 @@ private:
/// \since 1.0.0
template <typename... Args, typename Continuation>
auto make_continuable(Continuation&& continuation) {
auto hint = detail::composition::annotating::extract(
detail::traits::identity_of(continuation),
detail::traits::identity<Args...>{});
static_assert(sizeof...(Args) > 0,
"Since version 3.0.0 make_continuable requires an exact "
"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(
std::forward<Continuation>(continuation), hint,
std::forward<Continuation>(continuation),
detail::hints::extract(detail::traits::identity<Args...>{}),
detail::util::ownership{});
}

View File

@ -71,7 +71,7 @@ struct is_continuable<continuable_base<Data, Annotation>> : std::true_type {};
/// the continuable_base class.
struct attorney {
/// 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_) {
return continuable_base<std::decay_t<T>, std::decay_t<A>>(
std::forward<T>(continuation), ownership_);
@ -107,7 +107,7 @@ struct attorney {
// Returns the invoker of a callback, the next callback
// 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)
//
// 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));
}
@ -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>
auto wrap_continuation(Continuation&& continuation) {
continuation.freeze();

View File

@ -59,53 +59,7 @@ struct is_strategy<strategy_all_tag> : std::true_type {};
template <>
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>
constexpr void assign(traits::size_constant<Pos> /*pos*/, T& /*storage*/) {
// ...

View File

@ -42,28 +42,28 @@ namespace hints {
/// Represents a present signature hint
template <typename... 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
template <typename Data, typename... Args>
constexpr auto
hint_of(traits::identity<
continuable_base<Data, hints::signature_hint_tag<Args...>>>) {
hint_of(traits::identity<continuable_base<Data, 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 detail
} // 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{};
}
/// Creates a static functional validator object.
/// Creates a static callable validation object.
template <typename Check>
constexpr auto validator_of(Check&& check) noexcept(
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::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);
}