diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index bb79ec3b2..d30d6d746 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -185,11 +185,12 @@ SectionWithToggle *SectionWithToggle::setToggleShown( } rpl::producer SectionWithToggle::toggledValue() const { - return _toggle - ? (rpl::single(_toggle->checked()) + if (_toggle) { + return rpl::single(_toggle->checked()) | rpl::then( - base::ObservableViewer(_toggle->checkedChanged))) - : rpl::never(); + base::ObservableViewer(_toggle->checkedChanged)); + } + return rpl::never(); } rpl::producer SectionWithToggle::toggleShownValue() const { diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp index 39d44ca7f..e7e308abf 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp @@ -432,7 +432,7 @@ object_ptr InnerWidget::setupUserActions( st::infoBlockButtonSkip)); auto text = PeerUpdateValue(user, Notify::PeerUpdate::Flag::UserIsBlocked) - | rpl::map([user](auto&&) { + | rpl::map([user](auto&&) -> rpl::producer { switch (user->blockStatus()) { case UserData::BlockStatus::Blocked: return Lang::Viewer(lng_profile_unblock_user); diff --git a/Telegram/SourceFiles/rpl/after_next.h b/Telegram/SourceFiles/rpl/after_next.h index bac2bc4d7..5ceee13e4 100644 --- a/Telegram/SourceFiles/rpl/after_next.h +++ b/Telegram/SourceFiles/rpl/after_next.h @@ -33,10 +33,9 @@ public: : _method(std::forward(method)) { } - template - rpl::producer operator()( - rpl::producer &&initial) { - return [ + template + auto operator()(producer &&initial) { + return make_producer([ initial = std::move(initial), method = std::move(_method) ](const consumer &consumer) mutable { @@ -52,7 +51,7 @@ public: }, [consumer] { consumer.put_done(); }); - }; + }); } private: diff --git a/Telegram/SourceFiles/rpl/combine.h b/Telegram/SourceFiles/rpl/combine.h index 4a0284354..5e001483f 100644 --- a/Telegram/SourceFiles/rpl/combine.h +++ b/Telegram/SourceFiles/rpl/combine.h @@ -58,8 +58,8 @@ public: , _state(state) { } - template - void subscribe(producer &&producer) { + template + void subscribe(producer &&producer) { _consumer.add_lifetime(std::move(producer).start( [consumer = _consumer, state = _state](Value &&value) { if (!state->accumulated) { @@ -101,12 +101,13 @@ template < typename consumer_type, typename ...Values, typename ...Errors, + typename ...Generators, std::size_t ...I> inline void combine_subscribe( const consumer_type &consumer, combine_state *state, std::index_sequence, - producer &&...producers) { + producer &&...producers) { auto consume = { ( details::combine_subscribe_one< I, @@ -119,17 +120,18 @@ inline void combine_subscribe( (void)consume; } -template +template < + typename ...Values, + typename ...Errors, + typename ...Generators> inline auto combine_implementation( - producer &&...producers) { + producer &&...producers) { + using CombinedValue = std::tuple; using CombinedError = details::normalized_variant_t; - using Result = producer< - std::tuple, - CombinedError>; - using consumer_type = typename Result::consumer_type; + using consumer_type = consumer; auto result = []( const consumer_type &consumer, - producer &...producers) { + producer &...producers) { auto state = consumer.template make_state< details::combine_state>(); @@ -142,7 +144,7 @@ inline auto combine_implementation( return lifetime(); }; - return Result(std::bind( + return make_producer(std::bind( result, std::placeholders::_1, std::move(producers)...)); @@ -156,8 +158,12 @@ template constexpr bool combine_just_producers_v = combine_just_producers::value; -template -struct combine_just_producers...> +template < + typename ...Values, + typename ...Errors, + typename ...Generators> +struct combine_just_producers< + producer...> : std::true_type { }; @@ -173,8 +179,11 @@ template using combine_result_type_t = typename combine_result_type::type; -template -struct combine_result_type...> { +template < + typename ...Values, + typename ...Errors, + typename ...Generators> +struct combine_result_type...> { using type = std::tuple; }; @@ -222,10 +231,13 @@ template constexpr bool combine_producers_with_mapper_v = combine_producers_with_mapper::value; -template +template < + typename ...Values, + typename ...Errors, + typename ...Generators> inline decltype(auto) combine_helper( std::true_type, - producer &&...producers) { + producer &&...producers) { return combine_implementation(std::move(producers)...); } @@ -274,17 +286,14 @@ struct combine_vector_state { } // namespace details -template -inline producer, Error> combine( - std::vector> &&producers) { - if (producers.empty()) { - return complete, Error>(); - } - +template +inline auto combine( + std::vector> &&producers) { using state_type = details::combine_vector_state; using consumer_type = consumer, Error>; - return [producers = std::move(producers)]( - const consumer_type &consumer) mutable { + return make_producer, Error>([ + producers = std::move(producers) + ](const consumer_type &consumer) mutable { auto count = producers.size(); auto state = consumer.template make_state(); state->accumulated.resize(count); @@ -320,13 +329,20 @@ inline producer, Error> combine( } })); } + if (!count) { + consumer.put_done(); + } return lifetime(); - }; + }); } -template +template < + typename Value, + typename Error, + typename Generator, + typename Mapper> inline auto combine( - std::vector> &&producers, + std::vector> &&producers, Mapper &&mapper) { return combine(std::move(producers)) | map(std::forward(mapper)); diff --git a/Telegram/SourceFiles/rpl/combine_previous.h b/Telegram/SourceFiles/rpl/combine_previous.h index 000a14b54..f74a20998 100644 --- a/Telegram/SourceFiles/rpl/combine_previous.h +++ b/Telegram/SourceFiles/rpl/combine_previous.h @@ -28,14 +28,15 @@ namespace details { class combine_previous_helper { public: - template - rpl::producer, Error> operator()( - rpl::producer &&initial) const { + template + auto operator()( + producer &&initial) const { using consumer_type = consumer< std::tuple, Error>; - return [initial = std::move(initial)]( - const consumer_type &consumer) mutable { + return make_producer, Error>([ + initial = std::move(initial) + ](const consumer_type &consumer) mutable { auto previous = consumer.template make_state< base::optional >(); @@ -58,7 +59,7 @@ public: }, [consumer] { consumer.put_done(); }); - }; + }); } }; @@ -71,13 +72,12 @@ public: : _value(std::forward(value)) { } - template - rpl::producer, Error> operator()( - rpl::producer &&initial) { + template + auto operator()(producer &&initial) { using consumer_type = consumer< std::tuple, Error>; - return [ + return make_producer, Error>([ initial = std::move(initial), value = Value(std::move(_value)) ](const consumer_type &consumer) mutable { @@ -96,7 +96,7 @@ public: }, [consumer] { consumer.put_done(); }); - }; + }); } private: diff --git a/Telegram/SourceFiles/rpl/complete.h b/Telegram/SourceFiles/rpl/complete.h index 2f442a62f..ada04f864 100644 --- a/Telegram/SourceFiles/rpl/complete.h +++ b/Telegram/SourceFiles/rpl/complete.h @@ -25,11 +25,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -inline producer complete() { - return [](const consumer &consumer) mutable { +inline auto complete() { + return make_producer([]( + const consumer &consumer) { consumer.put_done(); return lifetime(); - }; + }); } } // namespace rpl diff --git a/Telegram/SourceFiles/rpl/deferred.h b/Telegram/SourceFiles/rpl/deferred.h index 5e9b1f15d..62ae2e63f 100644 --- a/Telegram/SourceFiles/rpl/deferred.h +++ b/Telegram/SourceFiles/rpl/deferred.h @@ -28,11 +28,12 @@ template < typename Creator, typename Value = typename decltype(std::declval()())::value_type, typename Error = typename decltype(std::declval()())::error_type> -inline producer deferred(Creator &&creator) { - return [creator = std::forward(creator)]( - const consumer &consumer) mutable { +inline auto deferred(Creator &&creator) { + return make_producer([ + creator = std::forward(creator) + ](const consumer &consumer) mutable { return std::move(creator)().start_existing(consumer); - }; + }); } } // namespace rpl diff --git a/Telegram/SourceFiles/rpl/details/superset_type.h b/Telegram/SourceFiles/rpl/details/superset_type.h new file mode 100644 index 000000000..f91baa2c4 --- /dev/null +++ b/Telegram/SourceFiles/rpl/details/superset_type.h @@ -0,0 +1,36 @@ +/* +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 { + +template +struct superset_type; + +template +using superset_type_t = typename superset_type::type; + +template +struct superset_type { + using type = Value; +}; + +} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/distinct_until_changed.h b/Telegram/SourceFiles/rpl/distinct_until_changed.h index 5f9249c77..329d2d35c 100644 --- a/Telegram/SourceFiles/rpl/distinct_until_changed.h +++ b/Telegram/SourceFiles/rpl/distinct_until_changed.h @@ -28,11 +28,12 @@ namespace details { class distinct_until_changed_helper { public: - template - rpl::producer operator()( - rpl::producer &&initial) const { - return [initial = std::move(initial)]( - const consumer &consumer) mutable { + template + auto operator()( + producer &&initial) const { + return make_producer([ + initial = std::move(initial) + ](const consumer &consumer) mutable { auto previous = consumer.template make_state< base::optional >(); @@ -47,7 +48,7 @@ public: }, [consumer] { consumer.put_done(); }); - }; + }); } }; diff --git a/Telegram/SourceFiles/rpl/event_stream.h b/Telegram/SourceFiles/rpl/event_stream.h index 61a15407e..36c0d9953 100644 --- a/Telegram/SourceFiles/rpl/event_stream.h +++ b/Telegram/SourceFiles/rpl/event_stream.h @@ -46,13 +46,29 @@ public: void fire_copy(const Value &value) const { return fire_forward(value); } - producer events() const; - producer events_starting_with( - Value &&value) const { + auto events() const { + using consumer_type = consumer; + return make_producer([weak = weak()]( + const consumer_type &consumer) { + if (auto strong = weak.lock()) { + auto result = [weak, consumer] { + if (auto strong = weak.lock()) { + auto it = base::find(*strong, consumer); + if (it != strong->end()) { + it->terminate(); + } + } + }; + strong->push_back(std::move(consumer)); + return lifetime(std::move(result)); + } + return lifetime(); + }); + } + auto events_starting_with(Value &&value) const { return single(std::move(value)) | then(events()); } - producer events_starting_with_copy( - const Value &value) const { + auto events_starting_with_copy(const Value &value) const { return single(value) | then(events()); } @@ -117,26 +133,6 @@ inline void event_stream::fire_forward( } } -template -inline producer event_stream::events() const { - return producer([weak = weak()]( - const consumer &consumer) { - if (auto strong = weak.lock()) { - auto result = [weak, consumer] { - if (auto strong = weak.lock()) { - auto it = base::find(*strong, consumer); - if (it != strong->end()) { - it->terminate(); - } - } - }; - strong->push_back(std::move(consumer)); - return lifetime(std::move(result)); - } - return lifetime(); - }); -} - template inline std::weak_ptr>> event_stream::weak() const { if (!_consumers) { @@ -171,9 +167,9 @@ public: start_spawning_helper(lifetime &alive_while) : _lifetime(alive_while) { } - template - producer operator()( - producer &&initial) { + + template + auto operator()(producer &&initial) { auto stream = _lifetime.make_state>(); auto collected = std::vector(); { @@ -185,9 +181,7 @@ public: [] {}); std::move(initial) | start_to_stream(*stream, _lifetime); } - return collected.empty() - ? stream->events() - : vector(std::move(collected)) + return vector(std::move(collected)) | then(stream->events()); } diff --git a/Telegram/SourceFiles/rpl/fail.h b/Telegram/SourceFiles/rpl/fail.h index 0533886c6..057327623 100644 --- a/Telegram/SourceFiles/rpl/fail.h +++ b/Telegram/SourceFiles/rpl/fail.h @@ -25,13 +25,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -inline producer> fail(Error &&error) { +inline auto fail(Error &&error) { using consumer_t = consumer>; - return [error = std::forward(error)]( - const consumer_t &consumer) mutable { + return make_producer>([ + error = std::forward(error) + ](const consumer_t &consumer) mutable { consumer.put_error(std::move(error)); return lifetime(); - }; + }); } } // namespace rpl diff --git a/Telegram/SourceFiles/rpl/filter.h b/Telegram/SourceFiles/rpl/filter.h index bd070f993..bbff03f9d 100644 --- a/Telegram/SourceFiles/rpl/filter.h +++ b/Telegram/SourceFiles/rpl/filter.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include +#include #include "base/optional.h" namespace rpl { @@ -38,15 +39,15 @@ public: template < typename Value, typename Error, + typename Generator, typename = std::enable_if_t< details::is_callable_v>> - rpl::producer operator()( - rpl::producer &&initial) { - return [ - initial = std::move(initial), - predicate = std::move(_predicate) - ]( - const consumer &consumer) mutable { + auto operator()(producer &&initial) { + using consumer_type = consumer; + return make_producer([ + initial = std::move(initial), + predicate = std::move(_predicate) + ](const consumer_type &consumer) mutable { return std::move(initial).start( [ consumer, @@ -66,7 +67,7 @@ public: }, [consumer] { consumer.put_done(); }); - }; + }); } private: @@ -85,27 +86,24 @@ inline auto filter(Predicate &&predicate) namespace details { -template <> -class filter_helper> { +template +class filter_helper> { public: - filter_helper(producer &&filterer) - : _filterer(std::move(filterer)) { + filter_helper( + producer &&filterer) + : _filterer(std::move(filterer)) { } - template < - typename Value, - typename Error> - rpl::producer operator()( - rpl::producer &&initial) { + template + auto operator()(producer &&initial) { + using namespace mappers; return combine(std::move(initial), std::move(_filterer)) - | filter([](auto &&value, bool let) { return let; }) - | map([](auto &&value, bool) { - return std::forward(value); - }); + | filter($2) + | map($1_of_two); } private: - producer _filterer; + producer _filterer; }; @@ -123,12 +121,15 @@ inline Value &&deref_optional_helper( class filter_optional_helper { public: - template - rpl::producer operator()( - rpl::producer, Error> &&initial - ) const { - return [initial = std::move(initial)]( - const consumer &consumer) mutable { + template + auto operator()(producer< + base::optional, + Error, + Generator> &&initial) const { + using consumer_type = consumer; + return make_producer([ + initial = std::move(initial) + ](const consumer_type &consumer) mutable { return std::move(initial).start( [consumer](auto &&value) { if (value) { @@ -143,7 +144,7 @@ public: }, [consumer] { consumer.put_done(); }); - }; + }); } }; diff --git a/Telegram/SourceFiles/rpl/flatten_latest.h b/Telegram/SourceFiles/rpl/flatten_latest.h index 2d8e519b4..5a882d4fd 100644 --- a/Telegram/SourceFiles/rpl/flatten_latest.h +++ b/Telegram/SourceFiles/rpl/flatten_latest.h @@ -27,17 +27,22 @@ namespace details { class flatten_latest_helper { public: - template - rpl::producer operator()( - rpl::producer< - rpl::producer, - Error - > &&initial) const { - return [initial = std::move(initial)]( - const consumer &consumer) mutable { + template < + typename Value, + typename Error, + typename Generator, + typename MetaGenerator> + auto operator()(producer< + producer, + Error, + MetaGenerator> &&initial) const { + using consumer_type = consumer; + return make_producer([ + initial = std::move(initial) + ](const consumer_type &consumer) mutable { auto state = std::make_shared(); return std::move(initial).start( - [consumer, state](rpl::producer &&inner) { + [consumer, state](producer &&inner) { state->finished = false; state->alive = std::move(inner).start( [consumer](auto &&value) { @@ -60,7 +65,7 @@ public: state->finished = true; } }); - }; + }); } private: diff --git a/Telegram/SourceFiles/rpl/map.h b/Telegram/SourceFiles/rpl/map.h index 38c2a4eb5..644e09655 100644 --- a/Telegram/SourceFiles/rpl/map.h +++ b/Telegram/SourceFiles/rpl/map.h @@ -84,12 +84,12 @@ public: template < typename Value, typename Error, + typename Generator, typename NewValue = details::callable_result< Transform, Value>> - rpl::producer operator()( - rpl::producer &&initial) { - return [ + auto operator()(producer &&initial) { + return make_producer([ initial = std::move(initial), transform = std::move(_transform) ](const consumer &consumer) mutable { @@ -103,7 +103,7 @@ public: }, [consumer] { consumer.put_done(); }); - }; + }); } private: @@ -181,12 +181,12 @@ public: template < typename Value, typename Error, + typename Generator, typename NewError = details::callable_result< Transform, Error>> - rpl::producer operator()( - rpl::producer &&initial) { - return [ + auto operator()(producer &&initial) { + return make_producer([ initial = std::move(initial), transform = std::move(_transform) ](const consumer &consumer) mutable { @@ -200,7 +200,7 @@ public: ), [consumer] { consumer.put_done(); }); - }; + }); } private: diff --git a/Telegram/SourceFiles/rpl/mappers.h b/Telegram/SourceFiles/rpl/mappers.h index 340f439a2..606af6d4f 100644 --- a/Telegram/SourceFiles/rpl/mappers.h +++ b/Telegram/SourceFiles/rpl/mappers.h @@ -501,5 +501,7 @@ inline auto $val(Type &&value) { return details::make_value_mapper(std::forward(value)); } +constexpr const auto $1_of_two = ((void)$2, $1); + } // namespace mappers } // namespace rpl diff --git a/Telegram/SourceFiles/rpl/never.h b/Telegram/SourceFiles/rpl/never.h index 9ef92f6a7..f9091355f 100644 --- a/Telegram/SourceFiles/rpl/never.h +++ b/Telegram/SourceFiles/rpl/never.h @@ -25,10 +25,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { template -inline producer never() { - return [](const consumer &consumer) mutable { +inline auto never() { + using consumer_type = consumer; + return make_producer([]( + const consumer_type &consumer) { return lifetime(); - }; + }); } } // namespace rpl diff --git a/Telegram/SourceFiles/rpl/operators_tests.cpp b/Telegram/SourceFiles/rpl/operators_tests.cpp index 163b2a3cd..e6bc5a8a9 100644 --- a/Telegram/SourceFiles/rpl/operators_tests.cpp +++ b/Telegram/SourceFiles/rpl/operators_tests.cpp @@ -123,7 +123,7 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { auto copyCount = std::make_shared(0); auto moveCount = std::make_shared(0); { - auto testing = complete(); + auto testing = complete() | type_erased(); for (auto i = 0; i != 5; ++i) { InvokeCounter counter(copyCount, moveCount); testing = std::move(testing) @@ -248,8 +248,18 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { | start_with_next([=](std::string &&value) { *sum += std::move(value) + ' '; }, lifetime); + single(single(1)) + | then(single(single(2) | then(single(3)))) + | then(single(single(4) | then(single(5)) | then(single(6)))) + | flatten_latest() + | map([](int value) { + return std::to_string(value); + }) + | start_with_next([=](std::string &&value) { + *sum += std::move(value) + ' '; + }, lifetime); } - REQUIRE(*sum == "1 2 3 4 5 6 "); + REQUIRE(*sum == "1 2 3 4 5 6 1 2 3 4 5 6 "); } SECTION("combine vector test") { @@ -363,4 +373,19 @@ TEST_CASE("basic operators tests", "[rpl::operators]") { } REQUIRE(*sum == "16192225"); } + + SECTION("after_next test") { + auto sum = std::make_shared(""); + { + rpl::lifetime lifetime; + rpl::ints(3) + | after_next([=](int value) { + *sum += std::to_string(-value-1); + }) + | start_with_next([=](int value) { + *sum += std::to_string(value); + }, lifetime); + } + REQUIRE(*sum == "0-11-22-3"); + } } diff --git a/Telegram/SourceFiles/rpl/producer.h b/Telegram/SourceFiles/rpl/producer.h index d59184548..9248d82d9 100644 --- a/Telegram/SourceFiles/rpl/producer.h +++ b/Telegram/SourceFiles/rpl/producer.h @@ -23,16 +23,27 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "base/lambda.h" #include #include +#include +#include namespace rpl { namespace details { +template +const consumer &const_ref_consumer(); + template class mutable_lambda_wrap { public: mutable_lambda_wrap(Lambda &&lambda) : _lambda(std::move(lambda)) { } + mutable_lambda_wrap(const mutable_lambda_wrap &other) = default; + mutable_lambda_wrap(mutable_lambda_wrap &&other) = default; + mutable_lambda_wrap &operator=( + const mutable_lambda_wrap &other) = default; + mutable_lambda_wrap &operator=( + mutable_lambda_wrap &&other) = default; template auto operator()(Args&&... args) const { @@ -46,62 +57,134 @@ private: }; // Type-erased copyable mutable lambda using base::lambda. -template class mutable_lambda; - -template -class mutable_lambda { +template +class type_erased_generator final { public: + using value_type = Value; + using error_type = Error; + using consumer_type = consumer; + + type_erased_generator( + const type_erased_generator &other) = default; + type_erased_generator( + type_erased_generator &&other) = default; + type_erased_generator &operator=( + const type_erased_generator &other) = default; + type_erased_generator &operator=( + type_erased_generator &&other) = default; - // Copy / move construct / assign from an arbitrary type. template < - typename Lambda, - typename = std::enable_if_t()( - std::declval()...)), - Return - >::value>> - mutable_lambda(Lambda other) : _implementation( - mutable_lambda_wrap(std::move(other))) { + typename Generator, + typename = std::enable_if_t< + std::is_convertible_v< + decltype(std::declval()( + const_ref_consumer())), + lifetime> && + !std::is_same_v< + std::decay_t, + type_erased_generator>>> + type_erased_generator(Generator other) : _implementation( + mutable_lambda_wrap(std::move(other))) { + } + template < + typename Generator, + typename = std::enable_if_t< + std::is_convertible_v< + decltype(std::declval()( + const_ref_consumer())), + lifetime> && + !std::is_same_v< + std::decay_t, + type_erased_generator>>> + type_erased_generator &operator=(Generator other) { + _implementation = mutable_lambda_wrap( + std::move(other)); + return *this; } - template < - typename ...OtherArgs, - typename = std::enable_if_t< - (sizeof...(Args) == sizeof...(OtherArgs))>> - Return operator()(OtherArgs&&... args) { - return _implementation(std::forward(args)...); + lifetime operator()(const consumer_type &consumer) { + return _implementation(consumer); } private: - base::lambda _implementation; + base::lambda _implementation; }; } // namespace details -template -class producer { +template < + typename Value = empty_value, + typename Error = no_error, + typename Generator = details::type_erased_generator< + Value, + Error>> +class producer; + +template < + typename Value1, + typename Value2, + typename Error1, + typename Error2, + typename Generator> +struct superset_type< + producer, + producer> { + using type = producer< + superset_type_t, + superset_type_t, + Generator>; +}; + +template < + typename Value, + typename Error, + typename Generator1, + typename Generator2> +struct superset_type< + producer, + producer> { + using type = producer; +}; + +template < + typename Value, + typename Error, + typename Generator> +struct superset_type< + producer, + producer> { + using type = producer; +}; + +namespace details { + +template +class producer_base { public: using value_type = Value; using error_type = Error; using consumer_type = consumer; template < - typename Generator, - typename = std::enable_if()( - std::declval())), - lifetime>::value>> - producer(Generator &&generator); + typename OtherGenerator, + typename = std::enable_if_t< + std::is_constructible_v>> + producer_base(OtherGenerator &&generator); + + producer_base(const producer_base &other) = default; + producer_base(producer_base &&other) = default; + producer_base &operator=(const producer_base &other) = default; + producer_base &operator=(producer_base &&other) = default; template < typename OnNext, typename OnError, typename OnDone, typename = std::enable_if_t< - details::is_callable_v - && details::is_callable_v - && details::is_callable_v>> + is_callable_v + && is_callable_v + && is_callable_v>> lifetime start( OnNext &&next, OnError &&error, @@ -112,9 +195,9 @@ public: typename OnError, typename OnDone, typename = std::enable_if_t< - details::is_callable_v - && details::is_callable_v - && details::is_callable_v>> + is_callable_v + && is_callable_v + && is_callable_v>> lifetime start_copy( OnNext &&next, OnError &&error, @@ -123,24 +206,30 @@ public: lifetime start_existing(const consumer_type &consumer) &&; private: - details::mutable_lambda< - lifetime(const consumer_type &)> _generator; + Generator _generator; + + template < + typename OtherValue, + typename OtherError, + typename OtherGenerator> + friend class ::rpl::producer; }; -template -template -inline producer::producer(Generator &&generator) -: _generator(std::forward(generator)) { +template +template +inline producer_base::producer_base( + OtherGenerator &&generator) +: _generator(std::forward(generator)) { } -template +template template < typename OnNext, typename OnError, typename OnDone, typename> -inline lifetime producer::start( +inline lifetime producer_base::start( OnNext &&next, OnError &&error, OnDone &&done) && { @@ -150,13 +239,13 @@ inline lifetime producer::start( std::forward(done))); } -template +template template < typename OnNext, typename OnError, typename OnDone, typename> -inline lifetime producer::start_copy( +inline lifetime producer_base::start_copy( OnNext &&next, OnError &&error, OnDone &&done) const & { @@ -167,26 +256,122 @@ inline lifetime producer::start_copy( std::forward(done)); } -template -inline lifetime producer::start_existing( +template +inline lifetime producer_base::start_existing( const consumer_type &consumer) && { consumer.add_lifetime(std::move(_generator)(consumer)); return [consumer] { consumer.terminate(); }; } template -inline producer duplicate( - const producer &value) { +using producer_base_type_erased = producer_base< + Value, + Error, + type_erased_generator>; + +} // namespace details + +template +class producer final +: public details::producer_base { + using parent_type = details::producer_base< + Value, + Error, + Generator>; +public: + using parent_type::parent_type; + +}; + +template +class producer< + Value, + Error, + details::type_erased_generator> final +: public details::producer_base_type_erased { + using parent_type = details::producer_base_type_erased< + Value, + Error>; +public: + using parent_type::parent_type;; + + producer(const producer &other) = default; + producer(producer &&other) = default; + producer &operator=(const producer &other) = default; + producer &operator=(producer &&other) = default; + + template < + typename Generic, + typename = std::enable_if_t>>> + producer(const details::producer_base &other) + : parent_type(other._generator) { + } + + template < + typename Generic, + typename = std::enable_if_t>>> + producer(details::producer_base &&other) + : parent_type(std::move(other._generator)) { + } + + template < + typename Generic, + typename = std::enable_if_t>>> + producer &operator=( + const details::producer_base &other) { + this->_generator = other._generator; + return *this; + } + + template < + typename Generic, + typename = std::enable_if_t>>> + producer &operator=( + details::producer_base &&other) { + this->_generator = std::move(other._generator); + return *this; + } + +}; + +template < + typename Value = empty_value, + typename Error = no_error, + typename Generator, + typename = std::enable_if_t< + std::is_convertible_v< + decltype(std::declval()( + details::const_ref_consumer())), + lifetime>>> +inline auto make_producer(Generator &&generator) +-> producer> { + return std::forward(generator); +} + +template +inline producer duplicate( + const producer &value) { return value; } template < typename Value, typename Error, + typename Generator, typename Method, typename = decltype(std::declval()( - std::declval>()))> -inline auto operator|(producer &&value, Method &&method) { + std::declval>()))> +inline auto operator|( + producer &&value, + Method &&method) { return std::forward(method)(std::move(value)); } @@ -333,9 +518,9 @@ inline auto start_with_next_error_done( namespace details { -template +template inline void operator|( - producer &&value, + producer &&value, lifetime_with_none &&lifetime) { lifetime.alive_while.add( std::move(value).start( @@ -347,10 +532,11 @@ inline void operator|( template < typename Value, typename Error, + typename Generator, typename OnNext, typename = std::enable_if_t>> inline void operator|( - producer &&value, + producer &&value, lifetime_with_next &&lifetime) { lifetime.alive_while.add( std::move(value).start( @@ -362,10 +548,11 @@ inline void operator|( template < typename Value, typename Error, + typename Generator, typename OnError, typename = std::enable_if_t>> inline void operator|( - producer &&value, + producer &&value, lifetime_with_error &&lifetime) { lifetime.alive_while.add( std::move(value).start( @@ -377,10 +564,11 @@ inline void operator|( template < typename Value, typename Error, + typename Generator, typename OnDone, typename = std::enable_if_t>> inline void operator|( - producer &&value, + producer &&value, lifetime_with_done &&lifetime) { lifetime.alive_while.add( std::move(value).start( @@ -392,13 +580,14 @@ inline void operator|( template < typename Value, typename Error, + typename Generator, typename OnNext, typename OnError, typename = std::enable_if_t< is_callable_v && is_callable_v>> inline void operator|( - producer &&value, + producer &&value, lifetime_with_next_error &&lifetime) { lifetime.alive_while.add( std::move(value).start( @@ -410,13 +599,14 @@ inline void operator|( template < typename Value, typename Error, + typename Generator, typename OnError, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v>> inline void operator|( - producer &&value, + producer &&value, lifetime_with_error_done &&lifetime) { lifetime.alive_while.add( std::move(value).start( @@ -428,13 +618,14 @@ inline void operator|( template < typename Value, typename Error, + typename Generator, typename OnNext, typename OnDone, typename = std::enable_if_t< is_callable_v && is_callable_v>> inline void operator|( - producer &&value, + producer &&value, lifetime_with_next_done &&lifetime) { lifetime.alive_while.add( std::move(value).start( @@ -446,6 +637,7 @@ inline void operator|( template < typename Value, typename Error, + typename Generator, typename OnNext, typename OnError, typename OnDone, @@ -454,7 +646,7 @@ template < is_callable_v && is_callable_v>> inline void operator|( - producer &&value, + producer &&value, lifetime_with_next_error_done< OnNext, OnError, diff --git a/Telegram/SourceFiles/rpl/producer_tests.cpp b/Telegram/SourceFiles/rpl/producer_tests.cpp index 41de8363e..924cd1b40 100644 --- a/Telegram/SourceFiles/rpl/producer_tests.cpp +++ b/Telegram/SourceFiles/rpl/producer_tests.cpp @@ -52,7 +52,7 @@ TEST_CASE("basic producer tests", "[rpl::producer]") { *destroyed = true; }); { - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { (void)destroyCaller; consumer.put_next(1); consumer.put_next(2); @@ -82,7 +82,7 @@ TEST_CASE("basic producer tests", "[rpl::producer]") { SECTION("producer error test") { auto errorGenerated = std::make_shared(false); { - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_error(true); return lifetime(); }).start([=](no_value) { @@ -99,7 +99,7 @@ TEST_CASE("basic producer tests", "[rpl::producer]") { { auto lifetimes = lifetime(); { - auto testProducer = producer([=](auto &&consumer) { + auto testProducer = make_producer([=](auto &&consumer) { return [=] { ++*lifetimeEndCount; }; @@ -123,8 +123,8 @@ TEST_CASE("basic producer tests", "[rpl::producer]") { auto lifetimeEndCount = std::make_shared(0); auto saved = lifetime(); { - saved = producer([=](auto &&consumer) { - auto inner = producer([=](auto &&consumer) { + saved = make_producer([=](auto &&consumer) { + auto inner = make_producer([=](auto &&consumer) { consumer.put_next(1); consumer.put_next(2); consumer.put_next(3); @@ -161,7 +161,8 @@ TEST_CASE("basic producer tests", "[rpl::producer]") { SECTION("tuple producer test") { auto result = std::make_shared(0); { - producer>([=](auto &&consumer) { + make_producer>([=]( + auto &&consumer) { consumer.put_next(std::make_tuple(1, 2.)); return lifetime(); }).start([=](int a, double b) { @@ -345,7 +346,7 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { auto dones = std::make_shared(0); { auto alive = lifetime(); - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_next(1); consumer.put_done(); return lifetime(); @@ -353,7 +354,7 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { *sum += value; }, alive); - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_next(11); consumer.put_error(111); return lifetime(); @@ -361,7 +362,7 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { *sum += value; }, alive); - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_next(1111); consumer.put_done(); return lifetime(); @@ -369,7 +370,7 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { *dones += 1; }, alive); - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_next(11111); consumer.put_next(11112); consumer.put_next(11113); @@ -383,7 +384,7 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { } auto alive = lifetime(); - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_next(111111); consumer.put_next(111112); consumer.put_next(111113); @@ -395,7 +396,7 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { *dones += 11; }, alive); - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_error(1111111); return lifetime(); }) | start_with_error_done([=](int value) { @@ -404,7 +405,7 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { *dones = 0; }, alive); - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_next(11111111); consumer.put_next(11111112); consumer.put_next(11111113); @@ -438,7 +439,7 @@ TEST_CASE("basic piping tests", "[rpl::producer]") { for (int i = 0; i != 3; ++i) { auto alive = lifetime(); - producer([=](auto &&consumer) { + make_producer([=](auto &&consumer) { consumer.put_next(1); consumer.put_done(); return lifetime(); diff --git a/Telegram/SourceFiles/rpl/range.h b/Telegram/SourceFiles/rpl/range.h index 089a6aef8..6f5cc5a4a 100644 --- a/Telegram/SourceFiles/rpl/range.h +++ b/Telegram/SourceFiles/rpl/range.h @@ -24,69 +24,78 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace rpl { -template -inline producer, Error> single(Value &&value) { - using consumer_t = consumer, Error>; - return [value = std::forward(value)]( - const consumer_t &consumer) mutable { +template +inline auto single(Value &&value) { + using consumer_type = consumer, no_error>; + return make_producer>([ + value = std::forward(value) + ](const consumer_type &consumer) mutable { consumer.put_next(std::move(value)); consumer.put_done(); return lifetime(); - }; + }); } -template -inline producer single() { - return [](const consumer &consumer) { +inline auto single() { + using consumer_type = consumer; + return make_producer<>([]( + const consumer_type &consumer) { consumer.put_next({}); consumer.put_done(); return lifetime(); - }; + }); } -template -inline producer vector(std::vector &&values) { - return [values = std::move(values)]( - const consumer &consumer) mutable { +template +inline auto vector(std::vector &&values) { + using consumer_type = consumer; + return make_producer([ + values = std::move(values) + ](const consumer_type &consumer) mutable { for (auto &value : values) { consumer.put_next(std::move(value)); } consumer.put_done(); return lifetime(); - }; + }); } -template -inline producer vector(std::vector &&values) { - return [values = std::move(values)]( - const consumer &consumer) mutable { +inline auto vector(std::vector &&values) { + using consumer_type = consumer; + return make_producer([ + values = std::move(values) + ](const consumer_type &consumer) { for (auto value : values) { consumer.put_next_copy(value); } consumer.put_done(); return lifetime(); - }; + }); } -template -inline producer range(Range &&range) { +template < + typename Range, + typename Value = std::decay_t< + decltype(*std::begin(std::declval()))>> +inline auto range(Range &&range) { return vector(std::vector( std::begin(range), std::end(range))); } -inline producer ints(int from, int till) { +inline auto ints(int from, int till) { Expects(from <= till); - return [from, till](const consumer &consumer) { + return make_producer([from, till]( + const consumer &consumer) { for (auto i = from; i != till; ++i) { consumer.put_next_copy(i); } consumer.put_done(); return lifetime(); - }; + }); } -inline producer ints(int count) { +inline auto ints(int count) { return ints(0, count); } diff --git a/Telegram/SourceFiles/rpl/rpl.h b/Telegram/SourceFiles/rpl/rpl.h index bb8234188..1b95382a4 100644 --- a/Telegram/SourceFiles/rpl/rpl.h +++ b/Telegram/SourceFiles/rpl/rpl.h @@ -36,6 +36,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include #include #include +#include #include #include #include diff --git a/Telegram/SourceFiles/rpl/then.h b/Telegram/SourceFiles/rpl/then.h index bd21ddf3c..c066c906b 100644 --- a/Telegram/SourceFiles/rpl/then.h +++ b/Telegram/SourceFiles/rpl/then.h @@ -23,36 +23,64 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include namespace rpl { +namespace details { -template -inline auto then(producer &&following) { - return [following = std::move(following)]( - producer &&initial) mutable - -> producer { - return [ +template +class then_helper { +public: + then_helper(producer &&following) + : _following(std::move(following)) { + } + + template < + typename OtherValue, + typename OtherError, + typename OtherGenerator, + typename NewValue = superset_type_t, + typename NewError = superset_type_t> + auto operator()( + producer &&initial + ) { + using consumer_type = consumer; + return make_producer([ initial = std::move(initial), - following = std::move(following) - ](const consumer &consumer) mutable { + following = std::move(_following) + ](const consumer_type &consumer) mutable { return std::move(initial).start( [consumer](auto &&value) { - consumer.put_next_forward(std::forward(value)); + consumer.put_next_forward( + std::forward(value)); }, [consumer](auto &&error) { - consumer.put_error_forward(std::forward(error)); + consumer.put_error_forward( + std::forward(error)); }, [ consumer, following = std::move(following) ]() mutable { consumer.add_lifetime(std::move(following).start( [consumer](auto &&value) { - consumer.put_next_forward(std::forward(value)); + consumer.put_next_forward( + std::forward(value)); }, [consumer](auto &&error) { - consumer.put_error_forward(std::forward(error)); + consumer.put_error_forward( + std::forward(error)); }, [consumer] { consumer.put_done(); })); }); - }; - }; + }); + } + +private: + producer _following; + +}; + +} // namespace details +template +inline auto then(producer &&following) +-> details::then_helper { + return { std::move(following) }; } } // namespace rpl diff --git a/Telegram/SourceFiles/rpl/type_erased.h b/Telegram/SourceFiles/rpl/type_erased.h new file mode 100644 index 000000000..8010139a7 --- /dev/null +++ b/Telegram/SourceFiles/rpl/type_erased.h @@ -0,0 +1,45 @@ +/* +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 + +namespace rpl { +namespace details { + +class type_erased_helper { +public: + template + producer operator()( + producer &&initial) const { + return std::move(initial); + } + +}; + +} // namespace details + +inline auto type_erased() +-> details::type_erased_helper { + return details::type_erased_helper(); +} + +} // namespace rpl diff --git a/Telegram/SourceFiles/rpl/variable.h b/Telegram/SourceFiles/rpl/variable.h index d93e58ea1..7181a92ba 100644 --- a/Telegram/SourceFiles/rpl/variable.h +++ b/Telegram/SourceFiles/rpl/variable.h @@ -62,9 +62,11 @@ public: template < typename OtherType, + typename Error, + typename Generator, typename = std::enable_if_t< std::is_assignable_v>> - variable(rpl::producer &&stream) { + variable(producer &&stream) { std::move(stream) | start_with_next([this](auto &&data) { *this = std::forward(data); @@ -73,9 +75,12 @@ public: template < typename OtherType, + typename Error, + typename Generator, typename = std::enable_if_t< std::is_assignable_v>> - variable &operator=(rpl::producer &&stream) { + variable &operator=( + producer &&stream) { _lifetime.destroy(); std::move(stream) | start_with_next([this](auto &&data) { @@ -86,7 +91,7 @@ public: Type current() const { return _data; } - rpl::producer value() const { + auto value() const { return _changes.events_starting_with_copy(_data); } @@ -100,8 +105,8 @@ private: } Type _data; - rpl::event_stream _changes; - rpl::lifetime _lifetime; + event_stream _changes; + lifetime _lifetime; }; diff --git a/Telegram/gyp/tests/tests.gyp b/Telegram/gyp/tests/tests.gyp index af38966fe..2c018118f 100644 --- a/Telegram/gyp/tests/tests.gyp +++ b/Telegram/gyp/tests/tests.gyp @@ -98,6 +98,7 @@ ], 'sources': [ '<(src_loc)/rpl/details/callable.h', + '<(src_loc)/rpl/details/superset_type.h', '<(src_loc)/rpl/details/type_list.h', '<(src_loc)/rpl/after_next.h', '<(src_loc)/rpl/before_next.h', @@ -121,6 +122,7 @@ '<(src_loc)/rpl/range.h', '<(src_loc)/rpl/rpl.h', '<(src_loc)/rpl/then.h', + '<(src_loc)/rpl/type_erased.h', '<(src_loc)/rpl/variable.h', ], }],