From ee9763c98f61ec7767a748d4aae3750793af91a3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 18 Sep 2017 19:39:45 +0300 Subject: [PATCH] Improve rpl::combine() and rpl::start(). --- .../SourceFiles/boxes/edit_privacy_box.cpp | 5 +- Telegram/SourceFiles/info/info_memento.cpp | 2 +- .../info/info_profile_inner_widget.cpp | 5 +- .../SourceFiles/info/info_profile_lines.cpp | 14 +- Telegram/SourceFiles/rpl/before_next.h | 2 +- Telegram/SourceFiles/rpl/combine.h | 333 +++++++++ Telegram/SourceFiles/rpl/combine_latest.h | 155 ---- Telegram/SourceFiles/rpl/complete.h | 2 +- Telegram/SourceFiles/rpl/consumer.h | 113 ++- Telegram/SourceFiles/rpl/deferred.h | 2 +- Telegram/SourceFiles/rpl/details/callable.h | 211 ++++++ Telegram/SourceFiles/rpl/details/type_list.h | 209 ++++++ .../SourceFiles/rpl/distinct_until_changed.h | 2 +- Telegram/SourceFiles/rpl/event_stream.h | 14 +- Telegram/SourceFiles/rpl/fail.h | 2 +- Telegram/SourceFiles/rpl/filter.h | 2 +- Telegram/SourceFiles/rpl/map.h | 37 +- Telegram/SourceFiles/rpl/mappers.h | 466 ++++++++++++ Telegram/SourceFiles/rpl/never.h | 2 +- Telegram/SourceFiles/rpl/operators_tests.cpp | 181 ++++- Telegram/SourceFiles/rpl/producer.h | 702 +++++++++++++++--- Telegram/SourceFiles/rpl/producer_tests.cpp | 46 ++ Telegram/SourceFiles/rpl/rpl.h | 3 +- Telegram/SourceFiles/rpl/single.h | 4 +- Telegram/SourceFiles/rpl/then.h | 2 +- Telegram/gyp/tests/tests.gyp | 6 +- 26 files changed, 2119 insertions(+), 403 deletions(-) create mode 100644 Telegram/SourceFiles/rpl/combine.h delete mode 100644 Telegram/SourceFiles/rpl/combine_latest.h create mode 100644 Telegram/SourceFiles/rpl/details/callable.h create mode 100644 Telegram/SourceFiles/rpl/details/type_list.h create mode 100644 Telegram/SourceFiles/rpl/mappers.h diff --git a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp index 31df97b03..f66802e93 100644 --- a/Telegram/SourceFiles/boxes/edit_privacy_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_privacy_box.cpp @@ -288,8 +288,9 @@ void EditPrivacyBox::createWidgets() { auto createExceptionLink = [this](Exception exception) { exceptionLink(exception).create(this, object_ptr(this, exceptionLinkText(exception)), exceptionLinkMargins()); exceptionLink(exception)->heightValue() - | rpl::on_next([this](int) { resizeToWidth(width()); }) - | rpl::start(lifetime()); + | rpl::start( + [this](int) { resizeToWidth(width()); }, + lifetime()); exceptionLink(exception)->entity()->setClickedCallback([this, exception] { editExceptionUsers(exception); }); }; diff --git a/Telegram/SourceFiles/info/info_memento.cpp b/Telegram/SourceFiles/info/info_memento.cpp index 8c12d74ef..490b318ed 100644 --- a/Telegram/SourceFiles/info/info_memento.cpp +++ b/Telegram/SourceFiles/info/info_memento.cpp @@ -107,7 +107,7 @@ Ui::RpWidget *ContentWidget::doSetInnerWidget( | rpl::on_next([this, inner = _inner](int value) { inner->setVisibleTopBottom( value, - value + _scroll->height()); // TODO rpl::combine_latest + value + _scroll->height()); // TODO rpl::combine }) | rpl::start(_inner->lifetime()); return _inner; diff --git a/Telegram/SourceFiles/info/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/info_profile_inner_widget.cpp index adf3cc816..43fcef6ab 100644 --- a/Telegram/SourceFiles/info/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/info_profile_inner_widget.cpp @@ -20,7 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "info/info_profile_inner_widget.h" -#include +#include #include "boxes/abstract_box.h" #include "mainwidget.h" #include "info/info_profile_widget.h" @@ -177,8 +177,7 @@ object_ptr InnerWidget::setupInfoLines( result, object_ptr(result, st::shadowFg), st::infoProfileSeparatorPadding)); - rpl::combine_latest(std::move(infoPartsShown)) - | rpl::map([](const std::vector &values) { + rpl::combine(std::move(infoPartsShown), [](const auto &values) { return base::find(values, true) != values.end(); }) | rpl::distinct_until_changed() diff --git a/Telegram/SourceFiles/info/info_profile_lines.cpp b/Telegram/SourceFiles/info/info_profile_lines.cpp index 1a4ee95a8..3919c3cb9 100644 --- a/Telegram/SourceFiles/info/info_profile_lines.cpp +++ b/Telegram/SourceFiles/info/info_profile_lines.cpp @@ -23,7 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include #include -#include +#include #include "styles/style_info.h" #include "profile/profile_userpic_button.h" #include "observer_peer.h" @@ -182,15 +182,11 @@ rpl::producer CanShareContactViewer( rpl::producer CanAddContactViewer( not_null user) { - return rpl::combine_latest( + using namespace rpl::mappers; + return rpl::combine( IsContactViewer(user), - CanShareContactViewer(user)) - | rpl::map([](auto &&value) { - return !std::get<0>(value) && std::get<1>(value); - }) - | rpl::map_error([](auto &&error) { - return *base::get_if(&error); - }); + CanShareContactViewer(user), + !$1 && $2); } FloatingIcon::FloatingIcon( diff --git a/Telegram/SourceFiles/rpl/before_next.h b/Telegram/SourceFiles/rpl/before_next.h index f401631ea..ffbbfb512 100644 --- a/Telegram/SourceFiles/rpl/before_next.h +++ b/Telegram/SourceFiles/rpl/before_next.h @@ -26,7 +26,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -auto before_next(SideEffect &&method) { +inline auto before_next(SideEffect &&method) { return filter([method = std::forward(method)]( const auto &value) { method(value); diff --git a/Telegram/SourceFiles/rpl/combine.h b/Telegram/SourceFiles/rpl/combine.h new file mode 100644 index 000000000..903a7cf77 --- /dev/null +++ b/Telegram/SourceFiles/rpl/combine.h @@ -0,0 +1,333 @@ +/* +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 +#include +#include +#include +#include + +namespace rpl { +namespace details { + +template +struct combine_state { + combine_state() : accumulated(std::tuple...>()) { + } + base::optional...>> accumulated; + base::optional> latest; + int invalid = sizeof...(Values); + int working = sizeof...(Values); +}; + +template +inline std::tuple combine_make_first( + std::tuple...> &&accumulated, + std::index_sequence) { + return std::make_tuple(std::move(*std::get(accumulated))...); +} + +template +class combine_subscribe_one { +public: + combine_subscribe_one( + const consumer_type &consumer, + combine_state *state) + : _consumer(consumer) + , _state(state) { + } + + template + void subscribe(producer &&producer) { + _consumer.add_lifetime(std::move(producer).start( + [consumer = _consumer, state = _state](Value &&value) { + if (!state->accumulated) { + std::get(*state->latest) = std::move(value); + consumer.put_next_copy(*state->latest); + } else { + auto &accumulated = std::get( + *state->accumulated); + if (accumulated) { + accumulated = std::move(value); + } else { + accumulated = std::move(value); + if (!--state->invalid) { + constexpr auto kArity = sizeof...(Values); + state->latest = combine_make_first( + std::move(*state->accumulated), + std::make_index_sequence()); + state->accumulated = base::none; + consumer.put_next_copy(*state->latest); + } + } + } + }, [consumer = _consumer](Error &&error) { + consumer.put_error(std::move(error)); + }, [consumer = _consumer, state = _state] { + if (!--state->working) { + consumer.put_done(); + } + })); + } + +private: + const consumer_type &_consumer; + combine_state *_state = nullptr; + +}; + +template < + typename consumer_type, + typename ...Values, + typename ...Errors, + std::size_t ...I> +inline void combine_subscribe( + const consumer_type &consumer, + combine_state *state, + std::index_sequence, + producer &&...producers) { + auto consume = { ( + details::combine_subscribe_one< + I, + consumer_type, + Values... + >( + consumer, + state + ).subscribe(std::move(producers)), 0)... }; + (void)consume; +} + +template +inline auto combine_implementation( + producer &&...producers) { + using CombinedError = details::normalized_variant_t; + using Result = producer< + std::tuple, + CombinedError>; + using consumer_type = typename Result::consumer_type; + auto result = []( + const consumer_type &consumer, + producer &...producers) { + auto state = consumer.template make_state< + details::combine_state>(); + + constexpr auto kArity = sizeof...(Values); + details::combine_subscribe( + consumer, + state, + std::make_index_sequence(), + std::move(producers)...); + + return lifetime(); + }; + return Result(std::bind( + result, + std::placeholders::_1, + std::move(producers)...)); +} + +template +struct combine_just_producers : std::false_type { +}; + +template +constexpr bool combine_just_producers_v + = combine_just_producers::value; + +template +struct combine_just_producers...> + : std::true_type { +}; + +template +struct combine_just_producers_list + : type_list::extract_to_t { +}; + +template +struct combine_result_type; + +template +using combine_result_type_t + = typename combine_result_type::type; + +template +struct combine_result_type...> { + using type = std::tuple; +}; + +template +struct combine_result_type_list + : type_list::extract_to_t { +}; + +template +using combine_result_type_list_t + = typename combine_result_type_list::type; + +template +using combine_producers_no_mapper_t + = type_list::chop_last_t; + +template +constexpr bool combine_is_good_mapper(std::true_type) { + return is_callable_v< + type_list::last_t, + combine_result_type_list_t< + combine_producers_no_mapper_t + >>; +} + +template +constexpr bool combine_is_good_mapper(std::false_type) { + return false; +} + +template +struct combine_producers_with_mapper_list : std::bool_constant< + combine_is_good_mapper( + combine_just_producers_list< + combine_producers_no_mapper_t + >())> { +}; + +template +struct combine_producers_with_mapper + : combine_producers_with_mapper_list> { +}; + +template +constexpr bool combine_producers_with_mapper_v + = combine_producers_with_mapper::value; + +template +inline decltype(auto) combine_helper( + std::true_type, + producer &&...producers) { + return combine_implementation(std::move(producers)...); +} + +template +inline decltype(auto) combine_call( + std::index_sequence, + Producers &&...producers) { + return combine_implementation( + argument_mapper::call(std::move(producers)...)...); +} + +template +inline decltype(auto) combine_helper( + std::false_type, + Args &&...args) { + constexpr auto kProducersCount = sizeof...(Args) - 1; + return combine_call( + std::make_index_sequence(), + std::forward(args)...) + | map(argument_mapper::call( + std::forward(args)...)); +} + +} // namespace details + +template < + typename ...Args, + typename = std::enable_if_t< + details::combine_just_producers_v + || details::combine_producers_with_mapper_v>> +inline decltype(auto) combine(Args &&...args) { + return details::combine_helper( + details::combine_just_producers(), + std::forward(args)...); +} + +namespace details { + +template +struct combine_vector_state { + std::vector> accumulated; + std::vector latest; + int invalid = 0; + int working = 0; +}; + +} // namespace details + +template +inline producer, Error> combine( + std::vector> &&producers) { + if (producers.empty()) { + return complete, Error>(); + } + + using state_type = details::combine_vector_state; + using consumer_type = consumer, Error>; + return [producers = std::move(producers)]( + const consumer_type &consumer) mutable { + auto count = producers.size(); + auto state = consumer.template make_state(); + state->accumulated.resize(count); + state->invalid = count; + state->working = count; + for (auto index = 0; index != count; ++index) { + auto &producer = producers[index]; + consumer.add_lifetime(std::move(producer).start( + [consumer, state, index](Value &&value) { + if (state->accumulated.empty()) { + state->latest[index] = std::move(value); + consumer.put_next_copy(state->latest); + } else if (state->accumulated[index]) { + state->accumulated[index] = std::move(value); + } else { + state->accumulated[index] = std::move(value); + if (!--state->invalid) { + state->latest.reserve( + state->accumulated.size()); + for (auto &&value : state->accumulated) { + state->latest.push_back( + std::move(*value)); + } + base::take(state->accumulated); + consumer.put_next_copy(state->latest); + } + } + }, [consumer](Error &&error) { + consumer.put_error(std::move(error)); + }, [consumer, state] { + if (!--state->working) { + consumer.put_done(); + } + })); + } + return lifetime(); + }; +} + +template +inline auto combine( + std::vector> &&producers, + Mapper &&mapper) { + return combine(std::move(producers)) + | map(std::forward(mapper)); +} + +} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/combine_latest.h b/Telegram/SourceFiles/rpl/combine_latest.h deleted file mode 100644 index 710b9e225..000000000 --- a/Telegram/SourceFiles/rpl/combine_latest.h +++ /dev/null @@ -1,155 +0,0 @@ -/* -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 -#include -#include "base/optional.h" - -namespace rpl { -namespace details { - -template -struct combine_latest_vector_state { - std::vector> accumulated; - std::vector latest; - int invalid = 0; - int working = 0; -}; - -} // namespace details - -template -producer, Error> combine_latest( - std::vector> &&producers) { - if (producers.empty()) { - return complete, Error>(); - } - - using state_type = details::combine_latest_vector_state; - using consumer_type = consumer, Error>; - return [producers = std::move(producers)]( - const consumer_type &consumer) mutable { - auto count = producers.size(); - auto state = consumer.make_state(); - state->accumulated.resize(count); - state->invalid = count; - state->working = count; - for (auto index = 0; index != count; ++index) { - auto &producer = producers[index]; - consumer.add_lifetime(std::move(producer).start( - [consumer, state, index](Value &&value) { - if (state->accumulated.empty()) { - state->latest[index] = std::move(value); - consumer.put_next_copy(state->latest); - } else if (state->accumulated[index]) { - state->accumulated[index] = std::move(value); - } else { - state->accumulated[index] = std::move(value); - if (!--state->invalid) { - state->latest.reserve( - state->accumulated.size()); - for (auto &&value : state->accumulated) { - state->latest.push_back( - std::move(*value)); - } - base::take(state->accumulated); - consumer.put_next_copy(state->latest); - } - } - }, [consumer](Error &&error) { - consumer.put_error(std::move(error)); - }, [consumer, state] { - if (!--state->working) { - consumer.put_done(); - } - })); - } - return lifetime(); - }; -} - -namespace details { - -template -struct combine_latest_tuple_state { - base::optional first; - base::optional> others; - int working = 2; -}; - -} // namespace details - -template < - typename Value, - typename Error, - typename ...Values, - typename ...Errors> -producer, base::variant> combine_latest( - producer &&first, - producer &&...others) { - auto others_combined = combine_latest(std::move(others)...); - return [ - first = std::move(first), - others = std::move(others_combined) - ](const consumer, base::variant> &consumer) mutable { - auto state = consumer.make_state>(); - consumer.add_lifetime(std::move(first).start([consumer, state](Value &&value) { - state->first = std::move(value); - if (state->others) { - consumer.put_next(std::tuple_cat(std::make_tuple(*state->first), *state->others)); - } - }, [consumer](Error &&error) { - consumer.put_error(std::move(error)); - }, [consumer, state] { - if (!--state->working) { - consumer.put_done(); - } - })); - consumer.add_lifetime(std::move(others).start([consumer, state](std::tuple &&value) { - state->others = std::move(value); - if (state->first) { - consumer.put_next(std::tuple_cat(std::make_tuple(*state->first), *state->others)); - } - }, [consumer](base::variant &&error) { - base::visit([&](auto &&errorValue) { - consumer.put_error(std::move(errorValue)); - }, std::move(error)); - }, [consumer, state] { - if (!--state->working) { - consumer.put_done(); - } - })); - return lifetime(); - }; -} - -template < - typename Value, - typename Error> -producer, Error> combine_latest( - producer &&producer) { - return std::move(producer) | map([](auto &&value) { - return std::make_tuple(std::forward(value)); - }); -} - -} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/complete.h b/Telegram/SourceFiles/rpl/complete.h index 18675590d..2f442a62f 100644 --- a/Telegram/SourceFiles/rpl/complete.h +++ b/Telegram/SourceFiles/rpl/complete.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -producer complete() { +inline producer complete() { return [](const consumer &consumer) mutable { consumer.put_done(); return lifetime(); diff --git a/Telegram/SourceFiles/rpl/consumer.h b/Telegram/SourceFiles/rpl/consumer.h index bbd69f985..3c41ec9b7 100644 --- a/Telegram/SourceFiles/rpl/consumer.h +++ b/Telegram/SourceFiles/rpl/consumer.h @@ -23,30 +23,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include #include +#include namespace rpl { -namespace details { - -template -const Arg &const_ref_val(); - -template < - typename Func, - typename Arg, - typename = decltype(std::declval()(const_ref_val()))> -void const_ref_call_helper(Func &handler, const Arg &value, int) { - handler(value); -} - -template < - typename Func, - typename Arg> -void const_ref_call_helper(Func &handler, const Arg &value, double) { - auto copy = value; - handler(std::move(copy)); -} - -} // namespace details struct no_value { no_value() = delete; @@ -69,8 +48,10 @@ public: typename OnNext, typename OnError, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()(std::declval())), + typename = std::enable_if_t< + details::is_callable_v>, + typename = decltype(std::declval()( + std::declval())), typename = decltype(std::declval()())> consumer( OnNext &&next, @@ -122,24 +103,24 @@ public: } private: - class abstract_consumer_instance; + class abstract_instance; template - class consumer_instance; + class instance; template - std::shared_ptr ConstructInstance( + std::shared_ptr ConstructInstance( OnNext &&next, OnError &&error, OnDone &&done); - mutable std::shared_ptr _instance; + mutable std::shared_ptr _instance; }; template -class consumer::abstract_consumer_instance { +class consumer::abstract_instance { public: virtual bool put_next(Value &&value) = 0; virtual bool put_next_copy(const Value &value) = 0; @@ -163,11 +144,14 @@ protected: template template -class consumer::consumer_instance - : public consumer::abstract_consumer_instance { +class consumer::instance + : public consumer::abstract_instance { public: - template - consumer_instance( + template < + typename OnNextImpl, + typename OnErrorImpl, + typename OnDoneImpl> + instance( OnNextImpl &&next, OnErrorImpl &&error, OnDoneImpl &&done) @@ -191,12 +175,13 @@ private: template template -std::shared_ptr::abstract_consumer_instance> +inline std::shared_ptr< + typename consumer::abstract_instance> consumer::ConstructInstance( OnNext &&next, OnError &&error, OnDone &&done) { - return std::make_shared, std::decay_t, std::decay_t>>( @@ -213,7 +198,7 @@ template < typename, typename, typename> -consumer::consumer( +inline consumer::consumer( OnNext &&next, OnError &&error, OnDone &&done) : _instance(ConstructInstance( @@ -223,7 +208,7 @@ consumer::consumer( } template -bool consumer::put_next(Value &&value) const { +inline bool consumer::put_next(Value &&value) const { if (_instance) { if (_instance->put_next(std::move(value))) { return true; @@ -234,7 +219,8 @@ bool consumer::put_next(Value &&value) const { } template -bool consumer::put_next_copy(const Value &value) const { +inline bool consumer::put_next_copy( + const Value &value) const { if (_instance) { if (_instance->put_next_copy(value)) { return true; @@ -245,28 +231,30 @@ bool consumer::put_next_copy(const Value &value) const { } template -void consumer::put_error(Error &&error) const { +inline void consumer::put_error(Error &&error) const { if (_instance) { - std::exchange(_instance, nullptr)->put_error(std::move(error)); + std::exchange(_instance, nullptr)->put_error( + std::move(error)); } } template -void consumer::put_error_copy(const Error &error) const { +inline void consumer::put_error_copy( + const Error &error) const { if (_instance) { std::exchange(_instance, nullptr)->put_error_copy(error); } } template -void consumer::put_done() const { +inline void consumer::put_done() const { if (_instance) { std::exchange(_instance, nullptr)->put_done(); } } template -void consumer::add_lifetime(lifetime &&lifetime) const { +inline void consumer::add_lifetime(lifetime &&lifetime) const { if (_instance) { _instance->add_lifetime(std::move(lifetime)); } else { @@ -276,20 +264,20 @@ void consumer::add_lifetime(lifetime &&lifetime) const { template template -Type *consumer::make_state(Args&& ...args) const { +inline Type *consumer::make_state(Args&& ...args) const { Expects(_instance != nullptr); return _instance->template make_state(std::forward(args)...); } template -void consumer::terminate() const { +inline void consumer::terminate() const { if (_instance) { std::exchange(_instance, nullptr)->terminate(); } } template -void consumer::abstract_consumer_instance::add_lifetime( +inline void consumer::abstract_instance::add_lifetime( lifetime &&lifetime) { std::unique_lock lock(_mutex); if (_terminated) { @@ -303,15 +291,15 @@ void consumer::abstract_consumer_instance::add_lifetime( template template -Type *consumer::abstract_consumer_instance::make_state( +inline Type *consumer::abstract_instance::make_state( Args&& ...args) { std::unique_lock lock(_mutex); Expects(!_terminated); - return _lifetime.template make_state(std::forward(args)...); + return _lifetime.make_state(std::forward(args)...); } template -void consumer::abstract_consumer_instance::terminate() { +inline void consumer::abstract_instance::terminate() { std::unique_lock lock(_mutex); if (!_terminated) { _terminated = true; @@ -324,7 +312,7 @@ void consumer::abstract_consumer_instance::terminate() { template template -bool consumer::consumer_instance::put_next( +bool consumer::instance::put_next( Value &&value) { std::unique_lock lock(this->_mutex); if (this->_terminated) { @@ -333,13 +321,16 @@ bool consumer::consumer_instance::put_nex auto handler = this->_next; lock.unlock(); - handler(std::move(value)); + details::callable_helper( + handler, + std::move(value), + details::is_callable_plain()); return true; } template template -bool consumer::consumer_instance::put_next_copy( +bool consumer::instance::put_next_copy( const Value &value) { std::unique_lock lock(this->_mutex); if (this->_terminated) { @@ -348,41 +339,47 @@ bool consumer::consumer_instance::put_nex auto handler = this->_next; lock.unlock(); - details::const_ref_call_helper(handler, value, 0); + details::const_ref_call_helper( + handler, + value, + details::allows_const_ref()); return true; } template template -void consumer::consumer_instance::put_error( +void consumer::instance::put_error( Error &&error) { std::unique_lock lock(this->_mutex); if (!this->_terminated) { auto handler = std::move(this->_error); lock.unlock(); - handler(std::move(error)); + details::callable_invoke(handler, std::move(error)); this->terminate(); } } template template -void consumer::consumer_instance::put_error_copy( +void consumer::instance::put_error_copy( const Error &error) { std::unique_lock lock(this->_mutex); if (!this->_terminated) { auto handler = std::move(this->_error); lock.unlock(); - details::const_ref_call_helper(handler, error, 0); + details::const_ref_call_helper( + handler, + error, + details::allows_const_ref()); this->terminate(); } } template template -void consumer::consumer_instance::put_done() { +void consumer::instance::put_done() { std::unique_lock lock(this->_mutex); if (!this->_terminated) { auto handler = std::move(this->_done); diff --git a/Telegram/SourceFiles/rpl/deferred.h b/Telegram/SourceFiles/rpl/deferred.h index eefff2162..5e9b1f15d 100644 --- a/Telegram/SourceFiles/rpl/deferred.h +++ b/Telegram/SourceFiles/rpl/deferred.h @@ -28,7 +28,7 @@ template < typename Creator, typename Value = typename decltype(std::declval()())::value_type, typename Error = typename decltype(std::declval()())::error_type> -producer deferred(Creator &&creator) { +inline producer deferred(Creator &&creator) { return [creator = std::forward(creator)]( const consumer &consumer) mutable { return std::move(creator)().start_existing(consumer); diff --git a/Telegram/SourceFiles/rpl/details/callable.h b/Telegram/SourceFiles/rpl/details/callable.h new file mode 100644 index 000000000..6b5b853e0 --- /dev/null +++ b/Telegram/SourceFiles/rpl/details/callable.h @@ -0,0 +1,211 @@ +/* +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/build_config.h" + + +// Custom libc++ build used for old OS X versions already has this. +#ifndef OS_MAC_OLD + +#if defined COMPILER_CLANG || defined COMPILER_GCC +namespace std { + +#ifdef COMPILER_GCC + +template +using bool_constant = integral_constant; + +#endif // COMPILER_GCC + +template +constexpr auto tuple_size_v = std::tuple_size::value; + +template +constexpr auto is_rvalue_reference_v = is_rvalue_reference::value; + +template +constexpr auto is_base_of_v = is_base_of::value; + +template +constexpr auto is_same_v = is_same::value; + +namespace detail { + +template +constexpr decltype(auto) apply_impl( + Method &&method, + Tuple &&tuple, + index_sequence) { + return forward(method)(get(forward(tuple))...); +} + +} // namespace detail + +template +constexpr decltype(auto) apply(Method &&method, Tuple&& tuple) { + return detail::apply_impl( + forward(method), + forward(tuple), + make_index_sequence>>{}); +} + +} // namespace std +#endif // COMPILER_CLANG || COMPILER_GCC + +#endif // OS_MAC_OLD + +namespace rpl { +namespace details { + +template +const Arg &const_ref_val() noexcept; + +template +Arg &lvalue_ref_val() noexcept; + +using false_t = char; +struct true_t { + false_t data[2]; +}; +static_assert(sizeof(false_t) != sizeof(true_t), "I can't work :("); + +template < + typename Method, + typename ...Args, + typename = decltype(std::declval()( + std::declval()...))> +true_t test_callable_plain(Method &&, Args &&...) noexcept; +false_t test_callable_plain(...) noexcept; + +template +struct is_callable_plain + : std::bool_constant<( + sizeof(test_callable_plain( + std::declval(), + std::declval()... + )) == sizeof(true_t))> { +}; + +template +constexpr bool is_callable_plain_v = is_callable_plain::value; + +template < + typename Method, + typename ...Types, + typename = decltype(std::declval()( + std::declval()...))> +true_t test_callable_tuple( + Method &&, + std::tuple &&) noexcept; +false_t test_callable_tuple(...) noexcept; + +template +constexpr bool is_callable_tuple_v = (sizeof(test_callable_tuple( + std::declval(), + std::declval())) == sizeof(true_t)); + +template +struct is_callable_tuple + : std::bool_constant< + is_callable_tuple_v> { +}; + +template +struct is_callable; + +template +struct is_callable + : std::bool_constant< + is_callable_plain_v> { +}; + +template +struct is_callable + : std::bool_constant< + is_callable_plain_v + || is_callable_tuple_v> { +}; + +template +constexpr bool is_callable_v = is_callable::value; + +template +inline decltype(auto) callable_helper(Method &&method, Arg &&arg, std::true_type) { + return std::move(method)(std::forward(arg)); +} + +template +inline decltype(auto) callable_helper(Method &&method, Arg &&arg, std::false_type) { + return std::apply(std::move(method), std::forward(arg)); +} + +template +inline decltype(auto) callable_invoke(Method &&method, Arg &&arg) { + return callable_helper( + std::forward(method), + std::forward(arg), + is_callable_plain()); +} + +template +using callable_result = decltype(callable_invoke( + std::declval(), + std::declval())); + +template < + typename Method, + typename Arg, + typename = decltype(std::declval()( + const_ref_val>()))> +true_t test_allows_const_ref(Method &&, Arg &&) noexcept; +false_t test_allows_const_ref(...) noexcept; + +template +constexpr bool allows_const_ref_v = (sizeof(test_allows_const_ref( + std::declval(), + std::declval())) == sizeof(true_t)); + +template +struct allows_const_ref + : std::bool_constant< + allows_const_ref_v> { +}; + +template +inline decltype(auto) const_ref_call_helper( + Method &method, + const Arg &arg, + std::true_type) { + return callable_invoke(method, arg); +} + +template +inline decltype(auto) const_ref_call_helper( + Method &method, + const Arg &arg, + std::false_type) { + auto copy = arg; + return callable_invoke(method, std::move(copy)); +} + +} // namespace details +} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/details/type_list.h b/Telegram/SourceFiles/rpl/details/type_list.h new file mode 100644 index 000000000..69c11fb3f --- /dev/null +++ b/Telegram/SourceFiles/rpl/details/type_list.h @@ -0,0 +1,209 @@ +/* +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 +#include "base/variant.h" + +namespace rpl { +namespace details { +namespace type_list { + +template +struct list { +}; + +template +struct list { + using head = Type; + using tail = list; +}; + +using empty_list = list<>; + +template +using head_t = typename TypeList::head; + +template +using tail_t = typename TypeList::tail; + +template +struct construct; + +template +using construct_t = typename construct::type; + +template +struct construct> { + using type = list; +}; + +template +struct size; + +template +constexpr std::size_t size_v = size::value; + +template +struct size> + : std::integral_constant< + std::size_t, + sizeof...(Types)> { +}; + +template +constexpr bool empty_v = (size_v == 0); + +template +struct empty : std::bool_constant> { +}; + +template +struct get; + +template +using get_t = typename get::type; + +template +struct get { + using type = get_t>; +}; + +template +struct get<0, TypeList> { + using type = head_t; +}; + +template +struct concat; + +template +using concat_t = typename concat::type; + +template +struct concat, list> { + using type = list; +}; + +template +struct remove_all; + +template +using remove_all_t = typename remove_all::type; + +template +struct remove_all { + using head = head_t; + using tail = tail_t; + using clean_tail = remove_all_t; + using type = std::conditional_t< + std::is_same_v, + clean_tail, + construct_t>; +}; + +template +struct remove_all { + using type = empty_list; +}; + +template +struct last; + +template +using last_t = typename last::type; + +template +struct last { + using type = last_t>; +}; + +template +struct last> { + using type = Type; +}; + +template +struct chop_last; + +template +using chop_last_t = typename chop_last::type; + +template +struct chop_last { + using type = construct_t< + head_t, + chop_last_t>>; +}; + +template +struct chop_last> { + using type = empty_list; +}; + +template +struct distinct; + +template +using distinct_t = typename distinct::type; + +template +struct distinct { + using type = construct_t< + head_t, + distinct_t< + remove_all_t, head_t>>>; +}; + +template <> +struct distinct { + using type = empty_list; +}; + +template typename To> +struct extract_to; + +template typename To> +using extract_to_t = typename extract_to::type; + +template typename To> +struct extract_to, To> { + using type = To; +}; + +} // namespace type_list + +template +struct normalized_variant { + using list = type_list::list; + using distinct = type_list::distinct_t; + using type = std::conditional_t< + type_list::size_v == 1, + type_list::get_t<0, distinct>, + type_list::extract_to_t>; +}; + +template +using normalized_variant_t + = typename normalized_variant::type; + +} // namespace details +} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/distinct_until_changed.h b/Telegram/SourceFiles/rpl/distinct_until_changed.h index ac03c52fd..5f9249c77 100644 --- a/Telegram/SourceFiles/rpl/distinct_until_changed.h +++ b/Telegram/SourceFiles/rpl/distinct_until_changed.h @@ -33,7 +33,7 @@ public: rpl::producer &&initial) const { return [initial = std::move(initial)]( const consumer &consumer) mutable { - auto previous = consumer.make_state< + auto previous = consumer.template make_state< base::optional >(); return std::move(initial).start( diff --git a/Telegram/SourceFiles/rpl/event_stream.h b/Telegram/SourceFiles/rpl/event_stream.h index e56f0bba7..7fc6eba0b 100644 --- a/Telegram/SourceFiles/rpl/event_stream.h +++ b/Telegram/SourceFiles/rpl/event_stream.h @@ -65,17 +65,17 @@ private: }; template -event_stream::event_stream() { +inline event_stream::event_stream() { } template -event_stream::event_stream(event_stream &&other) - : _consumers(base::take(other._consumers)) { +inline event_stream::event_stream(event_stream &&other) +: _consumers(base::take(other._consumers)) { } template template -void event_stream::fire_forward(OtherValue &&value) { +inline void event_stream::fire_forward(OtherValue &&value) { if (!_consumers) { return; } @@ -116,7 +116,7 @@ void event_stream::fire_forward(OtherValue &&value) { } template -producer event_stream::events() const { +inline producer event_stream::events() const { return producer([weak = weak()]( const consumer &consumer) { if (auto strong = weak.lock()) { @@ -136,7 +136,7 @@ producer event_stream::events() const { } template -std::weak_ptr>> event_stream::weak() const { +inline std::weak_ptr>> event_stream::weak() const { if (!_consumers) { _consumers = std::make_shared>>(); } @@ -145,7 +145,7 @@ std::weak_ptr>> event_stream::weak( template -event_stream::~event_stream() { +inline event_stream::~event_stream() { if (auto consumers = base::take(_consumers)) { for (auto &consumer : *consumers) { consumer.put_done(); diff --git a/Telegram/SourceFiles/rpl/fail.h b/Telegram/SourceFiles/rpl/fail.h index 06c851f07..0533886c6 100644 --- a/Telegram/SourceFiles/rpl/fail.h +++ b/Telegram/SourceFiles/rpl/fail.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -producer> fail(Error &&error) { +inline producer> fail(Error &&error) { using consumer_t = consumer>; return [error = std::forward(error)]( const consumer_t &consumer) mutable { diff --git a/Telegram/SourceFiles/rpl/filter.h b/Telegram/SourceFiles/rpl/filter.h index 8826ff2c9..5c0cbc772 100644 --- a/Telegram/SourceFiles/rpl/filter.h +++ b/Telegram/SourceFiles/rpl/filter.h @@ -66,7 +66,7 @@ private: } // namespace details template -auto filter(Predicate &&predicate) +inline auto filter(Predicate &&predicate) -> details::filter_helper> { return details::filter_helper>( std::forward(predicate)); diff --git a/Telegram/SourceFiles/rpl/map.h b/Telegram/SourceFiles/rpl/map.h index c4d52177d..38c2a4eb5 100644 --- a/Telegram/SourceFiles/rpl/map.h +++ b/Telegram/SourceFiles/rpl/map.h @@ -34,22 +34,24 @@ public: map_transform_helper( Transform &&transform, const consumer &consumer) - : _transform(std::move(transform)) - , _consumer(consumer) { + : _consumer(consumer) + , _transform(std::move(transform)) { } template < typename OtherValue, typename = std::enable_if_t< std::is_rvalue_reference_v>> void operator()(OtherValue &&value) const { - _consumer.put_next_forward(_transform(std::move(value))); + _consumer.put_next_forward( + details::callable_invoke(_transform, std::move(value))); } template < typename OtherValue, typename = decltype( std::declval()(const_ref_val()))> void operator()(const OtherValue &value) const { - _consumer.put_next_forward(_transform(value)); + _consumer.put_next_forward( + details::callable_invoke(_transform, value)); } private: @@ -64,7 +66,8 @@ template < typename Error, typename = std::enable_if_t< std::is_rvalue_reference_v>> -map_transform_helper map_transform( +inline map_transform_helper +map_transform( Transform &&transform, const consumer &consumer) { return { std::move(transform), consumer }; @@ -81,9 +84,9 @@ public: template < typename Value, typename Error, - typename NewValue = decltype( - std::declval()(std::declval()) - )> + typename NewValue = details::callable_result< + Transform, + Value>> rpl::producer operator()( rpl::producer &&initial) { return [ @@ -111,7 +114,7 @@ private: } // namespace details template -auto map(Transform &&transform) +inline auto map(Transform &&transform) -> details::map_helper> { return details::map_helper>( std::forward(transform)); @@ -136,14 +139,16 @@ public: typename = std::enable_if_t< std::is_rvalue_reference_v>> void operator()(OtherError &&error) const { - _consumer.put_error_forward(_transform(std::move(error))); + _consumer.put_error_forward( + details::callable_invoke(_transform, std::move(error))); } template < typename OtherError, typename = decltype( std::declval()(const_ref_val()))> void operator()(const OtherError &error) const { - _consumer.put_error_forward(_transform(error)); + _consumer.put_error_forward( + details::callable_invoke(_transform, error)); } private: @@ -158,7 +163,7 @@ template < typename NewError, typename = std::enable_if_t< std::is_rvalue_reference_v>> -map_error_transform_helper +inline map_error_transform_helper map_error_transform( Transform &&transform, const consumer &consumer) { @@ -176,9 +181,9 @@ public: template < typename Value, typename Error, - typename NewError = decltype( - std::declval()(std::declval()) - )> + typename NewError = details::callable_result< + Transform, + Error>> rpl::producer operator()( rpl::producer &&initial) { return [ @@ -206,7 +211,7 @@ private: } // namespace details template -auto map_error(Transform &&transform) +inline auto map_error(Transform &&transform) -> details::map_error_helper> { return details::map_error_helper>( std::forward(transform)); diff --git a/Telegram/SourceFiles/rpl/mappers.h b/Telegram/SourceFiles/rpl/mappers.h new file mode 100644 index 000000000..c39d6ddad --- /dev/null +++ b/Telegram/SourceFiles/rpl/mappers.h @@ -0,0 +1,466 @@ +/* +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 + +namespace rpl { +namespace details { + +struct base_mapper { +}; + +template +constexpr bool is_mapper_v = std::is_base_of_v< + base_mapper, + std::decay_t>; + +template +struct argument_mapper : base_mapper { + template < + typename Arg, + typename ...Args, + typename = std::enable_if_t<(sizeof...(Args) >= Index)>> + static constexpr decltype(auto) call(Arg &&arg, Args &&...args) { + return argument_mapper::call( + std::forward(args)...); + } + + template < + typename ...Args, + typename = std::enable_if_t<(sizeof...(Args) > Index)>> + constexpr auto operator()(Args &&...args) const { + return call(std::forward(args)...); + } + +}; + +template <> +struct argument_mapper<0> : base_mapper { + template < + typename Arg, + typename ...Args> + static constexpr decltype(auto) call(Arg &&arg, Args &&...args) { + return std::forward(arg); + } + + template < + typename Arg, + typename ...Args> + constexpr auto operator()(Arg &&arg, Args &&...args) const { + return std::forward(arg); + } + +}; + +template +class value_mapper : public base_mapper { +public: + template + constexpr value_mapper(OtherType &&value) + : _value(std::forward(value)) { + } + + template + constexpr auto operator()(Args &&...args) const { + return _value; + } + +private: + Type _value; + +}; + +template +inline value_mapper> make_value_mapper(Type &&value) { + return { std::forward(value) }; +} + +template +struct wrap_mapper { + using type = std::conditional_t< + is_mapper_v, + Type, + value_mapper>; +}; + +template +using wrap_mapper_t = typename wrap_mapper::type; + +template +class unary_operator_mapper : public base_mapper { + using TypeWrapper = wrap_mapper_t>; + +public: + template + constexpr unary_operator_mapper(OtherType &&value) + : _value(std::forward(value)) { + } + + template < + typename ...Args, + typename Result = decltype((Operator{})( + std::declval()(std::declval()...)))> + constexpr std::decay_t operator()(Args &&...args) const { + return (Operator{})( + _value(std::forward(args)...)); + } + +private: + TypeWrapper _value; + +}; + +template +class binary_operator_mapper : public base_mapper { + using LeftWrapper = wrap_mapper_t>; + using RightWrapper = wrap_mapper_t>; + +public: + template + constexpr binary_operator_mapper(OtherLeft &&left, OtherRight &&right) + : _left(std::forward(left)) + , _right(std::forward(right)) { + } + + template < + typename ...Args, + typename Result = decltype((Operator{})( + std::declval()(std::declval()...), + std::declval()(std::declval()...)))> + constexpr std::decay_t operator()(Args &&...args) const { + return (Operator{})( + _left(std::forward(args)...), + _right(std::forward(args)...)); + } + +private: + LeftWrapper _left; + RightWrapper _right; + +}; + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator+(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::plus<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator-(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::minus<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator*(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::multiplies<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator/(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::divides<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator%(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::modulus<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Type, + typename = std::enable_if_t< + is_mapper_v + >> +inline auto operator-(Type &&value) { + return unary_operator_mapper< + Type, + std::negate<>>( + std::forward(value)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator<(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::less<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator<=(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::less_equal<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator>(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::greater<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator>=(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::greater_equal<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator==(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::equal_to<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator!=(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::not_equal_to<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator&&(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::logical_and<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator||(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::logical_or<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Type, + typename = std::enable_if_t< + is_mapper_v + >> +inline auto operator!(Type &&value) { + return unary_operator_mapper< + Type, + std::logical_not<>>( + std::forward(value)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator&(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::bit_and<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator|(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::bit_or<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Left, + typename Right, + typename = std::enable_if_t< + is_mapper_v || is_mapper_v + >> +inline auto operator^(Left &&left, Right &&right) { + return binary_operator_mapper< + Left, + Right, + std::bit_xor<>>( + std::forward(left), + std::forward(right)); +} + +template < + typename Type, + typename = std::enable_if_t< + is_mapper_v + >> +inline auto operator~(Type &&value) { + return unary_operator_mapper< + Type, + std::bit_not<>>( + std::forward(value)); +} + +} // namespace details + +namespace mappers { + +constexpr const details::argument_mapper<0> $1; +constexpr const details::argument_mapper<1> $2; +constexpr const details::argument_mapper<2> $3; +constexpr const details::argument_mapper<3> $4; +constexpr const details::argument_mapper<4> $5; +constexpr const details::argument_mapper<5> $6; +constexpr const details::argument_mapper<6> $7; +constexpr const details::argument_mapper<7> $8; +constexpr const details::argument_mapper<8> $9; +constexpr const details::argument_mapper<9> $10; +constexpr const details::argument_mapper<10> $11; +constexpr const details::argument_mapper<11> $12; +constexpr const details::argument_mapper<12> $13; +constexpr const details::argument_mapper<13> $14; +constexpr const details::argument_mapper<14> $15; +constexpr const details::argument_mapper<15> $16; +constexpr const details::argument_mapper<16> $17; +constexpr const details::argument_mapper<17> $18; +constexpr const details::argument_mapper<18> $19; +constexpr const details::argument_mapper<19> $20; + +template +inline auto $val(Type &&value) { + return details::make_value_mapper(std::forward(value)); +} + +} // namespace mappers +} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/never.h b/Telegram/SourceFiles/rpl/never.h index c3d32c1dd..9ef92f6a7 100644 --- a/Telegram/SourceFiles/rpl/never.h +++ b/Telegram/SourceFiles/rpl/never.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -producer never() { +inline producer never() { return [](const consumer &consumer) mutable { return lifetime(); }; diff --git a/Telegram/SourceFiles/rpl/operators_tests.cpp b/Telegram/SourceFiles/rpl/operators_tests.cpp index 0f1c43ce5..5ab4ad3cc 100644 --- a/Telegram/SourceFiles/rpl/operators_tests.cpp +++ b/Telegram/SourceFiles/rpl/operators_tests.cpp @@ -69,6 +69,7 @@ public: if (_copyCounter) { ++*_copyCounter; } + return *this; } InvokeCounter &operator=(InvokeCounter &&other) { _copyCounter = base::take(other._copyCounter); @@ -76,6 +77,7 @@ public: if (_moveCounter) { ++*_moveCounter; } + return *this; } private: @@ -133,16 +135,19 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { rpl::lifetime lifetime; std::move(testing) - | then(complete()) - | on_next([=](InvokeCounter&&) { - (void)destroyCalled; - ++*sum; - }) | on_error([=](no_error) { - (void)destroyCalled; - }) | on_done([=] { - (void)destroyCalled; - *doneGenerated = true; - }) | start(lifetime); + | then(complete()) + | on_next([=](InvokeCounter&&) { + (void)destroyCalled; + ++*sum; + }) + | on_error([=](no_error) { + (void)destroyCalled; + }) + | on_done([=] { + (void)destroyCalled; + *doneGenerated = true; + }) + | start(lifetime); } REQUIRE(*sum == 5); REQUIRE(*doneGenerated); @@ -160,10 +165,12 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { | then(single(4)) | then(single(5)) | map([](int value) { - return std::to_string(value); - }) | on_next([=](std::string &&value) { - *sum += std::move(value) + ' '; - }) | start(lifetime); + return std::to_string(value); + }) + | on_next([=](std::string &&value) { + *sum += std::move(value) + ' '; + }) + | start(lifetime); } REQUIRE(*sum == "1 2 3 4 5 "); } @@ -186,7 +193,8 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { | on_next([=](int value) { REQUIRE(++*checked == *launched); REQUIRE(*checked == value); - }) | start(lifetime); + }) + | start(lifetime); REQUIRE(*launched == 5); } } @@ -203,9 +211,11 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { | filter([](int value) { return value != 2; }) | map([](int value) { return std::to_string(value); - }) | on_next([=](std::string &&value) { + }) + | on_next([=](std::string &&value) { *sum += std::move(value) + ' '; - }) | start(lifetime); + }) + | start(lifetime); } REQUIRE(*sum == "1 1 3 "); } @@ -221,10 +231,12 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { | then(single(3)) | distinct_until_changed() | map([](int value) { - return std::to_string(value); - }) | on_next([=](std::string &&value) { - *sum += std::move(value) + ' '; - }) | start(lifetime); + return std::to_string(value); + }) + | on_next([=](std::string &&value) { + *sum += std::move(value) + ' '; + }) + | start(lifetime); } REQUIRE(*sum == "1 2 3 "); } @@ -238,11 +250,130 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { | then(single(single(5) | then(single(6)))) | flatten_latest() | map([](int value) { - return std::to_string(value); - }) | on_next([=](std::string &&value) { - *sum += std::move(value) + ' '; - }) | start(lifetime); + return std::to_string(value); + }) + | on_next([=](std::string &&value) { + *sum += std::move(value) + ' '; + }) + | start(lifetime); } REQUIRE(*sum == "1 2 3 4 5 6 "); } + + SECTION("combine vector test") { + auto sum = std::make_shared(""); + { + rpl::lifetime lifetime; + rpl::event_stream a; + rpl::event_stream b; + rpl::event_stream c; + + std::vector> v; + v.push_back(a.events()); + v.push_back(b.events()); + v.push_back(c.events()); + + combine(std::move(v), [](const auto &values) { + return values[0] && values[1] && !values[2]; + }) + | on_next([=](bool value) { + *sum += std::to_string(value ? 1 : 0); + }) + | start(lifetime); + + a.fire(true); + b.fire(true); + c.fire(false); + a.fire(false); + b.fire(true); + a.fire(true); + c.fire(true); + } + REQUIRE(*sum == "10010"); + } + + SECTION("combine test") { + auto sum = std::make_shared(""); + { + rpl::lifetime lifetime; + rpl::event_stream a; + rpl::event_stream b; + rpl::event_stream c; + + combine( + a.events(), + b.events(), + c.events(), + [](long a, long b, long c) { + return a; + }) + | on_next([=](int value) { + *sum += std::to_string(value); + }) + | start(lifetime); + + combine( + a.events(), + b.events(), + c.events(), + [](auto &&value) { + return std::get<1>(value); + }) + | on_next([=](int value) { + *sum += std::to_string(value); + }) + | start(lifetime); + + combine(a.events(), b.events(), c.events()) + | map([](auto &&value) { + return std::make_tuple( + std::to_string(std::get<0>(value)), + std::to_string(std::get<1>(value)), + std::to_string(std::get<2>(value))); + }) + | on_next([=](auto &&value) { + *sum += std::get<0>(value) + ' ' + + std::get<1>(value) + ' ' + + std::get<2>(value) + ' '; + }) + | start(lifetime); + a.fire(1); + b.fire(2); + c.fire(3); + a.fire(4); + b.fire(5); + c.fire(6); + } + REQUIRE(*sum == "121 2 3 424 2 3 454 5 3 454 5 6 "); + } + + SECTION("mappers test") { + auto sum = std::make_shared(""); + { + rpl::lifetime lifetime; + rpl::event_stream a; + rpl::event_stream b; + rpl::event_stream c; + + using namespace mappers; + + combine( + a.events(), + b.events(), + c.events(), + $1 + $2 + $3 + 10) + | on_next([=](int value) { + *sum += std::to_string(value); + }) + | start(lifetime); + + a.fire(1); + b.fire(2); + c.fire(3); + a.fire(4); + b.fire(5); + c.fire(6); + } + REQUIRE(*sum == "16192225"); + } } diff --git a/Telegram/SourceFiles/rpl/producer.h b/Telegram/SourceFiles/rpl/producer.h index 80dc063e4..c3a12f1d6 100644 --- a/Telegram/SourceFiles/rpl/producer.h +++ b/Telegram/SourceFiles/rpl/producer.h @@ -56,15 +56,18 @@ public: template < typename Lambda, typename = std::enable_if_t()(std::declval()...)), + decltype(std::declval()( + std::declval()...)), Return >::value>> - mutable_lambda(Lambda other) : _implementation(mutable_lambda_wrap(std::move(other))) { + mutable_lambda(Lambda other) : _implementation( + mutable_lambda_wrap(std::move(other))) { } template < typename ...OtherArgs, - typename = std::enable_if_t<(sizeof...(Args) == sizeof...(OtherArgs))>> + typename = std::enable_if_t< + (sizeof...(Args) == sizeof...(OtherArgs))>> Return operator()(OtherArgs&&... args) { return _implementation(std::forward(args)...); } @@ -83,19 +86,22 @@ public: using error_type = Error; using consumer_type = consumer; - template ()(std::declval())), - lifetime - >::value>> + template < + typename Generator, + typename = std::enable_if()( + std::declval())), + lifetime>::value>> producer(Generator &&generator); template < typename OnNext, typename OnError, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + details::is_callable_v + && details::is_callable_v + && details::is_callable_v>> lifetime start( OnNext &&next, OnError &&error, @@ -105,9 +111,10 @@ public: typename OnNext, typename OnError, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + details::is_callable_v + && details::is_callable_v + && details::is_callable_v>> lifetime start_copy( OnNext &&next, OnError &&error, @@ -123,8 +130,8 @@ private: template template -producer::producer(Generator &&generator) - : _generator(std::forward(generator)) { +inline producer::producer(Generator &&generator) +: _generator(std::forward(generator)) { } template @@ -132,10 +139,8 @@ template < typename OnNext, typename OnError, typename OnDone, - typename, - typename, typename> -lifetime producer::start( +inline lifetime producer::start( OnNext &&next, OnError &&error, OnDone &&done) && { @@ -150,10 +155,8 @@ template < typename OnNext, typename OnError, typename OnDone, - typename, - typename, typename> -lifetime producer::start_copy( +inline lifetime producer::start_copy( OnNext &&next, OnError &&error, OnDone &&done) const & { @@ -165,43 +168,44 @@ lifetime producer::start_copy( } template -lifetime producer::start_existing( +inline lifetime producer::start_existing( const consumer_type &consumer) && { consumer.add_lifetime(std::move(_generator)(consumer)); return [consumer] { consumer.terminate(); }; } template -inline producer duplicate(const producer &producer) { - return producer; +inline producer duplicate( + const producer &value) { + return value; } template < typename Value, typename Error, typename Method, - typename = decltype(std::declval()(std::declval>()))> -inline auto operator|(producer &&producer, Method &&method) { - return std::forward(method)(std::move(producer)); + typename = decltype(std::declval()( + std::declval>()))> +inline auto operator|(producer &&value, Method &&method) { + return std::forward(method)(std::move(value)); } template inline auto bind_on_next(OnNext &&handler) { - return [handler = std::forward(handler)](auto &&existing) mutable { - using value_type = typename std::decay_t::value_type; + return [handler = std::forward(handler)]( + auto &&existing) mutable { using error_type = typename std::decay_t::error_type; return producer([ existing = std::move(existing), handler = std::move(handler) ](const consumer &consumer) mutable { return std::move(existing).start( - [handler = std::move(handler)](value_type &&value) { - handler(std::move(value)); - }, [consumer](error_type &&error) { - consumer.put_error(std::move(error)); - }, [consumer] { - consumer.put_done(); - }); + std::move(handler), + [consumer](error_type &&error) { + consumer.put_error(std::move(error)); + }, [consumer] { + consumer.put_done(); + }); }); }; } @@ -210,19 +214,18 @@ template inline auto bind_on_error(OnError &&handler) { return [handler = std::forward(handler)](auto &&existing) mutable { using value_type = typename std::decay_t::value_type; - using error_type = typename std::decay_t::error_type; return producer([ existing = std::move(existing), handler = std::move(handler) ](const consumer &consumer) mutable { return std::move(existing).start( - [consumer](value_type &&value) { - consumer.put_next(std::move(value)); - }, [handler = std::move(handler)](error_type &&error) { - handler(std::move(error)); - }, [consumer] { - consumer.put_done(); - }); + [consumer](value_type &&value) { + consumer.put_next(std::move(value)); + }, + std::move(handler), + [consumer] { + consumer.put_done(); + }); }); }; } @@ -237,13 +240,13 @@ inline auto bind_on_done(OnDone &&handler) { handler = std::move(handler) ](const consumer &consumer) mutable { return std::move(existing).start( - [consumer](value_type &&value) { - consumer.put_next(std::move(value)); - }, [consumer](error_type &&value) { - consumer.put_error(std::move(value)); - }, [handler = std::move(handler)] { - handler(); - }); + [consumer](value_type &&value) { + consumer.put_next(std::move(value)); + }, + [consumer](error_type &&value) { + consumer.put_error(std::move(value)); + }, + std::move(handler)); }); }; } @@ -270,7 +273,7 @@ template < typename Error, typename OnNext> struct producer_with_next { - producer producer; + producer bound; OnNext next; }; @@ -279,7 +282,7 @@ template < typename Error, typename OnError> struct producer_with_error { - producer producer; + producer bound; OnError error; }; @@ -288,7 +291,7 @@ template < typename Error, typename OnDone> struct producer_with_done { - producer producer; + producer bound; OnDone done; }; @@ -298,7 +301,7 @@ template < typename OnNext, typename OnError> struct producer_with_next_error { - producer producer; + producer bound; OnNext next; OnError error; }; @@ -309,7 +312,7 @@ template < typename OnNext, typename OnDone> struct producer_with_next_done { - producer producer; + producer bound; OnNext next; OnDone done; }; @@ -320,7 +323,7 @@ template < typename OnError, typename OnDone> struct producer_with_error_done { - producer producer; + producer bound; OnError error; OnDone done; }; @@ -332,7 +335,7 @@ template < typename OnError, typename OnDone> struct producer_with_next_error_done { - producer producer; + producer bound; OnNext next; OnError error; OnDone done; @@ -342,6 +345,149 @@ struct lifetime_holder { lifetime &alive_while; }; +template +struct lifetime_holder_1 { + Callback1 callback1; + lifetime &alive_while; +}; + +template +struct lifetime_holder_2 { + Callback1 callback1; + Callback2 callback2; + lifetime &alive_while; +}; + +template +struct lifetime_holder_3 { + Callback1 callback1; + Callback2 callback2; + Callback3 callback3; + lifetime &alive_while; +}; + +enum class CallbackType { + Next, + Error, + Done, + Invalid, +}; + +template +inline auto on_next_dispatch( + Callback &&callback, + std::integral_constant) { + return [](const auto&) {}; +} + +template +inline decltype(auto) on_next_dispatch( + Callback &&callback, + std::integral_constant) { + return std::forward(callback); +} + +template +inline auto on_error_dispatch( + Callback &&callback, + std::integral_constant) { + return [](const auto&) {}; +} + +template +inline decltype(auto) on_error_dispatch( + Callback &&callback, + std::integral_constant) { + return std::forward(callback); +} + +template +inline auto on_done_dispatch( + Callback &&callback, + std::integral_constant) { + return [] {}; +} + +template +inline decltype(auto) on_done_dispatch( + Callback &&callback, + std::integral_constant) { + return std::forward(callback); +} + +enum class CallbacksType { + NextError, + NextDone, + ErrorDone, + Invalid, +}; + +template +inline auto on_next_dispatch( + Callback1 &&callback1, + std::integral_constant) { + return [](const auto&) {}; +} + +template +inline decltype(auto) on_next_dispatch( + Callback1 &&callback1, + std::integral_constant) { + return std::forward(callback1); +} + +template +inline decltype(auto) on_next_dispatch( + Callback1 &&callback1, + std::integral_constant) { + return std::forward(callback1); +} + +template +inline auto on_error_dispatch( + Callback1 &&callback1, + Callback2 &&callback2, + std::integral_constant) { + return [](const auto&) {}; +} + +template +inline decltype(auto) on_error_dispatch( + Callback1 &&callback1, + Callback2 &&callback2, + std::integral_constant) { + return std::forward(callback2); +} + +template +inline decltype(auto) on_error_dispatch( + Callback1 &&callback1, + Callback2 &&callback2, + std::integral_constant) { + return std::forward(callback1); +} + +template +inline auto on_done_dispatch( + Callback2 &&callback2, + std::integral_constant) { + return [] {}; +} + +template +inline decltype(auto) on_done_dispatch( + Callback2 &&callback2, + std::integral_constant) { + return std::forward(callback2); +} + +template +inline decltype(auto) on_done_dispatch( + Callback2 &&callback2, + std::integral_constant) { + return std::forward(callback2); +} + } // namespace details template @@ -363,39 +509,81 @@ inline details::lifetime_holder start(lifetime &alive_while) { return { alive_while }; } +template +inline details::lifetime_holder_1< + std::decay_t> start( + Callback1 &&callback1, + lifetime &alive_while) { + return { + std::forward(callback1), + alive_while }; +} + +template +inline details::lifetime_holder_2< + std::decay_t, + std::decay_t> start( + Callback1 &&callback1, + Callback2 &&callback2, + lifetime &alive_while) { + return { + std::forward(callback1), + std::forward(callback2), + alive_while }; +} + +template +inline details::lifetime_holder_3< + std::decay_t, + std::decay_t, + std::decay_t> start( + Callback1 &&callback1, + Callback2 &&callback2, + Callback3 &&callback3, + lifetime &alive_while) { + return { + std::forward(callback1), + std::forward(callback2), + std::forward(callback3), + alive_while }; +} + namespace details { template < typename Value, typename Error, typename OnNext, - typename = decltype(std::declval()(std::declval()))> + typename = std::enable_if_t< + is_callable_v>> inline producer_with_next operator|( - producer &&producer, + producer &&value, next_holder &&handler) { - return { std::move(producer), std::move(handler.next) }; + return { std::move(value), std::move(handler.next) }; } template < typename Value, typename Error, typename OnError, - typename = decltype(std::declval()(std::declval()))> + typename = std::enable_if_t< + is_callable_v>> inline producer_with_error operator|( - producer &&producer, + producer &&value, error_holder &&handler) { - return { std::move(producer), std::move(handler.error) }; + return { std::move(value), std::move(handler.error) }; } template < typename Value, typename Error, typename OnDone, - typename = decltype(std::declval()())> + typename = std::enable_if_t< + is_callable_v>> inline producer_with_done operator|( - producer &&producer, + producer &&value, done_holder &&handler) { - return { std::move(producer), std::move(handler.done) }; + return { std::move(value), std::move(handler.done) }; } template < @@ -403,13 +591,15 @@ template < typename Error, typename OnNext, typename OnError, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()(std::declval()))> + typename = std::enable_if_t< + is_callable_v>, + typename = std::enable_if_t< + is_callable_v>> inline producer_with_next_error operator|( producer_with_next &&producer_with_next, error_holder &&handler) { return { - std::move(producer_with_next.producer), + std::move(producer_with_next.bound), std::move(producer_with_next.next), std::move(handler.error) }; } @@ -419,13 +609,15 @@ template < typename Error, typename OnNext, typename OnError, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()(std::declval()))> + typename = std::enable_if_t< + is_callable_v>, + typename = std::enable_if_t< + is_callable_v>> inline producer_with_next_error operator|( producer_with_error &&producer_with_error, next_holder &&handler) { return { - std::move(producer_with_error.producer), + std::move(producer_with_error.bound), std::move(handler.next), std::move(producer_with_error.error) }; } @@ -435,13 +627,14 @@ template < typename Error, typename OnNext, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + is_callable_v + && is_callable_v>> inline producer_with_next_done operator|( producer_with_next &&producer_with_next, done_holder &&handler) { return { - std::move(producer_with_next.producer), + std::move(producer_with_next.bound), std::move(producer_with_next.next), std::move(handler.done) }; } @@ -451,13 +644,14 @@ template < typename Error, typename OnNext, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + is_callable_v + && is_callable_v>> inline producer_with_next_done operator|( producer_with_done &&producer_with_done, next_holder &&handler) { return { - std::move(producer_with_done.producer), + std::move(producer_with_done.bound), std::move(handler.next), std::move(producer_with_done.done) }; } @@ -467,13 +661,14 @@ template < typename Error, typename OnError, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + is_callable_v + && is_callable_v>> inline producer_with_error_done operator|( producer_with_error &&producer_with_error, done_holder &&handler) { return { - std::move(producer_with_error.producer), + std::move(producer_with_error.bound), std::move(producer_with_error.error), std::move(handler.done) }; } @@ -483,13 +678,14 @@ template < typename Error, typename OnError, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + is_callable_v + && is_callable_v>> inline producer_with_error_done operator|( producer_with_done &&producer_with_done, error_holder &&handler) { return { - std::move(producer_with_done.producer), + std::move(producer_with_done.bound), std::move(handler.error), std::move(producer_with_done.done) }; } @@ -500,9 +696,10 @@ template < typename OnNext, typename OnError, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + is_callable_v + && is_callable_v + && is_callable_v>> inline producer_with_next_error_done< Value, Error, @@ -516,7 +713,7 @@ inline producer_with_next_error_done< OnError> &&producer_with_next_error, done_holder &&handler) { return { - std::move(producer_with_next_error.producer), + std::move(producer_with_next_error.bound), std::move(producer_with_next_error.next), std::move(producer_with_next_error.error), std::move(handler.done) }; @@ -528,9 +725,10 @@ template < typename OnNext, typename OnError, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + is_callable_v + && is_callable_v + && is_callable_v>> inline producer_with_next_error_done< Value, Error, @@ -544,7 +742,7 @@ inline producer_with_next_error_done< OnDone> &&producer_with_next_done, error_holder &&handler) { return { - std::move(producer_with_next_done.producer), + std::move(producer_with_next_done.bound), std::move(producer_with_next_done.next), std::move(handler.error), std::move(producer_with_next_done.done) }; @@ -556,9 +754,10 @@ template < typename OnNext, typename OnError, typename OnDone, - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()(std::declval())), - typename = decltype(std::declval()())> + typename = std::enable_if_t< + is_callable_v + && is_callable_v + && is_callable_v>> inline producer_with_next_error_done< Value, Error, @@ -572,7 +771,7 @@ inline producer_with_next_error_done< OnDone> &&producer_with_error_done, next_holder &&handler) { return { - std::move(producer_with_error_done.producer), + std::move(producer_with_error_done.bound), std::move(handler.next), std::move(producer_with_error_done.error), std::move(producer_with_error_done.done) }; @@ -593,7 +792,7 @@ inline void operator|( OnDone> &&producer_with_next_error_done, lifetime_holder &&lifetime) { lifetime.alive_while.add( - std::move(producer_with_next_error_done.producer).start( + std::move(producer_with_next_error_done.bound).start( std::move(producer_with_next_error_done.next), std::move(producer_with_next_error_done.error), std::move(producer_with_next_error_done.done))); @@ -601,46 +800,59 @@ inline void operator|( template inline void operator|( - producer &&producer, + producer &&value, lifetime_holder &&start_with_lifetime) { - return std::move(producer) - | on_next([](Value&&) {}) - | on_error([](Error&&) {}) + return std::move(value) + | on_next([](const Value&) {}) + | on_error([](const Error&) {}) | on_done([] {}) | std::move(start_with_lifetime); } template inline void operator|( - producer_with_next &&producer_with_next, + producer_with_next< + Value, + Error, + OnNext> &&producer_with_next, lifetime_holder &&start_with_lifetime) { return std::move(producer_with_next) - | on_error([](Error&&) {}) + | on_error([](const Error&) {}) | on_done([] {}) | std::move(start_with_lifetime); } template inline void operator|( - producer_with_error &&producer_with_error, + producer_with_error< + Value, + Error, + OnError> &&producer_with_error, lifetime_holder &&start_with_lifetime) { return std::move(producer_with_error) - | on_next([](Value&&) {}) + | on_next([](const Value&) {}) | on_done([] {}) | std::move(start_with_lifetime); } template inline void operator|( - producer_with_done &&producer_with_done, + producer_with_done< + Value, + Error, + OnDone> &&producer_with_done, lifetime_holder &&start_with_lifetime) { return std::move(producer_with_done) - | on_next([](Value&&) {}) - | on_error([](Error&&) {}) + | on_next([](const Value&) {}) + | on_error([](const Error&) {}) | std::move(start_with_lifetime); } -template +template < + typename Value, + typename Error, + typename OnNext, + typename OnError> inline void operator|( producer_with_next_error< Value, @@ -653,7 +865,11 @@ inline void operator|( | std::move(start_with_lifetime); } -template +template < + typename Value, + typename Error, + typename OnNext, + typename OnDone> inline void operator|( producer_with_next_done< Value, @@ -662,11 +878,15 @@ inline void operator|( OnDone> &&producer_with_next_done, lifetime_holder &&start_with_lifetime) { return std::move(producer_with_next_done) - | on_error([](Error&&) {}) + | on_error([](const Error&) {}) | std::move(start_with_lifetime); } -template +template < + typename Value, + typename Error, + typename OnError, + typename OnDone> inline void operator|( producer_with_error_done< Value, @@ -675,9 +895,263 @@ inline void operator|( OnDone> &&producer_with_error_done, lifetime_holder &&start_with_lifetime) { return std::move(producer_with_error_done) - | on_next([](Value&&) {}) + | on_next([](const Value&) {}) | std::move(start_with_lifetime); } +template < + typename Value, + typename Error, + typename Callback1, + typename = std::enable_if_t< + is_callable_v +// || is_callable_v + || is_callable_v + >> +inline void operator|( + producer &&value, + lifetime_holder_1 &&start_with_lifetime) { + using callback1_type = std::integral_constant ? CallbackType::Next : +// is_callable_v ? CallbackType::Error : + is_callable_v ? CallbackType::Done : + CallbackType::Invalid>; + start_with_lifetime.alive_while.add( + std::move(value).start( + on_next_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}), + on_error_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}), + on_done_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}))); +} + +template < + typename Value, + typename Error, + typename OnNext, + typename Callback1, + typename = std::enable_if_t< + is_callable_v + || is_callable_v + >> +inline void operator|( + producer_with_next &&producer_with_next, + lifetime_holder_1 &&start_with_lifetime) { + using callback1_type = std::integral_constant ? CallbackType::Error : + is_callable_v ? CallbackType::Done : + CallbackType::Invalid>; + start_with_lifetime.alive_while.add( + std::move(producer_with_next.bound).start( + std::move(producer_with_next.next), + on_error_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}), + on_done_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}))); +} + +template < + typename Value, + typename Error, + typename OnError, + typename Callback1, + typename = std::enable_if_t< + is_callable_v + || is_callable_v + >> +inline void operator|( + producer_with_error &&producer_with_error, + lifetime_holder_1 &&start_with_lifetime) { + using callback1_type = std::integral_constant ? CallbackType::Next : + is_callable_v ? CallbackType::Done : + CallbackType::Invalid>; + start_with_lifetime.alive_while.add( + std::move(producer_with_error.bound).start( + on_next_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}), + std::move(producer_with_error.error), + on_done_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}))); +} + +template < + typename Value, + typename Error, + typename OnDone, + typename Callback1, + typename = std::enable_if_t< + is_callable_v +// || is_callable_v + >> +inline void operator|( + producer_with_done &&producer_with_done, + lifetime_holder_1 &&start_with_lifetime) { + using callback1_type = std::integral_constant ? CallbackType::Next : +// is_callable_v ? CallbackType::Error : + CallbackType::Invalid>; + start_with_lifetime.alive_while.add( + std::move(producer_with_done.bound).start( + on_next_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}), + on_error_dispatch(std::move(start_with_lifetime.callback1), callback1_type{}), + std::move(producer_with_done.done))); +} + +template < + typename Value, + typename Error, + typename OnNext, + typename OnError, + typename Callback1, + typename = std::enable_if_t< + is_callable_v>> +inline void operator|( + producer_with_next_error &&producer_with_next_error, + lifetime_holder_1 &&start_with_lifetime) { + start_with_lifetime.alive_while.add( + std::move(producer_with_next_error.bound).start( + std::move(producer_with_next_error.next), + std::move(producer_with_next_error.error), + std::move(start_with_lifetime.callback1))); +} + +template < + typename Value, + typename Error, + typename OnNext, + typename OnDone, + typename Callback1, + typename = std::enable_if_t< + is_callable_v>> +inline void operator|( + producer_with_next_done &&producer_with_next_done, + lifetime_holder_1 &&start_with_lifetime) { + start_with_lifetime.alive_while.add( + std::move(producer_with_next_done.bound).start( + std::move(producer_with_next_done.next), + std::move(start_with_lifetime.callback1), + std::move(producer_with_next_done.done))); +} + +template < + typename Value, + typename Error, + typename OnError, + typename OnDone, + typename Callback1, + typename = std::enable_if_t< + is_callable_v>> +inline void operator|( + producer_with_error_done &&producer_with_error_done, + lifetime_holder_1 &&start_with_lifetime) { + start_with_lifetime.alive_while.add( + std::move(producer_with_error_done.bound).start( + std::move(start_with_lifetime.callback1), + std::move(producer_with_error_done.error), + std::move(producer_with_error_done.done))); +} + +template < + typename Value, + typename Error, + typename Callback1, + typename Callback2, + typename = std::enable_if_t< + (is_callable_v && is_callable_v) + || (is_callable_v && is_callable_v) +// || (is_callable_v && is_callable_v) + >> +inline void operator|( + producer &&value, + lifetime_holder_2 &&start_with_lifetime) { + using callbacks_type = std::integral_constant && is_callable_v) ? CallbacksType::NextError : + (is_callable_v && is_callable_v) ? CallbacksType::NextDone : +// (is_callable_v && is_callable_v) ? CallbacksType::ErrorDone : + CallbacksType::Invalid>; + start_with_lifetime.alive_while.add( + std::move(value).start( + on_next_dispatch(std::move(start_with_lifetime.callback1), callbacks_type{}), + on_error_dispatch(std::move(start_with_lifetime.callback1), std::move(start_with_lifetime.callback2), callbacks_type{}), + on_done_dispatch(std::move(start_with_lifetime.callback2), callbacks_type{}))); +} + +template < + typename Value, + typename Error, + typename OnNext, + typename Callback1, + typename Callback2, + typename = std::enable_if_t< + is_callable_v>, + typename = std::enable_if_t< + is_callable_v>> +inline void operator|( + producer_with_next &&producer_with_next, + lifetime_holder_2 &&start_with_lifetime) { + start_with_lifetime.alive_while.add( + std::move(producer_with_next.bound).start( + std::move(producer_with_next.next), + std::move(start_with_lifetime.callback1), + std::move(start_with_lifetime.callback2))); +} + +template < + typename Value, + typename Error, + typename OnError, + typename Callback1, + typename Callback2, + typename = std::enable_if_t< + is_callable_v>, + typename = std::enable_if_t< + is_callable_v>> +inline void operator|( + producer_with_error &&producer_with_error, + lifetime_holder_2 &&start_with_lifetime) { + start_with_lifetime.alive_while.add( + std::move(producer_with_error.bound).start( + std::move(start_with_lifetime.callback1), + std::move(producer_with_error.error), + std::move(start_with_lifetime.callback2))); +} + +template < + typename Value, + typename Error, + typename OnDone, + typename Callback1, + typename Callback2, + typename = std::enable_if_t< + is_callable_v>, + typename = std::enable_if_t< + is_callable_v>> +inline void operator|( + producer_with_done &&producer_with_done, + lifetime_holder_2 &&start_with_lifetime) { + start_with_lifetime.alive_while.add( + std::move(producer_with_done.bound).start( + std::move(start_with_lifetime.callback1), + std::move(start_with_lifetime.callback2), + std::move(producer_with_done.done))); +} + +template < + typename Value, + typename Error, + typename Callback1, + typename Callback2, + typename Callback3, + typename = std::enable_if_t< + is_callable_v>, + typename = std::enable_if_t< + is_callable_v>, + typename = std::enable_if_t< + is_callable_v>> +inline void operator|( + producer &&value, + lifetime_holder_3 &&start_with_lifetime) { + start_with_lifetime.alive_while.add( + std::move(value).start( + std::move(start_with_lifetime.callback1), + std::move(start_with_lifetime.callback2), + std::move(start_with_lifetime.callback3))); +} + } // namespace details } // namespace rpl diff --git a/Telegram/SourceFiles/rpl/producer_tests.cpp b/Telegram/SourceFiles/rpl/producer_tests.cpp index fcc9f6eae..16e7c7bd9 100644 --- a/Telegram/SourceFiles/rpl/producer_tests.cpp +++ b/Telegram/SourceFiles/rpl/producer_tests.cpp @@ -157,6 +157,21 @@ TEST_CASE("basic producer tests", "[rpl::producer]") { saved.destroy(); REQUIRE(*lifetimeEndCount == 3); } + + SECTION("tuple producer test") { + auto result = std::make_shared(0); + { + producer>([=](auto &&consumer) { + consumer.put_next(std::make_tuple(1, 2.)); + return lifetime(); + }).start([=](int a, double b) { + *result = a + int(b); + }, [=](no_error error) { + }, [=]() { + }); + } + REQUIRE(*result == 3); + } } TEST_CASE("basic event_streams tests", "[rpl::event_stream]") { @@ -447,4 +462,35 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { } REQUIRE(*sum == 3); } + + SECTION("rich start calls") { + auto sum = std::make_shared(0); + { + auto alive = lifetime(); + producer([=](auto &&consumer) { + consumer.put_next(33); + return lifetime(); + }) + | start([=](int value) { + *sum += value; + }, alive); + producer([=](auto &&consumer) { + consumer.put_error(33); + return lifetime(); + }) + | start([](no_value) { + }, [=](int value) { + *sum += value; + }, alive); + producer([=](auto &&consumer) { + consumer.put_next(33); + consumer.put_done(); + return lifetime(); + }) + | start([=] { + *sum += 33; + }, alive); + } + REQUIRE(*sum == 99); + } } diff --git a/Telegram/SourceFiles/rpl/rpl.h b/Telegram/SourceFiles/rpl/rpl.h index 031184de2..2d446336a 100644 --- a/Telegram/SourceFiles/rpl/rpl.h +++ b/Telegram/SourceFiles/rpl/rpl.h @@ -33,9 +33,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include #include +#include #include #include #include -#include +#include #include diff --git a/Telegram/SourceFiles/rpl/single.h b/Telegram/SourceFiles/rpl/single.h index 873131561..11a705dde 100644 --- a/Telegram/SourceFiles/rpl/single.h +++ b/Telegram/SourceFiles/rpl/single.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -producer, Error> single(Value &&value) { +inline producer, Error> single(Value &&value) { using consumer_t = consumer, Error>; return [value = std::forward(value)]( const consumer_t &consumer) mutable { @@ -36,7 +36,7 @@ producer, Error> single(Value &&value) { } template -producer single() { +inline producer single() { return [](const consumer &consumer) { consumer.put_next({}); consumer.put_done(); diff --git a/Telegram/SourceFiles/rpl/then.h b/Telegram/SourceFiles/rpl/then.h index 84aa20cb7..bd21ddf3c 100644 --- a/Telegram/SourceFiles/rpl/then.h +++ b/Telegram/SourceFiles/rpl/then.h @@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -auto then(producer &&following) { +inline auto then(producer &&following) { return [following = std::move(following)]( producer &&initial) mutable -> producer { diff --git a/Telegram/gyp/tests/tests.gyp b/Telegram/gyp/tests/tests.gyp index e7a8e6428..50d0bec94 100644 --- a/Telegram/gyp/tests/tests.gyp +++ b/Telegram/gyp/tests/tests.gyp @@ -97,10 +97,11 @@ 'common_test.gypi', ], 'sources': [ + '<(src_loc)/rpl/details/callable.h', + '<(src_loc)/rpl/details/type_list.h', '<(src_loc)/rpl/before_next.h', - '<(src_loc)/rpl/combine_latest.h', + '<(src_loc)/rpl/combine.h', '<(src_loc)/rpl/complete.h', - '<(src_loc)/rpl/combine_latest.h', '<(src_loc)/rpl/consumer.h', '<(src_loc)/rpl/deferred.h', '<(src_loc)/rpl/distinct_until_changed.h', @@ -110,6 +111,7 @@ '<(src_loc)/rpl/flatten_latest.h', '<(src_loc)/rpl/lifetime.h', '<(src_loc)/rpl/map.h', + '<(src_loc)/rpl/mappers.h', '<(src_loc)/rpl/never.h', '<(src_loc)/rpl/operators_tests.cpp', '<(src_loc)/rpl/producer.h',