What does the __completion_signature concept in stdexec (senders/ receivers) do?

75 views Asked by At

I'm looking at the reference implementation of P2300 (the senders and receivers proposal).

I'm having trouble understanding some of the code:

namespace __compl_sigs {
    template <same_as<set_value_t> _Tag, class _Ty = __q<__types>, class... _Args>
    __types<__minvoke<_Ty, _Args...>> __test(_Tag (*)(_Args...));

    template <same_as<set_error_t> _Tag, class _Ty = __q<__types>, class _Error>
    __types<__minvoke<_Ty, _Error>> __test(_Tag (*)(_Error));

    template <same_as<set_stopped_t> _Tag, class _Ty = __q<__types>>
    __types<__minvoke<_Ty>> __test(_Tag (*)());

    template <class, class = void>
    __types<> __test(...);

    template <class _Tag, class _Ty = void, class... _Args>
    void __test(_Tag (*)(_Args...) noexcept) = delete;

    template <class _Sig>
    concept __completion_signature = __typename<decltype(__compl_sigs::__test((_Sig*) nullptr))>;
} // namespace __compl_sigs

using __compl_sigs::__completion_signature;

// [...]

template <__compl_sigs::__completion_signature... _Sigs>
struct completion_signatures {
    // Uncomment this to see where completion_signatures is
    // erroneously getting instantiated:
    //static_assert(sizeof...(_Sigs) == -1u);
};

Fundamentally, can someone give me an example for a sender and what __types<__minvoke<_Ty, _Args...>>, __types<__minvoke<_Ty, _Error>> and __types<__minvoke<_Ty>> actually mean? I understand that they relate to set_value, set_error and set_stopped.

Finally, what is the use for completion_signatures?

1

There are 1 answers

0
Maikel On BEST ANSWER

completion_signatures<Ts...> shall be a type list where each Ts is either

  • set_value_t(Args...)
  • set_error_t(E)
  • set_stopped_t()

What you see in the implementation is, that a concept __completion_signature is defined such that each Ts of matches one of those forms. Otherwise, you cannot instantiate this template class. Note, that you are missing the definition of the __typename concept in your snippet, which is

  template <class... _Ts>
  concept __typename = requires { typename __types<_Ts...>; };

Each sender defines at least two methods

execution::connect(sender, receiver) -> operation execution::get_completion_signatures(sender, env) -> completion_signatures<...>

This constrained type list describes the possible completions of the async operation that results from the connect method.

Whenever you define a custom sender in P2300, you not only code the async code via the CPOs execution::connect (and execution::start for the operation) but you also have to manually code the type transformations on the completion signatures via the execution::get_completion_signatures CPO.

Unfortunately, it is not possible, or feasible, to compute the completion signatures automatically. Knowing the set of possible completions is necessary to store the results in intermediate containers within algorithms, such as execution::let_value or execution::when_all.