Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions include/stdexec/__detail/__diagnostics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,155 @@ namespace stdexec {
__mexception<_NOT_CALLABLE_<_Context>, _WITH_FUNCTION_<_Fun>, _WITH_ARGUMENTS_<_Args...>>;
};
} // namespace stdexec

////////////////////////////////////////////////////////////////////////////////
#define STDEXEC_ERROR_ENABLE_SENDER_IS_FALSE \
"\n" \
"\n" \
"The given type is not a sender because stdexec::enable_sender<Sender> is false. Either:\n" \
"\n" \
"1. Give the type a nested `::sender_concept` type that is an alias for `stdexec::sender_t`,\n" \
" as in:\n" \
"\n" \
" class MySender\n" \
" {\n" \
" public:\n" \
" using sender_concept = stdexec::sender_t;\n" \
" ...\n" \
" };\n" \
"\n" \
" or,\n" \
"\n" \
"2. Specialize the `stdexec::enable_sender` boolean trait for this type to true, as follows:\n" \
"\n" \
" class MySender\n" \
" {\n" \
" ...\n" \
" };\n" \
"\n" \
" template <>\n" \
" inline constexpr bool stdexec::enable_sender<MySender> = true;\n"

////////////////////////////////////////////////////////////////////////////////
#define STDEXEC_ERROR_CANNOT_COMPUTE_COMPLETION_SIGNATURES \
"\n" \
"\n" \
"The sender type was not able to report its completion signatures when asked.\n" \
"This is either because it lacks the necessary member function, or because the\n" \
"member function was ill-formed.\n" \
"\n" \
"A sender can declare its completion signatures in one of two ways:\n" \
"\n" \
"1. By defining a nested type alias named `completion_signatures` that is a\n" \
" specialization of `stdexec::completion_signatures<...>`, as follows:\n" \
"\n" \
" class MySender\n" \
" {\n" \
" public:\n" \
" using sender_concept = stdexec::sender_t;\n" \
" using completion_signatures = stdexec::completion_signatures<\n" \
" // This sender can complete successfully with an int and a float...\n" \
" stdexec::set_value_t(int, float),\n" \
" // ... or in error with an exception_ptr\n" \
" stdexec::set_error_t(std::exception_ptr)>;\n" \
" ...\n" \
" };\n" \
"\n" \
" or,\n" \
"\n" \
"2. By defining a member function named `get_completion_signatures` that returns\n" \
" a specialization of `stdexec::completion_signatures<...>`, as follows:\n" \
"\n" \
" class MySender\n" \
" {\n" \
" public:\n" \
" using sender_concept = stdexec::sender_t;\n" \
"\n" \
" template <class... _Env>\n" \
" auto get_completion_signatures(_Env&&...) -> stdexec::completion_signatures<\n" \
" // This sender can complete successfully with an int and a float...\n" \
" stdexec::set_value_t(int, float),\n" \
" // ... or in error with a std::exception_ptr.\n" \
" stdexec::set_error_t(std::exception_ptr)>\n" \
" {\n" \
" return {};\n" \
" }\n" \
" ...\n" \
" };\n"

////////////////////////////////////////////////////////////////////////////////
#define STDEXEC_ERROR_GET_COMPLETION_SIGNATURES_RETURNED_AN_ERROR \
"\n" \
"\n" \
"Trying to compute the sender's completion signatures resulted in an error. See\n" \
"the rest of the compiler diagnostic for clues. Look for the string \"_ERROR_\".\n"

#define STDEXEC_ERROR_GET_COMPLETION_SIGNATURES_HAS_INVALID_RETURN_TYPE \
"\n" \
"\n" \
"The member function `get_completion_signatures` of the sender returned an\n" \
"invalid type.\n" \
"\n" \
"A sender's `get_completion_signatures` function must return a specialization of\n" \
"`stdexec::completion_signatures<...>`, as follows:\n" \
"\n" \
" class MySender\n" \
" {\n" \
" public:\n" \
" using sender_concept = stdexec::sender_t;\n" \
"\n" \
" template <class... _Env>\n" \
" auto get_completion_signatures(_Env&&...) -> stdexec::completion_signatures<\n" \
" // This sender can complete successfully with an int and a float...\n" \
" stdexec::set_value_t(int, float),\n" \
" // ... or in error with a std::exception_ptr.\n" \
" stdexec::set_error_t(std::exception_ptr)>\n" \
" {\n" \
" return {};\n" \
" }\n" \
" ...\n" \
" };\n"

////////////////////////////////////////////////////////////////////////////////
#define STDEXEC_ERROR_CANNOT_CONNECT_SENDER_TO_RECEIVER \
"\n" \
"A sender must provide a `connect` member function that takes a receiver as an\n" \
"argument and returns an object whose type satisfies `stdexec::operation_state`,\n" \
"as shown below:\n" \
"\n" \
" class MySender\n" \
" {\n" \
" public:\n" \
" using sender_concept = stdexec::sender_t;\n" \
" using completion_signatures = stdexec::completion_signatures<stdexec::set_value_t()>;\n" \
"\n" \
" template <class Receiver>\n" \
" struct MyOpState\n" \
" {\n" \
" using operation_state_concept = stdexec::operation_state_t;\n" \
"\n" \
" void start() noexcept\n" \
" {\n" \
" // Start the operation, which will eventually complete and send its\n" \
" // result to rcvr_;\n" \
" }\n" \
"\n" \
" Receiver rcvr_;\n" \
" };\n" \
"\n" \
" template <stdexec::receiver Receiver>\n" \
" auto connect(Receiver rcvr) -> MyOpState<Receiver>\n" \
" {\n" \
" return MyOpState<Receiver>{std::move(rcvr)};\n" \
" }\n" \
"\n" \
" ...\n" \
" };\n"

////////////////////////////////////////////////////////////////////////////////
#define STDEXEC_ERROR_SYNC_WAIT_CANNOT_CONNECT_SENDER_TO_RECEIVER \
"\n" \
"\n" \
"The sender passed to `stdexec::sync_wait()` does not have a `connect`\n" \
"member function that accepts sync_wait's " \
"receiver.\n" STDEXEC_ERROR_CANNOT_CONNECT_SENDER_TO_RECEIVER
4 changes: 2 additions & 2 deletions include/stdexec/__detail/__receivers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,12 @@ namespace stdexec {
auto __try_completions(completion_signatures<_Sigs...> *) -> decltype((
__msuccess(),
...,
__detail::__try_completion<_Receiver>(static_cast<_Sigs *>(nullptr))));
__detail::__try_completion<__decay_t<_Receiver>>(static_cast<_Sigs *>(nullptr))));
} // namespace __detail

template <class _Receiver, class _Completions>
concept receiver_of = receiver<_Receiver> && requires(_Completions *__completions) {
{ __detail::__try_completions<__decay_t<_Receiver>>(__completions) } -> __ok;
{ __detail::__try_completions<_Receiver>(__completions) } -> __ok;
};

template <class _Receiver, class _Sender>
Expand Down
20 changes: 8 additions & 12 deletions include/stdexec/__detail/__senders.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ namespace stdexec {
return true;
}

template <class _OpState>
static constexpr void __check_operation_state() noexcept {
static_assert(operation_state<_OpState>, STDEXEC_ERROR_CANNOT_CONNECT_SENDER_TO_RECEIVER);
}

template <class _Sender, class _Receiver>
static constexpr auto __select_impl() noexcept {
using _Domain = __late_domain_of_t<_Sender, env_of_t<_Receiver>>;
Expand All @@ -222,31 +227,22 @@ namespace stdexec {

if constexpr (__with_static_member<_TfxSender, _Receiver>) {
using _Result = __static_member_result_t<_TfxSender, _Receiver>;
static_assert(
operation_state<_Result>,
"Sender::connect(sender, receiver) must return a type that "
"satisfies the operation_state concept");
__check_operation_state<_Result>();
constexpr bool _Nothrow = _NothrowTfxSender
&& noexcept(
__declval<_TfxSender>()
.connect(__declval<_TfxSender>(), __declval<_Receiver>()));
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
} else if constexpr (__with_member<_TfxSender, _Receiver>) {
using _Result = __member_result_t<_TfxSender, _Receiver>;
static_assert(
operation_state<_Result>,
"sender.connect(receiver) must return a type that "
"satisfies the operation_state concept");
__check_operation_state<_Result>();
constexpr bool _Nothrow = _NothrowTfxSender
&& noexcept(__declval<_TfxSender>()
.connect(__declval<_Receiver>()));
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
} else if constexpr (__with_tag_invoke<_TfxSender, _Receiver>) {
using _Result = tag_invoke_result_t<connect_t, _TfxSender, _Receiver>;
static_assert(
operation_state<_Result>,
"stdexec::connect(sender, receiver) must return a type that "
"satisfies the operation_state concept");
__check_operation_state<_Result>();
constexpr bool _Nothrow = _NothrowTfxSender
&& nothrow_tag_invocable<connect_t, _TfxSender, _Receiver>;
return static_cast<_Result (*)() noexcept(_Nothrow)>(nullptr);
Expand Down
Loading
Loading