include/unifex/create.hpp (121 lines of code) (raw):

/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <unifex/receiver_concepts.hpp> #include <unifex/sender_concepts.hpp> #include <unifex/type_traits.hpp> #include <unifex/detail/prologue.hpp> namespace unifex { namespace _create { template <typename Receiver, typename Fn, typename Context> struct _op { struct type { explicit type(Receiver rec, Fn fn, Context ctx) noexcept(std::is_nothrow_move_constructible_v<Receiver> && std::is_nothrow_move_constructible_v<Fn> && std::is_nothrow_move_constructible_v<Context>) : rec_((Receiver&&) rec), fn_((Fn&&) fn), ctx_((Context&&) ctx) {} template (typename... Ts) (requires receiver_of<Receiver, Ts...>) void set_value(Ts&&... ts) noexcept(is_nothrow_receiver_of_v<Receiver, Ts...>) { unifex::set_value((Receiver&&) rec_, (Ts&&) ts...); } template (typename Error) (requires receiver<Receiver, Error>) void set_error(Error&& error) noexcept { unifex::set_error((Receiver&&) rec_, (Error&&) error); } void set_done() noexcept { unifex::set_done((Receiver&&) rec_); } template (class Ctx = Context) (requires (!same_as<Ctx, detail::_empty<>>)) Context const& context() const & noexcept { return ctx_; } template (class Ctx = Context) (requires (!same_as<Ctx, detail::_empty<>>)) Context&& context() && noexcept { return (Context&&) ctx_; } private: friend void tag_invoke(tag_t<start>, type& self) noexcept try { self.fn_(self); } catch(...) { unifex::set_error((Receiver&&) self.rec_, std::current_exception()); } // Forward other receiver queries template (typename CPO) (requires is_receiver_query_cpo_v<CPO> AND is_callable_v<CPO, const Receiver&>) friend auto tag_invoke(CPO cpo, const type& self) noexcept(is_nothrow_callable_v<CPO, const Receiver&>) -> callable_result_t<CPO, const Receiver&> { return std::move(cpo)(self.rec_); } UNIFEX_NO_UNIQUE_ADDRESS Receiver rec_; UNIFEX_NO_UNIQUE_ADDRESS Fn fn_; UNIFEX_NO_UNIQUE_ADDRESS Context ctx_; }; }; template <typename Receiver, typename Fn, typename Context> using _operation = typename _op<Receiver, Fn, Context>::type; template <typename Fn, typename Context> struct _snd_base { struct type { template <template<typename...> class Variant> using error_types = Variant<std::exception_ptr>; static constexpr bool sends_done = true; template (typename Self, typename Receiver) (requires derived_from<remove_cvref_t<Self>, type> AND constructible_from<Fn, member_t<Self, Fn>> AND constructible_from<Context, member_t<Self, Context>>) friend _operation<remove_cvref_t<Receiver>, Fn, Context> tag_invoke(tag_t<connect>, Self&& self, Receiver&& rec) noexcept(std::is_nothrow_constructible_v< _operation<Receiver, Fn, Context>, Receiver, member_t<Self, Fn>, member_t<Self, Context>>) { return _operation<remove_cvref_t<Receiver>, Fn, Context>{ (Receiver&&) rec, ((Self&&) self).fn_, ((Self&&) self).ctx_}; } UNIFEX_NO_UNIQUE_ADDRESS Fn fn_; UNIFEX_NO_UNIQUE_ADDRESS Context ctx_{}; }; }; template <typename Fn, typename Context, typename... ValueTypes> struct _snd { struct type : _snd_base<Fn, Context>::type { template <template<typename...> class Variant, template <typename...> class Tuple> using value_types = Variant<Tuple<ValueTypes...>>; }; }; template <typename Fn, typename... ValueTypes> using _sender = typename _snd<Fn, detail::_empty<>, ValueTypes...>::type; template <typename Fn, typename Context, typename... ValueTypes> using _sender_with_context = typename _snd<Fn, Context, ValueTypes...>::type; template <typename T> T void_cast(void* pv) noexcept { return static_cast<T&&>(*static_cast<std::add_pointer_t<T>>(pv)); } namespace _cpo { template <typename... ValueTypes> struct _fn { template (typename Fn) (requires move_constructible<Fn>) _sender<Fn, ValueTypes...> operator()(Fn fn) const noexcept(std::is_nothrow_constructible_v<_sender<Fn, ValueTypes...>, Fn>) { return _sender<Fn, ValueTypes...>{{(Fn&&) fn}}; } template (typename Fn, typename Context) (requires move_constructible<Fn> AND move_constructible<Context>) _sender_with_context<Fn, Context, ValueTypes...> operator()(Fn fn, Context ctx) const noexcept(std::is_nothrow_constructible_v< _sender_with_context<Fn, Context, ValueTypes...>, Fn, Context>) { return _sender_with_context<Fn, Context, ValueTypes...>{{(Fn&&) fn, (Context&&) ctx}}; } }; } // namespace _cpo } // namespace _create /** * \fn template <class... ValueTypes> auto create(auto fn [, auto ctx]) * \brief A utility for building a sender-based API out of a C-style API that * accepts a void* context and a function pointer continuation. * * \em Example: * \code * // A void-returning C-style async API that accepts a context and a continuation: * using callback_t = void(void* context, int result); * void old_c_style_api(int a, int b, void* context, callback_t* callback_fn); * * // A sender-based async API implemented in terms of the C-style API (using C++20): * unifex::typed_sender auto new_sender_api(int a, int b) { * return unifex::create<int>([=](auto& rec) { * old_c_style_api(a, b, &rec, [](void* context, int result) { * unifex::void_cast<decltype(rec)>(context).set_value(result); * }); * }); * } * \endcode * \tparam ValueTypes The value types of the resulting sender. Should be the list of * value type(s) accepted by the callback (with the exception of the void* * context). * \param[in] fn A void-returning callable that accepts an lvalue reference to an object * whose type satisfies the \c unifex::receiver_of<ValueTypes...> concept. This function * should dispatch to the C-style callback (see example). * \param[in] ctx An optional extra bit of data to be bundled with the receiver passed to * \c fn. E.g., if \c fn is a lambda that accepts <tt>(auto& rec)</tt> and \c ctx is 42, * then from within the body of \c fn , the value of the expression \c rec.context() is * 42. * \return A sender that, when connected and started, dispatches to the wrapped C-style * API with the callback of your choosing. The receiver passed to \c fn wraps the * receiver passed to \c connect . Your callback should "complete" the receiver passed * to \c fn , which will complete the receiver passed to \c connect in turn. */ template <typename... ValueTypes> inline constexpr _create::_cpo::_fn<ValueTypes...> create {}; using _create::void_cast; } // namespace unifex #include <unifex/detail/epilogue.hpp>