// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. #pragma once /*! \file rx-with_latest_from.hpp \brief For each item from the first observable select the latest value from all the observables to emit from the new observable that is returned. \tparam AN types of scheduler (optional), aggregate function (optional), and source observables \param an scheduler (optional), aggregation function (optional), and source observables \return Observable that emits items that are the result of combining the items emitted by the source observables. If scheduler is omitted, identity_current_thread is used. If aggregation function is omitted, the resulting observable returns tuples of emitted items. \sample Neither scheduler nor aggregation function are present: \snippet with_latest_from.cpp with_latest_from sample \snippet output.txt with_latest_from sample Only scheduler is present: \snippet with_latest_from.cpp Coordination with_latest_from sample \snippet output.txt Coordination with_latest_from sample Only aggregation function is present: \snippet with_latest_from.cpp Selector with_latest_from sample \snippet output.txt Selector with_latest_from sample Both scheduler and aggregation function are present: \snippet with_latest_from.cpp Coordination+Selector with_latest_from sample \snippet output.txt Coordination+Selector with_latest_from sample */ #if !defined(RXCPP_OPERATORS_RX_WITH_LATEST_FROM_HPP) #define RXCPP_OPERATORS_RX_WITH_LATEST_FROM_HPP #include "../rx-includes.hpp" namespace rxcpp { namespace operators { namespace detail { template struct with_latest_from_invalid_arguments {}; template struct with_latest_from_invalid : public rxo::operator_base> { using type = observable, with_latest_from_invalid>; }; template using with_latest_from_invalid_t = typename with_latest_from_invalid::type; template struct is_with_latest_from_selector_check { typedef rxu::decay_t selector_type; struct tag_not_valid; template static auto check(int) -> decltype((*(CS*)nullptr)((*(typename CON::value_type*)nullptr)...)); template static tag_not_valid check(...); using type = decltype(check...>(0)); static const bool value = !std::is_same::value; }; template struct invalid_with_latest_from_selector { static const bool value = false; }; template struct is_with_latest_from_selector : public std::conditional< is_with_latest_from_selector_check::value, is_with_latest_from_selector_check, invalid_with_latest_from_selector>::type { }; template using result_with_latest_from_selector_t = typename is_with_latest_from_selector::type; template struct with_latest_from_traits { typedef std::tuple tuple_source_type; typedef std::tuple...> tuple_source_value_type; typedef rxu::decay_t selector_type; typedef rxu::decay_t coordination_type; typedef typename is_with_latest_from_selector::type value_type; }; template struct with_latest_from : public operator_base>> { typedef with_latest_from this_type; typedef with_latest_from_traits traits; typedef typename traits::tuple_source_type tuple_source_type; typedef typename traits::tuple_source_value_type tuple_source_value_type; typedef typename traits::selector_type selector_type; typedef typename traits::coordination_type coordination_type; typedef typename coordination_type::coordinator_type coordinator_type; struct values { values(tuple_source_type o, selector_type s, coordination_type sf) : source(std::move(o)) , selector(std::move(s)) , coordination(std::move(sf)) { } tuple_source_type source; selector_type selector; coordination_type coordination; }; values initial; with_latest_from(coordination_type sf, selector_type s, tuple_source_type ts) : initial(std::move(ts), std::move(s), std::move(sf)) { } template void subscribe_one(std::shared_ptr state) const { typedef typename std::tuple_element::type::value_type source_value_type; composite_subscription innercs; // when the out observer is unsubscribed all the // inner subscriptions are unsubscribed as well state->out.add(innercs); auto source = on_exception( [&](){return state->coordinator.in(std::get(state->source));}, state->out); if (source.empty()) { return; } // this subscribe does not share the observer subscription // so that when it is unsubscribed the observer can be called // until the inner subscriptions have finished auto sink = make_subscriber( state->out, innercs, // on_next [state](source_value_type st) { auto& value = std::get(state->latest); if (value.empty()) { ++state->valuesSet; } value.reset(st); if (state->valuesSet == sizeof... (ObservableN) && Index == 0) { auto values = rxu::surely(state->latest); auto selectedResult = rxu::apply(values, state->selector); state->out.on_next(selectedResult); } }, // on_error [state](std::exception_ptr e) { state->out.on_error(e); }, // on_completed [state]() { if (--state->pendingCompletions == 0) { state->out.on_completed(); } } ); auto selectedSink = on_exception( [&](){return state->coordinator.out(sink);}, state->out); if (selectedSink.empty()) { return; } source->subscribe(std::move(selectedSink.get())); } template void subscribe_all(std::shared_ptr state, rxu::values) const { bool subscribed[] = {(subscribe_one(state), true)...}; subscribed[0] = (*subscribed); // silence warning } template void on_subscribe(Subscriber scbr) const { static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); typedef Subscriber output_type; struct with_latest_from_state_type : public std::enable_shared_from_this , public values { with_latest_from_state_type(values i, coordinator_type coor, output_type oarg) : values(std::move(i)) , pendingCompletions(sizeof... (ObservableN)) , valuesSet(0) , coordinator(std::move(coor)) , out(std::move(oarg)) { } // on_completed on the output must wait until all the // subscriptions have received on_completed mutable int pendingCompletions; mutable int valuesSet; mutable tuple_source_value_type latest; coordinator_type coordinator; output_type out; }; auto coordinator = initial.coordination.create_coordinator(scbr.get_subscription()); // take a copy of the values for each subscription auto state = std::make_shared(initial, std::move(coordinator), std::move(scbr)); subscribe_all(state, typename rxu::values_from::type()); } }; } /*! @copydoc rx-with_latest_from.hpp */ template auto with_latest_from(AN&&... an) -> operator_factory { return operator_factory(std::make_tuple(std::forward(an)...)); } } template<> struct member_overload { template>, class with_latest_from = rxo::detail::with_latest_from, rxu::decay_t...>, class Value = rxu::value_type_t, class Result = observable> static Result member(Observable&& o, ObservableN&&... on) { return Result(with_latest_from(identity_current_thread(), rxu::pack(), std::make_tuple(std::forward(o), std::forward(on)...))); } template, all_observables>, class ResolvedSelector = rxu::decay_t, class with_latest_from = rxo::detail::with_latest_from, rxu::decay_t...>, class Value = rxu::value_type_t, class Result = observable> static Result member(Observable&& o, Selector&& s, ObservableN&&... on) { return Result(with_latest_from(identity_current_thread(), std::forward(s), std::make_tuple(std::forward(o), std::forward(on)...))); } template, all_observables>, class with_latest_from = rxo::detail::with_latest_from, rxu::decay_t...>, class Value = rxu::value_type_t, class Result = observable> static Result member(Observable&& o, Coordination&& cn, ObservableN&&... on) { return Result(with_latest_from(std::forward(cn), rxu::pack(), std::make_tuple(std::forward(o), std::forward(on)...))); } template, operators::detail::is_with_latest_from_selector, all_observables>, class ResolvedSelector = rxu::decay_t, class with_latest_from = rxo::detail::with_latest_from, rxu::decay_t...>, class Value = rxu::value_type_t, class Result = observable> static Result member(Observable&& o, Coordination&& cn, Selector&& s, ObservableN&&... on) { return Result(with_latest_from(std::forward(cn), std::forward(s), std::make_tuple(std::forward(o), std::forward(on)...))); } template static operators::detail::with_latest_from_invalid_t member(const AN&...) { std::terminate(); return {}; static_assert(sizeof...(AN) == 10000, "with_latest_from takes (optional Coordination, optional Selector, required Observable, optional Observable...), Selector takes (Observable::value_type...)"); } }; } #endif