/* 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 #include #include #include // GCC 7.2 can't handle not type-erased consumers. // It eats up 4GB RAM + 16GB swap on the unittest and dies. // Clang and Visual C++ both handle it without such problems. #if defined _DEBUG || defined COMPILER_GCC #define RPL_CONSUMER_TYPE_ERASED_ALWAYS #endif // _DEBUG || COMPILER_GCC namespace rpl { namespace details { template < typename Value, typename Error, typename OnNext, typename OnError, typename OnDone> class consumer_handlers; template class type_erased_handlers { public: virtual bool put_next(Value &&value) = 0; virtual bool put_next_copy(const Value &value) = 0; virtual void put_error(Error &&error) = 0; virtual void put_error_copy(const Error &error) = 0; virtual void put_done() = 0; bool add_lifetime(lifetime &&lifetime); template Type *make_state(Args&& ...args); void terminate(); virtual ~type_erased_handlers() = default; protected: lifetime _lifetime; bool _terminated = false; }; template struct is_type_erased_handlers : std::false_type { }; template struct is_type_erased_handlers> : std::true_type { }; template constexpr bool is_type_erased_handlers_v = is_type_erased_handlers::value; template class consumer_handlers final : public type_erased_handlers { public: template < typename OnNextOther, typename OnErrorOther, typename OnDoneOther> consumer_handlers( OnNextOther &&next, OnErrorOther &&error, OnDoneOther &&done) : _next(std::forward(next)) , _error(std::forward(error)) , _done(std::forward(done)) { } bool put_next(Value &&value) final override; bool put_next_copy(const Value &value) final override; void put_error(Error &&error) final override; void put_error_copy(const Error &error) final override; void put_done() final override; private: OnNext _next; OnError _error; OnDone _done; }; template inline bool type_erased_handlers::add_lifetime( lifetime &&lifetime) { if (_terminated) { lifetime.destroy(); return false; } _lifetime.add(std::move(lifetime)); return true; } template template inline Type *type_erased_handlers::make_state( Args&& ...args) { if (_terminated) { return nullptr; } return _lifetime.make_state(std::forward(args)...); } template inline void type_erased_handlers::terminate() { if (!_terminated) { _terminated = true; _lifetime.destroy(); } } template < typename Value, typename Error, typename OnNext, typename OnError, typename OnDone> bool consumer_handlers< Value, Error, OnNext, OnError, OnDone >::put_next(Value &&value) { if (this->_terminated) { return false; } auto handler = this->_next; details::callable_invoke(std::move(handler), std::move(value)); return true; } template < typename Value, typename Error, typename OnNext, typename OnError, typename OnDone> bool consumer_handlers< Value, Error, OnNext, OnError, OnDone >::put_next_copy(const Value &value) { if (this->_terminated) { return false; } auto handler = this->_next; details::const_ref_call_invoke(std::move(handler), value); return true; } template < typename Value, typename Error, typename OnNext, typename OnError, typename OnDone> void consumer_handlers< Value, Error, OnNext, OnError, OnDone >::put_error(Error &&error) { if (!this->_terminated) { details::callable_invoke( std::move(this->_error), std::move(error)); this->terminate(); } } template < typename Value, typename Error, typename OnNext, typename OnError, typename OnDone> void consumer_handlers< Value, Error, OnNext, OnError, OnDone >::put_error_copy(const Error &error) { if (!this->_terminated) { details::const_ref_call_invoke( std::move(this->_error), error); this->terminate(); } } template < typename Value, typename Error, typename OnNext, typename OnError, typename OnDone> void consumer_handlers< Value, Error, OnNext, OnError, OnDone >::put_done() { if (!this->_terminated) { std::move(this->_done)(); this->terminate(); } } } // namespace details struct no_value { no_value() = delete; }; struct no_error { no_error() = delete; }; struct empty_value { }; struct empty_error { }; template < typename Value = empty_value, typename Error = no_error, typename Handlers = details::type_erased_handlers> class consumer; namespace details { template class consumer_base { static constexpr bool is_type_erased = is_type_erased_handlers_v; public: template < typename OnNext, typename OnError, typename OnDone> consumer_base( OnNext &&next, OnError &&error, OnDone &&done); bool put_next(Value &&value) const; bool put_next_copy(const Value &value) const; bool put_next_forward(Value &&value) const { return put_next(std::move(value)); } bool put_next_forward(const Value &value) const { return put_next_copy(value); } void put_error(Error &&error) const; void put_error_copy(const Error &error) const; void put_error_forward(Error &&error) const { return put_error(std::move(error)); } void put_error_forward(const Error &error) const { return put_error_copy(error); } void put_done() const; bool add_lifetime(lifetime &&lifetime) const; template Type *make_state(Args&& ...args) const; void terminate() const; auto terminator() const { return [self = *this] { self.terminate(); }; } const details::type_erased_handlers *comparable() const { return _handlers.get(); } private: template < typename OtherHandlers, typename = std::enable_if_t< std::is_base_of_v>> consumer_base(const std::shared_ptr &handlers) : _handlers(handlers) { } template < typename OtherHandlers, typename = std::enable_if_t< std::is_base_of_v>> consumer_base(std::shared_ptr &&handlers) : _handlers(std::move(handlers)) { } mutable std::shared_ptr _handlers; bool handlers_put_next(Value &&value) const { if constexpr (is_type_erased) { return _handlers->put_next(std::move(value)); } else { return _handlers->Handlers::put_next(std::move(value)); } } bool handlers_put_next_copy(const Value &value) const { if constexpr (is_type_erased) { return _handlers->put_next_copy(value); } else { return _handlers->Handlers::put_next_copy(value); } } std::shared_ptr take_handlers() const { return std::exchange(_handlers, nullptr); } template < typename OtherValue, typename OtherError, typename OtherHandlers> friend class ::rpl::consumer; }; template template inline consumer_base::consumer_base( OnNext &&next, OnError &&error, OnDone &&done) : _handlers(std::make_shared, std::decay_t, std::decay_t>>( std::forward(next), std::forward(error), std::forward(done))) { } template inline bool consumer_base::put_next( Value &&value) const { if (_handlers) { if (handlers_put_next(std::move(value))) { return true; } _handlers = nullptr; } return false; } template inline bool consumer_base::put_next_copy( const Value &value) const { if (_handlers) { if (handlers_put_next_copy(value)) { return true; } _handlers = nullptr; } return false; } template inline void consumer_base::put_error( Error &&error) const { if (_handlers) { if constexpr (is_type_erased) { take_handlers()->put_error(std::move(error)); } else { take_handlers()->Handlers::put_error(std::move(error)); } } } template inline void consumer_base::put_error_copy( const Error &error) const { if (_handlers) { if constexpr (is_type_erased) { take_handlers()->put_error_copy(error); } else { take_handlers()->Handlers::put_error_copy(error); } } } template inline void consumer_base::put_done() const { if (_handlers) { if constexpr (is_type_erased) { take_handlers()->put_done(); } else { take_handlers()->Handlers::put_done(); } } } template inline bool consumer_base::add_lifetime( lifetime &&lifetime) const { if (!_handlers) { lifetime.destroy(); return false; } if (_handlers->add_lifetime(std::move(lifetime))) { return true; } _handlers = nullptr; return false; } template template inline Type *consumer_base::make_state( Args&& ...args) const { if (!_handlers) { return nullptr; } if (auto result = _handlers->template make_state( std::forward(args)...)) { return result; } _handlers = nullptr; return nullptr; } template inline void consumer_base::terminate() const { if (_handlers) { std::exchange(_handlers, nullptr)->terminate(); } } template using consumer_base_type_erased = consumer_base< Value, Error, details::type_erased_handlers>; template constexpr bool is_specific_handlers_v = !std::is_same_v< details::type_erased_handlers, Handlers > && std::is_base_of_v< details::type_erased_handlers, Handlers >; } // namespace details template class consumer final : public details::consumer_base { using parent_type = details::consumer_base< Value, Error, Handlers>; public: using parent_type::parent_type; }; template class consumer> final : public details::consumer_base_type_erased { using parent_type = details::consumer_base_type_erased< Value, Error>; public: using parent_type::parent_type; template < typename Handlers, typename = std::enable_if_t< details::is_specific_handlers_v>> consumer(const details::consumer_base &other) : parent_type(other._handlers) { } template < typename Handlers, typename = std::enable_if_t< details::is_specific_handlers_v>> consumer(details::consumer_base &&other) : parent_type(std::move(other._handlers)) { } template < typename Handlers, typename = std::enable_if_t< details::is_specific_handlers_v>> consumer &operator=( const details::consumer_base &other) { this->_handlers = other._handlers; return *this; } template < typename Handlers, typename = std::enable_if_t< details::is_specific_handlers_v>> consumer &operator=( details::consumer_base &&other) { this->_handlers = std::move(other._handlers); return *this; } }; template < typename Value, typename Error, typename Handlers1, typename Handlers2> inline bool operator==( const consumer &a, const consumer &b) { return a.comparable() == b.comparable(); } template < typename Value, typename Error, typename Handlers1, typename Handlers2> inline bool operator<( const consumer &a, const consumer &b) { return a.comparable() < b.comparable(); } template < typename Value, typename Error, typename Handlers1, typename Handlers2> inline bool operator!=( const consumer &a, const consumer &b) { return !(a == b); } template < typename Value, typename Error, typename Handlers1, typename Handlers2> inline bool operator>( const consumer &a, const consumer &b) { return b < a; } template < typename Value, typename Error, typename Handlers1, typename Handlers2> inline bool operator<=( const consumer &a, const consumer &b) { return !(b < a); } template < typename Value, typename Error, typename Handlers1, typename Handlers2> inline bool operator>=( const consumer &a, const consumer &b) { return !(a < b); } template < typename Value, typename Error, typename OnNext, typename OnError, typename OnDone, typename = std::enable_if_t< details::is_callable_v && details::is_callable_v && details::is_callable_v>> #ifdef RPL_CONSUMER_TYPE_ERASED_ALWAYS inline consumer make_consumer( #else // RPL_CONSUMER_TYPE_ERASED_ALWAYS inline auto make_consumer( #endif // !RPL_CONSUMER_TYPE_ERASED_ALWAYS OnNext &&next, OnError &&error, OnDone &&done) { return consumer, std::decay_t, std::decay_t>>( std::forward(next), std::forward(error), std::forward(done)); } } // namespace rpl