688 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			688 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
 | ||
|  | 
 | ||
|  | #pragma once
 | ||
|  | 
 | ||
|  | /*! \file rx-reduce.hpp
 | ||
|  | 
 | ||
|  |     \brief For each item from this observable use Accumulator to combine items, when completed use ResultSelector to produce a value that will be emitted from the new observable that is returned. | ||
|  | 
 | ||
|  |     \tparam Seed            the type of the initial value for the accumulator | ||
|  |     \tparam Accumulator     the type of the data accumulating function | ||
|  |     \tparam ResultSelector  the type of the result producing function | ||
|  | 
 | ||
|  |     \param seed  the initial value for the accumulator | ||
|  |     \param a     an accumulator function to be invoked on each item emitted by the source observable, the result of which will be used in the next accumulator call | ||
|  |     \param rs    a result producing function that makes the final value from the last accumulator call result | ||
|  | 
 | ||
|  |     \return  An observable that emits a single item that is the result of accumulating the output from the items emitted by the source observable. | ||
|  | 
 | ||
|  |     Some basic reduce-type operators have already been implemented: | ||
|  |     - rxcpp::operators::first | ||
|  |     - rxcpp::operators::last | ||
|  |     - rxcpp::operators::count | ||
|  |     - rxcpp::operators::sum | ||
|  |     - rxcpp::operators::average | ||
|  |     - rxcpp::operators::min | ||
|  |     - rxcpp::operators::max | ||
|  | 
 | ||
|  |     \sample | ||
|  |     Geometric mean of source values: | ||
|  |     \snippet reduce.cpp reduce sample | ||
|  |     \snippet output.txt reduce sample | ||
|  | 
 | ||
|  |     If the source observable completes without emitting any items, the resulting observable emits the result of passing the initial seed to the result selector: | ||
|  |     \snippet reduce.cpp reduce empty sample | ||
|  |     \snippet output.txt reduce empty sample | ||
|  | 
 | ||
|  |     If the accumulator raises an exception, it is returned by the resulting observable in on_error: | ||
|  |     \snippet reduce.cpp reduce exception from accumulator sample | ||
|  |     \snippet output.txt reduce exception from accumulator sample | ||
|  | 
 | ||
|  |     The same for exceptions raised by the result selector: | ||
|  |     \snippet reduce.cpp reduce exception from result selector sample | ||
|  |     \snippet output.txt reduce exception from result selector sample | ||
|  | */ | ||
|  | 
 | ||
|  | #if !defined(RXCPP_OPERATORS_RX_REDUCE_HPP)
 | ||
|  | #define RXCPP_OPERATORS_RX_REDUCE_HPP
 | ||
|  | 
 | ||
|  | #include "../rx-includes.hpp"
 | ||
|  | 
 | ||
|  | namespace rxcpp { | ||
|  | 
 | ||
|  | namespace operators { | ||
|  | 
 | ||
|  | namespace detail { | ||
|  | 
 | ||
|  | template<class... AN> | ||
|  | struct reduce_invalid_arguments {}; | ||
|  | 
 | ||
|  | template<class... AN> | ||
|  | struct reduce_invalid : public rxo::operator_base<reduce_invalid_arguments<AN...>> { | ||
|  |     using type = observable<reduce_invalid_arguments<AN...>, reduce_invalid<AN...>>; | ||
|  | }; | ||
|  | template<class... AN> | ||
|  | using reduce_invalid_t = typename reduce_invalid<AN...>::type; | ||
|  | 
 | ||
|  | template<class Seed, class ResultSelector> | ||
|  | struct is_result_function_for { | ||
|  | 
 | ||
|  |     typedef rxu::decay_t<ResultSelector> result_selector_type; | ||
|  |     typedef rxu::decay_t<Seed> seed_type; | ||
|  | 
 | ||
|  |     struct tag_not_valid {}; | ||
|  | 
 | ||
|  |     template<class CS, class CRS> | ||
|  |     static auto check(int) -> decltype((*(CRS*)nullptr)(*(CS*)nullptr)); | ||
|  |     template<class CS, class CRS> | ||
|  |     static tag_not_valid check(...); | ||
|  | 
 | ||
|  |     typedef rxu::decay_t<decltype(check<seed_type, result_selector_type>(0))> type; | ||
|  |     static const bool value = !std::is_same<type, tag_not_valid>::value; | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T, class Observable, class Accumulator, class ResultSelector, class Seed> | ||
|  | struct reduce_traits | ||
|  | { | ||
|  |     typedef rxu::decay_t<Observable> source_type; | ||
|  |     typedef rxu::decay_t<Accumulator> accumulator_type; | ||
|  |     typedef rxu::decay_t<ResultSelector> result_selector_type; | ||
|  |     typedef rxu::decay_t<Seed> seed_type; | ||
|  | 
 | ||
|  |     typedef T source_value_type; | ||
|  | 
 | ||
|  |     typedef typename is_result_function_for<seed_type, result_selector_type>::type value_type; | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T, class Observable, class Accumulator, class ResultSelector, class Seed> | ||
|  | struct reduce : public operator_base<rxu::value_type_t<reduce_traits<T, Observable, Accumulator, ResultSelector, Seed>>> | ||
|  | { | ||
|  |     typedef reduce<T, Observable, Accumulator, ResultSelector, Seed> this_type; | ||
|  |     typedef reduce_traits<T, Observable, Accumulator, ResultSelector, Seed> traits; | ||
|  | 
 | ||
|  |     typedef typename traits::source_type source_type; | ||
|  |     typedef typename traits::accumulator_type accumulator_type; | ||
|  |     typedef typename traits::result_selector_type result_selector_type; | ||
|  |     typedef typename traits::seed_type seed_type; | ||
|  | 
 | ||
|  |     typedef typename traits::source_value_type source_value_type; | ||
|  |     typedef typename traits::value_type value_type; | ||
|  | 
 | ||
|  |     struct reduce_initial_type | ||
|  |     { | ||
|  |         ~reduce_initial_type() | ||
|  |         { | ||
|  |         } | ||
|  |         reduce_initial_type(source_type o, accumulator_type a, result_selector_type rs, seed_type s) | ||
|  |             : source(std::move(o)) | ||
|  |             , accumulator(std::move(a)) | ||
|  |             , result_selector(std::move(rs)) | ||
|  |             , seed(std::move(s)) | ||
|  |         { | ||
|  |         } | ||
|  |         source_type source; | ||
|  |         accumulator_type accumulator; | ||
|  |         result_selector_type result_selector; | ||
|  |         seed_type seed; | ||
|  | 
 | ||
|  |     private: | ||
|  |         reduce_initial_type& operator=(reduce_initial_type o) RXCPP_DELETE; | ||
|  |     }; | ||
|  |     reduce_initial_type initial; | ||
|  | 
 | ||
|  |     ~reduce() | ||
|  |     { | ||
|  |     } | ||
|  |     reduce(source_type o, accumulator_type a, result_selector_type rs, seed_type s) | ||
|  |         : initial(std::move(o), std::move(a), std::move(rs), std::move(s)) | ||
|  |     { | ||
|  |     } | ||
|  |     template<class Subscriber> | ||
|  |     void on_subscribe(Subscriber o) const { | ||
|  |         struct reduce_state_type | ||
|  |             : public reduce_initial_type | ||
|  |             , public std::enable_shared_from_this<reduce_state_type> | ||
|  |         { | ||
|  |             reduce_state_type(reduce_initial_type i, Subscriber scrbr) | ||
|  |                 : reduce_initial_type(i) | ||
|  |                 , source(i.source) | ||
|  |                 , current(reduce_initial_type::seed) | ||
|  |                 , out(std::move(scrbr)) | ||
|  |             { | ||
|  |             } | ||
|  |             source_type source; | ||
|  |             seed_type current; | ||
|  |             Subscriber out; | ||
|  | 
 | ||
|  |         private: | ||
|  |             reduce_state_type& operator=(reduce_state_type o) RXCPP_DELETE; | ||
|  |         }; | ||
|  |         auto state = std::make_shared<reduce_state_type>(initial, std::move(o)); | ||
|  |         state->source.subscribe( | ||
|  |             state->out, | ||
|  |         // on_next
 | ||
|  |             [state](T t) { | ||
|  |                 seed_type next = state->accumulator(std::move(state->current), std::move(t)); | ||
|  |                 state->current = std::move(next); | ||
|  |             }, | ||
|  |         // on_error
 | ||
|  |             [state](rxu::error_ptr e) { | ||
|  |                 state->out.on_error(e); | ||
|  |             }, | ||
|  |         // on_completed
 | ||
|  |             [state]() { | ||
|  |                 auto result = on_exception( | ||
|  |                     [&](){return state->result_selector(std::move(state->current));}, | ||
|  |                     state->out); | ||
|  |                 if (result.empty()) { | ||
|  |                     return; | ||
|  |                 } | ||
|  |                 state->out.on_next(std::move(result.get())); | ||
|  |                 state->out.on_completed(); | ||
|  |             } | ||
|  |         ); | ||
|  |     } | ||
|  | private: | ||
|  |     reduce& operator=(reduce o) RXCPP_DELETE; | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T> | ||
|  | struct initialize_seeder { | ||
|  |     typedef T seed_type; | ||
|  |     static seed_type seed() { | ||
|  |         return seed_type{}; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T> | ||
|  | struct average { | ||
|  |     struct seed_type | ||
|  |     { | ||
|  |         seed_type() | ||
|  |             : value() | ||
|  |             , count(0) | ||
|  |         { | ||
|  |         } | ||
|  |         rxu::maybe<T> value; | ||
|  |         int count; | ||
|  |         rxu::detail::maybe<double> stage; | ||
|  |     }; | ||
|  |     static seed_type seed() { | ||
|  |         return seed_type{}; | ||
|  |     } | ||
|  |     template<class U> | ||
|  |     seed_type operator()(seed_type a, U&& v) { | ||
|  |         if (a.count != 0 && | ||
|  |             (a.count == std::numeric_limits<int>::max() || | ||
|  |             ((v > 0) && (*(a.value) > (std::numeric_limits<T>::max() - v))) || | ||
|  |             ((v < 0) && (*(a.value) < (std::numeric_limits<T>::min() - v))))) { | ||
|  |             // would overflow, calc existing and reset for next batch
 | ||
|  |             // this will add error to the final result, but the alternative
 | ||
|  |             // is to fail on overflow
 | ||
|  |             double avg = static_cast<double>(*(a.value)) / a.count; | ||
|  |             if (!a.stage.empty()) { | ||
|  |                 a.stage.reset((*a.stage + avg) / 2); | ||
|  |             } else { | ||
|  |                 a.stage.reset(avg); | ||
|  |             } | ||
|  |             a.value.reset(std::forward<U>(v)); | ||
|  |             a.count = 1; | ||
|  |         } else if (a.value.empty()) { | ||
|  |             a.value.reset(std::forward<U>(v)); | ||
|  |             a.count = 1; | ||
|  |         } else { | ||
|  |             *(a.value) += v; | ||
|  |             ++a.count; | ||
|  |         } | ||
|  |         return a; | ||
|  |     } | ||
|  |     double operator()(seed_type a) { | ||
|  |         if (!a.value.empty()) { | ||
|  |             double avg = static_cast<double>(*(a.value)) / a.count; | ||
|  |             if (!a.stage.empty()) { | ||
|  |                 avg = (*a.stage + avg) / 2; | ||
|  |             } | ||
|  |             return avg; | ||
|  |         } | ||
|  |         rxu::throw_exception(rxcpp::empty_error("average() requires a stream with at least one value")); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T> | ||
|  | struct sum { | ||
|  |     typedef rxu::maybe<T> seed_type; | ||
|  |     static seed_type seed() { | ||
|  |         return seed_type(); | ||
|  |     } | ||
|  |     template<class U> | ||
|  |     seed_type operator()(seed_type a, U&& v) const { | ||
|  |         if (a.empty()) | ||
|  |             a.reset(std::forward<U>(v)); | ||
|  |         else | ||
|  |             *a = *a + v; | ||
|  |         return a; | ||
|  |     } | ||
|  |     T operator()(seed_type a) const { | ||
|  |         if (a.empty()) | ||
|  |             rxu::throw_exception(rxcpp::empty_error("sum() requires a stream with at least one value")); | ||
|  |         return *a; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T> | ||
|  | struct max { | ||
|  |     typedef rxu::maybe<T> seed_type; | ||
|  |     static seed_type seed() { | ||
|  |         return seed_type(); | ||
|  |     } | ||
|  |     template<class U> | ||
|  |     seed_type operator()(seed_type a, U&& v) { | ||
|  |         if (a.empty() || *a < v) | ||
|  |             a.reset(std::forward<U>(v)); | ||
|  |         return a; | ||
|  |     } | ||
|  |     T operator()(seed_type a) { | ||
|  |         if (a.empty()) | ||
|  |             rxu::throw_exception(rxcpp::empty_error("max() requires a stream with at least one value")); | ||
|  |         return *a; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T> | ||
|  | struct min { | ||
|  |     typedef rxu::maybe<T> seed_type; | ||
|  |     static seed_type seed() { | ||
|  |         return seed_type(); | ||
|  |     } | ||
|  |     template<class U> | ||
|  |     seed_type operator()(seed_type a, U&& v) { | ||
|  |         if (a.empty() || v < *a) | ||
|  |             a.reset(std::forward<U>(v)); | ||
|  |         return a; | ||
|  |     } | ||
|  |     T operator()(seed_type a) { | ||
|  |         if (a.empty()) | ||
|  |             rxu::throw_exception(rxcpp::empty_error("min() requires a stream with at least one value")); | ||
|  |         return *a; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T> | ||
|  | struct first { | ||
|  |     using seed_type = rxu::maybe<T>; | ||
|  |     static seed_type seed() { | ||
|  |         return seed_type(); | ||
|  |     } | ||
|  |     template<class U> | ||
|  |     seed_type operator()(seed_type a, U&& v) { | ||
|  |         a.reset(std::forward<U>(v)); | ||
|  |         return a; | ||
|  |     } | ||
|  |     T operator()(seed_type a) { | ||
|  |         if (a.empty()) { | ||
|  |             rxu::throw_exception(rxcpp::empty_error("first() requires a stream with at least one value")); | ||
|  |         } | ||
|  |         return *a; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<class T> | ||
|  | struct last { | ||
|  |     using seed_type = rxu::maybe<T>; | ||
|  |     static seed_type seed() { | ||
|  |         return seed_type(); | ||
|  |     } | ||
|  |     template<class U> | ||
|  |     seed_type operator()(seed_type a, U&& v) { | ||
|  |         a.reset(std::forward<U>(v)); | ||
|  |         return a; | ||
|  |     } | ||
|  |     T operator()(seed_type a) { | ||
|  |         if (a.empty()) { | ||
|  |             rxu::throw_exception(rxcpp::empty_error("last() requires a stream with at least one value")); | ||
|  |         } | ||
|  |         return *a; | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | /*! @copydoc rx-reduce.hpp
 | ||
|  | */ | ||
|  | template<class... AN> | ||
|  | auto reduce(AN&&... an) | ||
|  |     ->     operator_factory<reduce_tag, AN...> { | ||
|  |     return operator_factory<reduce_tag, AN...>(std::make_tuple(std::forward<AN>(an)...)); | ||
|  | } | ||
|  | 
 | ||
|  | /*! @copydoc rx-reduce.hpp
 | ||
|  | */ | ||
|  | template<class... AN> | ||
|  | auto accumulate(AN&&... an) | ||
|  |     ->     operator_factory<reduce_tag, AN...> { | ||
|  |     return operator_factory<reduce_tag, AN...>(std::make_tuple(std::forward<AN>(an)...)); | ||
|  | } | ||
|  | 
 | ||
|  | /*! \brief For each item from this observable reduce it by sending only the first item.
 | ||
|  | 
 | ||
|  |     \return  An observable that emits only the very first item emitted by the source observable. | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet math.cpp first sample | ||
|  |     \snippet output.txt first sample | ||
|  | 
 | ||
|  |     When the source observable calls on_error: | ||
|  |     \snippet math.cpp first empty sample | ||
|  |     \snippet output.txt first empty sample | ||
|  | */ | ||
|  | inline auto first() | ||
|  |     ->     operator_factory<first_tag> { | ||
|  |     return operator_factory<first_tag>(std::tuple<>{}); | ||
|  | } | ||
|  | 
 | ||
|  | /*! \brief For each item from this observable reduce it by sending only the last item.
 | ||
|  | 
 | ||
|  |     \return  An observable that emits only the very last item emitted by the source observable. | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet math.cpp last sample | ||
|  |     \snippet output.txt last sample | ||
|  | 
 | ||
|  |     When the source observable calls on_error: | ||
|  |     \snippet math.cpp last empty sample | ||
|  |     \snippet output.txt last empty sample | ||
|  | */ | ||
|  | inline auto last() | ||
|  |     ->     operator_factory<last_tag> { | ||
|  |     return operator_factory<last_tag>(std::tuple<>{}); | ||
|  | } | ||
|  | 
 | ||
|  | /*! \brief For each item from this observable reduce it by incrementing a count.
 | ||
|  | 
 | ||
|  |     \return  An observable that emits a single item: the number of elements emitted by the source observable. | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet math.cpp count sample | ||
|  |     \snippet output.txt count sample | ||
|  | 
 | ||
|  |     When the source observable calls on_error: | ||
|  |     \snippet math.cpp count error sample | ||
|  |     \snippet output.txt count error sample | ||
|  | */ | ||
|  | inline auto count() | ||
|  |     ->     operator_factory<reduce_tag, int, rxu::count, rxu::detail::take_at<0>> { | ||
|  |     return operator_factory<reduce_tag, int, rxu::count, rxu::detail::take_at<0>>(std::make_tuple(0, rxu::count(), rxu::take_at<0>())); | ||
|  | } | ||
|  | 
 | ||
|  | /*! \brief For each item from this observable reduce it by adding to the previous values and then dividing by the number of items at the end.
 | ||
|  | 
 | ||
|  |     \return  An observable that emits a single item: the average of elements emitted by the source observable. | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet math.cpp average sample | ||
|  |     \snippet output.txt average sample | ||
|  | 
 | ||
|  |     When the source observable completes without emitting any items: | ||
|  |     \snippet math.cpp average empty sample | ||
|  |     \snippet output.txt average empty sample | ||
|  | 
 | ||
|  |     When the source observable calls on_error: | ||
|  |     \snippet math.cpp average error sample | ||
|  |     \snippet output.txt average error sample | ||
|  | */ | ||
|  | inline auto average() | ||
|  |     ->     operator_factory<average_tag> { | ||
|  |     return operator_factory<average_tag>(std::tuple<>{}); | ||
|  | } | ||
|  | 
 | ||
|  | /*! \brief For each item from this observable reduce it by adding to the previous items.
 | ||
|  | 
 | ||
|  |     \return  An observable that emits a single item: the sum of elements emitted by the source observable. | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet math.cpp sum sample | ||
|  |     \snippet output.txt sum sample | ||
|  | 
 | ||
|  |     When the source observable completes without emitting any items: | ||
|  |     \snippet math.cpp sum empty sample | ||
|  |     \snippet output.txt sum empty sample | ||
|  | 
 | ||
|  |     When the source observable calls on_error: | ||
|  |     \snippet math.cpp sum error sample | ||
|  |     \snippet output.txt sum error sample | ||
|  | */ | ||
|  | inline auto sum() | ||
|  |     ->     operator_factory<sum_tag> { | ||
|  |     return operator_factory<sum_tag>(std::tuple<>{}); | ||
|  | } | ||
|  | 
 | ||
|  | /*! \brief For each item from this observable reduce it by taking the min value of the previous items.
 | ||
|  | 
 | ||
|  |     \return  An observable that emits a single item: the min of elements emitted by the source observable. | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet math.cpp min sample | ||
|  |     \snippet output.txt min sample | ||
|  | 
 | ||
|  |     When the source observable completes without emitting any items: | ||
|  |     \snippet math.cpp min empty sample | ||
|  |     \snippet output.txt min empty sample | ||
|  | 
 | ||
|  |     When the source observable calls on_error: | ||
|  |     \snippet math.cpp min error sample | ||
|  |     \snippet output.txt min error sample | ||
|  | */ | ||
|  | inline auto min() | ||
|  |     ->     operator_factory<min_tag> { | ||
|  |     return operator_factory<min_tag>(std::tuple<>{}); | ||
|  | } | ||
|  | 
 | ||
|  | /*! \brief For each item from this observable reduce it by taking the max value of the previous items.
 | ||
|  | 
 | ||
|  |     \return  An observable that emits a single item: the max of elements emitted by the source observable. | ||
|  | 
 | ||
|  |     \sample | ||
|  |     \snippet math.cpp max sample | ||
|  |     \snippet output.txt max sample | ||
|  | 
 | ||
|  |     When the source observable completes without emitting any items: | ||
|  |     \snippet math.cpp max empty sample | ||
|  |     \snippet output.txt max empty sample | ||
|  | 
 | ||
|  |     When the source observable calls on_error: | ||
|  |     \snippet math.cpp max error sample | ||
|  |     \snippet output.txt max error sample | ||
|  | */ | ||
|  | inline auto max() | ||
|  |     ->     operator_factory<max_tag> { | ||
|  |     return operator_factory<max_tag>(std::tuple<>{}); | ||
|  | } | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | template<> | ||
|  | struct member_overload<reduce_tag> | ||
|  | { | ||
|  | 
 | ||
|  |     template<class Observable, class Seed, class Accumulator, class ResultSelector, | ||
|  |         class Reduce = rxo::detail::reduce<rxu::value_type_t<Observable>, rxu::decay_t<Observable>, rxu::decay_t<Accumulator>, rxu::decay_t<ResultSelector>, rxu::decay_t<Seed>>, | ||
|  |         class Value = rxu::value_type_t<Reduce>, | ||
|  |         class Result = observable<Value, Reduce>> | ||
|  |     static Result member(Observable&& o, Seed&& s, Accumulator&& a, ResultSelector&& r) | ||
|  |     { | ||
|  |         return Result(Reduce(std::forward<Observable>(o), std::forward<Accumulator>(a), std::forward<ResultSelector>(r), std::forward<Seed>(s))); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class Observable, class Seed, class Accumulator, | ||
|  |         class ResultSelector=rxu::detail::take_at<0>, | ||
|  |         class Reduce = rxo::detail::reduce<rxu::value_type_t<Observable>, rxu::decay_t<Observable>, rxu::decay_t<Accumulator>, rxu::decay_t<ResultSelector>, rxu::decay_t<Seed>>, | ||
|  |         class Value = rxu::value_type_t<Reduce>, | ||
|  |         class Result = observable<Value, Reduce>> | ||
|  |     static Result member(Observable&& o, Seed&& s, Accumulator&& a) | ||
|  |     { | ||
|  |         return Result(Reduce(std::forward<Observable>(o), std::forward<Accumulator>(a), rxu::detail::take_at<0>(), std::forward<Seed>(s))); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class... AN> | ||
|  |     static operators::detail::reduce_invalid_t<AN...> member(AN...) { | ||
|  |         std::terminate(); | ||
|  |         return {}; | ||
|  |         static_assert(sizeof...(AN) == 10000, "reduce takes (Seed, Accumulator, optional ResultSelector), Accumulator takes (Seed, Observable::value_type) -> Seed, ResultSelector takes (Observable::value_type) -> ResultValue"); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<> | ||
|  | struct member_overload<first_tag> | ||
|  | { | ||
|  |     template<class Observable, | ||
|  |         class SValue = rxu::value_type_t<Observable>, | ||
|  |         class Operation = operators::detail::first<SValue>, | ||
|  |         class Seed = decltype(Operation::seed()), | ||
|  |         class Accumulator = Operation, | ||
|  |         class ResultSelector = Operation, | ||
|  |         class TakeOne = decltype(((rxu::decay_t<Observable>*)nullptr)->take(1)), | ||
|  |         class Reduce = rxo::detail::reduce<SValue, rxu::decay_t<TakeOne>, rxu::decay_t<Accumulator>, rxu::decay_t<ResultSelector>, rxu::decay_t<Seed>>, | ||
|  |         class RValue = rxu::value_type_t<Reduce>, | ||
|  |         class Result = observable<RValue, Reduce>> | ||
|  |     static Result member(Observable&& o) | ||
|  |     { | ||
|  |         return Result(Reduce(o.take(1), Operation{}, Operation{}, Operation::seed())); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class... AN> | ||
|  |     static operators::detail::reduce_invalid_t<AN...> member(AN...) { | ||
|  |         std::terminate(); | ||
|  |         return {}; | ||
|  |         static_assert(sizeof...(AN) == 10000, "first does not support Observable::value_type"); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<> | ||
|  | struct member_overload<last_tag> | ||
|  | { | ||
|  |     template<class Observable, | ||
|  |         class SValue = rxu::value_type_t<Observable>, | ||
|  |         class Operation = operators::detail::last<SValue>, | ||
|  |         class Seed = decltype(Operation::seed()), | ||
|  |         class Accumulator = Operation, | ||
|  |         class ResultSelector = Operation, | ||
|  |         class Reduce = rxo::detail::reduce<SValue, rxu::decay_t<Observable>, rxu::decay_t<Accumulator>, rxu::decay_t<ResultSelector>, rxu::decay_t<Seed>>, | ||
|  |         class RValue = rxu::value_type_t<Reduce>, | ||
|  |         class Result = observable<RValue, Reduce>> | ||
|  |     static Result member(Observable&& o) | ||
|  |     { | ||
|  |         return Result(Reduce(std::forward<Observable>(o), Operation{}, Operation{}, Operation::seed())); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class... AN> | ||
|  |     static operators::detail::reduce_invalid_t<AN...> member(AN...) { | ||
|  |         std::terminate(); | ||
|  |         return {}; | ||
|  |         static_assert(sizeof...(AN) == 10000, "last does not support Observable::value_type"); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<> | ||
|  | struct member_overload<sum_tag> | ||
|  | { | ||
|  |     template<class Observable, | ||
|  |         class SValue = rxu::value_type_t<Observable>, | ||
|  |         class Operation = operators::detail::sum<SValue>, | ||
|  |         class Seed = decltype(Operation::seed()), | ||
|  |         class Accumulator = Operation, | ||
|  |         class ResultSelector = Operation, | ||
|  |         class Reduce = rxo::detail::reduce<SValue, rxu::decay_t<Observable>, rxu::decay_t<Accumulator>, rxu::decay_t<ResultSelector>, rxu::decay_t<Seed>>, | ||
|  |         class RValue = rxu::value_type_t<Reduce>, | ||
|  |         class Result = observable<RValue, Reduce>> | ||
|  |     static Result member(Observable&& o) | ||
|  |     { | ||
|  |         return Result(Reduce(std::forward<Observable>(o), Operation{}, Operation{}, Operation::seed())); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class... AN> | ||
|  |     static operators::detail::reduce_invalid_t<AN...> member(AN...) { | ||
|  |         std::terminate(); | ||
|  |         return {}; | ||
|  |         static_assert(sizeof...(AN) == 10000, "sum does not support Observable::value_type"); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<> | ||
|  | struct member_overload<average_tag> | ||
|  | { | ||
|  |     template<class Observable, | ||
|  |         class SValue = rxu::value_type_t<Observable>, | ||
|  |         class Operation = operators::detail::average<SValue>, | ||
|  |         class Seed = decltype(Operation::seed()), | ||
|  |         class Accumulator = Operation, | ||
|  |         class ResultSelector = Operation, | ||
|  |         class Reduce = rxo::detail::reduce<SValue, rxu::decay_t<Observable>, rxu::decay_t<Accumulator>, rxu::decay_t<ResultSelector>, rxu::decay_t<Seed>>, | ||
|  |         class RValue = rxu::value_type_t<Reduce>, | ||
|  |         class Result = observable<RValue, Reduce>> | ||
|  |     static Result member(Observable&& o) | ||
|  |     { | ||
|  |         return Result(Reduce(std::forward<Observable>(o), Operation{}, Operation{}, Operation::seed())); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class... AN> | ||
|  |     static operators::detail::reduce_invalid_t<AN...> member(AN...) { | ||
|  |         std::terminate(); | ||
|  |         return {}; | ||
|  |         static_assert(sizeof...(AN) == 10000, "average does not support Observable::value_type"); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<> | ||
|  | struct member_overload<max_tag> | ||
|  | { | ||
|  |     template<class Observable, | ||
|  |         class SValue = rxu::value_type_t<Observable>, | ||
|  |         class Operation = operators::detail::max<SValue>, | ||
|  |         class Seed = decltype(Operation::seed()), | ||
|  |         class Accumulator = Operation, | ||
|  |         class ResultSelector = Operation, | ||
|  |         class Reduce = rxo::detail::reduce<SValue, rxu::decay_t<Observable>, rxu::decay_t<Accumulator>, rxu::decay_t<ResultSelector>, rxu::decay_t<Seed>>, | ||
|  |         class RValue = rxu::value_type_t<Reduce>, | ||
|  |         class Result = observable<RValue, Reduce>> | ||
|  |     static Result member(Observable&& o) | ||
|  |     { | ||
|  |         return Result(Reduce(std::forward<Observable>(o), Operation{}, Operation{}, Operation::seed())); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class... AN> | ||
|  |     static operators::detail::reduce_invalid_t<AN...> member(AN...) { | ||
|  |         std::terminate(); | ||
|  |         return {}; | ||
|  |         static_assert(sizeof...(AN) == 10000, "max does not support Observable::value_type"); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | template<> | ||
|  | struct member_overload<min_tag> | ||
|  | { | ||
|  |     template<class Observable, | ||
|  |         class SValue = rxu::value_type_t<Observable>, | ||
|  |         class Operation = operators::detail::min<SValue>, | ||
|  |         class Seed = decltype(Operation::seed()), | ||
|  |         class Accumulator = Operation, | ||
|  |         class ResultSelector = Operation, | ||
|  |         class Reduce = rxo::detail::reduce<SValue, rxu::decay_t<Observable>, rxu::decay_t<Accumulator>, rxu::decay_t<ResultSelector>, rxu::decay_t<Seed>>, | ||
|  |         class RValue = rxu::value_type_t<Reduce>, | ||
|  |         class Result = observable<RValue, Reduce>> | ||
|  |     static Result member(Observable&& o) | ||
|  |     { | ||
|  |         return Result(Reduce(std::forward<Observable>(o), Operation{}, Operation{}, Operation::seed())); | ||
|  |     } | ||
|  | 
 | ||
|  |     template<class... AN> | ||
|  |     static operators::detail::reduce_invalid_t<AN...> member(AN...) { | ||
|  |         std::terminate(); | ||
|  |         return {}; | ||
|  |         static_assert(sizeof...(AN) == 10000, "min does not support Observable::value_type"); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | #endif
 |