/* This file is part of Telegram Desktop, the official desktop version of Telegram messaging app, see https://telegram.org Telegram Desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once #include "base/lambda.h" #include #include #include #include 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>> 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>> lifetime start_copy( OnNext &&next, OnError &&error, OnDone &&done) const &; template 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 lifetime producer_base::start( OnNext &&next, OnError &&error, OnDone &&done) && { return std::move(*this).start_existing(make_consumer( std::forward(next), std::forward(error), std::forward(done))); } template template < typename OnNext, typename OnError, typename OnDone, typename> inline lifetime producer_base::start_copy( OnNext &&next, OnError &&error, OnDone &&done) const & { auto copy = *this; return std::move(copy).start( std::forward(next), std::forward(error), std::forward(done)); } template template inline lifetime producer_base::start_existing( const consumer_type &consumer) && { if (consumer.add_lifetime(std::move(_generator)(consumer))) { return [consumer] { consumer.terminate(); }; } return lifetime(); } 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) -> producer> { 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 lifetime_with_none { lifetime &alive_while; }; template struct lifetime_with_next { lifetime &alive_while; OnNext next; }; template struct lifetime_with_error { lifetime &alive_while; OnError error; }; template struct lifetime_with_done { lifetime &alive_while; OnDone done; }; template struct lifetime_with_next_error { lifetime &alive_while; OnNext next; OnError error; }; template struct lifetime_with_error_done { lifetime &alive_while; OnError error; OnDone done; }; template struct lifetime_with_next_done { lifetime &alive_while; OnNext next; OnDone done; }; template struct lifetime_with_next_error_done { lifetime &alive_while; OnNext next; OnError error; OnDone done; }; } // namespace details inline auto start(lifetime &alive_while) -> details::lifetime_with_none { return { alive_while }; } 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, lifetime &alive_while) -> details::lifetime_with_error> { return { alive_while, std::forward(error) }; } 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, 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, 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, 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, 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 inline void operator|( producer &&value, lifetime_with_none &&lifetime) { lifetime.alive_while.add( std::move(value).start( [] {}, [] {}, [] {})); } template < typename Value, typename Error, typename Generator, typename OnNext, typename = std::enable_if_t>> inline void operator|( producer &&value, lifetime_with_next &&lifetime) { lifetime.alive_while.add( std::move(value).start( std::move(lifetime.next), [] {}, [] {})); } template < typename Value, typename Error, typename Generator, typename OnError, typename = std::enable_if_t>> inline void operator|( producer &&value, lifetime_with_error &&lifetime) { lifetime.alive_while.add( std::move(value).start( [] {}, std::move(lifetime.error), [] {})); } template < typename Value, typename Error, typename Generator, typename OnDone, typename = std::enable_if_t>> inline void operator|( producer &&value, lifetime_with_done &&lifetime) { lifetime.alive_while.add( std::move(value).start( [] {}, [] {}, std::move(lifetime.done))); } 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 &&lifetime) { lifetime.alive_while.add( std::move(value).start( std::move(lifetime.next), std::move(lifetime.error), [] {})); } 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 &&lifetime) { lifetime.alive_while.add( std::move(value).start( [] {}, std::move(lifetime.error), std::move(lifetime.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 &&lifetime) { lifetime.alive_while.add( std::move(value).start( std::move(lifetime.next), [] {}, std::move(lifetime.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> &&lifetime) { lifetime.alive_while.add( std::move(value).start( std::move(lifetime.next), std::move(lifetime.error), std::move(lifetime.done))); } } // namespace details } // namespace rpl