| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 |
- // This file is part of Desktop App Toolkit,
- // a set of libraries for developing nice desktop applications.
- //
- // For license and copyright information please follow this link:
- // https://github.com/desktop-app/legal/blob/master/LEGAL
- //
- #pragma once
- #include <memory>
- #include <gsl/assert>
- #include <rpl/lifetime.h>
- #include <rpl/details/callable.h>
- // 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 <typename Value, typename Error>
- 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 <typename Type, typename... Args>
- Type *make_state(Args&& ...args);
- void terminate();
- virtual ~type_erased_handlers() = default;
- protected:
- lifetime _lifetime;
- bool _terminated = false;
- };
- template <typename Handlers>
- struct is_type_erased_handlers
- : std::false_type {
- };
- template <typename Value, typename Error>
- struct is_type_erased_handlers<type_erased_handlers<Value, Error>>
- : std::true_type {
- };
- template <typename Handlers>
- constexpr bool is_type_erased_handlers_v
- = is_type_erased_handlers<Handlers>::value;
- template <typename Value, typename Error, typename OnNext, typename OnError, typename OnDone>
- class consumer_handlers final
- : public type_erased_handlers<Value, Error> {
- public:
- template <
- typename OnNextOther,
- typename OnErrorOther,
- typename OnDoneOther>
- consumer_handlers(
- OnNextOther &&next,
- OnErrorOther &&error,
- OnDoneOther &&done)
- : _next(std::forward<OnNextOther>(next))
- , _error(std::forward<OnErrorOther>(error))
- , _done(std::forward<OnDoneOther>(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 <typename Value, typename Error>
- inline bool type_erased_handlers<Value, Error>::add_lifetime(
- lifetime &&lifetime) {
- if (_terminated) {
- lifetime.destroy();
- return false;
- }
- _lifetime.add(std::move(lifetime));
- return true;
- }
- template <typename Value, typename Error>
- template <typename Type, typename... Args>
- inline Type *type_erased_handlers<Value, Error>::make_state(
- Args&& ...args) {
- if (_terminated) {
- return nullptr;
- }
- return _lifetime.make_state<Type>(std::forward<Args>(args)...);
- }
- template <typename Value, typename Error>
- inline void type_erased_handlers<Value, Error>::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 {
- };
- inline constexpr empty_value empty{};
- template <
- typename Value = empty_value,
- typename Error = no_error,
- typename Handlers = details::type_erased_handlers<Value, Error>>
- class consumer;
- namespace details {
- template <typename Value, typename Error, typename Handlers>
- class consumer_base {
- static constexpr bool is_type_erased
- = is_type_erased_handlers_v<Handlers>;
- 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 <typename Type, typename... Args>
- Type *make_state(Args&& ...args) const;
- void terminate() const;
- auto terminator() const {
- return [self = *this] {
- self.terminate();
- };
- }
- const details::type_erased_handlers<Value, Error> *comparable() const {
- return _handlers.get();
- }
- private:
- template <
- typename OtherHandlers,
- typename = std::enable_if_t<
- std::is_base_of_v<Handlers, OtherHandlers>>>
- consumer_base(const std::shared_ptr<OtherHandlers> &handlers)
- : _handlers(handlers) {
- }
- template <
- typename OtherHandlers,
- typename = std::enable_if_t<
- std::is_base_of_v<Handlers, OtherHandlers>>>
- consumer_base(std::shared_ptr<OtherHandlers> &&handlers)
- : _handlers(std::move(handlers)) {
- }
- mutable std::shared_ptr<Handlers> _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<Handlers> take_handlers() const {
- return std::exchange(_handlers, nullptr);
- }
- template <
- typename OtherValue,
- typename OtherError,
- typename OtherHandlers>
- friend class ::rpl::consumer;
- };
- template <typename Value, typename Error, typename Handlers>
- template <typename OnNext, typename OnError, typename OnDone>
- inline consumer_base<Value, Error, Handlers>::consumer_base(
- OnNext &&next,
- OnError &&error,
- OnDone &&done)
- : _handlers(std::make_shared<consumer_handlers<
- Value,
- Error,
- std::decay_t<OnNext>,
- std::decay_t<OnError>,
- std::decay_t<OnDone>>>(
- std::forward<OnNext>(next),
- std::forward<OnError>(error),
- std::forward<OnDone>(done))) {
- }
- template <typename Value, typename Error, typename Handlers>
- inline bool consumer_base<Value, Error, Handlers>::put_next(
- Value &&value) const {
- if (_handlers) {
- if (handlers_put_next(std::move(value))) {
- return true;
- }
- _handlers = nullptr;
- }
- return false;
- }
- template <typename Value, typename Error, typename Handlers>
- inline bool consumer_base<Value, Error, Handlers>::put_next_copy(
- const Value &value) const {
- if (_handlers) {
- if (handlers_put_next_copy(value)) {
- return true;
- }
- _handlers = nullptr;
- }
- return false;
- }
- template <typename Value, typename Error, typename Handlers>
- inline void consumer_base<Value, Error, Handlers>::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 <typename Value, typename Error, typename Handlers>
- inline void consumer_base<Value, Error, Handlers>::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 <typename Value, typename Error, typename Handlers>
- inline void consumer_base<Value, Error, Handlers>::put_done() const {
- if (_handlers) {
- if constexpr (is_type_erased) {
- take_handlers()->put_done();
- } else {
- take_handlers()->Handlers::put_done();
- }
- }
- }
- template <typename Value, typename Error, typename Handlers>
- inline bool consumer_base<Value, Error, Handlers>::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 <typename Value, typename Error, typename Handlers>
- template <typename Type, typename... Args>
- inline Type *consumer_base<Value, Error, Handlers>::make_state(
- Args&& ...args) const {
- if (!_handlers) {
- return nullptr;
- }
- if (auto result = _handlers->template make_state<Type>(
- std::forward<Args>(args)...)) {
- return result;
- }
- _handlers = nullptr;
- return nullptr;
- }
- template <typename Value, typename Error, typename Handlers>
- inline void consumer_base<Value, Error, Handlers>::terminate() const {
- if (_handlers) {
- std::exchange(_handlers, nullptr)->terminate();
- }
- }
- template <typename Value, typename Error>
- using consumer_base_type_erased = consumer_base<
- Value,
- Error,
- details::type_erased_handlers<Value, Error>>;
- template <typename Value, typename Error, typename Handlers>
- constexpr bool is_specific_handlers_v = !std::is_same_v<
- details::type_erased_handlers<Value, Error>,
- Handlers
- > && std::is_base_of_v<
- details::type_erased_handlers<Value, Error>,
- Handlers
- >;
- } // namespace details
- template <typename Value, typename Error, typename Handlers>
- class consumer final
- : public details::consumer_base<Value, Error, Handlers> {
- using parent_type = details::consumer_base<
- Value,
- Error,
- Handlers>;
- public:
- using parent_type::parent_type;
- };
- template <typename Value, typename Error>
- class consumer<Value, Error, details::type_erased_handlers<Value, Error>> final
- : public details::consumer_base_type_erased<Value, Error> {
- 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<Value, Error, Handlers>>>
- consumer(const details::consumer_base<Value, Error, Handlers> &other)
- : parent_type(other._handlers) {
- }
- template <
- typename Handlers,
- typename = std::enable_if_t<
- details::is_specific_handlers_v<Value, Error, Handlers>>>
- consumer(details::consumer_base<Value, Error, Handlers> &&other)
- : parent_type(std::move(other._handlers)) {
- }
- template <
- typename Handlers,
- typename = std::enable_if_t<
- details::is_specific_handlers_v<Value, Error, Handlers>>>
- consumer &operator=(
- const details::consumer_base<Value, Error, Handlers> &other) {
- this->_handlers = other._handlers;
- return *this;
- }
- template <
- typename Handlers,
- typename = std::enable_if_t<
- details::is_specific_handlers_v<Value, Error, Handlers>>>
- consumer &operator=(
- details::consumer_base<Value, Error, Handlers> &&other) {
- this->_handlers = std::move(other._handlers);
- return *this;
- }
- };
- template <
- typename Value,
- typename Error,
- typename Handlers1,
- typename Handlers2>
- inline bool operator==(
- const consumer<Value, Error, Handlers1> &a,
- const consumer<Value, Error, Handlers2> &b) {
- return a.comparable() == b.comparable();
- }
- template <
- typename Value,
- typename Error,
- typename Handlers1,
- typename Handlers2>
- inline bool operator<(
- const consumer<Value, Error, Handlers1> &a,
- const consumer<Value, Error, Handlers2> &b) {
- return a.comparable() < b.comparable();
- }
- template <
- typename Value,
- typename Error,
- typename Handlers1,
- typename Handlers2>
- inline bool operator!=(
- const consumer<Value, Error, Handlers1> &a,
- const consumer<Value, Error, Handlers2> &b) {
- return !(a == b);
- }
- template <
- typename Value,
- typename Error,
- typename Handlers1,
- typename Handlers2>
- inline bool operator>(
- const consumer<Value, Error, Handlers1> &a,
- const consumer<Value, Error, Handlers2> &b) {
- return b < a;
- }
- template <
- typename Value,
- typename Error,
- typename Handlers1,
- typename Handlers2>
- inline bool operator<=(
- const consumer<Value, Error, Handlers1> &a,
- const consumer<Value, Error, Handlers2> &b) {
- return !(b < a);
- }
- template <
- typename Value,
- typename Error,
- typename Handlers1,
- typename Handlers2>
- inline bool operator>=(
- const consumer<Value, Error, Handlers1> &a,
- const consumer<Value, Error, Handlers2> &b) {
- return !(a < b);
- }
- template <
- typename Value,
- typename Error,
- typename OnNext,
- typename OnError,
- typename OnDone,
- typename = std::enable_if_t<
- details::is_callable_v<OnNext, Value> &&
- details::is_callable_v<OnError, Error> &&
- details::is_callable_v<OnDone>>>
- #ifdef RPL_CONSUMER_TYPE_ERASED_ALWAYS
- inline consumer<Value, Error> 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<Value, Error, details::consumer_handlers<
- Value,
- Error,
- std::decay_t<OnNext>,
- std::decay_t<OnError>,
- std::decay_t<OnDone>>>(
- std::forward<OnNext>(next),
- std::forward<OnError>(error),
- std::forward<OnDone>(done));
- }
- } // namespace rpl
|