/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "base/lambda.h" #include #include #include #include #if defined _DEBUG #define RPL_PRODUCER_TYPE_ERASED_ALWAYS #endif // _DEBUG namespace rpl { namespace details { template const consumer &const_ref_consumer(); template class mutable_lambda_wrap { public: mutable_lambda_wrap(Lambda &&lambda) : _lambda(std::move(lambda)) { } mutable_lambda_wrap(const mutable_lambda_wrap &other) = default; mutable_lambda_wrap(mutable_lambda_wrap &&other) = default; mutable_lambda_wrap &operator=( const mutable_lambda_wrap &other) = default; mutable_lambda_wrap &operator=( mutable_lambda_wrap &&other) = default; template auto operator()(Args&&... args) const { return (const_cast(this)->_lambda)( std::forward(args)...); } private: Lambda _lambda; }; // Type-erased copyable mutable lambda using base::lambda. template class type_erased_generator final { public: template using consumer_type = consumer; using value_type = Value; using error_type = Error; type_erased_generator( const type_erased_generator &other) = default; type_erased_generator( type_erased_generator &&other) = default; type_erased_generator &operator=( const type_erased_generator &other) = default; type_erased_generator &operator=( type_erased_generator &&other) = default; template < typename Generator, typename = std::enable_if_t< std::is_convertible_v< decltype(std::declval()( const_ref_consumer())), lifetime> && !std::is_same_v< std::decay_t, type_erased_generator>>> type_erased_generator(Generator other) : _implementation( mutable_lambda_wrap(std::move(other))) { } template < typename Generator, typename = std::enable_if_t< std::is_convertible_v< decltype(std::declval()( const_ref_consumer())), lifetime> && !std::is_same_v< std::decay_t, type_erased_generator>>> type_erased_generator &operator=(Generator other) { _implementation = mutable_lambda_wrap( std::move(other)); return *this; } template lifetime operator()(const consumer_type &consumer) { return _implementation(consumer); } private: base::lambda> &)> _implementation; }; } // namespace details template < typename Value = empty_value, typename Error = no_error, typename Generator = details::type_erased_generator< Value, Error>> class producer; template < typename Value1, typename Value2, typename Error1, typename Error2, typename Generator> struct superset_type< producer, producer> { using type = producer< superset_type_t, superset_type_t, Generator>; }; template < typename Value, typename Error, typename Generator1, typename Generator2> struct superset_type< producer, producer> { using type = producer; }; template < typename Value, typename Error, typename Generator> struct superset_type< producer, producer> { using type = producer; }; namespace details { template class producer_base { public: template using consumer_type = consumer; using value_type = Value; using error_type = Error; template < typename OtherGenerator, typename = std::enable_if_t< std::is_constructible_v>> producer_base(OtherGenerator &&generator); producer_base(const producer_base &other) = default; producer_base(producer_base &&other) = default; producer_base &operator=(const producer_base &other) = default; producer_base &operator=(producer_base &&other) = default; template < typename OnNext, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v && is_callable_v>> void start( OnNext &&next, OnError &&error, OnDone &&done, lifetime &alive_while) &&; template < typename OnNext, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v && is_callable_v>> [[nodiscard]] lifetime start( OnNext &&next, OnError &&error, OnDone &&done) &&; template < typename OnNext, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v && is_callable_v>> void start_copy( OnNext &&next, OnError &&error, OnDone &&done, lifetime &alive_while) const &; template < typename OnNext, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v && is_callable_v>> [[nodiscard]] lifetime start_copy( OnNext &&next, OnError &&error, OnDone &&done) const &; template void start_existing( const consumer_type &consumer, lifetime &alive_while) &&; template [[nodiscard]] lifetime start_existing( const consumer_type &consumer) &&; private: Generator _generator; template < typename OtherValue, typename OtherError, typename OtherGenerator> friend class ::rpl::producer; }; template template inline producer_base::producer_base( OtherGenerator &&generator) : _generator(std::forward(generator)) { } template template < typename OnNext, typename OnError, typename OnDone, typename> inline void producer_base::start( OnNext &&next, OnError &&error, OnDone &&done, lifetime &alive_while) && { return std::move(*this).start_existing( make_consumer( std::forward(next), std::forward(error), std::forward(done)), alive_while); } template template < typename OnNext, typename OnError, typename OnDone, typename> [[nodiscard]] inline lifetime producer_base::start( OnNext &&next, OnError &&error, OnDone &&done) && { auto result = lifetime(); std::move(*this).start_existing( make_consumer( std::forward(next), std::forward(error), std::forward(done)), result); return result; } template template < typename OnNext, typename OnError, typename OnDone, typename> inline void producer_base::start_copy( OnNext &&next, OnError &&error, OnDone &&done, lifetime &alive_while) const & { auto copy = *this; return std::move(copy).start_existing( make_consumer( std::forward(next), std::forward(error), std::forward(done)), alive_while); } template template < typename OnNext, typename OnError, typename OnDone, typename> [[nodiscard]] inline lifetime producer_base::start_copy( OnNext &&next, OnError &&error, OnDone &&done) const & { auto result = lifetime(); auto copy = *this; std::move(copy).start_existing( make_consumer( std::forward(next), std::forward(error), std::forward(done)), result); return result; } template template inline void producer_base::start_existing( const consumer_type &consumer, lifetime &alive_while) && { alive_while.add(consumer.terminator()); consumer.add_lifetime(std::move(_generator)(consumer)); } template template [[nodiscard]] inline lifetime producer_base::start_existing( const consumer_type &consumer) && { auto result = lifetime(); std::move(*this).start_existing(consumer, result); return result; } template using producer_base_type_erased = producer_base< Value, Error, type_erased_generator>; } // namespace details template class producer final : public details::producer_base { using parent_type = details::producer_base< Value, Error, Generator>; public: using parent_type::parent_type; }; template class producer< Value, Error, details::type_erased_generator> final : public details::producer_base_type_erased { using parent_type = details::producer_base_type_erased< Value, Error>; public: using parent_type::parent_type;; producer(const producer &other) = default; producer(producer &&other) = default; producer &operator=(const producer &other) = default; producer &operator=(producer &&other) = default; template < typename Generic, typename = std::enable_if_t>>> producer(const details::producer_base &other) : parent_type(other._generator) { } template < typename Generic, typename = std::enable_if_t>>> producer(details::producer_base &&other) : parent_type(std::move(other._generator)) { } template < typename Generic, typename = std::enable_if_t>>> producer &operator=( const details::producer_base &other) { this->_generator = other._generator; return *this; } template < typename Generic, typename = std::enable_if_t>>> producer &operator=( details::producer_base &&other) { this->_generator = std::move(other._generator); return *this; } }; template < typename Value = empty_value, typename Error = no_error, typename Generator, typename = std::enable_if_t< std::is_convertible_v< decltype(std::declval()( details::const_ref_consumer())), lifetime>>> inline auto make_producer(Generator &&generator) #ifdef RPL_PRODUCER_TYPE_ERASED_ALWAYS -> producer { #else // RPL_CONSUMER_TYPE_ERASED_ALWAYS -> producer> { #endif // !RPL_CONSUMER_TYPE_ERASED_ALWAYS return std::forward(generator); } template inline producer duplicate( const producer &value) { return value; } template < typename Value, typename Error, typename Generator, typename Method, typename = decltype(std::declval()( std::declval>()))> inline auto operator|( producer &&value, Method &&method) { return std::forward(method)(std::move(value)); } namespace details { struct with_none { }; struct lifetime_with_none { lifetime &alive_while; }; template struct with_next { OnNext next; }; template struct lifetime_with_next { lifetime &alive_while; OnNext next; }; template struct with_error { OnError error; }; template struct lifetime_with_error { lifetime &alive_while; OnError error; }; template struct with_done { OnDone done; }; template struct lifetime_with_done { lifetime &alive_while; OnDone done; }; template struct with_next_error { OnNext next; OnError error; }; template struct lifetime_with_next_error { lifetime &alive_while; OnNext next; OnError error; }; template struct with_error_done { OnError error; OnDone done; }; template struct lifetime_with_error_done { lifetime &alive_while; OnError error; OnDone done; }; template struct with_next_done { OnNext next; OnDone done; }; template struct lifetime_with_next_done { lifetime &alive_while; OnNext next; OnDone done; }; template struct with_next_error_done { OnNext next; OnError error; OnDone done; }; template struct lifetime_with_next_error_done { lifetime &alive_while; OnNext next; OnError error; OnDone done; }; } // namespace details inline auto start() -> details::with_none { return {}; } inline auto start(lifetime &alive_while) -> details::lifetime_with_none { return { alive_while }; } template inline auto start_with_next(OnNext &&next) -> details::with_next> { return { std::forward(next) }; } template inline auto start_with_next(OnNext &&next, lifetime &alive_while) -> details::lifetime_with_next> { return { alive_while, std::forward(next) }; } template inline auto start_with_error(OnError &&error) -> details::with_error> { return { std::forward(error) }; } template inline auto start_with_error(OnError &&error, lifetime &alive_while) -> details::lifetime_with_error> { return { alive_while, std::forward(error) }; } template inline auto start_with_done(OnDone &&done) -> details::with_done> { return { std::forward(done) }; } template inline auto start_with_done(OnDone &&done, lifetime &alive_while) -> details::lifetime_with_done> { return { alive_while, std::forward(done) }; } template inline auto start_with_next_error( OnNext &&next, OnError &&error) -> details::with_next_error< std::decay_t, std::decay_t> { return { std::forward(next), std::forward(error) }; } template inline auto start_with_next_error( OnNext &&next, OnError &&error, lifetime &alive_while) -> details::lifetime_with_next_error< std::decay_t, std::decay_t> { return { alive_while, std::forward(next), std::forward(error) }; } template inline auto start_with_error_done( OnError &&error, OnDone &&done) -> details::with_error_done< std::decay_t, std::decay_t> { return { std::forward(error), std::forward(done) }; } template inline auto start_with_error_done( OnError &&error, OnDone &&done, lifetime &alive_while) -> details::lifetime_with_error_done< std::decay_t, std::decay_t> { return { alive_while, std::forward(error), std::forward(done) }; } template inline auto start_with_next_done( OnNext &&next, OnDone &&done) -> details::with_next_done< std::decay_t, std::decay_t> { return { std::forward(next), std::forward(done) }; } template inline auto start_with_next_done( OnNext &&next, OnDone &&done, lifetime &alive_while) -> details::lifetime_with_next_done< std::decay_t, std::decay_t> { return { alive_while, std::forward(next), std::forward(done) }; } template inline auto start_with_next_error_done( OnNext &&next, OnError &&error, OnDone &&done) -> details::with_next_error_done< std::decay_t, std::decay_t, std::decay_t> { return { std::forward(next), std::forward(error), std::forward(done) }; } template inline auto start_with_next_error_done( OnNext &&next, OnError &&error, OnDone &&done, lifetime &alive_while) -> details::lifetime_with_next_error_done< std::decay_t, std::decay_t, std::decay_t> { return { alive_while, std::forward(next), std::forward(error), std::forward(done) }; } namespace details { template [[nodiscard]] inline lifetime operator|( producer &&value, with_none &&handlers) { return std::move(value).start( [] {}, [] {}, [] {}); } template inline void operator|( producer &&value, lifetime_with_none &&handlers) { std::move(value).start( [] {}, [] {}, [] {}, handlers.alive_while); } template < typename Value, typename Error, typename Generator, typename OnNext, typename = std::enable_if_t>> [[nodiscard]] inline lifetime operator|( producer &&value, with_next &&handlers) { return std::move(value).start( std::move(handlers.next), [] {}, [] {}); } template < typename Value, typename Error, typename Generator, typename OnNext, typename = std::enable_if_t>> inline void operator|( producer &&value, lifetime_with_next &&handlers) { std::move(value).start( std::move(handlers.next), [] {}, [] {}, handlers.alive_while); } template < typename Value, typename Error, typename Generator, typename OnError, typename = std::enable_if_t>> [[nodiscard]] inline lifetime operator|( producer &&value, with_error &&handlers) { return std::move(value).start( [] {}, std::move(handlers.error), [] {}); } template < typename Value, typename Error, typename Generator, typename OnError, typename = std::enable_if_t>> inline void operator|( producer &&value, lifetime_with_error &&handlers) { std::move(value).start( [] {}, std::move(handlers.error), [] {}, handlers.alive_while); } template < typename Value, typename Error, typename Generator, typename OnDone, typename = std::enable_if_t>> [[nodiscard]] inline lifetime operator|( producer &&value, with_done &&handlers) { return std::move(value).start( [] {}, [] {}, std::move(handlers.done)); } template < typename Value, typename Error, typename Generator, typename OnDone, typename = std::enable_if_t>> inline void operator|( producer &&value, lifetime_with_done &&handlers) { std::move(value).start( [] {}, [] {}, std::move(handlers.done), handlers.alive_while); } template < typename Value, typename Error, typename Generator, typename OnNext, typename OnError, typename = std::enable_if_t< is_callable_v && is_callable_v>> [[nodiscard]] inline lifetime operator|( producer &&value, with_next_error &&handlers) { return std::move(value).start( std::move(handlers.next), std::move(handlers.error), [] {}); } template < typename Value, typename Error, typename Generator, typename OnNext, typename OnError, typename = std::enable_if_t< is_callable_v && is_callable_v>> inline void operator|( producer &&value, lifetime_with_next_error &&handlers) { std::move(value).start( std::move(handlers.next), std::move(handlers.error), [] {}, handlers.alive_while); } template < typename Value, typename Error, typename Generator, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v>> [[nodiscard]] inline lifetime operator|( producer &&value, with_error_done &&handlers) { return std::move(value).start( [] {}, std::move(handlers.error), std::move(handlers.done)); } template < typename Value, typename Error, typename Generator, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v>> inline void operator|( producer &&value, lifetime_with_error_done &&handlers) { std::move(value).start( [] {}, std::move(handlers.error), std::move(handlers.done), handlers.alive_while); } template < typename Value, typename Error, typename Generator, typename OnNext, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v>> [[nodiscard]] inline lifetime operator|( producer &&value, with_next_done &&handlers) { return std::move(value).start( std::move(handlers.next), [] {}, std::move(handlers.done)); } template < typename Value, typename Error, typename Generator, typename OnNext, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v>> inline void operator|( producer &&value, lifetime_with_next_done &&handlers) { std::move(value).start( std::move(handlers.next), [] {}, std::move(handlers.done), handlers.alive_while); } template < typename Value, typename Error, typename Generator, typename OnNext, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v && is_callable_v>> [[nodiscard]] inline lifetime operator|( producer &&value, with_next_error_done< OnNext, OnError, OnDone> &&handlers) { return std::move(value).start( std::move(handlers.next), std::move(handlers.error), std::move(handlers.done)); } template < typename Value, typename Error, typename Generator, typename OnNext, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v && is_callable_v>> inline void operator|( producer &&value, lifetime_with_next_error_done< OnNext, OnError, OnDone> &&handlers) { std::move(value).start( std::move(handlers.next), std::move(handlers.error), std::move(handlers.done), handlers.alive_while); } } // namespace details } // namespace rpl