feat add rxcpp

This commit is contained in:
tqcq
2024-03-14 20:50:17 +08:00
parent 15bdc54bef
commit 90da26f0a4
124 changed files with 27992 additions and 511 deletions

View File

@ -0,0 +1,182 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-all.hpp
\brief Returns an Observable that emits true if every item emitted by the source Observable satisfies a specified condition, otherwise false.
Emits true if the source Observable terminates without emitting any item.
\tparam Predicate the type of the test function.
\param p the test function to test items emitted by the source Observable.
\return Observable that emits true if every item emitted by the source observable satisfies a specified condition, otherwise false.
\sample
\snippet all.cpp all sample
\snippet output.txt all sample
*/
#if !defined(RXCPP_OPERATORS_RX_ALL_HPP)
#define RXCPP_OPERATORS_RX_ALL_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct all_invalid_arguments {};
template<class... AN>
struct all_invalid : public rxo::operator_base<all_invalid_arguments<AN...>> {
using type = observable<all_invalid_arguments<AN...>, all_invalid<AN...>>;
};
template<class... AN>
using all_invalid_t = typename all_invalid<AN...>::type;
template<class T, class Predicate>
struct all
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Predicate> test_type;
test_type test;
typedef bool value_type;
all(test_type t)
: test(std::move(t))
{
}
template<class Subscriber>
struct all_observer
{
typedef all_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
test_type test;
mutable bool done;
all_observer(dest_type d, test_type t)
: dest(std::move(d))
, test(std::move(t)),
done(false)
{
}
void on_next(source_value_type v) const {
auto filtered = on_exception([&]() {
return !this->test(v); },
dest);
if (filtered.empty()) {
return;
}
if (filtered.get() && !done) {
done = true;
dest.on_next(false);
dest.on_completed();
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
if(!done) {
done = true;
dest.on_next(true);
dest.on_completed();
}
}
static subscriber<value_type, observer_type> make(dest_type d, test_type t) {
return make_subscriber<value_type>(d, this_type(d, std::move(t)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(all_observer<Subscriber>::make(std::move(dest), test)) {
return all_observer<Subscriber>::make(std::move(dest), test);
}
};
}
/*! @copydoc rx-all.hpp
*/
template<class... AN>
auto all(AN&&... an)
-> operator_factory<all_tag, AN...> {
return operator_factory<all_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! \brief Returns an Observable that emits true if the source Observable is empty, otherwise false.
\return An observable that emits a boolean value.
\sample
\snippet is_empty.cpp is_empty sample
\snippet output.txt is_empty sample
*/
template<class... AN>
auto is_empty(AN&&... an)
-> operator_factory<is_empty_tag, AN...> {
return operator_factory<is_empty_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<all_tag>
{
template<class Observable, class Predicate,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class All = rxo::detail::all<SourceValue, rxu::decay_t<Predicate>>,
class Value = rxu::value_type_t<All>>
static auto member(Observable&& o, Predicate&& p)
-> decltype(o.template lift<Value>(All(std::forward<Predicate>(p)))) {
return o.template lift<Value>(All(std::forward<Predicate>(p)));
}
template<class... AN>
static operators::detail::all_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "all takes (Predicate)");
}
};
template<>
struct member_overload<is_empty_tag>
{
template<class Observable,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class Predicate = std::function<bool(SourceValue)>,
class IsEmpty = rxo::detail::all<SourceValue, rxu::decay_t<Predicate>>,
class Value = rxu::value_type_t<IsEmpty>>
static auto member(Observable&& o)
-> decltype(o.template lift<Value>(IsEmpty(nullptr))) {
return o.template lift<Value>(IsEmpty([](SourceValue) { return false; }));
}
template<class... AN>
static operators::detail::all_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "is_empty takes no arguments");
}
};
}
#endif

View File

@ -0,0 +1,298 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-amb.hpp
\brief For each item from only the first of the given observables deliver from the new observable that is returned, on the specified scheduler.
There are 2 variants of the operator:
- The source observable emits nested observables, one of the nested observables is selected.
- The source observable and the arguments v0...vn are used to provide the observables to select from.
\tparam Coordination the type of the scheduler (optional).
\tparam Value0 ... (optional).
\tparam ValueN types of source observables (optional).
\param cn the scheduler to synchronize sources from different contexts (optional).
\param v0 ... (optional).
\param vn source observables (optional).
\return Observable that emits the same sequence as whichever of the source observables first emitted an item or sent a termination notification.
If scheduler is omitted, identity_current_thread is used.
\sample
\snippet amb.cpp threaded implicit amb sample
\snippet output.txt threaded implicit amb sample
\snippet amb.cpp implicit amb sample
\snippet output.txt implicit amb sample
\snippet amb.cpp amb sample
\snippet output.txt amb sample
\snippet amb.cpp threaded amb sample
\snippet output.txt threaded amb sample
*/
#if !defined(RXCPP_OPERATORS_RX_AMB_HPP)
#define RXCPP_OPERATORS_RX_AMB_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct amb_invalid_arguments {};
template<class... AN>
struct amb_invalid : public rxo::operator_base<amb_invalid_arguments<AN...>> {
using type = observable<amb_invalid_arguments<AN...>, amb_invalid<AN...>>;
};
template<class... AN>
using amb_invalid_t = typename amb_invalid<AN...>::type;
template<class T, class Observable, class Coordination>
struct amb
: public operator_base<rxu::value_type_t<T>>
{
//static_assert(is_observable<Observable>::value, "amb requires an observable");
//static_assert(is_observable<T>::value, "amb requires an observable that contains observables");
typedef amb<T, Observable, Coordination> this_type;
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Observable> source_type;
typedef typename source_type::source_operator_type source_operator_type;
typedef typename source_value_type::value_type value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values
{
values(source_operator_type o, coordination_type sf)
: source_operator(std::move(o))
, coordination(std::move(sf))
{
}
source_operator_type source_operator;
coordination_type coordination;
};
values initial;
amb(const source_type& o, coordination_type sf)
: initial(o.source_operator, std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct amb_state_type
: public std::enable_shared_from_this<amb_state_type>
, public values
{
amb_state_type(values i, coordinator_type coor, output_type oarg)
: values(i)
, source(i.source_operator)
, coordinator(std::move(coor))
, out(std::move(oarg))
, pendingObservables(0)
, firstEmitted(false)
{
}
observable<source_value_type, source_operator_type> source;
coordinator_type coordinator;
output_type out;
int pendingObservables;
bool firstEmitted;
std::vector<composite_subscription> innerSubscriptions;
};
auto coordinator = initial.coordination.create_coordinator(scbr.get_subscription());
// take a copy of the values for each subscription
auto state = std::make_shared<amb_state_type>(initial, std::move(coordinator), std::move(scbr));
composite_subscription outercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
state->out.add(outercs);
auto source = on_exception(
[&](){return state->coordinator.in(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<source_value_type>(
state->out,
outercs,
// on_next
[state](source_value_type st) {
if (state->firstEmitted)
return;
composite_subscription innercs;
state->innerSubscriptions.push_back(innercs);
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innercstoken = state->out.add(innercs);
innercs.add(make_subscription([state, innercstoken](){
state->out.remove(innercstoken);
}));
auto selectedSource = state->coordinator.in(st);
auto current_id = state->pendingObservables++;
// this subscribe does not share the source subscription
// so that when it is unsubscribed the source will continue
auto sinkInner = make_subscriber<value_type>(
state->out,
innercs,
// on_next
[state, st, current_id](value_type ct) {
state->out.on_next(std::move(ct));
if (!state->firstEmitted) {
state->firstEmitted = true;
auto do_unsubscribe = [](composite_subscription cs) {
cs.unsubscribe();
};
std::for_each(state->innerSubscriptions.begin(), state->innerSubscriptions.begin() + current_id, do_unsubscribe);
std::for_each(state->innerSubscriptions.begin() + current_id + 1, state->innerSubscriptions.end(), do_unsubscribe);
}
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
//on_completed
[state](){
state->out.on_completed();
}
);
auto selectedSinkInner = state->coordinator.out(sinkInner);
selectedSource.subscribe(std::move(selectedSinkInner));
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state]() {
if (state->pendingObservables == 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()));
}
};
}
/*! @copydoc rx-amb.hpp
*/
template<class... AN>
auto amb(AN&&... an)
-> operator_factory<amb_tag, AN...> {
return operator_factory<amb_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<amb_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Amb = rxo::detail::amb<SourceValue, rxu::decay_t<Observable>, identity_one_worker>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, Amb>
>
static Result member(Observable&& o) {
return Result(Amb(std::forward<Observable>(o), identity_current_thread()));
}
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Amb = rxo::detail::amb<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, Amb>
>
static Result member(Observable&& o, Coordination&& cn) {
return Result(Amb(std::forward<Observable>(o), std::forward<Coordination>(cn)));
}
template<class Observable, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Value0, ValueN...>>,
class EmittedValue = rxu::value_type_t<Observable>,
class SourceValue = observable<EmittedValue>,
class ObservableObservable = observable<SourceValue>,
class Amb = typename rxu::defer_type<rxo::detail::amb, SourceValue, ObservableObservable, identity_one_worker>::type,
class Value = rxu::value_type_t<Amb>,
class Result = observable<Value, Amb>
>
static Result member(Observable&& o, Value0&& v0, ValueN&&... vn) {
return Result(Amb(rxs::from(o.as_dynamic(), v0.as_dynamic(), vn.as_dynamic()...), identity_current_thread()));
}
template<class Observable, class Coordination, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Value0, ValueN...>,
is_coordination<Coordination>>,
class EmittedValue = rxu::value_type_t<Observable>,
class SourceValue = observable<EmittedValue>,
class ObservableObservable = observable<SourceValue>,
class Amb = typename rxu::defer_type<rxo::detail::amb, SourceValue, ObservableObservable, rxu::decay_t<Coordination>>::type,
class Value = rxu::value_type_t<Amb>,
class Result = observable<Value, Amb>
>
static Result member(Observable&& o, Coordination&& cn, Value0&& v0, ValueN&&... vn) {
return Result(Amb(rxs::from(o.as_dynamic(), v0.as_dynamic(), vn.as_dynamic()...), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::amb_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "amb takes (optional Coordination, optional Value0, optional ValueN...)");
}
};
}
#endif

View File

@ -0,0 +1,225 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-any.hpp
\brief Returns an Observable that emits true if any item emitted by the source Observable satisfies a specified condition, otherwise false. Emits false if the source Observable terminates without emitting any item.
\tparam Predicate the type of the test function.
\param p the test function to test items emitted by the source Observable.
\return An observable that emits true if any item emitted by the source observable satisfies a specified condition, otherwise false.
Some basic any- operators have already been implemented:
- rxcpp::operators::exists
- rxcpp::operators::contains
\sample
\snippet exists.cpp exists sample
\snippet output.txt exists sample
\sample
\snippet contains.cpp contains sample
\snippet output.txt contains sample
*/
#if !defined(RXCPP_OPERATORS_RX_ANY_HPP)
#define RXCPP_OPERATORS_RX_ANY_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct any_invalid_arguments {};
template<class... AN>
struct any_invalid : public rxo::operator_base<any_invalid_arguments<AN...>> {
using type = observable<any_invalid_arguments<AN...>, any_invalid<AN...>>;
};
template<class... AN>
using any_invalid_t = typename any_invalid<AN...>::type;
template<class T, class Predicate>
struct any
{
typedef rxu::decay_t<T> source_value_type;
typedef bool value_type;
typedef rxu::decay_t<Predicate> test_type;
test_type test;
any(test_type t)
: test(std::move(t))
{
}
template<class Subscriber>
struct any_observer
{
typedef any_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
test_type test;
mutable bool done;
any_observer(dest_type d, test_type t)
: dest(std::move(d))
, test(std::move(t)),
done(false)
{
}
void on_next(source_value_type v) const {
auto filtered = on_exception([&]() {
return !this->test(v); },
dest);
if (filtered.empty()) {
return;
}
if (!filtered.get() && !done) {
done = true;
dest.on_next(true);
dest.on_completed();
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
if(!done) {
done = true;
dest.on_next(false);
dest.on_completed();
}
}
static subscriber<value_type, observer_type> make(dest_type d, test_type t) {
return make_subscriber<value_type>(d, this_type(d, std::move(t)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(any_observer<Subscriber>::make(std::move(dest), test)) {
return any_observer<Subscriber>::make(std::move(dest), test);
}
};
}
/*! @copydoc rx-any.hpp
*/
template<class... AN>
auto any(AN&&... an)
-> operator_factory<any_tag, AN...> {
return operator_factory<any_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! \brief Returns an Observable that emits true if any item emitted by the source Observable satisfies a specified condition, otherwise false. Emits false if the source Observable terminates without emitting any item.
\tparam Predicate the type of the test function.
\param p the test function to test items emitted by the source Observable.
\return An observable that emits true if any item emitted by the source observable satisfies a specified condition, otherwise false.
\sample
\snippet exists.cpp exists sample
\snippet output.txt exists sample
*/
template<class... AN>
auto exists(AN&&... an)
-> operator_factory<exists_tag, AN...> {
return operator_factory<exists_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! \brief Returns an Observable that emits true if the source Observable emitted a specified item, otherwise false. Emits false if the source Observable terminates without emitting any item.
\tparam T the type of the item to search for.
\param value the item to search for.
\return An observable that emits true if the source Observable emitted a specified item, otherwise false.
\sample
\snippet contains.cpp contains sample
\snippet output.txt contains sample
*/
template<class... AN>
auto contains(AN&&... an)
-> operator_factory<contains_tag, AN...> {
return operator_factory<contains_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<any_tag>
{
template<class Observable, class Predicate,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class Any = rxo::detail::any<SourceValue, rxu::decay_t<Predicate>>,
class Value = rxu::value_type_t<Any>>
static auto member(Observable&& o, Predicate&& p)
-> decltype(o.template lift<Value>(Any(std::forward<Predicate>(p)))) {
return o.template lift<Value>(Any(std::forward<Predicate>(p)));
}
template<class... AN>
static operators::detail::any_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "any takes (Predicate)");
}
};
template<>
struct member_overload<exists_tag>
: member_overload<any_tag>
{
using member_overload<any_tag>::member;
template<class... AN>
static operators::detail::any_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "exists takes (Predicate)");
}
};
template<>
struct member_overload<contains_tag>
{
template<class Observable, class T,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class Predicate = std::function<bool(T)>,
class Any = rxo::detail::any<SourceValue, rxu::decay_t<Predicate>>,
class Value = rxu::value_type_t<Any>>
static auto member(Observable&& o, T&& value)
-> decltype(o.template lift<Value>(Any(nullptr))) {
return o.template lift<Value>(Any([value](T n) { return n == value; }));
}
template<class... AN>
static operators::detail::any_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "contains takes (T)");
}
};
}
#endif

View File

@ -0,0 +1,178 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-buffer_count.hpp
\brief Return an observable that emits connected, non-overlapping buffer, each containing at most count items from the source observable.
If the skip parameter is set, return an observable that emits buffers every skip items containing at most count items from the source observable.
\param count the maximum size of each buffers before it should be emitted.
\param skip how many items need to be skipped before starting a new buffers (optional).
\return Observable that emits connected, non-overlapping buffers, each containing at most count items from the source observable.
If the skip parameter is set, return an Observable that emits buffers every skip items containing at most count items from the source observable.
\sample
\snippet buffer.cpp buffer count sample
\snippet output.txt buffer count sample
\sample
\snippet buffer.cpp buffer count+skip sample
\snippet output.txt buffer count+skip sample
*/
#if !defined(RXCPP_OPERATORS_RX_BUFFER_COUNT_HPP)
#define RXCPP_OPERATORS_RX_BUFFER_COUNT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct buffer_count_invalid_arguments {};
template<class... AN>
struct buffer_count_invalid : public rxo::operator_base<buffer_count_invalid_arguments<AN...>> {
using type = observable<buffer_count_invalid_arguments<AN...>, buffer_count_invalid<AN...>>;
};
template<class... AN>
using buffer_count_invalid_t = typename buffer_count_invalid<AN...>::type;
template<class T>
struct buffer_count
{
typedef rxu::decay_t<T> source_value_type;
typedef std::vector<source_value_type> value_type;
struct buffer_count_values
{
buffer_count_values(int c, int s)
: count(c)
, skip(s)
{
}
int count;
int skip;
};
buffer_count_values initial;
buffer_count(int count, int skip)
: initial(count, skip)
{
}
template<class Subscriber>
struct buffer_count_observer : public buffer_count_values
{
typedef buffer_count_observer<Subscriber> this_type;
typedef std::vector<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
mutable int cursor;
mutable std::deque<value_type> chunks;
buffer_count_observer(dest_type d, buffer_count_values v)
: buffer_count_values(v)
, dest(std::move(d))
, cursor(0)
{
}
void on_next(T v) const {
if (cursor++ % this->skip == 0) {
chunks.emplace_back();
}
for(auto& chunk : chunks) {
chunk.push_back(v);
}
while (!chunks.empty() && int(chunks.front().size()) == this->count) {
dest.on_next(std::move(chunks.front()));
chunks.pop_front();
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
auto done = on_exception(
[&](){
while (!chunks.empty()) {
dest.on_next(std::move(chunks.front()));
chunks.pop_front();
}
return true;
},
dest);
if (done.empty()) {
return;
}
dest.on_completed();
}
static subscriber<T, observer<T, this_type>> make(dest_type d, buffer_count_values v) {
auto cs = d.get_subscription();
return make_subscriber<T>(std::move(cs), this_type(std::move(d), std::move(v)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(buffer_count_observer<Subscriber>::make(std::move(dest), initial)) {
return buffer_count_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-buffer_count.hpp
*/
template<class... AN>
auto buffer(AN&&... an)
-> operator_factory<buffer_count_tag, AN...> {
return operator_factory<buffer_count_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<buffer_count_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class BufferCount = rxo::detail::buffer_count<SourceValue>,
class Value = rxu::value_type_t<BufferCount>>
static auto member(Observable&& o, int count, int skip)
-> decltype(o.template lift<Value>(BufferCount(count, skip))) {
return o.template lift<Value>(BufferCount(count, skip));
}
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class BufferCount = rxo::detail::buffer_count<SourceValue>,
class Value = rxu::value_type_t<BufferCount>>
static auto member(Observable&& o, int count)
-> decltype(o.template lift<Value>(BufferCount(count, count))) {
return o.template lift<Value>(BufferCount(count, count));
}
template<class... AN>
static operators::detail::buffer_count_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "buffer takes (Count, optional Skip)");
}
};
}
#endif

View File

@ -0,0 +1,326 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-buffer_time.hpp
\brief Return an observable that emits buffers every period time interval and collects items from this observable for period of time into each produced buffer.
If the skip parameter is set, Return an observable that emits buffers every skip time interval and collects items from this observable for period of time into each produced buffer, on the specified scheduler.
\tparam Duration the type of the time interval
\tparam Coordination the type of the scheduler (optional).
\param period the period of time each buffer collects items before it is emitted.
\param skip the period of time after which a new buffer will be created (optional).
\param coordination the scheduler for the buffers (optional).
\return Observable that emits buffers every period time interval and collect items from this observable for period of time into each produced buffer.
If the skip parameter is set, return an Observable that emits buffers every skip time interval and collect items from this observable for period of time into each produced buffer.
\sample
\snippet buffer.cpp buffer period+skip+coordination sample
\snippet output.txt buffer period+skip+coordination sample
\sample
\snippet buffer.cpp buffer period+skip sample
\snippet output.txt buffer period+skip sample
Overlapping buffers are allowed:
\snippet buffer.cpp buffer period+skip overlapping sample
\snippet output.txt buffer period+skip overlapping sample
If no items are emitted, an empty buffer is returned:
\snippet buffer.cpp buffer period+skip empty sample
\snippet output.txt buffer period+skip empty sample
\sample
\snippet buffer.cpp buffer period+coordination sample
\snippet output.txt buffer period+coordination sample
\sample
\snippet buffer.cpp buffer period sample
\snippet output.txt buffer period sample
*/
#if !defined(RXCPP_OPERATORS_RX_BUFFER_WITH_TIME_HPP)
#define RXCPP_OPERATORS_RX_BUFFER_WITH_TIME_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct buffer_with_time_invalid_arguments {};
template<class... AN>
struct buffer_with_time_invalid : public rxo::operator_base<buffer_with_time_invalid_arguments<AN...>> {
using type = observable<buffer_with_time_invalid_arguments<AN...>, buffer_with_time_invalid<AN...>>;
};
template<class... AN>
using buffer_with_time_invalid_t = typename buffer_with_time_invalid<AN...>::type;
template<class T, class Duration, class Coordination>
struct buffer_with_time
{
typedef rxu::decay_t<T> source_value_type;
typedef std::vector<source_value_type> value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef rxu::decay_t<Duration> duration_type;
struct buffer_with_time_values
{
buffer_with_time_values(duration_type p, duration_type s, coordination_type c)
: period(p)
, skip(s)
, coordination(c)
{
}
duration_type period;
duration_type skip;
coordination_type coordination;
};
buffer_with_time_values initial;
buffer_with_time(duration_type period, duration_type skip, coordination_type coordination)
: initial(period, skip, coordination)
{
}
template<class Subscriber>
struct buffer_with_time_observer
{
typedef buffer_with_time_observer<Subscriber> this_type;
typedef std::vector<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
struct buffer_with_time_subscriber_values : public buffer_with_time_values
{
buffer_with_time_subscriber_values(composite_subscription cs, dest_type d, buffer_with_time_values v, coordinator_type c)
: buffer_with_time_values(v)
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
, expected(worker.now())
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
mutable std::deque<value_type> chunks;
rxsc::scheduler::clock_type::time_point expected;
};
std::shared_ptr<buffer_with_time_subscriber_values> state;
buffer_with_time_observer(composite_subscription cs, dest_type d, buffer_with_time_values v, coordinator_type c)
: state(std::make_shared<buffer_with_time_subscriber_values>(buffer_with_time_subscriber_values(std::move(cs), std::move(d), v, std::move(c))))
{
auto localState = state;
auto disposer = [=](const rxsc::schedulable&){
localState->cs.unsubscribe();
localState->dest.unsubscribe();
localState->worker.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){return localState->coordinator.act(disposer);},
localState->dest);
if (selectedDisposer.empty()) {
return;
}
localState->dest.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
localState->cs.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
//
// The scheduler is FIFO for any time T. Since the observer is scheduling
// on_next/on_error/oncompleted the timed schedule calls must be resheduled
// when they occur to ensure that production happens after on_next/on_error/oncompleted
//
auto produce_buffer = [localState](const rxsc::schedulable&) {
localState->dest.on_next(std::move(localState->chunks.front()));
localState->chunks.pop_front();
};
auto selectedProduce = on_exception(
[&](){return localState->coordinator.act(produce_buffer);},
localState->dest);
if (selectedProduce.empty()) {
return;
}
auto create_buffer = [localState, selectedProduce](const rxsc::schedulable&) {
localState->chunks.emplace_back();
auto produce_at = localState->expected + localState->period;
localState->expected += localState->skip;
localState->worker.schedule(produce_at, [localState, selectedProduce](const rxsc::schedulable&) {
localState->worker.schedule(selectedProduce.get());
});
};
auto selectedCreate = on_exception(
[&](){return localState->coordinator.act(create_buffer);},
localState->dest);
if (selectedCreate.empty()) {
return;
}
state->worker.schedule_periodically(
state->expected,
state->skip,
[localState, selectedCreate](const rxsc::schedulable&) {
localState->worker.schedule(selectedCreate.get());
});
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable&){
for(auto& chunk : localState->chunks) {
chunk.push_back(v);
}
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&){
localState->dest.on_error(e);
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&){
on_exception(
[&](){
while (!localState->chunks.empty()) {
localState->dest.on_next(std::move(localState->chunks.front()));
localState->chunks.pop_front();
}
return true;
},
localState->dest);
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static subscriber<T, observer<T, this_type>> make(dest_type d, buffer_with_time_values v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator();
return make_subscriber<T>(cs, this_type(cs, std::move(d), std::move(v), std::move(coordinator)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(buffer_with_time_observer<Subscriber>::make(std::move(dest), initial)) {
return buffer_with_time_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-buffer_time.hpp
*/
template<class... AN>
auto buffer_with_time(AN&&... an)
-> operator_factory<buffer_with_time_tag, AN...> {
return operator_factory<buffer_with_time_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<buffer_with_time_tag>
{
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class BufferWithTime = rxo::detail::buffer_with_time<SourceValue, rxu::decay_t<Duration>, identity_one_worker>,
class Value = rxu::value_type_t<BufferWithTime>>
static auto member(Observable&& o, Duration period)
-> decltype(o.template lift<Value>(BufferWithTime(period, period, identity_current_thread()))) {
return o.template lift<Value>(BufferWithTime(period, period, identity_current_thread()));
}
template<class Observable, class Duration, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class BufferWithTime = rxo::detail::buffer_with_time<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<BufferWithTime>>
static auto member(Observable&& o, Duration period, Coordination&& cn)
-> decltype(o.template lift<Value>(BufferWithTime(period, period, std::forward<Coordination>(cn)))) {
return o.template lift<Value>(BufferWithTime(period, period, std::forward<Coordination>(cn)));
}
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class BufferWithTime = rxo::detail::buffer_with_time<SourceValue, rxu::decay_t<Duration>, identity_one_worker>,
class Value = rxu::value_type_t<BufferWithTime>>
static auto member(Observable&& o, Duration&& period, Duration&& skip)
-> decltype(o.template lift<Value>(BufferWithTime(std::forward<Duration>(period), std::forward<Duration>(skip), identity_current_thread()))) {
return o.template lift<Value>(BufferWithTime(std::forward<Duration>(period), std::forward<Duration>(skip), identity_current_thread()));
}
template<class Observable, class Duration, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class BufferWithTime = rxo::detail::buffer_with_time<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<BufferWithTime>>
static auto member(Observable&& o, Duration&& period, Duration&& skip, Coordination&& cn)
-> decltype(o.template lift<Value>(BufferWithTime(std::forward<Duration>(period), std::forward<Duration>(skip), std::forward<Coordination>(cn)))) {
return o.template lift<Value>(BufferWithTime(std::forward<Duration>(period), std::forward<Duration>(skip), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::buffer_with_time_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "buffer_with_time takes (Duration, optional Duration, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,275 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-buffer_time_count.hpp
\brief Return an observable that emits connected, non-overlapping buffers of items from the source observable that were emitted during a fixed duration of time or when the buffer has reached maximum capacity (whichever occurs first), on the specified scheduler.
\tparam Duration the type of the time interval.
\tparam Coordination the type of the scheduler (optional).
\param period the period of time each buffer collects items before it is emitted and replaced with a new buffer.
\param count the maximum size of each buffer before it is emitted and new buffer is created.
\param coordination the scheduler for the buffers (optional).
\return Observable that emits connected, non-overlapping buffers of items from the source observable that were emitted during a fixed duration of time or when the buffer has reached maximum capacity (whichever occurs first).
\sample
\snippet buffer.cpp buffer period+count+coordination sample
\snippet output.txt buffer period+count+coordination sample
\sample
\snippet buffer.cpp buffer period+count sample
\snippet output.txt buffer period+count sample
*/
#if !defined(RXCPP_OPERATORS_RX_BUFFER_WITH_TIME_OR_COUNT_HPP)
#define RXCPP_OPERATORS_RX_BUFFER_WITH_TIME_OR_COUNT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct buffer_with_time_or_count_invalid_arguments {};
template<class... AN>
struct buffer_with_time_or_count_invalid : public rxo::operator_base<buffer_with_time_or_count_invalid_arguments<AN...>> {
using type = observable<buffer_with_time_or_count_invalid_arguments<AN...>, buffer_with_time_or_count_invalid<AN...>>;
};
template<class... AN>
using buffer_with_time_or_count_invalid_t = typename buffer_with_time_or_count_invalid<AN...>::type;
template<class T, class Duration, class Coordination>
struct buffer_with_time_or_count
{
typedef rxu::decay_t<T> source_value_type;
typedef std::vector<source_value_type> value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef rxu::decay_t<Duration> duration_type;
struct buffer_with_time_or_count_values
{
buffer_with_time_or_count_values(duration_type p, int n, coordination_type c)
: period(p)
, count(n)
, coordination(c)
{
}
duration_type period;
int count;
coordination_type coordination;
};
buffer_with_time_or_count_values initial;
buffer_with_time_or_count(duration_type period, int count, coordination_type coordination)
: initial(period, count, coordination)
{
}
template<class Subscriber>
struct buffer_with_time_or_count_observer
{
typedef buffer_with_time_or_count_observer<Subscriber> this_type;
typedef std::vector<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
struct buffer_with_time_or_count_subscriber_values : public buffer_with_time_or_count_values
{
buffer_with_time_or_count_subscriber_values(composite_subscription cs, dest_type d, buffer_with_time_or_count_values v, coordinator_type c)
: buffer_with_time_or_count_values(std::move(v))
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
, chunk_id(0)
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
mutable int chunk_id;
mutable value_type chunk;
};
typedef std::shared_ptr<buffer_with_time_or_count_subscriber_values> state_type;
state_type state;
buffer_with_time_or_count_observer(composite_subscription cs, dest_type d, buffer_with_time_or_count_values v, coordinator_type c)
: state(std::make_shared<buffer_with_time_or_count_subscriber_values>(buffer_with_time_or_count_subscriber_values(std::move(cs), std::move(d), std::move(v), std::move(c))))
{
auto new_id = state->chunk_id;
auto produce_time = state->worker.now() + state->period;
auto localState = state;
auto disposer = [=](const rxsc::schedulable&){
localState->cs.unsubscribe();
localState->dest.unsubscribe();
localState->worker.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){return localState->coordinator.act(disposer);},
localState->dest);
if (selectedDisposer.empty()) {
return;
}
localState->dest.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
localState->cs.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
//
// The scheduler is FIFO for any time T. Since the observer is scheduling
// on_next/on_error/oncompleted the timed schedule calls must be resheduled
// when they occur to ensure that production happens after on_next/on_error/oncompleted
//
localState->worker.schedule(produce_time, [new_id, produce_time, localState](const rxsc::schedulable&){
localState->worker.schedule(produce_buffer(new_id, produce_time, localState));
});
}
static std::function<void(const rxsc::schedulable&)> produce_buffer(int id, rxsc::scheduler::clock_type::time_point expected, state_type state) {
auto produce = [id, expected, state](const rxsc::schedulable&) {
if (id != state->chunk_id)
return;
state->dest.on_next(state->chunk);
state->chunk.resize(0);
auto new_id = ++state->chunk_id;
auto produce_time = expected + state->period;
state->worker.schedule(produce_time, [new_id, produce_time, state](const rxsc::schedulable&){
state->worker.schedule(produce_buffer(new_id, produce_time, state));
});
};
auto selectedProduce = on_exception(
[&](){return state->coordinator.act(produce);},
state->dest);
if (selectedProduce.empty()) {
return std::function<void(const rxsc::schedulable&)>();
}
return std::function<void(const rxsc::schedulable&)>(selectedProduce.get());
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable& self){
localState->chunk.push_back(v);
if (int(localState->chunk.size()) == localState->count) {
produce_buffer(localState->chunk_id, localState->worker.now(), localState)(self);
}
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&){
localState->dest.on_error(e);
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&){
localState->dest.on_next(localState->chunk);
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static subscriber<T, observer<T, this_type>> make(dest_type d, buffer_with_time_or_count_values v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator();
return make_subscriber<T>(cs, this_type(cs, std::move(d), std::move(v), std::move(coordinator)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(buffer_with_time_or_count_observer<Subscriber>::make(std::move(dest), initial)) {
return buffer_with_time_or_count_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-buffer_time_count.hpp
*/
template<class... AN>
auto buffer_with_time_or_count(AN&&... an)
-> operator_factory<buffer_with_time_or_count_tag, AN...> {
return operator_factory<buffer_with_time_or_count_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<buffer_with_time_or_count_tag>
{
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class BufferTimeCount = rxo::detail::buffer_with_time_or_count<SourceValue, rxu::decay_t<Duration>, identity_one_worker>,
class Value = rxu::value_type_t<BufferTimeCount>>
static auto member(Observable&& o, Duration&& period, int count)
-> decltype(o.template lift<Value>(BufferTimeCount(std::forward<Duration>(period), count, identity_current_thread()))) {
return o.template lift<Value>(BufferTimeCount(std::forward<Duration>(period), count, identity_current_thread()));
}
template<class Observable, class Duration, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class BufferTimeCount = rxo::detail::buffer_with_time_or_count<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<BufferTimeCount>>
static auto member(Observable&& o, Duration&& period, int count, Coordination&& cn)
-> decltype(o.template lift<Value>(BufferTimeCount(std::forward<Duration>(period), count, std::forward<Coordination>(cn)))) {
return o.template lift<Value>(BufferTimeCount(std::forward<Duration>(period), count, std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::buffer_with_time_or_count_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "buffer_with_time_or_count takes (Duration, Count, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,312 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-combine_latest.hpp
\brief For each item from all of the observables select a value 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 combine_latest.cpp combine_latest sample
\snippet output.txt combine_latest sample
Only scheduler is present:
\snippet combine_latest.cpp Coordination combine_latest sample
\snippet output.txt Coordination combine_latest sample
Only aggregation function is present:
\snippet combine_latest.cpp Selector combine_latest sample
\snippet output.txt Selector combine_latest sample
Both scheduler and aggregation function are present:
\snippet combine_latest.cpp Coordination+Selector combine_latest sample
\snippet output.txt Coordination+Selector combine_latest sample
*/
#if !defined(RXCPP_OPERATORS_RX_COMBINE_LATEST_HPP)
#define RXCPP_OPERATORS_RX_COMBINE_LATEST_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct combine_latest_invalid_arguments {};
template<class... AN>
struct combine_latest_invalid : public rxo::operator_base<combine_latest_invalid_arguments<AN...>> {
using type = observable<combine_latest_invalid_arguments<AN...>, combine_latest_invalid<AN...>>;
};
template<class... AN>
using combine_latest_invalid_t = typename combine_latest_invalid<AN...>::type;
template<class Selector, class... ObservableN>
struct is_combine_latest_selector_check {
typedef rxu::decay_t<Selector> selector_type;
struct tag_not_valid;
template<class CS, class... CON>
static auto check(int) -> decltype((*(CS*)nullptr)((*(typename CON::value_type*)nullptr)...));
template<class CS, class... CON>
static tag_not_valid check(...);
using type = decltype(check<selector_type, rxu::decay_t<ObservableN>...>(0));
static const bool value = !std::is_same<type, tag_not_valid>::value;
};
template<class Selector, class... ObservableN>
struct invalid_combine_latest_selector {
static const bool value = false;
};
template<class Selector, class... ObservableN>
struct is_combine_latest_selector : public std::conditional<
is_combine_latest_selector_check<Selector, ObservableN...>::value,
is_combine_latest_selector_check<Selector, ObservableN...>,
invalid_combine_latest_selector<Selector, ObservableN...>>::type {
};
template<class Selector, class... ON>
using result_combine_latest_selector_t = typename is_combine_latest_selector<Selector, ON...>::type;
template<class Coordination, class Selector, class... ObservableN>
struct combine_latest_traits {
typedef std::tuple<ObservableN...> tuple_source_type;
typedef std::tuple<rxu::detail::maybe<typename ObservableN::value_type>...> tuple_source_value_type;
typedef rxu::decay_t<Selector> selector_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename is_combine_latest_selector<selector_type, ObservableN...>::type value_type;
};
template<class Coordination, class Selector, class... ObservableN>
struct combine_latest : public operator_base<rxu::value_type_t<combine_latest_traits<Coordination, Selector, ObservableN...>>>
{
typedef combine_latest<Coordination, Selector, ObservableN...> this_type;
typedef combine_latest_traits<Coordination, Selector, ObservableN...> 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;
combine_latest(coordination_type sf, selector_type s, tuple_source_type ts)
: initial(std::move(ts), std::move(s), std::move(sf))
{
}
template<int Index, class State>
void subscribe_one(std::shared_ptr<State> state) const {
typedef typename std::tuple_element<Index, tuple_source_type>::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<Index>(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<source_value_type>(
state->out,
innercs,
// on_next
[state](source_value_type st) {
auto& value = std::get<Index>(state->latest);
if (value.empty()) {
++state->valuesSet;
}
value.reset(st);
if (state->valuesSet == sizeof... (ObservableN)) {
auto values = rxu::surely(state->latest);
auto selectedResult = rxu::apply(values, state->selector);
state->out.on_next(selectedResult);
}
},
// on_error
[state](rxu::error_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<class State, int... IndexN>
void subscribe_all(std::shared_ptr<State> state, rxu::values<int, IndexN...>) const {
bool subscribed[] = {(subscribe_one<IndexN>(state), true)...};
subscribed[0] = (*subscribed); // silence warning
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct combine_latest_state_type
: public std::enable_shared_from_this<combine_latest_state_type>
, public values
{
combine_latest_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<combine_latest_state_type>(initial, std::move(coordinator), std::move(scbr));
subscribe_all(state, typename rxu::values_from<int, sizeof...(ObservableN)>::type());
}
};
}
/*! @copydoc rx-combine_latest.hpp
*/
template<class... AN>
auto combine_latest(AN&&... an)
-> operator_factory<combine_latest_tag, AN...> {
return operator_factory<combine_latest_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<combine_latest_tag>
{
template<class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, ObservableN...>>,
class combine_latest = rxo::detail::combine_latest<identity_one_worker, rxu::detail::pack, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<combine_latest>,
class Result = observable<Value, combine_latest>>
static Result member(Observable&& o, ObservableN&&... on)
{
return Result(combine_latest(identity_current_thread(), rxu::pack(), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Observable, class Selector, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
operators::detail::is_combine_latest_selector<Selector, Observable, ObservableN...>,
all_observables<Observable, ObservableN...>>,
class ResolvedSelector = rxu::decay_t<Selector>,
class combine_latest = rxo::detail::combine_latest<identity_one_worker, ResolvedSelector, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<combine_latest>,
class Result = observable<Value, combine_latest>>
static Result member(Observable&& o, Selector&& s, ObservableN&&... on)
{
return Result(combine_latest(identity_current_thread(), std::forward<Selector>(s), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Coordination, class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
is_coordination<Coordination>,
all_observables<Observable, ObservableN...>>,
class combine_latest = rxo::detail::combine_latest<Coordination, rxu::detail::pack, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<combine_latest>,
class Result = observable<Value, combine_latest>>
static Result member(Observable&& o, Coordination&& cn, ObservableN&&... on)
{
return Result(combine_latest(std::forward<Coordination>(cn), rxu::pack(), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Coordination, class Selector, class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
is_coordination<Coordination>,
operators::detail::is_combine_latest_selector<Selector, Observable, ObservableN...>,
all_observables<Observable, ObservableN...>>,
class ResolvedSelector = rxu::decay_t<Selector>,
class combine_latest = rxo::detail::combine_latest<Coordination, ResolvedSelector, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<combine_latest>,
class Result = observable<Value, combine_latest>>
static Result member(Observable&& o, Coordination&& cn, Selector&& s, ObservableN&&... on)
{
return Result(combine_latest(std::forward<Coordination>(cn), std::forward<Selector>(s), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class... AN>
static operators::detail::combine_latest_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "combine_latest takes (optional Coordination, optional Selector, required Observable, optional Observable...), Selector takes (Observable::value_type...)");
}
};
}
#endif

View File

@ -0,0 +1,309 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-concat.hpp
\brief For each item from this observable subscribe to one at a time, in the order received.
For each item from all of the given observables deliver from the new observable that is returned.
There are 2 variants of the operator:
- The source observable emits nested observables, nested observables are concatenated.
- The source observable and the arguments v0...vn are used to provide the observables to concatenate.
\tparam Coordination the type of the scheduler (optional).
\tparam Value0 ... (optional).
\tparam ValueN types of source observables (optional).
\param cn the scheduler to synchronize sources from different contexts (optional).
\param v0 ... (optional).
\param vn source observables (optional).
\return Observable that emits the items emitted by each of the Observables emitted by the source observable, one after the other, without interleaving them.
\sample
\snippet concat.cpp implicit concat sample
\snippet output.txt implicit concat sample
\sample
\snippet concat.cpp threaded implicit concat sample
\snippet output.txt threaded implicit concat sample
\sample
\snippet concat.cpp concat sample
\snippet output.txt concat sample
\sample
\snippet concat.cpp threaded concat sample
\snippet output.txt threaded concat sample
*/
#if !defined(RXCPP_OPERATORS_RX_CONCAT_HPP)
#define RXCPP_OPERATORS_RX_CONCAT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct concat_invalid_arguments {};
template<class... AN>
struct concat_invalid : public rxo::operator_base<concat_invalid_arguments<AN...>> {
using type = observable<concat_invalid_arguments<AN...>, concat_invalid<AN...>>;
};
template<class... AN>
using concat_invalid_t = typename concat_invalid<AN...>::type;
template<class T, class Observable, class Coordination>
struct concat
: public operator_base<rxu::value_type_t<rxu::decay_t<T>>>
{
typedef concat<T, Observable, Coordination> this_type;
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef typename source_type::source_operator_type source_operator_type;
typedef source_value_type collection_type;
typedef typename collection_type::value_type value_type;
struct values
{
values(source_operator_type o, coordination_type sf)
: source_operator(std::move(o))
, coordination(std::move(sf))
{
}
source_operator_type source_operator;
coordination_type coordination;
};
values initial;
concat(const source_type& o, coordination_type sf)
: initial(o.source_operator, std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct concat_state_type
: public std::enable_shared_from_this<concat_state_type>
, public values
{
concat_state_type(values i, coordinator_type coor, output_type oarg)
: values(i)
, source(i.source_operator)
, sourceLifetime(composite_subscription::empty())
, collectionLifetime(composite_subscription::empty())
, coordinator(std::move(coor))
, out(std::move(oarg))
{
}
void subscribe_to(collection_type st)
{
auto state = this->shared_from_this();
collectionLifetime = composite_subscription();
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innercstoken = state->out.add(collectionLifetime);
collectionLifetime.add(make_subscription([state, innercstoken](){
state->out.remove(innercstoken);
}));
auto selectedSource = on_exception(
[&](){return state->coordinator.in(std::move(st));},
state->out);
if (selectedSource.empty()) {
return;
}
// this subscribe does not share the out subscription
// so that when it is unsubscribed the out will continue
auto sinkInner = make_subscriber<value_type>(
state->out,
collectionLifetime,
// on_next
[state, st](value_type ct) {
state->out.on_next(ct);
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
//on_completed
[state](){
if (!state->selectedCollections.empty()) {
auto value = state->selectedCollections.front();
state->selectedCollections.pop_front();
state->collectionLifetime.unsubscribe();
state->subscribe_to(value);
} else if (!state->sourceLifetime.is_subscribed()) {
state->out.on_completed();
}
}
);
auto selectedSinkInner = on_exception(
[&](){return state->coordinator.out(sinkInner);},
state->out);
if (selectedSinkInner.empty()) {
return;
}
selectedSource->subscribe(std::move(selectedSinkInner.get()));
}
observable<source_value_type, source_operator_type> source;
composite_subscription sourceLifetime;
composite_subscription collectionLifetime;
std::deque<collection_type> selectedCollections;
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<concat_state_type>(initial, std::move(coordinator), std::move(scbr));
state->sourceLifetime = composite_subscription();
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
state->out.add(state->sourceLifetime);
auto source = on_exception(
[&](){return state->coordinator.in(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<collection_type>(
state->out,
state->sourceLifetime,
// on_next
[state](collection_type st) {
if (state->collectionLifetime.is_subscribed()) {
state->selectedCollections.push_back(st);
} else if (state->selectedCollections.empty()) {
state->subscribe_to(st);
}
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state]() {
if (!state->collectionLifetime.is_subscribed() && state->selectedCollections.empty()) {
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()));
}
};
}
/*! @copydoc rx-concat.hpp
*/
template<class... AN>
auto concat(AN&&... an)
-> operator_factory<concat_tag, AN...> {
return operator_factory<concat_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<concat_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Concat = rxo::detail::concat<SourceValue, rxu::decay_t<Observable>, identity_one_worker>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, Concat>
>
static Result member(Observable&& o) {
return Result(Concat(std::forward<Observable>(o), identity_current_thread()));
}
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Concat = rxo::detail::concat<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, Concat>
>
static Result member(Observable&& o, Coordination&& cn) {
return Result(Concat(std::forward<Observable>(o), std::forward<Coordination>(cn)));
}
template<class Observable, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Value0, ValueN...>>,
class EmittedValue = rxu::value_type_t<Observable>,
class SourceValue = observable<EmittedValue>,
class ObservableObservable = observable<SourceValue>,
class Concat = typename rxu::defer_type<rxo::detail::concat, SourceValue, ObservableObservable, identity_one_worker>::type,
class Value = rxu::value_type_t<Concat>,
class Result = observable<Value, Concat>
>
static Result member(Observable&& o, Value0&& v0, ValueN&&... vn) {
return Result(Concat(rxs::from(o.as_dynamic(), v0.as_dynamic(), vn.as_dynamic()...), identity_current_thread()));
}
template<class Observable, class Coordination, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Value0, ValueN...>,
is_coordination<Coordination>>,
class EmittedValue = rxu::value_type_t<Observable>,
class SourceValue = observable<EmittedValue>,
class ObservableObservable = observable<SourceValue>,
class Concat = typename rxu::defer_type<rxo::detail::concat, SourceValue, ObservableObservable, rxu::decay_t<Coordination>>::type,
class Value = rxu::value_type_t<Concat>,
class Result = observable<Value, Concat>
>
static Result member(Observable&& o, Coordination&& cn, Value0&& v0, ValueN&&... vn) {
return Result(Concat(rxs::from(o.as_dynamic(), v0.as_dynamic(), vn.as_dynamic()...), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::concat_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "concat takes (optional Coordination, optional Value0, optional ValueN...)");
}
};
}
#endif

View File

@ -0,0 +1,373 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-concat_map.hpp
\brief For each item from this observable use the CollectionSelector to produce an observable and subscribe to that observable.
For each item from all of the produced observables use the ResultSelector to produce a value to emit from the new observable that is returned.
\tparam CollectionSelector the type of the observable producing function. CollectionSelector must be a function with the signature: observable(concat_map::source_value_type)
\tparam ResultSelector the type of the aggregation function (optional). ResultSelector must be a function with the signature: concat_map::value_type(concat_map::source_value_type, concat_map::collection_value_type)
\tparam Coordination the type of the scheduler (optional).
\param s a function that returns an observable for each item emitted by the source observable.
\param rs a function that combines one item emitted by each of the source and collection observables and returns an item to be emitted by the resulting observable (optional).
\param cn the scheduler to synchronize sources from different contexts. (optional).
\return Observable that emits the results of applying a function to a pair of values emitted by the source observable and the collection observable.
Observables, produced by the CollectionSelector, are concatenated. There is another operator rxcpp::observable<T,SourceType>::flat_map that works similar but merges the observables.
\sample
\snippet concat_map.cpp concat_map sample
\snippet output.txt concat_map sample
\sample
\snippet concat_map.cpp threaded concat_map sample
\snippet output.txt threaded concat_map sample
*/
#if !defined(RXCPP_OPERATORS_RX_CONCATMAP_HPP)
#define RXCPP_OPERATORS_RX_CONCATMAP_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct concat_map_invalid_arguments {};
template<class... AN>
struct concat_map_invalid : public rxo::operator_base<concat_map_invalid_arguments<AN...>> {
using type = observable<concat_map_invalid_arguments<AN...>, concat_map_invalid<AN...>>;
};
template<class... AN>
using concat_map_invalid_t = typename concat_map_invalid<AN...>::type;
template<class Observable, class CollectionSelector, class ResultSelector, class Coordination>
struct concat_traits {
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<CollectionSelector> collection_selector_type;
typedef rxu::decay_t<ResultSelector> result_selector_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename source_type::value_type source_value_type;
struct tag_not_valid {};
template<class CV, class CCS>
static auto collection_check(int) -> decltype((*(CCS*)nullptr)(*(CV*)nullptr));
template<class CV, class CCS>
static tag_not_valid collection_check(...);
static_assert(!std::is_same<decltype(collection_check<source_value_type, collection_selector_type>(0)), tag_not_valid>::value, "concat_map CollectionSelector must be a function with the signature observable(concat_map::source_value_type)");
typedef decltype((*(collection_selector_type*)nullptr)((*(source_value_type*)nullptr))) collection_type;
//#if _MSC_VER >= 1900
static_assert(is_observable<collection_type>::value, "concat_map CollectionSelector must return an observable");
//#endif
typedef typename collection_type::value_type collection_value_type;
template<class CV, class CCV, class CRS>
static auto result_check(int) -> decltype((*(CRS*)nullptr)(*(CV*)nullptr, *(CCV*)nullptr));
template<class CV, class CCV, class CRS>
static tag_not_valid result_check(...);
static_assert(!std::is_same<decltype(result_check<source_value_type, collection_value_type, result_selector_type>(0)), tag_not_valid>::value, "concat_map ResultSelector must be a function with the signature concat_map::value_type(concat_map::source_value_type, concat_map::collection_value_type)");
typedef rxu::decay_t<decltype((*(result_selector_type*)nullptr)(*(source_value_type*)nullptr, *(collection_value_type*)nullptr))> value_type;
};
template<class Observable, class CollectionSelector, class ResultSelector, class Coordination>
struct concat_map
: public operator_base<rxu::value_type_t<concat_traits<Observable, CollectionSelector, ResultSelector, Coordination>>>
{
typedef concat_map<Observable, CollectionSelector, ResultSelector, Coordination> this_type;
typedef concat_traits<Observable, CollectionSelector, ResultSelector, Coordination> traits;
typedef typename traits::source_type source_type;
typedef typename traits::collection_selector_type collection_selector_type;
typedef typename traits::result_selector_type result_selector_type;
typedef typename traits::source_value_type source_value_type;
typedef typename traits::collection_type collection_type;
typedef typename traits::collection_value_type collection_value_type;
typedef typename traits::coordination_type coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values
{
values(source_type o, collection_selector_type s, result_selector_type rs, coordination_type sf)
: source(std::move(o))
, selectCollection(std::move(s))
, selectResult(std::move(rs))
, coordination(std::move(sf))
{
}
source_type source;
collection_selector_type selectCollection;
result_selector_type selectResult;
coordination_type coordination;
private:
values& operator=(const values&) RXCPP_DELETE;
};
values initial;
concat_map(source_type o, collection_selector_type s, result_selector_type rs, coordination_type sf)
: initial(std::move(o), std::move(s), std::move(rs), std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct concat_map_state_type
: public std::enable_shared_from_this<concat_map_state_type>
, public values
{
concat_map_state_type(values i, coordinator_type coor, output_type oarg)
: values(std::move(i))
, sourceLifetime(composite_subscription::empty())
, collectionLifetime(composite_subscription::empty())
, coordinator(std::move(coor))
, out(std::move(oarg))
{
}
void subscribe_to(source_value_type st)
{
auto state = this->shared_from_this();
auto selectedCollection = on_exception(
[&](){return state->selectCollection(st);},
state->out);
if (selectedCollection.empty()) {
return;
}
collectionLifetime = composite_subscription();
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innercstoken = state->out.add(collectionLifetime);
collectionLifetime.add(make_subscription([state, innercstoken](){
state->out.remove(innercstoken);
}));
auto selectedSource = on_exception(
[&](){return state->coordinator.in(selectedCollection.get());},
state->out);
if (selectedSource.empty()) {
return;
}
// this subscribe does not share the source subscription
// so that when it is unsubscribed the source will continue
auto sinkInner = make_subscriber<collection_value_type>(
state->out,
collectionLifetime,
// on_next
[state, st](collection_value_type ct) {
auto selectedResult = state->selectResult(st, std::move(ct));
state->out.on_next(std::move(selectedResult));
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
//on_completed
[state](){
if (!state->selectedCollections.empty()) {
auto value = state->selectedCollections.front();
state->selectedCollections.pop_front();
state->collectionLifetime.unsubscribe();
state->subscribe_to(value);
} else if (!state->sourceLifetime.is_subscribed()) {
state->out.on_completed();
}
}
);
auto selectedSinkInner = on_exception(
[&](){return state->coordinator.out(sinkInner);},
state->out);
if (selectedSinkInner.empty()) {
return;
}
selectedSource->subscribe(std::move(selectedSinkInner.get()));
}
composite_subscription sourceLifetime;
composite_subscription collectionLifetime;
std::deque<source_value_type> selectedCollections;
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<concat_map_state_type>(initial, std::move(coordinator), std::move(scbr));
state->sourceLifetime = composite_subscription();
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
state->out.add(state->sourceLifetime);
auto source = on_exception(
[&](){return state->coordinator.in(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<source_value_type>(
state->out,
state->sourceLifetime,
// on_next
[state](source_value_type st) {
if (state->collectionLifetime.is_subscribed()) {
state->selectedCollections.push_back(st);
} else if (state->selectedCollections.empty()) {
state->subscribe_to(st);
}
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state]() {
if (!state->collectionLifetime.is_subscribed() && state->selectedCollections.empty()) {
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()));
}
private:
concat_map& operator=(const concat_map&) RXCPP_DELETE;
};
}
/*! @copydoc rx-concat_map.hpp
*/
template<class... AN>
auto concat_map(AN&&... an)
-> operator_factory<concat_map_tag, AN...> {
return operator_factory<concat_map_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! @copydoc rx-concat_map.hpp
*/
template<class... AN>
auto concat_transform(AN&&... an)
-> operator_factory<concat_map_tag, AN...> {
return operator_factory<concat_map_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<concat_map_tag>
{
template<class Observable, class CollectionSelector,
class CollectionSelectorType = rxu::decay_t<CollectionSelector>,
class SourceValue = rxu::value_type_t<Observable>,
class CollectionType = rxu::result_of_t<CollectionSelectorType(SourceValue)>,
class ResultSelectorType = rxu::detail::take_at<1>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, CollectionType>>,
class ConcatMap = rxo::detail::concat_map<rxu::decay_t<Observable>, rxu::decay_t<CollectionSelector>, ResultSelectorType, identity_one_worker>,
class CollectionValueType = rxu::value_type_t<CollectionType>,
class Value = rxu::result_of_t<ResultSelectorType(SourceValue, CollectionValueType)>,
class Result = observable<Value, ConcatMap>
>
static Result member(Observable&& o, CollectionSelector&& s) {
return Result(ConcatMap(std::forward<Observable>(o), std::forward<CollectionSelector>(s), ResultSelectorType(), identity_current_thread()));
}
template<class Observable, class CollectionSelector, class Coordination,
class CollectionSelectorType = rxu::decay_t<CollectionSelector>,
class SourceValue = rxu::value_type_t<Observable>,
class CollectionType = rxu::result_of_t<CollectionSelectorType(SourceValue)>,
class ResultSelectorType = rxu::detail::take_at<1>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, CollectionType>,
is_coordination<Coordination>>,
class ConcatMap = rxo::detail::concat_map<rxu::decay_t<Observable>, rxu::decay_t<CollectionSelector>, ResultSelectorType, rxu::decay_t<Coordination>>,
class CollectionValueType = rxu::value_type_t<CollectionType>,
class Value = rxu::result_of_t<ResultSelectorType(SourceValue, CollectionValueType)>,
class Result = observable<Value, ConcatMap>
>
static Result member(Observable&& o, CollectionSelector&& s, Coordination&& cn) {
return Result(ConcatMap(std::forward<Observable>(o), std::forward<CollectionSelector>(s), ResultSelectorType(), std::forward<Coordination>(cn)));
}
template<class Observable, class CollectionSelector, class ResultSelector,
class IsCoordination = is_coordination<ResultSelector>,
class CollectionSelectorType = rxu::decay_t<CollectionSelector>,
class SourceValue = rxu::value_type_t<Observable>,
class CollectionType = rxu::result_of_t<CollectionSelectorType(SourceValue)>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, CollectionType>,
rxu::negation<IsCoordination>>,
class ConcatMap = rxo::detail::concat_map<rxu::decay_t<Observable>, rxu::decay_t<CollectionSelector>, rxu::decay_t<ResultSelector>, identity_one_worker>,
class CollectionValueType = rxu::value_type_t<CollectionType>,
class ResultSelectorType = rxu::decay_t<ResultSelector>,
class Value = rxu::result_of_t<ResultSelectorType(SourceValue, CollectionValueType)>,
class Result = observable<Value, ConcatMap>
>
static Result member(Observable&& o, CollectionSelector&& s, ResultSelector&& rs) {
return Result(ConcatMap(std::forward<Observable>(o), std::forward<CollectionSelector>(s), std::forward<ResultSelector>(rs), identity_current_thread()));
}
template<class Observable, class CollectionSelector, class ResultSelector, class Coordination,
class CollectionSelectorType = rxu::decay_t<CollectionSelector>,
class SourceValue = rxu::value_type_t<Observable>,
class CollectionType = rxu::result_of_t<CollectionSelectorType(SourceValue)>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, CollectionType>,
is_coordination<Coordination>>,
class ConcatMap = rxo::detail::concat_map<rxu::decay_t<Observable>, rxu::decay_t<CollectionSelector>, rxu::decay_t<ResultSelector>, rxu::decay_t<Coordination>>,
class CollectionValueType = rxu::value_type_t<CollectionType>,
class ResultSelectorType = rxu::decay_t<ResultSelector>,
class Value = rxu::result_of_t<ResultSelectorType(SourceValue, CollectionValueType)>,
class Result = observable<Value, ConcatMap>
>
static Result member(Observable&& o, CollectionSelector&& s, ResultSelector&& rs, Coordination&& cn) {
return Result(ConcatMap(std::forward<Observable>(o), std::forward<CollectionSelector>(s), std::forward<ResultSelector>(rs), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::concat_map_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "concat_map takes (CollectionSelector, optional ResultSelector, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,90 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-connect_forever.hpp
\brief takes a connectable_observable source and calls connect during the construction of the expression.
This means that the source starts running without any subscribers and continues running after all subscriptions have been unsubscribed.
\return An observable that emitting the items from its source.
*/
#if !defined(RXCPP_OPERATORS_RX_CONNECT_FOREVER_HPP)
#define RXCPP_OPERATORS_RX_CONNECT_FOREVER_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct connect_forever_invalid_arguments {};
template<class... AN>
struct connect_forever_invalid : public rxo::operator_base<connect_forever_invalid_arguments<AN...>> {
using type = observable<connect_forever_invalid_arguments<AN...>, connect_forever_invalid<AN...>>;
};
template<class... AN>
using connect_forever_invalid_t = typename connect_forever_invalid<AN...>::type;
template<class T, class ConnectableObservable>
struct connect_forever : public operator_base<T>
{
typedef rxu::decay_t<ConnectableObservable> source_type;
source_type source;
explicit connect_forever(source_type o)
: source(std::move(o))
{
source.connect();
}
template<class Subscriber>
void on_subscribe(Subscriber&& o) const {
source.subscribe(std::forward<Subscriber>(o));
}
};
}
/*! @copydoc rx-connect_forever.hpp
*/
template<class... AN>
auto connect_forever(AN&&... an)
-> operator_factory<connect_forever_tag, AN...> {
return operator_factory<connect_forever_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<connect_forever_tag>
{
template<class ConnectableObservable,
class Enabled = rxu::enable_if_all_true_type_t<
is_connectable_observable<ConnectableObservable>>,
class SourceValue = rxu::value_type_t<ConnectableObservable>,
class ConnectForever = rxo::detail::connect_forever<SourceValue, rxu::decay_t<ConnectableObservable>>,
class Value = rxu::value_type_t<ConnectForever>,
class Result = observable<Value, ConnectForever>
>
static Result member(ConnectableObservable&& o) {
return Result(ConnectForever(std::forward<ConnectableObservable>(o)));
}
template<class... AN>
static operators::detail::connect_forever_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "connect_forever takes no arguments");
}
};
}
#endif

View File

@ -0,0 +1,268 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-debounce.hpp
\brief Return an observable that emits an item if a particular timespan has passed without emitting another item from the source observable.
\tparam Duration the type of the time interval
\tparam Coordination the type of the scheduler
\param period the period of time to suppress any emitted items
\param coordination the scheduler to manage timeout for each event
\return Observable that emits an item if a particular timespan has passed without emitting another item from the source observable.
\sample
\snippet debounce.cpp debounce sample
\snippet output.txt debounce sample
*/
#if !defined(RXCPP_OPERATORS_RX_DEBOUNCE_HPP)
#define RXCPP_OPERATORS_RX_DEBOUNCE_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct debounce_invalid_arguments {};
template<class... AN>
struct debounce_invalid : public rxo::operator_base<debounce_invalid_arguments<AN...>> {
using type = observable<debounce_invalid_arguments<AN...>, debounce_invalid<AN...>>;
};
template<class... AN>
using debounce_invalid_t = typename debounce_invalid<AN...>::type;
template<class T, class Duration, class Coordination>
struct debounce
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef rxu::decay_t<Duration> duration_type;
struct debounce_values
{
debounce_values(duration_type p, coordination_type c)
: period(p)
, coordination(c)
{
}
duration_type period;
coordination_type coordination;
};
debounce_values initial;
debounce(duration_type period, coordination_type coordination)
: initial(period, coordination)
{
}
template<class Subscriber>
struct debounce_observer
{
typedef debounce_observer<Subscriber> this_type;
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
struct debounce_subscriber_values : public debounce_values
{
debounce_subscriber_values(composite_subscription cs, dest_type d, debounce_values v, coordinator_type c)
: debounce_values(v)
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
, index(0)
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
mutable std::size_t index;
mutable rxu::maybe<value_type> value;
};
typedef std::shared_ptr<debounce_subscriber_values> state_type;
state_type state;
debounce_observer(composite_subscription cs, dest_type d, debounce_values v, coordinator_type c)
: state(std::make_shared<debounce_subscriber_values>(debounce_subscriber_values(std::move(cs), std::move(d), v, std::move(c))))
{
auto localState = state;
auto disposer = [=](const rxsc::schedulable&){
localState->cs.unsubscribe();
localState->dest.unsubscribe();
localState->worker.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){ return localState->coordinator.act(disposer); },
localState->dest);
if (selectedDisposer.empty()) {
return;
}
localState->dest.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
localState->cs.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
}
static std::function<void(const rxsc::schedulable&)> produce_item(std::size_t id, state_type state) {
auto produce = [id, state](const rxsc::schedulable&) {
if(id != state->index)
return;
state->dest.on_next(*state->value);
state->value.reset();
};
auto selectedProduce = on_exception(
[&](){ return state->coordinator.act(produce); },
state->dest);
if (selectedProduce.empty()) {
return std::function<void(const rxsc::schedulable&)>();
}
return std::function<void(const rxsc::schedulable&)>(selectedProduce.get());
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable&) {
auto new_id = ++localState->index;
auto produce_time = localState->worker.now() + localState->period;
localState->value.reset(v);
localState->worker.schedule(produce_time, produce_item(new_id, localState));
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&) {
localState->dest.on_error(e);
localState->value.reset();
};
auto selectedWork = on_exception(
[&](){ return localState->coordinator.act(work); },
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&) {
if(!localState->value.empty()) {
localState->dest.on_next(*localState->value);
}
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){ return localState->coordinator.act(work); },
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static subscriber<T, observer_type> make(dest_type d, debounce_values v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator();
return make_subscriber<T>(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(debounce_observer<Subscriber>::make(std::move(dest), initial)) {
return debounce_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-debounce.hpp
*/
template<class... AN>
auto debounce(AN&&... an)
-> operator_factory<debounce_tag, AN...> {
return operator_factory<debounce_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<debounce_tag>
{
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class Debounce = rxo::detail::debounce<SourceValue, rxu::decay_t<Duration>, identity_one_worker>>
static auto member(Observable&& o, Duration&& d)
-> decltype(o.template lift<SourceValue>(Debounce(std::forward<Duration>(d), identity_current_thread()))) {
return o.template lift<SourceValue>(Debounce(std::forward<Duration>(d), identity_current_thread()));
}
template<class Observable, class Coordination, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class Debounce = rxo::detail::debounce<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Coordination&& cn, Duration&& d)
-> decltype(o.template lift<SourceValue>(Debounce(std::forward<Duration>(d), std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(Debounce(std::forward<Duration>(d), std::forward<Coordination>(cn)));
}
template<class Observable, class Coordination, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class Debounce = rxo::detail::debounce<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Duration&& d, Coordination&& cn)
-> decltype(o.template lift<SourceValue>(Debounce(std::forward<Duration>(d), std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(Debounce(std::forward<Duration>(d), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::debounce_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "debounce takes (optional Coordination, required Duration) or (required Duration, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,237 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-delay.hpp
\brief Return an observable that emits each item emitted by the source observable after the specified delay.
\tparam Duration the type of time interval
\tparam Coordination the type of the scheduler
\param period the period of time each item is delayed
\param coordination the scheduler for the delays
\return Observable that emits each item emitted by the source observable after the specified delay.
\sample
\snippet delay.cpp delay period+coordination sample
\snippet output.txt delay period+coordination sample
*/
#if !defined(RXCPP_OPERATORS_RX_DELAY_HPP)
#define RXCPP_OPERATORS_RX_DELAY_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct delay_invalid_arguments {};
template<class... AN>
struct delay_invalid : public rxo::operator_base<delay_invalid_arguments<AN...>> {
using type = observable<delay_invalid_arguments<AN...>, delay_invalid<AN...>>;
};
template<class... AN>
using delay_invalid_t = typename delay_invalid<AN...>::type;
template<class T, class Duration, class Coordination>
struct delay
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef rxu::decay_t<Duration> duration_type;
struct delay_values
{
delay_values(duration_type p, coordination_type c)
: period(p)
, coordination(c)
{
}
duration_type period;
coordination_type coordination;
};
delay_values initial;
delay(duration_type period, coordination_type coordination)
: initial(period, coordination)
{
}
template<class Subscriber>
struct delay_observer
{
typedef delay_observer<Subscriber> this_type;
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
struct delay_subscriber_values : public delay_values
{
delay_subscriber_values(composite_subscription cs, dest_type d, delay_values v, coordinator_type c)
: delay_values(v)
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
, expected(worker.now())
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
rxsc::scheduler::clock_type::time_point expected;
};
std::shared_ptr<delay_subscriber_values> state;
delay_observer(composite_subscription cs, dest_type d, delay_values v, coordinator_type c)
: state(std::make_shared<delay_subscriber_values>(delay_subscriber_values(std::move(cs), std::move(d), v, std::move(c))))
{
auto localState = state;
auto disposer = [=](const rxsc::schedulable&){
localState->cs.unsubscribe();
localState->dest.unsubscribe();
localState->worker.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){return localState->coordinator.act(disposer);},
localState->dest);
if (selectedDisposer.empty()) {
return;
}
localState->dest.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
localState->cs.add([=](){
localState->worker.schedule(localState->worker.now() + localState->period, selectedDisposer.get());
});
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable&){
localState->dest.on_next(v);
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(localState->worker.now() + localState->period, selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&){
localState->dest.on_error(e);
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&){
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(localState->worker.now() + localState->period, selectedWork.get());
}
static subscriber<T, observer_type> make(dest_type d, delay_values v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator();
return make_subscriber<T>(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(delay_observer<Subscriber>::make(std::move(dest), initial)) {
return delay_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-delay.hpp
*/
template<class... AN>
auto delay(AN&&... an)
-> operator_factory<delay_tag, AN...> {
return operator_factory<delay_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<delay_tag>
{
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class delay = rxo::detail::delay<SourceValue, rxu::decay_t<Duration>, identity_one_worker>>
static auto member(Observable&& o, Duration&& d)
-> decltype(o.template lift<SourceValue>(delay(std::forward<Duration>(d), identity_current_thread()))) {
return o.template lift<SourceValue>(delay(std::forward<Duration>(d), identity_current_thread()));
}
template<class Observable, class Coordination, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class delay = rxo::detail::delay<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Coordination&& cn, Duration&& d)
-> decltype(o.template lift<SourceValue>(delay(std::forward<Duration>(d), std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(delay(std::forward<Duration>(d), std::forward<Coordination>(cn)));
}
template<class Observable, class Coordination, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class delay = rxo::detail::delay<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Duration&& d, Coordination&& cn)
-> decltype(o.template lift<SourceValue>(delay(std::forward<Duration>(d), std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(delay(std::forward<Duration>(d), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::delay_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "delay takes (optional Coordination, required Duration) or (required Duration, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,119 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-distinct.hpp
\brief For each item from this observable, filter out repeated values and emit only items that have not already been emitted.
\return Observable that emits those items from the source observable that are distinct.
\note istinct keeps an unordered_set<T> of past values. Due to an issue in multiple implementations of std::hash<T>, rxcpp maintains a whitelist of hashable types. new types can be added by specializing rxcpp::filtered_hash<T>
\sample
\snippet distinct.cpp distinct sample
\snippet output.txt distinct sample
*/
#if !defined(RXCPP_OPERATORS_RX_DISTINCT_HPP)
#define RXCPP_OPERATORS_RX_DISTINCT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct distinct_invalid_arguments {};
template<class... AN>
struct distinct_invalid : public rxo::operator_base<distinct_invalid_arguments<AN...>> {
using type = observable<distinct_invalid_arguments<AN...>, distinct_invalid<AN...>>;
};
template<class... AN>
using distinct_invalid_t = typename distinct_invalid<AN...>::type;
template<class T>
struct distinct
{
typedef rxu::decay_t<T> source_value_type;
template<class Subscriber>
struct distinct_observer
{
typedef distinct_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
mutable std::unordered_set<source_value_type, rxcpp::filtered_hash<source_value_type>> remembered;
distinct_observer(dest_type d)
: dest(d)
{
}
void on_next(source_value_type v) const {
if (remembered.empty() || remembered.count(v) == 0) {
remembered.insert(v);
dest.on_next(v);
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer<value_type, this_type>> make(dest_type d) {
return make_subscriber<value_type>(d, this_type(d));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(distinct_observer<Subscriber>::make(std::move(dest))) {
return distinct_observer<Subscriber>::make(std::move(dest));
}
};
}
/*! @copydoc rx-distinct.hpp
*/
template<class... AN>
auto distinct(AN&&... an)
-> operator_factory<distinct_tag, AN...> {
return operator_factory<distinct_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<distinct_tag>
{
template<class Observable,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_hashable<SourceValue>>,
class Distinct = rxo::detail::distinct<SourceValue>>
static auto member(Observable&& o)
-> decltype(o.template lift<SourceValue>(Distinct())) {
return o.template lift<SourceValue>(Distinct());
}
template<class... AN>
static operators::detail::distinct_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "distinct takes no arguments");
}
};
}
#endif

View File

@ -0,0 +1,142 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-distinct_until_changed.hpp
\brief For each item from this observable, filter out consequentially repeated values and emit only changes from the new observable that is returned.
\tparam BinaryPredicate (optional) the type of the value comparing function. The signature should be equivalent to the following: bool pred(const T1& a, const T2& b);
\param pred (optional) the function that implements comparison of two values.
\return Observable that emits those items from the source observable that are distinct from their immediate predecessors.
\sample
\snippet distinct_until_changed.cpp distinct_until_changed sample
\snippet output.txt distinct_until_changed sample
*/
#if !defined(RXCPP_OPERATORS_RX_DISTINCT_UNTIL_CHANGED_HPP)
#define RXCPP_OPERATORS_RX_DISTINCT_UNTIL_CHANGED_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct distinct_until_changed_invalid_arguments {};
template<class... AN>
struct distinct_until_changed_invalid : public rxo::operator_base<distinct_until_changed_invalid_arguments<AN...>> {
using type = observable<distinct_until_changed_invalid_arguments<AN...>, distinct_until_changed_invalid<AN...>>;
};
template<class... AN>
using distinct_until_changed_invalid_t = typename distinct_until_changed_invalid<AN...>::type;
template<class T, class BinaryPredicate>
struct distinct_until_changed
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<BinaryPredicate> predicate_type;
predicate_type pred;
distinct_until_changed(predicate_type p)
: pred(std::move(p))
{
}
template<class Subscriber>
struct distinct_until_changed_observer
{
typedef distinct_until_changed_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
predicate_type pred;
mutable rxu::detail::maybe<source_value_type> remembered;
distinct_until_changed_observer(dest_type d, predicate_type pred)
: dest(std::move(d))
, pred(std::move(pred))
{
}
void on_next(source_value_type v) const {
if (remembered.empty() || !pred(v, remembered.get())) {
remembered.reset(v);
dest.on_next(v);
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer_type> make(dest_type d, predicate_type p) {
return make_subscriber<value_type>(d, this_type(d, std::move(p)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(distinct_until_changed_observer<Subscriber>::make(std::move(dest), pred)) {
return distinct_until_changed_observer<Subscriber>::make(std::move(dest), pred);
}
};
}
/*! @copydoc rx-distinct_until_changed.hpp
*/
template<class... AN>
auto distinct_until_changed(AN&&... an)
-> operator_factory<distinct_until_changed_tag, AN...> {
return operator_factory<distinct_until_changed_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<distinct_until_changed_tag>
{
template<class Observable,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class DistinctUntilChanged = rxo::detail::distinct_until_changed<SourceValue, rxu::equal_to<>>>
static auto member(Observable&& o)
-> decltype(o.template lift<SourceValue>(DistinctUntilChanged(rxu::equal_to<>()))) {
return o.template lift<SourceValue>(DistinctUntilChanged(rxu::equal_to<>()));
}
template<class Observable,
class BinaryPredicate,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class DistinctUntilChanged = rxo::detail::distinct_until_changed<SourceValue, BinaryPredicate>>
static auto member(Observable&& o, BinaryPredicate&& pred)
-> decltype(o.template lift<SourceValue>(DistinctUntilChanged(std::forward<BinaryPredicate>(pred)))) {
return o.template lift<SourceValue>(DistinctUntilChanged(std::forward<BinaryPredicate>(pred)));
}
template<class... AN>
static operators::detail::distinct_until_changed_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "distinct_until_changed takes (optional BinaryPredicate)");
}
};
}
#endif

View File

@ -0,0 +1,137 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-element_at.hpp
\brief Pulls an item located at a specified index location in the sequence of items and emits that item as its own sole emission.
\param index the index of the element to return.
\return An observable that emit an item located at a specified index location.
\sample
\snippet element_at.cpp element_at sample
\snippet output.txt element_at sample
*/
#if !defined(RXCPP_OPERATORS_RX_ELEMENT_AT_HPP)
#define RXCPP_OPERATORS_RX_ELEMENT_AT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct element_at_invalid_arguments {};
template<class... AN>
struct element_at_invalid : public rxo::operator_base<element_at_invalid_arguments<AN...>> {
using type = observable<element_at_invalid_arguments<AN...>, element_at_invalid<AN...>>;
};
template<class... AN>
using element_at_invalid_t = typename element_at_invalid<AN...>::type;
template<class T>
struct element_at {
typedef rxu::decay_t<T> source_value_type;
struct element_at_values {
element_at_values(int i)
: index(i)
{
}
int index;
};
element_at_values initial;
element_at(int i)
: initial(i)
{
}
template<class Subscriber>
struct element_at_observer : public element_at_values
{
typedef element_at_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
mutable int current;
element_at_observer(dest_type d, element_at_values v)
: element_at_values(v),
dest(d),
current(0)
{
}
void on_next(source_value_type v) const {
if (current++ == this->index) {
dest.on_next(v);
dest.on_completed();
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
if(current <= this->index) {
dest.on_error(rxu::make_error_ptr(std::range_error("index is out of bounds")));
}
}
static subscriber<value_type, observer_type> make(dest_type d, element_at_values v) {
return make_subscriber<value_type>(d, this_type(d, v));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(element_at_observer<Subscriber>::make(std::move(dest), initial)) {
return element_at_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-element_at.hpp
*/
template<class... AN>
auto element_at(AN&&... an)
-> operator_factory<element_at_tag, AN...> {
return operator_factory<element_at_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<element_at_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>
>,
class SourceValue = rxu::value_type_t<Observable>,
class element_at = rxo::detail::element_at<SourceValue>>
static auto member(Observable&& o, int index)
-> decltype(o.template lift<SourceValue>(element_at(index))) {
return o.template lift<SourceValue>(element_at(index));
}
template<class... AN>
static operators::detail::element_at_invalid_t<AN...> member(const AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "element_at takes (required int)");
}
};
}
#endif

View File

@ -0,0 +1,134 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-filter.hpp
\brief For each item from this observable use Predicate to select which items to emit from the new observable that is returned.
\tparam Predicate the type of the filter function
\param p the filter function
\return Observable that emits only those items emitted by the source observable that the filter evaluates as true.
\sample
\snippet filter.cpp filter sample
\snippet output.txt filter sample
*/
#if !defined(RXCPP_OPERATORS_RX_FILTER_HPP)
#define RXCPP_OPERATORS_RX_FILTER_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct filter_invalid_arguments {};
template<class... AN>
struct filter_invalid : public rxo::operator_base<filter_invalid_arguments<AN...>> {
using type = observable<filter_invalid_arguments<AN...>, filter_invalid<AN...>>;
};
template<class... AN>
using filter_invalid_t = typename filter_invalid<AN...>::type;
template<class T, class Predicate>
struct filter
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Predicate> test_type;
test_type test;
filter(test_type t)
: test(std::move(t))
{
}
template<class Subscriber>
struct filter_observer
{
typedef filter_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
mutable test_type test;
filter_observer(dest_type d, test_type t)
: dest(std::move(d))
, test(std::move(t))
{
}
template <class Value>
void on_next(Value&& v) const {
auto filtered = on_exception([&](){
return !this->test(rxu::as_const(v));
},
dest);
if (filtered.empty()) {
return;
}
if (!filtered.get()) {
dest.on_next(std::forward<Value>(v));
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer_type> make(dest_type d, test_type t) {
return make_subscriber<value_type>(d, this_type(d, std::move(t)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(filter_observer<Subscriber>::make(std::move(dest), test)) {
return filter_observer<Subscriber>::make(std::move(dest), test);
}
};
}
/*! @copydoc rx-filter.hpp
*/
template<class... AN>
auto filter(AN&&... an)
-> operator_factory<filter_tag, AN...> {
return operator_factory<filter_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<filter_tag>
{
template<class Observable, class Predicate,
class SourceValue = rxu::value_type_t<Observable>,
class Filter = rxo::detail::filter<SourceValue, rxu::decay_t<Predicate>>>
static auto member(Observable&& o, Predicate&& p)
-> decltype(o.template lift<SourceValue>(Filter(std::forward<Predicate>(p)))) {
return o.template lift<SourceValue>(Filter(std::forward<Predicate>(p)));
}
template<class... AN>
static operators::detail::filter_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "filter takes (Predicate)");
}
};
}
#endif

View File

@ -0,0 +1,134 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-finally.hpp
\brief Add a new action at the end of the new observable that is returned.
\tparam LastCall the type of the action function
\param lc the action function
\return Observable that emits the same items as the source observable, then invokes the given action.
\sample
\snippet finally.cpp finally sample
\snippet output.txt finally sample
If the source observable generates an error, the final action is still being called:
\snippet finally.cpp error finally sample
\snippet output.txt error finally sample
*/
#if !defined(RXCPP_OPERATORS_RX_FINALLY_HPP)
#define RXCPP_OPERATORS_RX_FINALLY_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct finally_invalid_arguments {};
template<class... AN>
struct finally_invalid : public rxo::operator_base<finally_invalid_arguments<AN...>> {
using type = observable<finally_invalid_arguments<AN...>, finally_invalid<AN...>>;
};
template<class... AN>
using finally_invalid_t = typename finally_invalid<AN...>::type;
template<class T, class LastCall>
struct finally
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<LastCall> last_call_type;
last_call_type last_call;
finally(last_call_type lc)
: last_call(std::move(lc))
{
}
template<class Subscriber>
struct finally_observer
{
typedef finally_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
finally_observer(dest_type d)
: dest(std::move(d))
{
}
void on_next(source_value_type v) const {
dest.on_next(v);
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer_type> make(dest_type d, const last_call_type& lc) {
auto dl = d.get_subscription();
composite_subscription cs;
dl.add(cs);
cs.add([=](){
dl.unsubscribe();
lc();
});
return make_subscriber<value_type>(cs, this_type(d));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(finally_observer<Subscriber>::make(std::move(dest), last_call)) {
return finally_observer<Subscriber>::make(std::move(dest), last_call);
}
};
}
/*! @copydoc rx-finally.hpp
*/
template<class... AN>
auto finally(AN&&... an)
-> operator_factory<finally_tag, AN...> {
return operator_factory<finally_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<finally_tag>
{
template<class Observable, class LastCall,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class Finally = rxo::detail::finally<SourceValue, rxu::decay_t<LastCall>>>
static auto member(Observable&& o, LastCall&& lc)
-> decltype(o.template lift<SourceValue>(Finally(std::forward<LastCall>(lc)))) {
return o.template lift<SourceValue>(Finally(std::forward<LastCall>(lc)));
}
template<class... AN>
static operators::detail::finally_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "finally takes (LastCall)");
}
};
}
#endif

View File

@ -0,0 +1,340 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-flat_map.hpp
\brief For each item from this observable use the CollectionSelector to produce an observable and subscribe to that observable.
For each item from all of the produced observables use the ResultSelector to produce a value to emit from the new observable that is returned.
\tparam CollectionSelector the type of the observable producing function. CollectionSelector must be a function with the signature observable(flat_map::source_value_type)
\tparam ResultSelector the type of the aggregation function (optional). ResultSelector must be a function with the signature flat_map::value_type(flat_map::source_value_type, flat_map::collection_value_type).
\tparam Coordination the type of the scheduler (optional).
\param s a function that returns an observable for each item emitted by the source observable.
\param rs a function that combines one item emitted by each of the source and collection observables and returns an item to be emitted by the resulting observable (optional).
\param cn the scheduler to synchronize sources from different contexts (optional).
\return Observable that emits the results of applying a function to a pair of values emitted by the source observable and the collection observable.
Observables, produced by the CollectionSelector, are merged. There is another operator rxcpp::observable<T,SourceType>::flat_map that works similar but concatenates the observables.
\sample
\snippet flat_map.cpp flat_map sample
\snippet output.txt flat_map sample
\sample
\snippet flat_map.cpp threaded flat_map sample
\snippet output.txt threaded flat_map sample
*/
#if !defined(RXCPP_OPERATORS_RX_FLATMAP_HPP)
#define RXCPP_OPERATORS_RX_FLATMAP_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct flat_map_invalid_arguments {};
template<class... AN>
struct flat_map_invalid : public rxo::operator_base<flat_map_invalid_arguments<AN...>> {
using type = observable<flat_map_invalid_arguments<AN...>, flat_map_invalid<AN...>>;
};
template<class... AN>
using flat_map_invalid_t = typename flat_map_invalid<AN...>::type;
template<class Observable, class CollectionSelector, class ResultSelector, class Coordination>
struct flat_map_traits {
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<CollectionSelector> collection_selector_type;
typedef rxu::decay_t<ResultSelector> result_selector_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename source_type::value_type source_value_type;
struct tag_not_valid {};
template<class CV, class CCS>
static auto collection_check(int) -> decltype((*(CCS*)nullptr)(*(CV*)nullptr));
template<class CV, class CCS>
static tag_not_valid collection_check(...);
static_assert(!std::is_same<decltype(collection_check<source_value_type, collection_selector_type>(0)), tag_not_valid>::value, "flat_map CollectionSelector must be a function with the signature observable(flat_map::source_value_type)");
typedef rxu::decay_t<decltype((*(collection_selector_type*)nullptr)((*(source_value_type*)nullptr)))> collection_type;
static_assert(is_observable<collection_type>::value, "flat_map CollectionSelector must return an observable");
typedef typename collection_type::value_type collection_value_type;
template<class CV, class CCV, class CRS>
static auto result_check(int) -> decltype((*(CRS*)nullptr)(*(CV*)nullptr, *(CCV*)nullptr));
template<class CV, class CCV, class CRS>
static tag_not_valid result_check(...);
static_assert(!std::is_same<decltype(result_check<source_value_type, collection_value_type, result_selector_type>(0)), tag_not_valid>::value, "flat_map ResultSelector must be a function with the signature flat_map::value_type(flat_map::source_value_type, flat_map::collection_value_type)");
typedef rxu::decay_t<decltype((*(result_selector_type*)nullptr)(*(source_value_type*)nullptr, *(collection_value_type*)nullptr))> value_type;
};
template<class Observable, class CollectionSelector, class ResultSelector, class Coordination>
struct flat_map
: public operator_base<rxu::value_type_t<flat_map_traits<Observable, CollectionSelector, ResultSelector, Coordination>>>
{
typedef flat_map<Observable, CollectionSelector, ResultSelector, Coordination> this_type;
typedef flat_map_traits<Observable, CollectionSelector, ResultSelector, Coordination> traits;
typedef typename traits::source_type source_type;
typedef typename traits::collection_selector_type collection_selector_type;
typedef typename traits::result_selector_type result_selector_type;
typedef typename traits::source_value_type source_value_type;
typedef typename traits::collection_type collection_type;
typedef typename traits::collection_value_type collection_value_type;
typedef typename traits::coordination_type coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values
{
values(source_type o, collection_selector_type s, result_selector_type rs, coordination_type sf)
: source(std::move(o))
, selectCollection(std::move(s))
, selectResult(std::move(rs))
, coordination(std::move(sf))
{
}
source_type source;
collection_selector_type selectCollection;
result_selector_type selectResult;
coordination_type coordination;
};
values initial;
flat_map(source_type o, collection_selector_type s, result_selector_type rs, coordination_type sf)
: initial(std::move(o), std::move(s), std::move(rs), std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct state_type
: public std::enable_shared_from_this<state_type>
, public values
{
state_type(values i, coordinator_type coor, output_type oarg)
: values(std::move(i))
, pendingCompletions(0)
, coordinator(std::move(coor))
, out(std::move(oarg))
{
}
// on_completed on the output must wait until all the
// subscriptions have received on_completed
int pendingCompletions;
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<state_type>(initial, std::move(coordinator), std::move(scbr));
composite_subscription outercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
state->out.add(outercs);
auto source = on_exception(
[&](){return state->coordinator.in(state->source);},
state->out);
if (source.empty()) {
return;
}
++state->pendingCompletions;
// 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<source_value_type>(
state->out,
outercs,
// on_next
[state](source_value_type st) {
composite_subscription innercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innercstoken = state->out.add(innercs);
innercs.add(make_subscription([state, innercstoken](){
state->out.remove(innercstoken);
}));
auto selectedCollection = state->selectCollection(st);
auto selectedSource = state->coordinator.in(selectedCollection);
++state->pendingCompletions;
// this subscribe does not share the source subscription
// so that when it is unsubscribed the source will continue
auto sinkInner = make_subscriber<collection_value_type>(
state->out,
innercs,
// on_next
[state, st](collection_value_type ct) {
auto selectedResult = state->selectResult(st, std::move(ct));
state->out.on_next(std::move(selectedResult));
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
//on_completed
[state](){
if (--state->pendingCompletions == 0) {
state->out.on_completed();
}
}
);
auto selectedSinkInner = state->coordinator.out(sinkInner);
selectedSource.subscribe(std::move(selectedSinkInner));
},
// on_error
[state](rxu::error_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()));
}
};
}
/*! @copydoc rx-flat_map.hpp
*/
template<class... AN>
auto flat_map(AN&&... an)
-> operator_factory<flat_map_tag, AN...> {
return operator_factory<flat_map_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! @copydoc rx-flat_map.hpp
*/
template<class... AN>
auto merge_transform(AN&&... an)
-> operator_factory<flat_map_tag, AN...> {
return operator_factory<flat_map_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<flat_map_tag>
{
template<class Observable, class CollectionSelector,
class CollectionSelectorType = rxu::decay_t<CollectionSelector>,
class SourceValue = rxu::value_type_t<Observable>,
class CollectionType = rxu::result_of_t<CollectionSelectorType(SourceValue)>,
class ResultSelectorType = rxu::detail::take_at<1>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, CollectionType>>,
class FlatMap = rxo::detail::flat_map<rxu::decay_t<Observable>, rxu::decay_t<CollectionSelector>, ResultSelectorType, identity_one_worker>,
class CollectionValueType = rxu::value_type_t<CollectionType>,
class Value = rxu::result_of_t<ResultSelectorType(SourceValue, CollectionValueType)>,
class Result = observable<Value, FlatMap>
>
static Result member(Observable&& o, CollectionSelector&& s) {
return Result(FlatMap(std::forward<Observable>(o), std::forward<CollectionSelector>(s), ResultSelectorType(), identity_current_thread()));
}
template<class Observable, class CollectionSelector, class Coordination,
class CollectionSelectorType = rxu::decay_t<CollectionSelector>,
class SourceValue = rxu::value_type_t<Observable>,
class CollectionType = rxu::result_of_t<CollectionSelectorType(SourceValue)>,
class ResultSelectorType = rxu::detail::take_at<1>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, CollectionType>,
is_coordination<Coordination>>,
class FlatMap = rxo::detail::flat_map<rxu::decay_t<Observable>, rxu::decay_t<CollectionSelector>, ResultSelectorType, rxu::decay_t<Coordination>>,
class CollectionValueType = rxu::value_type_t<CollectionType>,
class Value = rxu::result_of_t<ResultSelectorType(SourceValue, CollectionValueType)>,
class Result = observable<Value, FlatMap>
>
static Result member(Observable&& o, CollectionSelector&& s, Coordination&& cn) {
return Result(FlatMap(std::forward<Observable>(o), std::forward<CollectionSelector>(s), ResultSelectorType(), std::forward<Coordination>(cn)));
}
template<class Observable, class CollectionSelector, class ResultSelector,
class IsCoordination = is_coordination<ResultSelector>,
class CollectionSelectorType = rxu::decay_t<CollectionSelector>,
class SourceValue = rxu::value_type_t<Observable>,
class CollectionType = rxu::result_of_t<CollectionSelectorType(SourceValue)>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, CollectionType>,
rxu::negation<IsCoordination>>,
class FlatMap = rxo::detail::flat_map<rxu::decay_t<Observable>, rxu::decay_t<CollectionSelector>, rxu::decay_t<ResultSelector>, identity_one_worker>,
class CollectionValueType = rxu::value_type_t<CollectionType>,
class ResultSelectorType = rxu::decay_t<ResultSelector>,
class Value = rxu::result_of_t<ResultSelectorType(SourceValue, CollectionValueType)>,
class Result = observable<Value, FlatMap>
>
static Result member(Observable&& o, CollectionSelector&& s, ResultSelector&& rs) {
return Result(FlatMap(std::forward<Observable>(o), std::forward<CollectionSelector>(s), std::forward<ResultSelector>(rs), identity_current_thread()));
}
template<class Observable, class CollectionSelector, class ResultSelector, class Coordination,
class CollectionSelectorType = rxu::decay_t<CollectionSelector>,
class SourceValue = rxu::value_type_t<Observable>,
class CollectionType = rxu::result_of_t<CollectionSelectorType(SourceValue)>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, CollectionType>,
is_coordination<Coordination>>,
class FlatMap = rxo::detail::flat_map<rxu::decay_t<Observable>, rxu::decay_t<CollectionSelector>, rxu::decay_t<ResultSelector>, rxu::decay_t<Coordination>>,
class CollectionValueType = rxu::value_type_t<CollectionType>,
class ResultSelectorType = rxu::decay_t<ResultSelector>,
class Value = rxu::result_of_t<ResultSelectorType(SourceValue, CollectionValueType)>,
class Result = observable<Value, FlatMap>
>
static Result member(Observable&& o, CollectionSelector&& s, ResultSelector&& rs, Coordination&& cn) {
return Result(FlatMap(std::forward<Observable>(o), std::forward<CollectionSelector>(s), std::forward<ResultSelector>(rs), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::flat_map_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "flat_map takes (CollectionSelector, optional ResultSelector, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,402 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-group_by.hpp
\brief Return an observable that emits grouped_observables, each of which corresponds to a unique key value and each of which emits those items from the source observable that share that key value.
\tparam KeySelector the type of the key extracting function
\tparam MarbleSelector the type of the element extracting function
\tparam BinaryPredicate the type of the key comparing function
\tparam DurationSelector the type of the duration observable function
\param ks a function that extracts the key for each item (optional)
\param ms a function that extracts the return element for each item (optional)
\param p a function that implements comparison of two keys (optional)
\return Observable that emits values of grouped_observable type, each of which corresponds to a unique key value and each of which emits those items from the source observable that share that key value.
\sample
\snippet group_by.cpp group_by full intro
\snippet group_by.cpp group_by full sample
\snippet output.txt group_by full sample
\sample
\snippet group_by.cpp group_by sample
\snippet output.txt group_by sample
*/
#if !defined(RXCPP_OPERATORS_RX_GROUP_BY_HPP)
#define RXCPP_OPERATORS_RX_GROUP_BY_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct group_by_invalid_arguments {};
template<class... AN>
struct group_by_invalid : public rxo::operator_base<group_by_invalid_arguments<AN...>> {
using type = observable<group_by_invalid_arguments<AN...>, group_by_invalid<AN...>>;
};
template<class... AN>
using group_by_invalid_t = typename group_by_invalid<AN...>::type;
template<class T, class Selector>
struct is_group_by_selector_for {
typedef rxu::decay_t<Selector> selector_type;
typedef T source_value_type;
struct tag_not_valid {};
template<class CV, class CS>
static auto check(int) -> decltype((*(CS*)nullptr)(*(CV*)nullptr));
template<class CV, class CS>
static tag_not_valid check(...);
typedef decltype(check<source_value_type, selector_type>(0)) type;
static const bool value = !std::is_same<type, tag_not_valid>::value;
};
template<class T, class Observable, class KeySelector, class MarbleSelector, class BinaryPredicate, class DurationSelector>
struct group_by_traits
{
typedef T source_value_type;
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<KeySelector> key_selector_type;
typedef rxu::decay_t<MarbleSelector> marble_selector_type;
typedef rxu::decay_t<BinaryPredicate> predicate_type;
typedef rxu::decay_t<DurationSelector> duration_selector_type;
static_assert(is_group_by_selector_for<source_value_type, key_selector_type>::value, "group_by KeySelector must be a function with the signature key_type(source_value_type)");
typedef typename is_group_by_selector_for<source_value_type, key_selector_type>::type key_type;
static_assert(is_group_by_selector_for<source_value_type, marble_selector_type>::value, "group_by MarbleSelector must be a function with the signature marble_type(source_value_type)");
typedef typename is_group_by_selector_for<source_value_type, marble_selector_type>::type marble_type;
typedef rxsub::subject<marble_type> subject_type;
typedef std::map<key_type, typename subject_type::subscriber_type, predicate_type> key_subscriber_map_type;
typedef grouped_observable<key_type, marble_type> grouped_observable_type;
};
template<class T, class Observable, class KeySelector, class MarbleSelector, class BinaryPredicate, class DurationSelector>
struct group_by
{
typedef group_by_traits<T, Observable, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector> traits_type;
typedef typename traits_type::key_selector_type key_selector_type;
typedef typename traits_type::marble_selector_type marble_selector_type;
typedef typename traits_type::marble_type marble_type;
typedef typename traits_type::predicate_type predicate_type;
typedef typename traits_type::duration_selector_type duration_selector_type;
typedef typename traits_type::subject_type subject_type;
typedef typename traits_type::key_type key_type;
typedef typename traits_type::key_subscriber_map_type group_map_type;
typedef std::vector<typename composite_subscription::weak_subscription> bindings_type;
struct group_by_state_type
{
group_by_state_type(composite_subscription sl, predicate_type p)
: source_lifetime(sl)
, groups(p)
, observers(0)
{}
composite_subscription source_lifetime;
rxsc::worker worker;
group_map_type groups;
std::atomic<int> observers;
};
template<class Subscriber>
static void stopsource(Subscriber&& dest, std::shared_ptr<group_by_state_type>& state) {
++state->observers;
dest.add([state](){
if (!state->source_lifetime.is_subscribed()) {
return;
}
--state->observers;
if (state->observers == 0) {
state->source_lifetime.unsubscribe();
}
});
}
struct group_by_values
{
group_by_values(key_selector_type ks, marble_selector_type ms, predicate_type p, duration_selector_type ds)
: keySelector(std::move(ks))
, marbleSelector(std::move(ms))
, predicate(std::move(p))
, durationSelector(std::move(ds))
{
}
mutable key_selector_type keySelector;
mutable marble_selector_type marbleSelector;
mutable predicate_type predicate;
mutable duration_selector_type durationSelector;
};
group_by_values initial;
group_by(key_selector_type ks, marble_selector_type ms, predicate_type p, duration_selector_type ds)
: initial(std::move(ks), std::move(ms), std::move(p), std::move(ds))
{
}
struct group_by_observable : public rxs::source_base<marble_type>
{
mutable std::shared_ptr<group_by_state_type> state;
subject_type subject;
key_type key;
group_by_observable(std::shared_ptr<group_by_state_type> st, subject_type s, key_type k)
: state(std::move(st))
, subject(std::move(s))
, key(k)
{
}
template<class Subscriber>
void on_subscribe(Subscriber&& o) const {
group_by::stopsource(o, state);
subject.get_observable().subscribe(std::forward<Subscriber>(o));
}
key_type on_get_key() {
return key;
}
};
template<class Subscriber>
struct group_by_observer : public group_by_values
{
typedef group_by_observer<Subscriber> this_type;
typedef typename traits_type::grouped_observable_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
dest_type dest;
mutable std::shared_ptr<group_by_state_type> state;
group_by_observer(composite_subscription l, dest_type d, group_by_values v)
: group_by_values(v)
, dest(std::move(d))
, state(std::make_shared<group_by_state_type>(l, group_by_values::predicate))
{
group_by::stopsource(dest, state);
}
void on_next(T v) const {
auto selectedKey = on_exception(
[&](){
return this->keySelector(v);},
[this](rxu::error_ptr e){on_error(e);});
if (selectedKey.empty()) {
return;
}
auto g = state->groups.find(selectedKey.get());
if (g == state->groups.end()) {
if (!dest.is_subscribed()) {
return;
}
auto sub = subject_type();
g = state->groups.insert(std::make_pair(selectedKey.get(), sub.get_subscriber())).first;
auto obs = make_dynamic_grouped_observable<key_type, marble_type>(group_by_observable(state, sub, selectedKey.get()));
auto durationObs = on_exception(
[&](){
return this->durationSelector(obs);},
[this](rxu::error_ptr e){on_error(e);});
if (durationObs.empty()) {
return;
}
dest.on_next(obs);
composite_subscription duration_sub;
auto ssub = state->source_lifetime.add(duration_sub);
auto expire_state = state;
auto expire_dest = g->second;
auto expire = [=]() {
auto g = expire_state->groups.find(selectedKey.get());
if (g != expire_state->groups.end()) {
expire_state->groups.erase(g);
expire_dest.on_completed();
}
expire_state->source_lifetime.remove(ssub);
};
auto robs = durationObs.get().take(1);
duration_sub.add(robs.subscribe(
[](const typename decltype(robs)::value_type &){},
[=](rxu::error_ptr) {expire();},
[=](){expire();}
));
}
auto selectedMarble = on_exception(
[&](){
return this->marbleSelector(v);},
[this](rxu::error_ptr e){on_error(e);});
if (selectedMarble.empty()) {
return;
}
g->second.on_next(std::move(selectedMarble.get()));
}
void on_error(rxu::error_ptr e) const {
for(auto& g : state->groups) {
g.second.on_error(e);
}
dest.on_error(e);
}
void on_completed() const {
for(auto& g : state->groups) {
g.second.on_completed();
}
dest.on_completed();
}
static subscriber<T, observer_type> make(dest_type d, group_by_values v) {
auto cs = composite_subscription();
return make_subscriber<T>(cs, observer_type(this_type(cs, std::move(d), std::move(v))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(group_by_observer<Subscriber>::make(std::move(dest), initial)) {
return group_by_observer<Subscriber>::make(std::move(dest), initial);
}
};
template<class KeySelector, class MarbleSelector, class BinaryPredicate, class DurationSelector>
class group_by_factory
{
typedef rxu::decay_t<KeySelector> key_selector_type;
typedef rxu::decay_t<MarbleSelector> marble_selector_type;
typedef rxu::decay_t<BinaryPredicate> predicate_type;
typedef rxu::decay_t<DurationSelector> duration_selector_type;
key_selector_type keySelector;
marble_selector_type marbleSelector;
predicate_type predicate;
duration_selector_type durationSelector;
public:
group_by_factory(key_selector_type ks, marble_selector_type ms, predicate_type p, duration_selector_type ds)
: keySelector(std::move(ks))
, marbleSelector(std::move(ms))
, predicate(std::move(p))
, durationSelector(std::move(ds))
{
}
template<class Observable>
struct group_by_factory_traits
{
typedef rxu::value_type_t<rxu::decay_t<Observable>> value_type;
typedef detail::group_by_traits<value_type, Observable, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector> traits_type;
typedef detail::group_by<value_type, Observable, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector> group_by_type;
};
template<class Observable>
auto operator()(Observable&& source)
-> decltype(source.template lift<typename group_by_factory_traits<Observable>::traits_type::grouped_observable_type>(typename group_by_factory_traits<Observable>::group_by_type(std::move(keySelector), std::move(marbleSelector), std::move(predicate), std::move(durationSelector)))) {
return source.template lift<typename group_by_factory_traits<Observable>::traits_type::grouped_observable_type>(typename group_by_factory_traits<Observable>::group_by_type(std::move(keySelector), std::move(marbleSelector), std::move(predicate), std::move(durationSelector)));
}
};
}
/*! @copydoc rx-group_by.hpp
*/
template<class... AN>
auto group_by(AN&&... an)
-> operator_factory<group_by_tag, AN...> {
return operator_factory<group_by_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<group_by_tag>
{
template<class Observable, class KeySelector, class MarbleSelector, class BinaryPredicate, class DurationSelector,
class SourceValue = rxu::value_type_t<Observable>,
class Traits = rxo::detail::group_by_traits<SourceValue, rxu::decay_t<Observable>, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>,
class GroupBy = rxo::detail::group_by<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<KeySelector>, rxu::decay_t<MarbleSelector>, rxu::decay_t<BinaryPredicate>, rxu::decay_t<DurationSelector>>,
class Value = typename Traits::grouped_observable_type>
static auto member(Observable&& o, KeySelector&& ks, MarbleSelector&& ms, BinaryPredicate&& p, DurationSelector&& ds)
-> decltype(o.template lift<Value>(GroupBy(std::forward<KeySelector>(ks), std::forward<MarbleSelector>(ms), std::forward<BinaryPredicate>(p), std::forward<DurationSelector>(ds)))) {
return o.template lift<Value>(GroupBy(std::forward<KeySelector>(ks), std::forward<MarbleSelector>(ms), std::forward<BinaryPredicate>(p), std::forward<DurationSelector>(ds)));
}
template<class Observable, class KeySelector, class MarbleSelector, class BinaryPredicate,
class DurationSelector=rxu::ret<observable<int, rxs::detail::never<int>>>,
class SourceValue = rxu::value_type_t<Observable>,
class Traits = rxo::detail::group_by_traits<SourceValue, rxu::decay_t<Observable>, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>,
class GroupBy = rxo::detail::group_by<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<KeySelector>, rxu::decay_t<MarbleSelector>, rxu::decay_t<BinaryPredicate>, rxu::decay_t<DurationSelector>>,
class Value = typename Traits::grouped_observable_type>
static auto member(Observable&& o, KeySelector&& ks, MarbleSelector&& ms, BinaryPredicate&& p)
-> decltype(o.template lift<Value>(GroupBy(std::forward<KeySelector>(ks), std::forward<MarbleSelector>(ms), std::forward<BinaryPredicate>(p), rxu::ret<observable<int, rxs::detail::never<int>>>()))) {
return o.template lift<Value>(GroupBy(std::forward<KeySelector>(ks), std::forward<MarbleSelector>(ms), std::forward<BinaryPredicate>(p), rxu::ret<observable<int, rxs::detail::never<int>>>()));
}
template<class Observable, class KeySelector, class MarbleSelector,
class BinaryPredicate=rxu::less,
class DurationSelector=rxu::ret<observable<int, rxs::detail::never<int>>>,
class SourceValue = rxu::value_type_t<Observable>,
class Traits = rxo::detail::group_by_traits<SourceValue, rxu::decay_t<Observable>, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>,
class GroupBy = rxo::detail::group_by<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<KeySelector>, rxu::decay_t<MarbleSelector>, rxu::decay_t<BinaryPredicate>, rxu::decay_t<DurationSelector>>,
class Value = typename Traits::grouped_observable_type>
static auto member(Observable&& o, KeySelector&& ks, MarbleSelector&& ms)
-> decltype(o.template lift<Value>(GroupBy(std::forward<KeySelector>(ks), std::forward<MarbleSelector>(ms), rxu::less(), rxu::ret<observable<int, rxs::detail::never<int>>>()))) {
return o.template lift<Value>(GroupBy(std::forward<KeySelector>(ks), std::forward<MarbleSelector>(ms), rxu::less(), rxu::ret<observable<int, rxs::detail::never<int>>>()));
}
template<class Observable, class KeySelector,
class MarbleSelector=rxu::detail::take_at<0>,
class BinaryPredicate=rxu::less,
class DurationSelector=rxu::ret<observable<int, rxs::detail::never<int>>>,
class SourceValue = rxu::value_type_t<Observable>,
class Traits = rxo::detail::group_by_traits<SourceValue, rxu::decay_t<Observable>, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>,
class GroupBy = rxo::detail::group_by<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<KeySelector>, rxu::decay_t<MarbleSelector>, rxu::decay_t<BinaryPredicate>, rxu::decay_t<DurationSelector>>,
class Value = typename Traits::grouped_observable_type>
static auto member(Observable&& o, KeySelector&& ks)
-> decltype(o.template lift<Value>(GroupBy(std::forward<KeySelector>(ks), rxu::detail::take_at<0>(), rxu::less(), rxu::ret<observable<int, rxs::detail::never<int>>>()))) {
return o.template lift<Value>(GroupBy(std::forward<KeySelector>(ks), rxu::detail::take_at<0>(), rxu::less(), rxu::ret<observable<int, rxs::detail::never<int>>>()));
}
template<class Observable,
class KeySelector=rxu::detail::take_at<0>,
class MarbleSelector=rxu::detail::take_at<0>,
class BinaryPredicate=rxu::less,
class DurationSelector=rxu::ret<observable<int, rxs::detail::never<int>>>,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Traits = rxo::detail::group_by_traits<SourceValue, rxu::decay_t<Observable>, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>,
class GroupBy = rxo::detail::group_by<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<KeySelector>, rxu::decay_t<MarbleSelector>, rxu::decay_t<BinaryPredicate>, rxu::decay_t<DurationSelector>>,
class Value = typename Traits::grouped_observable_type>
static auto member(Observable&& o)
-> decltype(o.template lift<Value>(GroupBy(rxu::detail::take_at<0>(), rxu::detail::take_at<0>(), rxu::less(), rxu::ret<observable<int, rxs::detail::never<int>>>()))) {
return o.template lift<Value>(GroupBy(rxu::detail::take_at<0>(), rxu::detail::take_at<0>(), rxu::less(), rxu::ret<observable<int, rxs::detail::never<int>>>()));
}
template<class... AN>
static operators::detail::group_by_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "group_by takes (optional KeySelector, optional MarbleSelector, optional BinaryKeyPredicate, optional DurationSelector), KeySelector takes (Observable::value_type) -> KeyValue, MarbleSelector takes (Observable::value_type) -> MarbleValue, BinaryKeyPredicate takes (KeyValue, KeyValue) -> bool, DurationSelector takes (Observable::value_type) -> Observable");
}
};
}
#endif

View File

@ -0,0 +1,114 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-ignore_elements.hpp
\brief Do not emit any items from the source Observable, but allow termination notification (either onError or onCompleted) to pass through unchanged.
\return Observable that emits termination notification from the source observable.
\sample
\snippet ignore_elements.cpp ignore_elements sample
\snippet output.txt ignore_elements sample
*/
#if !defined(RXCPP_OPERATORS_RX_IGNORE_ELEMENTS_HPP)
#define RXCPP_OPERATORS_RX_IGNORE_ELEMENTS_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct ignore_elements_invalid_arguments {};
template<class... AN>
struct ignore_elements_invalid : public rxo::operator_base<ignore_elements_invalid_arguments<AN...>> {
using type = observable<ignore_elements_invalid_arguments<AN...>, ignore_elements_invalid<AN...>>;
};
template<class... AN>
using ignore_elements_invalid_t = typename ignore_elements_invalid<AN...>::type;
template<class T>
struct ignore_elements {
typedef rxu::decay_t<T> source_value_type;
template<class Subscriber>
struct ignore_elements_observer
{
typedef ignore_elements_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
ignore_elements_observer(dest_type d)
: dest(d)
{
}
void on_next(source_value_type) const {
// no-op; ignore element
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer_type> make(dest_type d) {
return make_subscriber<value_type>(d, this_type(d));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(ignore_elements_observer<Subscriber>::make(std::move(dest))) {
return ignore_elements_observer<Subscriber>::make(std::move(dest));
}
};
}
/*! @copydoc rx-ignore_elements.hpp
*/
template<class... AN>
auto ignore_elements(AN&&... an)
-> operator_factory<ignore_elements_tag, AN...> {
return operator_factory<ignore_elements_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<ignore_elements_tag>
{
template<class Observable,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class IgnoreElements = rxo::detail::ignore_elements<SourceValue>>
static auto member(Observable&& o)
-> decltype(o.template lift<SourceValue>(IgnoreElements())) {
return o.template lift<SourceValue>(IgnoreElements());
}
template<class... AN>
static operators::detail::ignore_elements_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "ignore_elements takes no arguments");
}
};
}
#endif

View File

@ -0,0 +1,110 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-lift.hpp
\brief takes any function that will take a subscriber for this observable and produce a subscriber.
this is intended to allow externally defined operators, that use make_subscriber, to be connected into the expression.
\tparam ResultType the type of the emitted results.
\tparam Operator the type of the operator.
\return An observable that emitting the items from its source.
*/
#if !defined(RXCPP_OPERATORS_RX_LIFT_HPP)
#define RXCPP_OPERATORS_RX_LIFT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace detail {
template<class V, class S, class F>
struct is_lift_function_for {
struct tag_not_valid {};
template<class CS, class CF>
static auto check(int) -> decltype((*(CF*)nullptr)(*(CS*)nullptr));
template<class CS, class CF>
static tag_not_valid check(...);
using for_type = rxu::decay_t<S>;
using func_type = rxu::decay_t<F>;
using detail_result = decltype(check<for_type, func_type>(0));
static const bool value = rxu::all_true_type<
is_subscriber<detail_result>,
is_subscriber<for_type>,
std::is_convertible<V, typename rxu::value_type_from<detail_result>::type>>::value;
};
}
namespace operators {
namespace detail {
template<class ResultType, class SourceOperator, class Operator>
struct lift_traits
{
typedef rxu::decay_t<ResultType> result_value_type;
typedef rxu::decay_t<SourceOperator> source_operator_type;
typedef rxu::decay_t<Operator> operator_type;
typedef typename source_operator_type::value_type source_value_type;
};
template<class ResultType, class SourceOperator, class Operator>
struct lift_operator : public operator_base<typename lift_traits<ResultType, SourceOperator, Operator>::result_value_type>
{
typedef lift_traits<ResultType, SourceOperator, Operator> traits;
typedef typename traits::source_operator_type source_operator_type;
typedef typename traits::operator_type operator_type;
source_operator_type source;
operator_type chain;
lift_operator(source_operator_type s, operator_type op)
: source(std::move(s))
, chain(std::move(op))
{
}
template<class Subscriber>
void on_subscribe(Subscriber o) const {
auto lifted = chain(std::move(o));
trace_activity().lift_enter(source, chain, o, lifted);
source.on_subscribe(std::move(lifted));
trace_activity().lift_return(source, chain);
}
};
template<class ResultType, class Operator>
class lift_factory
{
typedef rxu::decay_t<Operator> operator_type;
operator_type chain;
public:
lift_factory(operator_type op) : chain(std::move(op)) {}
template<class Observable>
auto operator()(const Observable& source)
-> decltype(source.template lift<ResultType>(chain)) {
return source.template lift<ResultType>(chain);
static_assert(rxcpp::detail::is_lift_function_for<rxu::value_type_t<Observable>, subscriber<ResultType>, Operator>::value, "Function passed for lift() must have the signature subscriber<...>(subscriber<T, ...>)");
}
};
}
template<class ResultType, class Operator>
auto lift(Operator&& op)
-> detail::lift_factory<ResultType, Operator> {
return detail::lift_factory<ResultType, Operator>(std::forward<Operator>(op));
}
}
}
#endif

View File

@ -0,0 +1,145 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-map.hpp
\brief For each item from this observable use Selector to produce an item to emit from the new observable that is returned.
\tparam Selector the type of the transforming function
\param s the selector function
\return Observable that emits the items from the source observable, transformed by the specified function.
\sample
\snippet map.cpp map sample
\snippet output.txt map sample
*/
#if !defined(RXCPP_OPERATORS_RX_MAP_HPP)
#define RXCPP_OPERATORS_RX_MAP_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct map_invalid_arguments {};
template<class... AN>
struct map_invalid : public rxo::operator_base<map_invalid_arguments<AN...>> {
using type = observable<map_invalid_arguments<AN...>, map_invalid<AN...>>;
};
template<class... AN>
using map_invalid_t = typename map_invalid<AN...>::type;
template<class T, class Selector>
struct map
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Selector> select_type;
typedef decltype((*(select_type*)nullptr)(*(source_value_type*)nullptr)) value_type;
select_type selector;
map(select_type s)
: selector(std::move(s))
{
}
template<class Subscriber>
struct map_observer
{
typedef map_observer<Subscriber> this_type;
typedef decltype((*(select_type*)nullptr)(*(source_value_type*)nullptr)) value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<source_value_type, this_type> observer_type;
dest_type dest;
mutable select_type selector;
map_observer(dest_type d, select_type s)
: dest(std::move(d))
, selector(std::move(s))
{
}
template<class Value>
void on_next(Value&& v) const {
auto selected = on_exception(
[&](){
return this->selector(std::forward<Value>(v));},
dest);
if (selected.empty()) {
return;
}
dest.on_next(std::move(selected.get()));
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<source_value_type, observer_type> make(dest_type d, select_type s) {
auto cs = d.get_subscription();
return make_subscriber<source_value_type>(std::move(cs), observer_type(this_type(std::move(d), std::move(s))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(map_observer<Subscriber>::make(std::move(dest), selector)) {
return map_observer<Subscriber>::make(std::move(dest), selector);
}
};
}
/*! @copydoc rx-map.hpp
*/
template<class... AN>
auto map(AN&&... an)
-> operator_factory<map_tag, AN...> {
return operator_factory<map_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! @copydoc rx-map.hpp
*/
template<class... AN>
auto transform(AN&&... an)
-> operator_factory<map_tag, AN...> {
return operator_factory<map_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<map_tag>
{
template<class Observable, class Selector,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class ResolvedSelector = rxu::decay_t<Selector>,
class SourceValue = rxu::value_type_t<Observable>,
class Map = rxo::detail::map<SourceValue, ResolvedSelector>,
class Value = rxu::value_type_t<Map>>
static auto member(Observable&& o, Selector&& s)
-> decltype(o.template lift<Value>(Map(std::forward<Selector>(s)))) {
return o.template lift<Value>(Map(std::forward<Selector>(s)));
}
template<class... AN>
static operators::detail::map_invalid_t<AN...> member(const AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "map takes Selector");
}
};
}
#endif

View File

@ -0,0 +1,290 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-merge.hpp
\brief For each given observable subscribe.
For each item emitted from all of the given observables, deliver from the new observable that is returned.
There are 2 variants of the operator:
- The source observable emits nested observables, nested observables are merged.
- The source observable and the arguments v0...vn are used to provide the observables to merge.
\tparam Coordination the type of the scheduler (optional).
\tparam Value0 ... (optional).
\tparam ValueN types of source observables (optional).
\param cn the scheduler to synchronize sources from different contexts (optional).
\param v0 ... (optional).
\param vn source observables (optional).
\return Observable that emits items that are the result of flattening the observables emitted by the source observable.
If scheduler is omitted, identity_current_thread is used.
\sample
\snippet merge.cpp threaded implicit merge sample
\snippet output.txt threaded implicit merge sample
\sample
\snippet merge.cpp implicit merge sample
\snippet output.txt implicit merge sample
\sample
\snippet merge.cpp merge sample
\snippet output.txt merge sample
\sample
\snippet merge.cpp threaded merge sample
\snippet output.txt threaded merge sample
*/
#if !defined(RXCPP_OPERATORS_RX_MERGE_HPP)
#define RXCPP_OPERATORS_RX_MERGE_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct merge_invalid_arguments {};
template<class... AN>
struct merge_invalid : public rxo::operator_base<merge_invalid_arguments<AN...>> {
using type = observable<merge_invalid_arguments<AN...>, merge_invalid<AN...>>;
};
template<class... AN>
using merge_invalid_t = typename merge_invalid<AN...>::type;
template<class T, class Observable, class Coordination>
struct merge
: public operator_base<rxu::value_type_t<rxu::decay_t<T>>>
{
//static_assert(is_observable<Observable>::value, "merge requires an observable");
//static_assert(is_observable<T>::value, "merge requires an observable that contains observables");
typedef merge<T, Observable, Coordination> this_type;
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Observable> source_type;
typedef typename source_type::source_operator_type source_operator_type;
typedef typename source_value_type::value_type value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values
{
values(source_operator_type o, coordination_type sf)
: source_operator(std::move(o))
, coordination(std::move(sf))
{
}
source_operator_type source_operator;
coordination_type coordination;
};
values initial;
merge(const source_type& o, coordination_type sf)
: initial(o.source_operator, std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct merge_state_type
: public std::enable_shared_from_this<merge_state_type>
, public values
{
merge_state_type(values i, coordinator_type coor, output_type oarg)
: values(i)
, source(i.source_operator)
, pendingCompletions(0)
, coordinator(std::move(coor))
, out(std::move(oarg))
{
}
observable<source_value_type, source_operator_type> source;
// on_completed on the output must wait until all the
// subscriptions have received on_completed
int pendingCompletions;
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<merge_state_type>(initial, std::move(coordinator), std::move(scbr));
composite_subscription outercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
state->out.add(outercs);
auto source = on_exception(
[&](){return state->coordinator.in(state->source);},
state->out);
if (source.empty()) {
return;
}
++state->pendingCompletions;
// 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<source_value_type>(
state->out,
outercs,
// on_next
[state](source_value_type st) {
composite_subscription innercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innercstoken = state->out.add(innercs);
innercs.add(make_subscription([state, innercstoken](){
state->out.remove(innercstoken);
}));
auto selectedSource = state->coordinator.in(st);
++state->pendingCompletions;
// this subscribe does not share the source subscription
// so that when it is unsubscribed the source will continue
auto sinkInner = make_subscriber<value_type>(
state->out,
innercs,
// on_next
[state, st](value_type ct) {
state->out.on_next(std::move(ct));
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
//on_completed
[state](){
if (--state->pendingCompletions == 0) {
state->out.on_completed();
}
}
);
auto selectedSinkInner = state->coordinator.out(sinkInner);
selectedSource.subscribe(std::move(selectedSinkInner));
},
// on_error
[state](rxu::error_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()));
}
};
}
/*! @copydoc rx-merge.hpp
*/
template<class... AN>
auto merge(AN&&... an)
-> operator_factory<merge_tag, AN...> {
return operator_factory<merge_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<merge_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Merge = rxo::detail::merge<SourceValue, rxu::decay_t<Observable>, identity_one_worker>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, Merge>
>
static Result member(Observable&& o) {
return Result(Merge(std::forward<Observable>(o), identity_current_thread()));
}
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Merge = rxo::detail::merge<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, Merge>
>
static Result member(Observable&& o, Coordination&& cn) {
return Result(Merge(std::forward<Observable>(o), std::forward<Coordination>(cn)));
}
template<class Observable, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Value0, ValueN...>>,
class EmittedValue = rxu::value_type_t<Observable>,
class SourceValue = observable<EmittedValue>,
class ObservableObservable = observable<SourceValue>,
class Merge = typename rxu::defer_type<rxo::detail::merge, SourceValue, ObservableObservable, identity_one_worker>::type,
class Value = rxu::value_type_t<Merge>,
class Result = observable<Value, Merge>
>
static Result member(Observable&& o, Value0&& v0, ValueN&&... vn) {
return Result(Merge(rxs::from(o.as_dynamic(), v0.as_dynamic(), vn.as_dynamic()...), identity_current_thread()));
}
template<class Observable, class Coordination, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Value0, ValueN...>,
is_coordination<Coordination>>,
class EmittedValue = rxu::value_type_t<Observable>,
class SourceValue = observable<EmittedValue>,
class ObservableObservable = observable<SourceValue>,
class Merge = typename rxu::defer_type<rxo::detail::merge, SourceValue, ObservableObservable, rxu::decay_t<Coordination>>::type,
class Value = rxu::value_type_t<Merge>,
class Result = observable<Value, Merge>
>
static Result member(Observable&& o, Coordination&& cn, Value0&& v0, ValueN&&... vn) {
return Result(Merge(rxs::from(o.as_dynamic(), v0.as_dynamic(), vn.as_dynamic()...), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::merge_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "merge takes (optional Coordination, optional Value0, optional ValueN...)");
}
};
}
#endif

View File

@ -0,0 +1,304 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-merge_delay_error.hpp
\brief For each given observable subscribe.
For each item emitted from all of the given observables, deliver from the new observable that is returned.
The first error to occure is hold off until all of the given non-error-emitting observables have finished their emission.
There are 2 variants of the operator:
- The source observable emits nested observables, nested observables are merged.
- The source observable and the arguments v0...vn are used to provide the observables to merge.
\tparam Coordination the type of the scheduler (optional).
\tparam Value0 ... (optional).
\tparam ValueN types of source observables (optional).
\param cn the scheduler to synchronize sources from different contexts (optional).
\param v0 ... (optional).
\param vn source observables (optional).
\return Observable that emits items that are the result of flattening the observables emitted by the source observable.
If scheduler is omitted, identity_current_thread is used.
\sample
\snippet merge_delay_error.cpp threaded implicit merge sample
\snippet output.txt threaded implicit merge sample
\sample
\snippet merge_delay_error.cpp implicit merge sample
\snippet output.txt implicit merge sample
\sample
\snippet merge_delay_error.cpp merge sample
\snippet output.txt merge sample
\sample
\snippet merge_delay_error.cpp threaded merge sample
\snippet output.txt threaded merge sample
*/
#if !defined(RXCPP_OPERATORS_RX_MERGE_DELAY_ERROR_HPP)
#define RXCPP_OPERATORS_RX_MERGE_DELAY_ERROR_HPP
#include "rx-merge.hpp"
#include "../rx-composite_exception.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class T, class Observable, class Coordination>
struct merge_delay_error
: public operator_base<rxu::value_type_t<rxu::decay_t<T>>>
{
//static_assert(is_observable<Observable>::value, "merge requires an observable");
//static_assert(is_observable<T>::value, "merge requires an observable that contains observables");
typedef merge_delay_error<T, Observable, Coordination> this_type;
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Observable> source_type;
typedef typename source_type::source_operator_type source_operator_type;
typedef typename source_value_type::value_type value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values
{
values(source_operator_type o, coordination_type sf)
: source_operator(std::move(o))
, coordination(std::move(sf))
{
}
source_operator_type source_operator;
coordination_type coordination;
};
values initial;
merge_delay_error(const source_type& o, coordination_type sf)
: initial(o.source_operator, std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct merge_state_type
: public std::enable_shared_from_this<merge_state_type>
, public values
{
merge_state_type(values i, coordinator_type coor, output_type oarg)
: values(i)
, source(i.source_operator)
, pendingCompletions(0)
, coordinator(std::move(coor))
, out(std::move(oarg))
{
}
observable<source_value_type, source_operator_type> source;
// on_completed on the output must wait until all the
// subscriptions have received on_completed
int pendingCompletions;
composite_exception exception;
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<merge_state_type>(initial, std::move(coordinator), std::move(scbr));
composite_subscription outercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
state->out.add(outercs);
auto source = on_exception(
[&](){return state->coordinator.in(state->source);},
state->out);
if (source.empty()) {
return;
}
++state->pendingCompletions;
// 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<source_value_type>(
state->out,
outercs,
// on_next
[state](source_value_type st) {
composite_subscription innercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innercstoken = state->out.add(innercs);
innercs.add(make_subscription([state, innercstoken](){
state->out.remove(innercstoken);
}));
auto selectedSource = state->coordinator.in(st);
++state->pendingCompletions;
// this subscribe does not share the source subscription
// so that when it is unsubscribed the source will continue
auto sinkInner = make_subscriber<value_type>(
state->out,
innercs,
// on_next
[state, st](value_type ct) {
state->out.on_next(std::move(ct));
},
// on_error
[state](rxu::error_ptr e) {
if(--state->pendingCompletions == 0) {
state->out.on_error(
rxu::make_error_ptr(std::move(state->exception.add(e))));
} else {
state->exception.add(e);
}
},
//on_completed
[state](){
if (--state->pendingCompletions == 0) {
if(!state->exception.empty()) {
state->out.on_error(
rxu::make_error_ptr(std::move(state->exception)));
} else {
state->out.on_completed();
}
}
}
);
auto selectedSinkInner = state->coordinator.out(sinkInner);
selectedSource.subscribe(std::move(selectedSinkInner));
},
// on_error
[state](rxu::error_ptr e) {
if(--state->pendingCompletions == 0) {
state->out.on_error(
rxu::make_error_ptr(std::move(state->exception.add(e))));
} else {
state->exception.add(e);
}
},
// on_completed
[state]() {
if (--state->pendingCompletions == 0) {
if(!state->exception.empty()) {
state->out.on_error(
rxu::make_error_ptr(std::move(state->exception)));
} else {
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()));
}
};
}
/*! @copydoc rx-merge-delay-error.hpp
*/
template<class... AN>
auto merge_delay_error(AN&&... an)
-> operator_factory<merge_delay_error_tag, AN...> {
return operator_factory<merge_delay_error_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<merge_delay_error_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Merge = rxo::detail::merge_delay_error<SourceValue, rxu::decay_t<Observable>, identity_one_worker>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, Merge>
>
static Result member(Observable&& o) {
return Result(Merge(std::forward<Observable>(o), identity_current_thread()));
}
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Merge = rxo::detail::merge_delay_error<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, Merge>
>
static Result member(Observable&& o, Coordination&& cn) {
return Result(Merge(std::forward<Observable>(o), std::forward<Coordination>(cn)));
}
template<class Observable, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Value0, ValueN...>>,
class EmittedValue = rxu::value_type_t<Observable>,
class SourceValue = observable<EmittedValue>,
class ObservableObservable = observable<SourceValue>,
class Merge = typename rxu::defer_type<rxo::detail::merge_delay_error, SourceValue, ObservableObservable, identity_one_worker>::type,
class Value = rxu::value_type_t<Merge>,
class Result = observable<Value, Merge>
>
static Result member(Observable&& o, Value0&& v0, ValueN&&... vn) {
return Result(Merge(rxs::from(o.as_dynamic(), v0.as_dynamic(), vn.as_dynamic()...), identity_current_thread()));
}
template<class Observable, class Coordination, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Value0, ValueN...>,
is_coordination<Coordination>>,
class EmittedValue = rxu::value_type_t<Observable>,
class SourceValue = observable<EmittedValue>,
class ObservableObservable = observable<SourceValue>,
class Merge = typename rxu::defer_type<rxo::detail::merge_delay_error, SourceValue, ObservableObservable, rxu::decay_t<Coordination>>::type,
class Value = rxu::value_type_t<Merge>,
class Result = observable<Value, Merge>
>
static Result member(Observable&& o, Coordination&& cn, Value0&& v0, ValueN&&... vn) {
return Result(Merge(rxs::from(o.as_dynamic(), v0.as_dynamic(), vn.as_dynamic()...), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::merge_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "merge_delay_error takes (optional Coordination, optional Value0, optional ValueN...)");
}
};
}
#endif

View File

@ -0,0 +1,123 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-multicast.hpp
\brief allows connections to the source to be independent of subscriptions.
\tparam Subject the subject to multicast the source Observable.
\param sub the subject.
*/
#if !defined(RXCPP_OPERATORS_RX_MULTICAST_HPP)
#define RXCPP_OPERATORS_RX_MULTICAST_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct multicast_invalid_arguments {};
template<class... AN>
struct multicast_invalid : public rxo::operator_base<multicast_invalid_arguments<AN...>> {
using type = observable<multicast_invalid_arguments<AN...>, multicast_invalid<AN...>>;
};
template<class... AN>
using multicast_invalid_t = typename multicast_invalid<AN...>::type;
template<class T, class Observable, class Subject>
struct multicast : public operator_base<T>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Subject> subject_type;
struct multicast_state : public std::enable_shared_from_this<multicast_state>
{
multicast_state(source_type o, subject_type sub)
: source(std::move(o))
, subject_value(std::move(sub))
{
}
source_type source;
subject_type subject_value;
rxu::detail::maybe<typename composite_subscription::weak_subscription> connection;
};
std::shared_ptr<multicast_state> state;
multicast(source_type o, subject_type sub)
: state(std::make_shared<multicast_state>(std::move(o), std::move(sub)))
{
}
template<class Subscriber>
void on_subscribe(Subscriber&& o) const {
state->subject_value.get_observable().subscribe(std::forward<Subscriber>(o));
}
void on_connect(composite_subscription cs) const {
if (state->connection.empty()) {
auto destination = state->subject_value.get_subscriber();
// the lifetime of each connect is nested in the subject lifetime
state->connection.reset(destination.add(cs));
auto localState = state;
// when the connection is finished it should shutdown the connection
cs.add(
[destination, localState](){
if (!localState->connection.empty()) {
destination.remove(localState->connection.get());
localState->connection.reset();
}
});
// use cs not destination for lifetime of subscribe.
state->source.subscribe(cs, destination);
}
}
};
}
/*! @copydoc rx-multicast.hpp
*/
template<class... AN>
auto multicast(AN&&... an)
-> operator_factory<multicast_tag, AN...> {
return operator_factory<multicast_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<multicast_tag>
{
template<class Observable, class Subject,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Subject>>,
class Value = rxu::value_type_t<Multicast>,
class Result = connectable_observable<Value, Multicast>>
static Result member(Observable&& o, Subject&& sub) {
return Result(Multicast(std::forward<Observable>(o), std::forward<Subject>(sub)));
}
template<class... AN>
static operators::detail::multicast_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "multicast takes (Subject)");
}
};
}
#endif

View File

@ -0,0 +1,335 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-observe_on.hpp
\brief All values are queued and delivered using the scheduler from the supplied coordination.
\tparam Coordination the type of the scheduler.
\param cn the scheduler to notify observers on.
\return The source observable modified so that its observers are notified on the specified scheduler.
\sample
\snippet observe_on.cpp observe_on sample
\snippet output.txt observe_on sample
Invoking rxcpp::observable::subscribe_on operator, instead of observe_on, gives following results:
\snippet output.txt subscribe_on sample
*/
#if !defined(RXCPP_OPERATORS_RX_OBSERVE_ON_HPP)
#define RXCPP_OPERATORS_RX_OBSERVE_ON_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct observe_on_invalid_arguments {};
template<class... AN>
struct observe_on_invalid : public rxo::operator_base<observe_on_invalid_arguments<AN...>> {
using type = observable<observe_on_invalid_arguments<AN...>, observe_on_invalid<AN...>>;
};
template<class... AN>
using observe_on_invalid_t = typename observe_on_invalid<AN...>::type;
template<class T, class Coordination>
struct observe_on
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
coordination_type coordination;
observe_on(coordination_type cn)
: coordination(std::move(cn))
{
}
template<class Subscriber>
struct observe_on_observer
{
typedef observe_on_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
typedef rxn::notification<T> notification_type;
typedef typename notification_type::type base_notification_type;
typedef std::deque<base_notification_type> queue_type;
struct mode
{
enum type {
Invalid = 0,
Processing,
Empty,
Disposed,
Errored
};
};
struct observe_on_state : std::enable_shared_from_this<observe_on_state>
{
mutable std::mutex lock;
mutable queue_type fill_queue;
mutable queue_type drain_queue;
composite_subscription lifetime;
mutable typename mode::type current;
coordinator_type coordinator;
dest_type destination;
observe_on_state(dest_type d, coordinator_type coor, composite_subscription cs)
: lifetime(std::move(cs))
, current(mode::Empty)
, coordinator(std::move(coor))
, destination(std::move(d))
{
}
void finish(std::unique_lock<std::mutex>& guard, typename mode::type end) const {
if (!guard.owns_lock()) {
std::terminate();
}
if (current == mode::Errored || current == mode::Disposed) {return;}
current = end;
queue_type fill_expired;
swap(fill_expired, fill_queue);
queue_type drain_expired;
swap(drain_expired, drain_queue);
RXCPP_UNWIND_AUTO([&](){guard.lock();});
guard.unlock();
lifetime.unsubscribe();
destination.unsubscribe();
}
void ensure_processing(std::unique_lock<std::mutex>& guard) const {
if (!guard.owns_lock()) {
std::terminate();
}
if (current == mode::Empty) {
current = mode::Processing;
if (!lifetime.is_subscribed() && fill_queue.empty() && drain_queue.empty()) {
finish(guard, mode::Disposed);
}
auto keepAlive = this->shared_from_this();
auto drain = [keepAlive, this](const rxsc::schedulable& self){
using std::swap;
RXCPP_TRY {
for (;;) {
if (drain_queue.empty() || !destination.is_subscribed()) {
std::unique_lock<std::mutex> guard(lock);
if (!destination.is_subscribed() ||
(!lifetime.is_subscribed() && fill_queue.empty() && drain_queue.empty())) {
finish(guard, mode::Disposed);
return;
}
if (drain_queue.empty()) {
if (fill_queue.empty()) {
current = mode::Empty;
return;
}
swap(fill_queue, drain_queue);
}
}
auto notification = std::move(drain_queue.front());
drain_queue.pop_front();
notification->accept(destination);
std::unique_lock<std::mutex> guard(lock);
self();
if (lifetime.is_subscribed()) break;
}
}
RXCPP_CATCH(...) {
destination.on_error(rxu::current_exception());
std::unique_lock<std::mutex> guard(lock);
finish(guard, mode::Errored);
}
};
auto selectedDrain = on_exception(
[&](){return coordinator.act(drain);},
destination);
if (selectedDrain.empty()) {
finish(guard, mode::Errored);
return;
}
auto processor = coordinator.get_worker();
RXCPP_UNWIND_AUTO([&](){guard.lock();});
guard.unlock();
processor.schedule(selectedDrain.get());
}
}
};
std::shared_ptr<observe_on_state> state;
observe_on_observer(dest_type d, coordinator_type coor, composite_subscription cs)
: state(std::make_shared<observe_on_state>(std::move(d), std::move(coor), std::move(cs)))
{
}
void on_next(source_value_type v) const {
std::unique_lock<std::mutex> guard(state->lock);
if (state->current == mode::Errored || state->current == mode::Disposed) { return; }
state->fill_queue.push_back(notification_type::on_next(std::move(v)));
state->ensure_processing(guard);
}
void on_error(rxu::error_ptr e) const {
std::unique_lock<std::mutex> guard(state->lock);
if (state->current == mode::Errored || state->current == mode::Disposed) { return; }
state->fill_queue.push_back(notification_type::on_error(e));
state->ensure_processing(guard);
}
void on_completed() const {
std::unique_lock<std::mutex> guard(state->lock);
if (state->current == mode::Errored || state->current == mode::Disposed) { return; }
state->fill_queue.push_back(notification_type::on_completed());
state->ensure_processing(guard);
}
static subscriber<value_type, observer<value_type, this_type>> make(dest_type d, coordination_type cn, composite_subscription cs = composite_subscription()) {
auto coor = cn.create_coordinator(d.get_subscription());
d.add(cs);
this_type o(d, std::move(coor), cs);
auto keepAlive = o.state;
cs.add([=](){
std::unique_lock<std::mutex> guard(keepAlive->lock);
keepAlive->ensure_processing(guard);
});
return make_subscriber<value_type>(d, cs, make_observer<value_type>(std::move(o)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(observe_on_observer<decltype(dest.as_dynamic())>::make(dest.as_dynamic(), coordination)) {
return observe_on_observer<decltype(dest.as_dynamic())>::make(dest.as_dynamic(), coordination);
}
};
}
/*! @copydoc rx-observe_on.hpp
*/
template<class... AN>
auto observe_on(AN&&... an)
-> operator_factory<observe_on_tag, AN...> {
return operator_factory<observe_on_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<observe_on_tag>
{
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class ObserveOn = rxo::detail::observe_on<SourceValue, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Coordination&& cn)
-> decltype(o.template lift<SourceValue>(ObserveOn(std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(ObserveOn(std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::observe_on_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "observe_on takes (Coordination)");
}
};
class observe_on_one_worker : public coordination_base
{
rxsc::scheduler factory;
class input_type
{
rxsc::worker controller;
rxsc::scheduler factory;
identity_one_worker coordination;
public:
explicit input_type(rxsc::worker w)
: controller(w)
, factory(rxsc::make_same_worker(w))
, coordination(factory)
{
}
inline rxsc::worker get_worker() const {
return controller;
}
inline rxsc::scheduler get_scheduler() const {
return factory;
}
inline rxsc::scheduler::clock_type::time_point now() const {
return factory.now();
}
template<class Observable>
auto in(Observable o) const
-> decltype(o.observe_on(coordination)) {
return o.observe_on(coordination);
}
template<class Subscriber>
auto out(Subscriber s) const
-> Subscriber {
return s;
}
template<class F>
auto act(F f) const
-> F {
return f;
}
};
public:
explicit observe_on_one_worker(rxsc::scheduler sc) : factory(sc) {}
typedef coordinator<input_type> coordinator_type;
inline rxsc::scheduler::clock_type::time_point now() const {
return factory.now();
}
inline coordinator_type create_coordinator(composite_subscription cs = composite_subscription()) const {
auto w = factory.create_worker(std::move(cs));
return coordinator_type(input_type(std::move(w)));
}
};
inline observe_on_one_worker observe_on_run_loop(const rxsc::run_loop& rl) {
return observe_on_one_worker(rxsc::make_run_loop(rl));
}
inline observe_on_one_worker observe_on_event_loop() {
static observe_on_one_worker r(rxsc::make_event_loop());
return r;
}
inline observe_on_one_worker observe_on_new_thread() {
static observe_on_one_worker r(rxsc::make_new_thread());
return r;
}
}
#endif

View File

@ -0,0 +1,147 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-on_error_resume_next.hpp
\brief If an error occurs, take the result from the Selector and subscribe to that instead.
\tparam Selector the actual type of a function of the form `observable<T>(rxu::error_ptr)`
\param s the function of the form `observable<T>(rxu::error_ptr)`
\return Observable that emits the items from the source observable and switches to a new observable on error.
\sample
\snippet on_error_resume_next.cpp on_error_resume_next sample
\snippet output.txt on_error_resume_next sample
*/
#if !defined(RXCPP_OPERATORS_RX_ON_ERROR_RESUME_NEXT_HPP)
#define RXCPP_OPERATORS_RX_ON_ERROR_RESUME_NEXT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct on_error_resume_next_invalid_arguments {};
template<class... AN>
struct on_error_resume_next_invalid : public rxo::operator_base<on_error_resume_next_invalid_arguments<AN...>> {
using type = observable<on_error_resume_next_invalid_arguments<AN...>, on_error_resume_next_invalid<AN...>>;
};
template<class... AN>
using on_error_resume_next_invalid_t = typename on_error_resume_next_invalid<AN...>::type;
template<class T, class Selector>
struct on_error_resume_next
{
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Selector> select_type;
typedef decltype((*(select_type*)nullptr)(rxu::error_ptr())) fallback_type;
select_type selector;
on_error_resume_next(select_type s)
: selector(std::move(s))
{
}
template<class Subscriber>
struct on_error_resume_next_observer
{
typedef on_error_resume_next_observer<Subscriber> this_type;
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Selector> select_type;
typedef decltype((*(select_type*)nullptr)(rxu::error_ptr())) fallback_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
dest_type dest;
composite_subscription lifetime;
select_type selector;
on_error_resume_next_observer(dest_type d, composite_subscription cs, select_type s)
: dest(std::move(d))
, lifetime(std::move(cs))
, selector(std::move(s))
{
dest.add(lifetime);
}
void on_next(value_type v) const {
dest.on_next(std::move(v));
}
void on_error(rxu::error_ptr e) const {
auto selected = on_exception(
[&](){
return this->selector(std::move(e));},
dest);
if (selected.empty()) {
return;
}
selected->subscribe(dest);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<T, observer_type> make(dest_type d, select_type s) {
auto cs = composite_subscription();
return make_subscriber<T>(cs, observer_type(this_type(std::move(d), cs, std::move(s))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(on_error_resume_next_observer<Subscriber>::make(std::move(dest), selector)) {
return on_error_resume_next_observer<Subscriber>::make(std::move(dest), selector);
}
};
}
/*! @copydoc rx-on_error_resume_next.hpp
*/
template<class... AN>
auto on_error_resume_next(AN&&... an)
-> operator_factory<on_error_resume_next_tag, AN...> {
return operator_factory<on_error_resume_next_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! @copydoc rx-on_error_resume_next.hpp
*/
template<class... AN>
auto switch_on_error(AN&&... an)
-> operator_factory<on_error_resume_next_tag, AN...> {
return operator_factory<on_error_resume_next_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<on_error_resume_next_tag>
{
template<class Observable, class Selector,
class SourceValue = rxu::value_type_t<Observable>,
class OnErrorResumeNext = rxo::detail::on_error_resume_next<SourceValue, rxu::decay_t<Selector>>,
class Value = rxu::value_type_t<OnErrorResumeNext>>
static auto member(Observable&& o, Selector&& p)
-> decltype(o.template lift<Value>(OnErrorResumeNext(std::forward<Selector>(p)))) {
return o.template lift<Value>(OnErrorResumeNext(std::forward<Selector>(p)));
}
template<class... AN>
static operators::detail::on_error_resume_next_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "on_error_resume_next takes (Selector)");
}
};
}
#endif

View File

@ -0,0 +1,126 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-pairwise.hpp
\brief Take values pairwise from this observable.
\return Observable that emits tuples of two the most recent items emitted by the source observable.
\sample
\snippet pairwise.cpp pairwise sample
\snippet output.txt pairwise sample
If the source observable emits less than two items, no pairs are emitted by the source observable:
\snippet pairwise.cpp pairwise short sample
\snippet output.txt pairwise short sample
*/
#if !defined(RXCPP_OPERATORS_RX_PAIRWISE_HPP)
#define RXCPP_OPERATORS_RX_PAIRWISE_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct pairwise_invalid_arguments {};
template<class... AN>
struct pairwise_invalid : public rxo::operator_base<pairwise_invalid_arguments<AN...>> {
using type = observable<pairwise_invalid_arguments<AN...>, pairwise_invalid<AN...>>;
};
template<class... AN>
using pairwise_invalid_t = typename pairwise_invalid<AN...>::type;
template<class T>
struct pairwise
{
typedef rxu::decay_t<T> source_value_type;
typedef std::tuple<source_value_type, source_value_type> value_type;
template<class Subscriber>
struct pairwise_observer
{
typedef pairwise_observer<Subscriber> this_type;
typedef std::tuple<source_value_type, source_value_type> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
dest_type dest;
mutable rxu::detail::maybe<source_value_type> remembered;
pairwise_observer(dest_type d)
: dest(std::move(d))
{
}
void on_next(source_value_type v) const {
if (remembered.empty()) {
remembered.reset(v);
return;
}
dest.on_next(std::make_tuple(remembered.get(), v));
remembered.reset(v);
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<T, observer_type> make(dest_type d) {
auto cs = d.get_subscription();
return make_subscriber<T>(std::move(cs), observer_type(this_type(std::move(d))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(pairwise_observer<Subscriber>::make(std::move(dest))) {
return pairwise_observer<Subscriber>::make(std::move(dest));
}
};
}
/*! @copydoc rx-pairwise.hpp
*/
template<class... AN>
auto pairwise(AN&&... an)
-> operator_factory<pairwise_tag, AN...> {
return operator_factory<pairwise_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<pairwise_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Pairwise = rxo::detail::pairwise<SourceValue>,
class Value = rxu::value_type_t<Pairwise>>
static auto member(Observable&& o)
-> decltype(o.template lift<Value>(Pairwise())) {
return o.template lift<Value>(Pairwise());
}
template<class... AN>
static operators::detail::pairwise_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "pairwise takes no arguments");
}
};
}
#endif

View File

@ -0,0 +1,164 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-publish.hpp
\brief Turn a cold observable hot and allow connections to the source to be independent of subscriptions.
Turn a cold observable hot, send the most recent value to any new subscriber, and allow connections to the source to be independent of subscriptions.
\tparam T the type of the emitted item (optional).
\param first an initial item to be emitted by the resulting observable at connection time before emitting the items from the source observable; not emitted to observers that subscribe after the time of connection (optional).
\param cs the subscription to control lifetime (optional).
\return rxcpp::connectable_observable that upon connection causes the source observable to emit items to its observers.
\sample
\snippet publish.cpp publish subject sample
\snippet output.txt publish subject sample
\sample
\snippet publish.cpp publish behavior sample
\snippet output.txt publish behavior sample
\sample
\snippet publish.cpp publish diamond samethread sample
\snippet output.txt publish diamond samethread sample
\sample
\snippet publish.cpp publish diamond bgthread sample
\snippet output.txt publish diamond bgthread sample
\sample
\snippet ref_count.cpp ref_count other diamond sample
\snippet output.txt ref_count other diamond sample
*/
#if !defined(RXCPP_OPERATORS_RX_PUBLISH_HPP)
#define RXCPP_OPERATORS_RX_PUBLISH_HPP
#include "../rx-includes.hpp"
#include "./rx-multicast.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct publish_invalid_arguments {};
template<class... AN>
struct publish_invalid : public rxo::operator_base<publish_invalid_arguments<AN...>> {
using type = observable<publish_invalid_arguments<AN...>, publish_invalid<AN...>>;
};
template<class... AN>
using publish_invalid_t = typename publish_invalid<AN...>::type;
}
/*! @copydoc rx-publish.hpp
*/
template<class... AN>
auto publish(AN&&... an)
-> operator_factory<publish_tag, AN...> {
return operator_factory<publish_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! \brief Turn a cold observable hot and allow connections to the source to be independent of subscriptions.
\tparam Coordination the type of the scheduler.
\param cn a scheduler all values are queued and delivered on.
\param cs the subscription to control lifetime (optional).
\return rxcpp::connectable_observable that upon connection causes the source observable to emit items to its observers, on the specified scheduler.
\sample
\snippet publish.cpp publish_synchronized sample
\snippet output.txt publish_synchronized sample
*/
template<class... AN>
auto publish_synchronized(AN&&... an)
-> operator_factory<publish_synchronized_tag, AN...> {
return operator_factory<publish_synchronized_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<publish_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::subject<SourceValue>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o) {
return Result(Multicast(std::forward<Observable>(o), Subject(composite_subscription())));
}
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::subject<SourceValue>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, composite_subscription cs) {
return Result(Multicast(std::forward<Observable>(o), Subject(cs)));
}
template<class Observable, class T,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::behavior<SourceValue>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, T first, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(first, cs)));
}
template<class... AN>
static operators::detail::publish_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "publish takes (optional CompositeSubscription) or (T, optional CompositeSubscription)");
}
};
template<>
struct member_overload<publish_synchronized_tag>
{
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::synchronize<SourceValue, rxu::decay_t<Coordination>>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, Coordination&& cn, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(std::forward<Coordination>(cn), cs)));
}
template<class... AN>
static operators::detail::publish_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "publish_synchronized takes (Coordination, optional CompositeSubscription)");
}
};
}
#endif

View File

@ -0,0 +1,687 @@
// 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

View File

@ -0,0 +1,222 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-ref_count.hpp
\brief Make some \c connectable_observable behave like an ordinary \c observable.
Uses a reference count of the subscribers to control the connection to the published observable.
The first subscription will cause a call to \c connect(), and the last \c unsubscribe will unsubscribe the connection.
There are 2 variants of the operator:
\li \c ref_count(): calls \c connect on the \c source \c connectable_observable.
\li \c ref_count(other): calls \c connect on the \c other \c connectable_observable.
\tparam ConnectableObservable the type of the \c other \c connectable_observable (optional)
\param other \c connectable_observable to call \c connect on (optional)
If \c other is omitted, then \c source is used instead (which must be a \c connectable_observable).
Otherwise, \c source can be a regular \c observable.
\return An \c observable that emits the items from its \c source.
\sample
\snippet ref_count.cpp ref_count other diamond sample
\snippet output.txt ref_count other diamond sample
*/
#if !defined(RXCPP_OPERATORS_RX_REF_COUNT_HPP)
#define RXCPP_OPERATORS_RX_REF_COUNT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct ref_count_invalid_arguments {};
template<class... AN>
struct ref_count_invalid : public rxo::operator_base<ref_count_invalid_arguments<AN...>> {
using type = observable<ref_count_invalid_arguments<AN...>, ref_count_invalid<AN...>>;
};
template<class... AN>
using ref_count_invalid_t = typename ref_count_invalid<AN...>::type;
// ref_count(other) takes a regular observable source, not a connectable_observable.
// use template specialization to avoid instantiating 'subscribe' for two different types
// which would cause a compilation error.
template <typename connectable_type, typename observable_type>
struct ref_count_state_base {
ref_count_state_base(connectable_type other, observable_type source)
: connectable(std::move(other))
, subscribable(std::move(source)) {}
connectable_type connectable; // connects to this. subscribes to this if subscribable empty.
observable_type subscribable; // subscribes to this if non-empty.
template <typename Subscriber>
void subscribe(Subscriber&& o) {
subscribable.subscribe(std::forward<Subscriber>(o));
}
};
// Note: explicit specializations have to be at namespace scope prior to C++17.
template <typename connectable_type>
struct ref_count_state_base<connectable_type, void> {
explicit ref_count_state_base(connectable_type c)
: connectable(std::move(c)) {}
connectable_type connectable; // connects to this. subscribes to this if subscribable empty.
template <typename Subscriber>
void subscribe(Subscriber&& o) {
connectable.subscribe(std::forward<Subscriber>(o));
}
};
template<class T,
class ConnectableObservable,
class Observable = void> // note: type order flipped versus the operator.
struct ref_count : public operator_base<T>
{
typedef rxu::decay_t<Observable> observable_type;
typedef rxu::decay_t<ConnectableObservable> connectable_type;
// ref_count() == false
// ref_count(other) == true
using has_observable_t = rxu::negation<std::is_same<void, Observable>>;
// removed constexpr to support older VC compilers
static /*constexpr */ const bool has_observable_v = has_observable_t::value;
struct ref_count_state : public std::enable_shared_from_this<ref_count_state>,
public ref_count_state_base<ConnectableObservable, Observable>
{
template <class HasObservable = has_observable_t,
class Enabled = rxu::enable_if_all_true_type_t<
rxu::negation<HasObservable>>>
explicit ref_count_state(connectable_type source)
: ref_count_state_base<ConnectableObservable, Observable>(std::move(source))
, subscribers(0)
{
}
template <bool HasObservableV = has_observable_v>
ref_count_state(connectable_type other,
typename std::enable_if<HasObservableV, observable_type>::type source)
: ref_count_state_base<ConnectableObservable, Observable>(std::move(other),
std::move(source))
, subscribers(0)
{
}
std::mutex lock;
long subscribers;
composite_subscription connection;
};
std::shared_ptr<ref_count_state> state;
// connectable_observable<T> source = ...;
// source.ref_count();
//
// calls connect on source after the subscribe on source.
template <class HasObservable = has_observable_t,
class Enabled = rxu::enable_if_all_true_type_t<
rxu::negation<HasObservable>>>
explicit ref_count(connectable_type source)
: state(std::make_shared<ref_count_state>(std::move(source)))
{
}
// connectable_observable<?> other = ...;
// observable<T> source = ...;
// source.ref_count(other);
//
// calls connect on 'other' after the subscribe on 'source'.
template <bool HasObservableV = has_observable_v>
ref_count(connectable_type other,
typename std::enable_if<HasObservableV, observable_type>::type source)
: state(std::make_shared<ref_count_state>(std::move(other), std::move(source)))
{
}
template<class Subscriber>
void on_subscribe(Subscriber&& o) const {
std::unique_lock<std::mutex> guard(state->lock);
auto needConnect = ++state->subscribers == 1;
auto keepAlive = state;
guard.unlock();
o.add(
[keepAlive](){
std::unique_lock<std::mutex> guard_unsubscribe(keepAlive->lock);
if (--keepAlive->subscribers == 0) {
keepAlive->connection.unsubscribe();
keepAlive->connection = composite_subscription();
}
});
keepAlive->subscribe(std::forward<Subscriber>(o));
if (needConnect) {
keepAlive->connectable.connect(keepAlive->connection);
}
}
};
}
/*! @copydoc rx-ref_count.hpp
*/
template<class... AN>
auto ref_count(AN&&... an)
-> operator_factory<ref_count_tag, AN...> {
return operator_factory<ref_count_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<ref_count_tag>
{
template<class ConnectableObservable,
class Enabled = rxu::enable_if_all_true_type_t<
is_connectable_observable<ConnectableObservable>>,
class SourceValue = rxu::value_type_t<ConnectableObservable>,
class RefCount = rxo::detail::ref_count<SourceValue, rxu::decay_t<ConnectableObservable>>,
class Value = rxu::value_type_t<RefCount>,
class Result = observable<Value, RefCount>
>
static Result member(ConnectableObservable&& o) {
return Result(RefCount(std::forward<ConnectableObservable>(o)));
}
template<class Observable,
class ConnectableObservable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_connectable_observable<ConnectableObservable>>,
class SourceValue = rxu::value_type_t<Observable>,
class RefCount = rxo::detail::ref_count<SourceValue,
rxu::decay_t<ConnectableObservable>,
rxu::decay_t<Observable>>,
class Value = rxu::value_type_t<RefCount>,
class Result = observable<Value, RefCount>
>
static Result member(Observable&& o, ConnectableObservable&& other) {
return Result(RefCount(std::forward<ConnectableObservable>(other),
std::forward<Observable>(o)));
}
template<class... AN>
static operators::detail::ref_count_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "ref_count takes (optional ConnectableObservable)");
}
};
}
#endif

View File

@ -0,0 +1,122 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-repeat.hpp
\brief Repeat this observable for the given number of times or infinitely.
\tparam Count the type of the counter (optional).
\param t The number of times the source observable items are repeated (optional). If not specified, infinitely repeats the source observable. Specifying 0 returns an empty sequence immediately
\return An observable that repeats the sequence of items emitted by the source observable for t times.
\sample
\snippet repeat.cpp repeat count sample
\snippet output.txt repeat count sample
If the source observable calls on_error, repeat stops:
\snippet repeat.cpp repeat error sample
\snippet output.txt repeat error sample
*/
#if !defined(RXCPP_OPERATORS_RX_REPEAT_HPP)
#define RXCPP_OPERATORS_RX_REPEAT_HPP
#include "../rx-includes.hpp"
#include "rx-retry-repeat-common.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct repeat_invalid_arguments {};
template<class... AN>
struct repeat_invalid : public rxo::operator_base<repeat_invalid_arguments<AN...>> {
using type = observable<repeat_invalid_arguments<AN...>, repeat_invalid<AN...>>;
};
template<class... AN>
using repeat_invalid_t = typename repeat_invalid<AN...>::type;
// Contain repeat variations in a namespace
namespace repeat {
struct event_handlers {
template <typename State>
static inline void on_error(State& state, rxu::error_ptr& e) {
state->out.on_error(e);
}
template <typename State>
static inline void on_completed(State& state) {
// Functions update() and completed_predicate() vary between finite and infinte versions
state->update();
if (state->completed_predicate()) {
state->out.on_completed();
} else {
state->do_subscribe();
}
}
};
// Finite repeat case (explicitely limited with the number of times)
template <class T, class Observable, class Count>
using finite = ::rxcpp::operators::detail::retry_repeat_common::finite
<event_handlers, T, Observable, Count>;
// Infinite repeat case
template <class T, class Observable>
using infinite = ::rxcpp::operators::detail::retry_repeat_common::infinite
<event_handlers, T, Observable>;
}
} // detail
/*! @copydoc rx-repeat.hpp
*/
template<class... AN>
auto repeat(AN&&... an)
-> operator_factory<repeat_tag, AN...> {
return operator_factory<repeat_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<repeat_tag> {
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Repeat = rxo::detail::repeat::infinite<SourceValue, rxu::decay_t<Observable>>,
class Value = rxu::value_type_t<Repeat>,
class Result = observable<Value, Repeat>>
static Result member(Observable&& o) {
return Result(Repeat(std::forward<Observable>(o)));
}
template<class Observable,
class Count,
class Enabled = rxu::enable_if_all_true_type_t<is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Repeat = rxo::detail::repeat::finite<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Count>>,
class Value = rxu::value_type_t<Repeat>,
class Result = observable<Value, Repeat>>
static Result member(Observable&& o, Count&& c) {
return Result(Repeat(std::forward<Observable>(o), std::forward<Count>(c)));
}
template<class... AN>
static operators::detail::repeat_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "repeat takes (optional Count)");
}
};
}
#endif

View File

@ -0,0 +1,234 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-replay.hpp
\brief 1) replay(optional Coordination, optional CompositeSubscription)
Turn a cold observable hot, send all earlier emitted values to any new subscriber, and allow connections to the source to be independent of subscriptions.
2) replay(Count, optional Coordination, optional CompositeSubscription)
Turn a cold observable hot, send at most count of earlier emitted values to any new subscriber, and allow connections to the source to be independent of subscriptions.
3) replay(Duration, optional Coordination, optional CompositeSubscription)
Turn a cold observable hot, send values emitted within a specified time window to any new subscriber, and allow connections to the source to be independent of subscriptions.
4) replay(Count, Duration, optional Coordination, optional CompositeSubscription)
Turn a cold observable hot, send at most count of values emitted within a specified time window to any new subscriber, and allow connections to the source to be independent of subscriptions.
\tparam Duration the type of the time interval (optional).
\tparam Count the type of the maximum number of the most recent items sent to new observers (optional).
\tparam Coordination the type of the scheduler (optional).
\param count the maximum number of the most recent items sent to new observers (optional).
\param d the duration of the window in which the replayed items must be emitted
\param cn a scheduler all values are queued and delivered on (optional).
\param cs the subscription to control lifetime (optional).
\return rxcpp::connectable_observable that shares a single subscription to the underlying observable that will replay all of its items and notifications to any future observer.
\sample
\snippet replay.cpp replay sample
\snippet output.txt replay sample
\sample
\snippet replay.cpp threaded replay sample
\snippet output.txt threaded replay sample
\sample
\snippet replay.cpp replay count sample
\snippet output.txt replay count sample
\sample
\snippet replay.cpp threaded replay count sample
\snippet output.txt threaded replay count sample
\sample
\snippet replay.cpp replay period sample
\snippet output.txt replay period sample
\sample
\snippet replay.cpp threaded replay period sample
\snippet output.txt threaded replay period sample
\sample
\snippet replay.cpp replay count+period sample
\snippet output.txt replay count+period sample
\sample
\snippet replay.cpp threaded replay count+period sample
\snippet output.txt threaded replay count+period sample
*/
#if !defined(RXCPP_OPERATORS_RX_REPLAY_HPP)
#define RXCPP_OPERATORS_RX_REPLAY_HPP
#include "../rx-includes.hpp"
#include "./rx-multicast.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct replay_invalid_arguments {};
template<class... AN>
struct replay_invalid : public rxo::operator_base<replay_invalid_arguments<AN...>> {
using type = observable<replay_invalid_arguments<AN...>, replay_invalid<AN...>>;
};
template<class... AN>
using replay_invalid_t = typename replay_invalid<AN...>::type;
}
/*! @copydoc rx-replay.hpp
*/
template<class... AN>
auto replay(AN&&... an)
-> operator_factory<replay_tag, AN...> {
return operator_factory<replay_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<replay_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, identity_one_worker>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o) {
return Result(Multicast(std::forward<Observable>(o), Subject(identity_current_thread(), composite_subscription())));
}
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, identity_one_worker>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, composite_subscription cs) {
return Result(Multicast(std::forward<Observable>(o), Subject(identity_current_thread(), cs)));
}
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, rxu::decay_t<Coordination>>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, Coordination&& cn, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(std::forward<Coordination>(cn), cs)));
}
template<class Observable, class Count,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_integral<Count>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, identity_one_worker>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, Count count, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(count, identity_current_thread(), cs)));
}
template<class Observable, class Count, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_integral<Count>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, rxu::decay_t<Coordination>>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, Count count, Coordination&& cn, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(count, std::forward<Coordination>(cn), cs)));
}
template<class Observable, class Duration,
class IsDuration = rxu::is_duration<Duration>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
IsDuration>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, identity_one_worker>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, Duration&& d, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(std::forward<Duration>(d), identity_current_thread(), cs)));
}
template<class Observable, class Duration, class Coordination,
class IsDuration = rxu::is_duration<Duration>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
IsDuration,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, rxu::decay_t<Coordination>>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, Duration&& d, Coordination&& cn, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(std::forward<Duration>(d), std::forward<Coordination>(cn), cs)));
}
template<class Observable, class Count, class Duration,
class IsDuration = rxu::is_duration<Duration>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_integral<Count>,
IsDuration>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, identity_one_worker>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, Count count, Duration&& d, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(count, std::forward<Duration>(d), identity_current_thread(), cs)));
}
template<class Observable, class Count, class Duration, class Coordination,
class IsDuration = rxu::is_duration<Duration>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_integral<Count>,
IsDuration,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Subject = rxsub::replay<SourceValue, rxu::decay_t<Coordination>>,
class Multicast = rxo::detail::multicast<SourceValue, rxu::decay_t<Observable>, Subject>,
class Result = connectable_observable<SourceValue, Multicast>
>
static Result member(Observable&& o, Count count, Duration&& d, Coordination&& cn, composite_subscription cs = composite_subscription()) {
return Result(Multicast(std::forward<Observable>(o), Subject(count, std::forward<Duration>(d), std::forward<Coordination>(cn), cs)));
}
template<class... AN>
static operators::detail::replay_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "replay takes (optional Count, optional Duration, optional Coordination, optional CompositeSubscription)");
}
};
}
#endif

View File

@ -0,0 +1,153 @@
#pragma once
/*! \file rx-retry-repeat-common.hpp
\brief Implementation commonalities between retry and repeat operators abstracted away from rx-retry.hpp and rx-repeat.hpp files. Should be used only from rx-retry.hpp and rx-repeat.hpp
*/
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
namespace retry_repeat_common {
// Structure to perform general retry/repeat operations on state
template <class Values, class Subscriber, class EventHandlers, class T>
struct state_type : public std::enable_shared_from_this<state_type<Values, Subscriber, EventHandlers, T>>,
public Values {
typedef Subscriber output_type;
state_type(const Values& i, const output_type& oarg)
: Values(i),
source_lifetime(composite_subscription::empty()),
out(oarg) {
}
void do_subscribe() {
auto state = this->shared_from_this();
state->out.remove(state->lifetime_token);
state->source_lifetime.unsubscribe();
state->source_lifetime = composite_subscription();
state->lifetime_token = state->out.add(state->source_lifetime);
state->source.subscribe(
state->out,
state->source_lifetime,
// on_next
[state](T t) {
state->out.on_next(t);
},
// on_error
[state](rxu::error_ptr e) {
EventHandlers::on_error(state, e);
},
// on_completed
[state]() {
EventHandlers::on_completed(state);
}
);
}
composite_subscription source_lifetime;
output_type out;
composite_subscription::weak_subscription lifetime_token;
};
// Finite case (explicitely limited with the number of times)
template <class EventHandlers, class T, class Observable, class Count>
struct finite : public operator_base<T> {
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Count> count_type;
struct values {
values(source_type s, count_type t)
: source(std::move(s)),
remaining_(std::move(t)) {
}
inline bool completed_predicate() const {
// Return true if we are completed
return remaining_ <= 0;
}
inline void update() {
// Decrement counter
--remaining_;
}
source_type source;
private:
// Counter to hold number of times remaining to complete
count_type remaining_;
};
finite(source_type s, count_type t)
: initial_(std::move(s), std::move(t)) {
}
template<class Subscriber>
void on_subscribe(const Subscriber& s) const {
typedef state_type<values, Subscriber, EventHandlers, T> state_t;
// take a copy of the values for each subscription
auto state = std::make_shared<state_t>(initial_, s);
if (initial_.completed_predicate()) {
// return completed
state->out.on_completed();
} else {
// start the first iteration
state->do_subscribe();
}
}
private:
values initial_;
};
// Infinite case
template <class EventHandlers, class T, class Observable>
struct infinite : public operator_base<T> {
typedef rxu::decay_t<Observable> source_type;
struct values {
values(source_type s)
: source(std::move(s)) {
}
static inline bool completed_predicate() {
// Infinite never completes
return false;
}
static inline void update() {
// Infinite does not need to update state
}
source_type source;
};
infinite(source_type s) : initial_(std::move(s)) {
}
template<class Subscriber>
void on_subscribe(const Subscriber& s) const {
typedef state_type<values, Subscriber, EventHandlers, T> state_t;
// take a copy of the values for each subscription
auto state = std::make_shared<state_t>(initial_, s);
// start the first iteration
state->do_subscribe();
}
private:
values initial_;
};
}
}
}
}

View File

@ -0,0 +1,121 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-retry.hpp
\brief Retry this observable for the given number of times.
\tparam Count the type of the counter (optional)
\param t the total number of tries (optional), i.e. retry(2) means one normal try, before an error occurs, and one retry. If not specified, infinitely retries the source observable. Specifying 0 returns immediately without subscribing
\return An observable that mirrors the source observable, resubscribing to it if it calls on_error up to a specified number of retries.
\sample
\snippet retry.cpp retry count sample
\snippet output.txt retry count sample
*/
#if !defined(RXCPP_OPERATORS_RX_RETRY_HPP)
#define RXCPP_OPERATORS_RX_RETRY_HPP
#include "../rx-includes.hpp"
#include "rx-retry-repeat-common.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct retry_invalid_arguments {};
template<class... AN>
struct retry_invalid : public rxo::operator_base<retry_invalid_arguments<AN...>> {
using type = observable<retry_invalid_arguments<AN...>, retry_invalid<AN...>>;
};
template<class... AN>
using retry_invalid_t = typename retry_invalid<AN...>::type;
// Contain retry variations in a namespace
namespace retry {
struct event_handlers {
template <typename State>
static inline void on_error(State& state, rxu::error_ptr& e) {
state->update();
// Use specialized predicate for finite/infinte case
if (state->completed_predicate()) {
state->out.on_error(e);
} else {
state->do_subscribe();
}
}
template <typename State>
static inline void on_completed(State& state) {
state->out.on_completed();
}
};
// Finite repeat case (explicitely limited with the number of times)
template <class T, class Observable, class Count>
using finite = ::rxcpp::operators::detail::retry_repeat_common::finite
<event_handlers, T, Observable, Count>;
// Infinite repeat case
template <class T, class Observable>
using infinite = ::rxcpp::operators::detail::retry_repeat_common::infinite
<event_handlers, T, Observable>;
}
} // detail
/*! @copydoc rx-retry.hpp
*/
template<class... AN>
auto retry(AN&&... an)
-> operator_factory<retry_tag, AN...> {
return operator_factory<retry_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<retry_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Retry = rxo::detail::retry::infinite<SourceValue, rxu::decay_t<Observable>>,
class Value = rxu::value_type_t<Retry>,
class Result = observable<Value, Retry>
>
static Result member(Observable&& o) {
return Result(Retry(std::forward<Observable>(o)));
}
template<class Observable,
class Count,
class Enabled = rxu::enable_if_all_true_type_t<is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Retry = rxo::detail::retry::finite<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Count>>,
class Value = rxu::value_type_t<Retry>,
class Result = observable<Value, Retry>
>
static Result member(Observable&& o, Count&& c) {
return Result(Retry(std::forward<Observable>(o), std::forward<Count>(c)));
}
template<class... AN>
static operators::detail::retry_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "retry takes (optional Count)");
}
};
}
#endif

View File

@ -0,0 +1,256 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-sample_time.hpp
\brief Return an Observable that emits the most recent items emitted by the source Observable within periodic time intervals.
\tparam Duration the type of time interval.
\tparam Coordination the type of the scheduler (optional).
\param period the period of time to sample the source observable.
\param coordination the scheduler for the items (optional).
\return Observable that emits the most recently emitted item since the previous sampling.
\sample
\snippet sample.cpp sample period sample
\snippet output.txt sample period sample
*/
#if !defined(RXCPP_OPERATORS_RX_SAMPLE_WITH_TIME_HPP)
#define RXCPP_OPERATORS_RX_SAMPLE_WITH_TIME_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct sample_with_time_invalid_arguments {};
template<class... AN>
struct sample_with_time_invalid : public rxo::operator_base<sample_with_time_invalid_arguments<AN...>> {
using type = observable<sample_with_time_invalid_arguments<AN...>, sample_with_time_invalid<AN...>>;
};
template<class... AN>
using sample_with_time_invalid_t = typename sample_with_time_invalid<AN...>::type;
template<class T, class Duration, class Coordination>
struct sample_with_time
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef rxu::decay_t<Duration> duration_type;
struct sample_with_time_value
{
sample_with_time_value(duration_type p, coordination_type c)
: period(p)
, coordination(c)
{
}
duration_type period;
coordination_type coordination;
};
sample_with_time_value initial;
sample_with_time(duration_type period, coordination_type coordination)
: initial(period, coordination)
{
}
template<class Subscriber>
struct sample_with_time_observer
{
typedef sample_with_time_observer<Subscriber> this_type;
typedef T value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
struct sample_with_time_subscriber_value : public sample_with_time_value
{
sample_with_time_subscriber_value(composite_subscription cs, dest_type d, sample_with_time_value v, coordinator_type c)
: sample_with_time_value(v)
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
mutable rxu::maybe<value_type> value;
};
std::shared_ptr<sample_with_time_subscriber_value> state;
sample_with_time_observer(composite_subscription cs, dest_type d, sample_with_time_value v, coordinator_type c)
: state(std::make_shared<sample_with_time_subscriber_value>(sample_with_time_subscriber_value(std::move(cs), std::move(d), v, std::move(c))))
{
auto localState = state;
auto disposer = [=](const rxsc::schedulable&){
localState->cs.unsubscribe();
localState->dest.unsubscribe();
localState->worker.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){ return localState->coordinator.act(disposer); },
localState->dest);
if (selectedDisposer.empty()) {
return;
}
localState->dest.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
localState->cs.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
auto produce_sample = [localState](const rxsc::schedulable&) {
if(!localState->value.empty()) {
localState->dest.on_next(*localState->value);
localState->value.reset();
}
};
auto selectedProduce = on_exception(
[&](){ return localState->coordinator.act(produce_sample); },
localState->dest);
if (selectedProduce.empty()) {
return;
}
state->worker.schedule_periodically(
localState->worker.now(),
localState->period,
[localState, selectedProduce](const rxsc::schedulable&) {
localState->worker.schedule(selectedProduce.get());
});
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable&) {
localState->value.reset(v);
};
auto selectedWork = on_exception(
[&](){ return localState->coordinator.act(work); },
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&) {
localState->dest.on_error(e);
};
auto selectedWork = on_exception(
[&](){ return localState->coordinator.act(work); },
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&) {
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){ return localState->coordinator.act(work); },
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static subscriber<T, observer<T, this_type>> make(dest_type d, sample_with_time_value v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator();
return make_subscriber<T>(cs, this_type(cs, std::move(d), std::move(v), std::move(coordinator)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(sample_with_time_observer<Subscriber>::make(std::move(dest), initial)) {
return sample_with_time_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-sample_time.hpp
*/
template<class... AN>
auto sample_with_time(AN&&... an)
-> operator_factory<sample_with_time_tag, AN...> {
return operator_factory<sample_with_time_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<sample_with_time_tag>
{
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class SampleWithTime = rxo::detail::sample_with_time<SourceValue, rxu::decay_t<Duration>, identity_one_worker>>
static auto member(Observable&& o, Duration&& d)
-> decltype(o.template lift<SourceValue>(SampleWithTime(std::forward<Duration>(d), identity_current_thread()))) {
return o.template lift<SourceValue>(SampleWithTime(std::forward<Duration>(d), identity_current_thread()));
}
template<class Observable, class Coordination, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class SampleWithTime = rxo::detail::sample_with_time<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Coordination&& cn, Duration&& d)
-> decltype(o.template lift<SourceValue>(SampleWithTime(std::forward<Duration>(d), std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(SampleWithTime(std::forward<Duration>(d), std::forward<Coordination>(cn)));
}
template<class Observable, class Coordination, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class SampleWithTime = rxo::detail::sample_with_time<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Duration&& d, Coordination&& cn)
-> decltype(o.template lift<SourceValue>(SampleWithTime(std::forward<Duration>(d), std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(SampleWithTime(std::forward<Duration>(d), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::sample_with_time_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "sample_with_time takes (optional Coordination, required Duration) or (required Duration, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,141 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-scan.hpp
\brief For each item from this observable use Accumulator to combine items into 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.
\param seed the initial value for the accumulator.
\param a an accumulator function to be invoked on each item emitted by the source observable, whose result will be emitted and used in the next accumulator call.
\return An observable that emits the results of each call to the accumulator function.
\sample
\snippet scan.cpp scan sample
\snippet output.txt scan sample
*/
#if !defined(RXCPP_OPERATORS_RX_SCAN_HPP)
#define RXCPP_OPERATORS_RX_SCAN_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct scan_invalid_arguments {};
template<class... AN>
struct scan_invalid : public rxo::operator_base<scan_invalid_arguments<AN...>> {
using type = observable<scan_invalid_arguments<AN...>, scan_invalid<AN...>>;
};
template<class... AN>
using scan_invalid_t = typename scan_invalid<AN...>::type;
template<class T, class Observable, class Accumulator, class Seed>
struct scan : public operator_base<rxu::decay_t<Seed>>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Accumulator> accumulator_type;
typedef rxu::decay_t<Seed> seed_type;
struct scan_initial_type
{
scan_initial_type(source_type o, accumulator_type a, seed_type s)
: source(std::move(o))
, accumulator(std::move(a))
, seed(s)
{
}
source_type source;
accumulator_type accumulator;
seed_type seed;
};
scan_initial_type initial;
scan(source_type o, accumulator_type a, seed_type s)
: initial(std::move(o), a, s)
{
}
template<class Subscriber>
void on_subscribe(Subscriber o) const {
struct scan_state_type
: public scan_initial_type
, public std::enable_shared_from_this<scan_state_type>
{
scan_state_type(scan_initial_type i, Subscriber scrbr)
: scan_initial_type(i)
, result(scan_initial_type::seed)
, out(std::move(scrbr))
{
}
seed_type result;
Subscriber out;
};
auto state = std::make_shared<scan_state_type>(initial, std::move(o));
state->source.subscribe(
state->out,
// on_next
[state](T t) {
state->result = state->accumulator(state->result, t);
state->out.on_next(state->result);
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state]() {
state->out.on_completed();
}
);
}
};
}
/*! @copydoc rx-scan.hpp
*/
template<class... AN>
auto scan(AN&&... an)
-> operator_factory<scan_tag, AN...> {
return operator_factory<scan_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<scan_tag>
{
template<class Observable, class Seed, class Accumulator,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_accumulate_function_for<rxu::value_type_t<Observable>, rxu::decay_t<Seed>, rxu::decay_t<Accumulator>>>,
class SourceValue = rxu::value_type_t<Observable>,
class Scan = rxo::detail::scan<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Accumulator>, rxu::decay_t<Seed>>,
class Value = rxu::value_type_t<Scan>,
class Result = observable<Value, Scan>>
static Result member(Observable&& o, Seed s, Accumulator&& a) {
return Result(Scan(std::forward<Observable>(o), std::forward<Accumulator>(a), s));
}
template<class... AN>
static operators::detail::scan_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "scan takes (Seed, Accumulator); Accumulator must be a function with the signature Seed(Seed, T)");
}
};
}
#endif

View File

@ -0,0 +1,282 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-sequence_equal.hpp
\brief Determine whether two Observables emit the same sequence of items.
\tparam OtherSource the type of the other observable.
\tparam BinaryPredicate the type of the value comparing function (optional). The signature should be equivalent to the following: bool pred(const T1& a, const T2& b);
\tparam Coordination the type of the scheduler (optional).
\param t the other Observable that emits items to compare.
\param pred the function that implements comparison of two values (optional).
\param cn the scheduler (optional).
\return Observable that emits true only if both sequences terminate normally after emitting the same sequence of items in the same order; otherwise it will emit false.
\sample
\snippet sequence_equal.cpp sequence_equal sample
\snippet output.txt sequence_equal sample
*/
#if !defined(RXCPP_OPERATORS_RX_SEQUENCE_EQUAL_HPP)
#define RXCPP_OPERATORS_RX_SEQUENCE_EQUAL_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct sequence_equal_invalid_arguments {};
template<class... AN>
struct sequence_equal_invalid : public rxo::operator_base<sequence_equal_invalid_arguments<AN...>> {
using type = observable<sequence_equal_invalid_arguments<AN...>, sequence_equal_invalid<AN...>>;
};
template<class... AN>
using sequence_equal_invalid_t = typename sequence_equal_invalid<AN...>::type;
template<class T, class Observable, class OtherObservable, class BinaryPredicate, class Coordination>
struct sequence_equal : public operator_base<bool>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<OtherObservable> other_source_type;
typedef typename other_source_type::value_type other_source_value_type;
typedef rxu::decay_t<BinaryPredicate> predicate_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values {
values(source_type s, other_source_type t, predicate_type pred, coordination_type sf)
: source(std::move(s))
, other(std::move(t))
, pred(std::move(pred))
, coordination(std::move(sf))
{
}
source_type source;
other_source_type other;
predicate_type pred;
coordination_type coordination;
};
values initial;
sequence_equal(source_type s, other_source_type t, predicate_type pred, coordination_type sf)
: initial(std::move(s), std::move(t), std::move(pred), std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber s) const {
typedef Subscriber output_type;
struct state_type
: public std::enable_shared_from_this<state_type>
, public values
{
state_type(const values& vals, coordinator_type coor, const output_type& o)
: values(vals)
, coordinator(std::move(coor))
, out(o)
, source_completed(false)
, other_completed(false)
{
out.add(other_lifetime);
out.add(source_lifetime);
}
composite_subscription other_lifetime;
composite_subscription source_lifetime;
coordinator_type coordinator;
output_type out;
mutable std::list<source_value_type> source_values;
mutable std::list<other_source_value_type> other_values;
mutable bool source_completed;
mutable bool other_completed;
};
auto coordinator = initial.coordination.create_coordinator();
auto state = std::make_shared<state_type>(initial, std::move(coordinator), std::move(s));
auto other = on_exception(
[&](){ return state->coordinator.in(state->other); },
state->out);
if (other.empty()) {
return;
}
auto source = on_exception(
[&](){ return state->coordinator.in(state->source); },
state->out);
if (source.empty()) {
return;
}
auto check_equal = [state]() {
if(!state->source_values.empty() && !state->other_values.empty()) {
auto x = std::move(state->source_values.front());
state->source_values.pop_front();
auto y = std::move(state->other_values.front());
state->other_values.pop_front();
if (!state->pred(x, y)) {
state->out.on_next(false);
state->out.on_completed();
}
} else {
if((!state->source_values.empty() && state->other_completed) ||
(!state->other_values.empty() && state->source_completed)) {
state->out.on_next(false);
state->out.on_completed();
}
}
};
auto check_complete = [state]() {
if(state->source_completed && state->other_completed) {
state->out.on_next(state->source_values.empty() && state->other_values.empty());
state->out.on_completed();
}
};
auto sinkOther = make_subscriber<other_source_value_type>(
state->out,
state->other_lifetime,
// on_next
[state, check_equal](other_source_value_type t) {
auto& values = state->other_values;
values.push_back(t);
check_equal();
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state, check_complete]() {
auto& completed = state->other_completed;
completed = true;
check_complete();
}
);
auto selectedSinkOther = on_exception(
[&](){ return state->coordinator.out(sinkOther); },
state->out);
if (selectedSinkOther.empty()) {
return;
}
other->subscribe(std::move(selectedSinkOther.get()));
source.get().subscribe(
state->source_lifetime,
// on_next
[state, check_equal](source_value_type t) {
auto& values = state->source_values;
values.push_back(t);
check_equal();
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state, check_complete]() {
auto& completed = state->source_completed;
completed = true;
check_complete();
}
);
}
};
}
/*! @copydoc rx-sequence_equal.hpp
*/
template<class... AN>
auto sequence_equal(AN&&... an)
-> operator_factory<sequence_equal_tag, AN...> {
return operator_factory<sequence_equal_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<sequence_equal_tag>
{
template<class Observable, class OtherObservable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_observable<OtherObservable>>,
class SourceValue = rxu::value_type_t<Observable>,
class SequenceEqual = rxo::detail::sequence_equal<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<OtherObservable>, rxu::equal_to<>, identity_one_worker>,
class Value = rxu::value_type_t<SequenceEqual>,
class Result = observable<Value, SequenceEqual>>
static Result member(Observable&& o, OtherObservable&& t) {
return Result(SequenceEqual(std::forward<Observable>(o), std::forward<OtherObservable>(t), rxu::equal_to<>(), identity_current_thread()));
}
template<class Observable, class OtherObservable, class BinaryPredicate,
class IsCoordination = is_coordination<BinaryPredicate>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_observable<OtherObservable>,
rxu::negation<IsCoordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class SequenceEqual = rxo::detail::sequence_equal<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<OtherObservable>, rxu::decay_t<BinaryPredicate>, identity_one_worker>,
class Value = rxu::value_type_t<SequenceEqual>,
class Result = observable<Value, SequenceEqual>>
static Result member(Observable&& o, OtherObservable&& t, BinaryPredicate&& pred) {
return Result(SequenceEqual(std::forward<Observable>(o), std::forward<OtherObservable>(t), std::forward<BinaryPredicate>(pred), identity_current_thread()));
}
template<class Observable, class OtherObservable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_observable<OtherObservable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class SequenceEqual = rxo::detail::sequence_equal<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<OtherObservable>, rxu::equal_to<>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SequenceEqual>,
class Result = observable<Value, SequenceEqual>>
static Result member(Observable&& o, OtherObservable&& t, Coordination&& cn) {
return Result(SequenceEqual(std::forward<Observable>(o), std::forward<OtherObservable>(t), rxu::equal_to<>(), std::forward<Coordination>(cn)));
}
template<class Observable, class OtherObservable, class BinaryPredicate, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_observable<OtherObservable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class SequenceEqual = rxo::detail::sequence_equal<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<OtherObservable>, rxu::decay_t<BinaryPredicate>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SequenceEqual>,
class Result = observable<Value, SequenceEqual>>
static Result member(Observable&& o, OtherObservable&& t, BinaryPredicate&& pred, Coordination&& cn) {
return Result(SequenceEqual(std::forward<Observable>(o), std::forward<OtherObservable>(t), std::forward<BinaryPredicate>(pred), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::sequence_equal_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "sequence_equal takes (OtherObservable, optional BinaryPredicate, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,162 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-skip.hpp
\brief Make new observable with skipped first count items from this observable.
\tparam Count the type of the items counter
\param t the number of items to skip
\return An observable that is identical to the source observable except that it does not emit the first t items that the source observable emits.
\sample
\snippet skip.cpp skip sample
\snippet output.txt skip sample
*/
#if !defined(RXCPP_OPERATORS_RX_SKIP_HPP)
#define RXCPP_OPERATORS_RX_SKIP_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct skip_invalid_arguments {};
template<class... AN>
struct skip_invalid : public rxo::operator_base<skip_invalid_arguments<AN...>> {
using type = observable<skip_invalid_arguments<AN...>, skip_invalid<AN...>>;
};
template<class... AN>
using skip_invalid_t = typename skip_invalid<AN...>::type;
template<class T, class Observable, class Count>
struct skip : public operator_base<T>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Count> count_type;
struct values
{
values(source_type s, count_type t)
: source(std::move(s))
, count(std::move(t))
{
}
source_type source;
count_type count;
};
values initial;
skip(source_type s, count_type t)
: initial(std::move(s), std::move(t))
{
}
struct mode
{
enum type {
skipping, // ignore messages
triggered, // capture messages
errored, // error occured
stopped // observable completed
};
};
template<class Subscriber>
void on_subscribe(const Subscriber& s) const {
typedef Subscriber output_type;
struct state_type
: public std::enable_shared_from_this<state_type>
, public values
{
state_type(const values& i, const output_type& oarg)
: values(i)
, mode_value(i.count > 0 ? mode::skipping : mode::triggered)
, out(oarg)
{
}
typename mode::type mode_value;
output_type out;
};
// take a copy of the values for each subscription
auto state = std::make_shared<state_type>(initial, s);
composite_subscription source_lifetime;
s.add(source_lifetime);
state->source.subscribe(
// split subscription lifetime
source_lifetime,
// on_next
[state](T t) {
if (state->mode_value == mode::skipping) {
if (--state->count == 0) {
state->mode_value = mode::triggered;
}
} else {
state->out.on_next(t);
}
},
// on_error
[state](rxu::error_ptr e) {
state->mode_value = mode::errored;
state->out.on_error(e);
},
// on_completed
[state]() {
state->mode_value = mode::stopped;
state->out.on_completed();
}
);
}
};
}
/*! @copydoc rx-skip.hpp
*/
template<class... AN>
auto skip(AN&&... an)
-> operator_factory<skip_tag, AN...> {
return operator_factory<skip_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<skip_tag>
{
template<class Observable,
class Count,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Skip = rxo::detail::skip<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Count>>,
class Value = rxu::value_type_t<Skip>,
class Result = observable<Value, Skip>>
static Result member(Observable&& o, Count&& c) {
return Result(Skip(std::forward<Observable>(o), std::forward<Count>(c)));
}
template<class... AN>
static operators::detail::skip_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "skip takes (optional Count)");
}
};
}
#endif

View File

@ -0,0 +1,153 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-skip_last.hpp
\brief Make new observable with skipped last count items from this observable.
\tparam Count the type of the items counter.
\param t the number of last items to skip.
\return An observable that is identical to the source observable except that it does not emit the last t items that the source observable emits.
\sample
\snippet skip_last.cpp skip_last sample
\snippet output.txt skip_last sample
*/
#if !defined(RXCPP_OPERATORS_RX_SKIP_LAST_HPP)
#define RXCPP_OPERATORS_RX_SKIP_LAST_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct skip_last_invalid_arguments {};
template<class... AN>
struct skip_last_invalid : public rxo::operator_base<skip_last_invalid_arguments<AN...>> {
using type = observable<skip_last_invalid_arguments<AN...>, skip_last_invalid<AN...>>;
};
template<class... AN>
using skip_last_invalid_t = typename skip_last_invalid<AN...>::type;
template<class T, class Observable, class Count>
struct skip_last : public operator_base<T>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Count> count_type;
typedef std::queue<T> queue_type;
typedef typename queue_type::size_type queue_size_type;
struct values
{
values(source_type s, count_type t)
: source(std::move(s))
, count(static_cast<queue_size_type>(t))
{
}
source_type source;
queue_size_type count;
};
values initial;
skip_last(source_type s, count_type t)
: initial(std::move(s), std::move(t))
{
}
template<class Subscriber>
void on_subscribe(const Subscriber& s) const {
typedef Subscriber output_type;
struct state_type
: public std::enable_shared_from_this<state_type>
, public values
{
state_type(const values& i, const output_type& oarg)
: values(i)
, out(oarg)
{
}
queue_type items;
output_type out;
};
// take a copy of the values for each subscription
auto state = std::make_shared<state_type>(initial, s);
composite_subscription source_lifetime;
s.add(source_lifetime);
state->source.subscribe(
// split subscription lifetime
source_lifetime,
// on_next
[state](T t) {
if(state->count > 0) {
if (state->items.size() == state->count) {
state->out.on_next(std::move(state->items.front()));
state->items.pop();
}
state->items.push(t);
} else {
state->out.on_next(t);
}
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state]() {
state->out.on_completed();
}
);
}
};
}
/*! @copydoc rx-skip_last.hpp
*/
template<class... AN>
auto skip_last(AN&&... an)
-> operator_factory<skip_last_tag, AN...> {
return operator_factory<skip_last_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<skip_last_tag>
{
template<class Observable, class Count,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class SkipLast = rxo::detail::skip_last<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Count>>,
class Value = rxu::value_type_t<SkipLast>,
class Result = observable<Value, SkipLast>>
static Result member(Observable&& o, Count&& c) {
return Result(SkipLast(std::forward<Observable>(o), std::forward<Count>(c)));
}
template<class... AN>
static operators::detail::skip_last_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "skip_last takes (Count)");
}
};
}
#endif

View File

@ -0,0 +1,276 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-skip_until.hpp
\brief Make new observable with items skipped until on_next occurs on the trigger observable or until the specified time.
skip_until takes (TriggerObservable, optional Coordination) or (TimePoint, optional Coordination)
\tparam TriggerSource the type of the trigger observable.
\tparam Coordination the type of the scheduler (optional).
\param t an observable that has to emit an item before the source observable's elements begin to be mirrored by the resulting observable.
\param cn the scheduler to use for scheduling the items (optional).
\return An observable that skips items from the source observable until the second observable emits an item or the time runs out, then emits the remaining items.
\sample
\snippet skip_until.cpp skip_until sample
\snippet output.txt skip_until sample
\sample
\snippet skip_until.cpp threaded skip_until sample
\snippet output.txt threaded skip_until sample
*/
#if !defined(RXCPP_OPERATORS_RX_SKIP_UNTIL_HPP)
#define RXCPP_OPERATORS_RX_SKIP_UNTIL_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct skip_until_invalid_arguments {};
template<class... AN>
struct skip_until_invalid : public rxo::operator_base<skip_until_invalid_arguments<AN...>> {
using type = observable<skip_until_invalid_arguments<AN...>, skip_until_invalid<AN...>>;
};
template<class... AN>
using skip_until_invalid_t = typename skip_until_invalid<AN...>::type;
template<class T, class Observable, class TriggerObservable, class Coordination>
struct skip_until : public operator_base<T>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<TriggerObservable> trigger_source_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values
{
values(source_type s, trigger_source_type t, coordination_type sf)
: source(std::move(s))
, trigger(std::move(t))
, coordination(std::move(sf))
{
}
source_type source;
trigger_source_type trigger;
coordination_type coordination;
};
values initial;
skip_until(source_type s, trigger_source_type t, coordination_type sf)
: initial(std::move(s), std::move(t), std::move(sf))
{
}
struct mode
{
enum type {
skipping, // no messages from trigger
clear, // trigger completed
triggered, // trigger sent on_next
errored, // error either on trigger or on observable
stopped // observable completed
};
};
template<class Subscriber>
void on_subscribe(Subscriber s) const {
typedef Subscriber output_type;
struct state_type
: public std::enable_shared_from_this<state_type>
, public values
{
state_type(const values& i, coordinator_type coor, const output_type& oarg)
: values(i)
, mode_value(mode::skipping)
, coordinator(std::move(coor))
, out(oarg)
{
out.add(trigger_lifetime);
out.add(source_lifetime);
}
typename mode::type mode_value;
composite_subscription trigger_lifetime;
composite_subscription source_lifetime;
coordinator_type coordinator;
output_type out;
};
auto coordinator = initial.coordination.create_coordinator();
// take a copy of the values for each subscription
auto state = std::make_shared<state_type>(initial, std::move(coordinator), std::move(s));
auto trigger = on_exception(
[&](){return state->coordinator.in(state->trigger);},
state->out);
if (trigger.empty()) {
return;
}
auto source = on_exception(
[&](){return state->coordinator.in(state->source);},
state->out);
if (source.empty()) {
return;
}
auto sinkTrigger = make_subscriber<typename trigger_source_type::value_type>(
// share parts of subscription
state->out,
// new lifetime
state->trigger_lifetime,
// on_next
[state](const typename trigger_source_type::value_type&) {
if (state->mode_value != mode::skipping) {
return;
}
state->mode_value = mode::triggered;
state->trigger_lifetime.unsubscribe();
},
// on_error
[state](rxu::error_ptr e) {
if (state->mode_value != mode::skipping) {
return;
}
state->mode_value = mode::errored;
state->out.on_error(e);
},
// on_completed
[state]() {
if (state->mode_value != mode::skipping) {
return;
}
state->mode_value = mode::clear;
state->trigger_lifetime.unsubscribe();
}
);
auto selectedSinkTrigger = on_exception(
[&](){return state->coordinator.out(sinkTrigger);},
state->out);
if (selectedSinkTrigger.empty()) {
return;
}
trigger->subscribe(std::move(selectedSinkTrigger.get()));
source.get().subscribe(
// split subscription lifetime
state->source_lifetime,
// on_next
[state](T t) {
if (state->mode_value != mode::triggered) {
return;
}
state->out.on_next(t);
},
// on_error
[state](rxu::error_ptr e) {
if (state->mode_value > mode::triggered) {
return;
}
state->mode_value = mode::errored;
state->out.on_error(e);
},
// on_completed
[state]() {
if (state->mode_value != mode::triggered) {
return;
}
state->mode_value = mode::stopped;
state->out.on_completed();
}
);
}
};
}
/*! @copydoc rx-skip_until.hpp
*/
template<class... AN>
auto skip_until(AN&&... an)
-> operator_factory<skip_until_tag, AN...> {
return operator_factory<skip_until_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<skip_until_tag>
{
template<class Observable, class TimePoint,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<TimePoint, rxsc::scheduler::clock_type::time_point>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timer = typename rxu::defer_type<rxs::detail::timer, identity_one_worker>::type,
class TimerValue = rxu::value_type_t<Timer>,
class TriggerObservable = observable<TimerValue, Timer>,
class SkipUntil = rxo::detail::skip_until<SourceValue, rxu::decay_t<Observable>, TriggerObservable, identity_one_worker>,
class Value = rxu::value_type_t<SkipUntil>,
class Result = observable<Value, SkipUntil>>
static Result member(Observable&& o, TimePoint&& when) {
auto cn = identity_current_thread();
return Result(SkipUntil(std::forward<Observable>(o), rxs::timer(std::forward<TimePoint>(when), cn), cn));
}
template<class Observable, class TimePoint, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
std::is_convertible<TimePoint, rxsc::scheduler::clock_type::time_point>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timer = typename rxu::defer_type<rxs::detail::timer, rxu::decay_t<Coordination>>::type,
class TimerValue = rxu::value_type_t<Timer>,
class TriggerObservable = observable<TimerValue, Timer>,
class SkipUntil = rxo::detail::skip_until<SourceValue, rxu::decay_t<Observable>, TriggerObservable, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SkipUntil>,
class Result = observable<Value, SkipUntil>>
static Result member(Observable&& o, TimePoint&& when, Coordination cn) {
return Result(SkipUntil(std::forward<Observable>(o), rxs::timer(std::forward<TimePoint>(when), cn), cn));
}
template<class Observable, class TriggerObservable,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, TriggerObservable>>,
class SourceValue = rxu::value_type_t<Observable>,
class SkipUntil = rxo::detail::skip_until<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<TriggerObservable>, identity_one_worker>,
class Value = rxu::value_type_t<SkipUntil>,
class Result = observable<Value, SkipUntil>>
static Result member(Observable&& o, TriggerObservable&& t) {
return Result(SkipUntil(std::forward<Observable>(o), std::forward<TriggerObservable>(t), identity_current_thread()));
}
template<class Observable, class TriggerObservable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, TriggerObservable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class SkipUntil = rxo::detail::skip_until<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<TriggerObservable>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SkipUntil>,
class Result = observable<Value, SkipUntil>>
static Result member(Observable&& o, TriggerObservable&& t, Coordination&& cn) {
return Result(SkipUntil(std::forward<Observable>(o), std::forward<TriggerObservable>(t), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::skip_until_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "skip_until takes (TriggerObservable, optional Coordination) or (TimePoint, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,131 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-skip_while.hpp
\brief Discards the first items fulfilling the predicate from this observable emit them from the new observable that is returned.
\tparam Predicate the type of the predicate
\param t the predicate
\return An observable that discards the first items until condition emitted by the source Observable is not fulfilling the predicate, or all of the items from the source observable if the predicate never returns false
\sample
\snippet skip_while.cpp skip_while sample
\snippet output.txt skip_while sample
*/
#if !defined(RXCPP_OPERATORS_RX_SKIP_WHILE_HPP)
#define RXCPP_OPERATORS_RX_SKIP_WHILE_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct skip_while_invalid_arguments {};
template<class... AN>
struct skip_while_invalid : public rxo::operator_base<skip_while_invalid_arguments<AN...>> {
using type = observable<skip_while_invalid_arguments<AN...>, skip_while_invalid<AN...>>;
};
template<class... AN>
using skip_while_invalid_t = typename skip_while_invalid<AN...>::type;
template<class T, class Predicate>
struct skip_while
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Predicate> test_type;
test_type test;
skip_while(test_type t)
: test(std::move(t))
{
}
template<class Subscriber>
struct skip_while_observer
{
typedef skip_while_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
test_type test;
bool pass;
skip_while_observer(dest_type d, test_type t)
: dest(std::move(d))
, test(std::move(t)),
pass(false)
{
}
void on_next(source_value_type v) {
if(pass || !test(v))
{
pass = true;
dest.on_next(v);
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer_type> make(dest_type d, test_type t) {
return make_subscriber<value_type>(d, this_type(d, std::move(t)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(skip_while_observer<Subscriber>::make(std::move(dest), test)) {
return skip_while_observer<Subscriber>::make(std::move(dest), test);
}
};
}
/*! @copydoc rx-skip_while.hpp
*/
template<class... AN>
auto skip_while(AN&&... an)
-> operator_factory<skip_while_tag, AN...> {
return operator_factory<skip_while_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<skip_while_tag>
{
template<class Observable, class Predicate,
class SourceValue = rxu::value_type_t<Observable>,
class TakeWhile = rxo::detail::skip_while<SourceValue, rxu::decay_t<Predicate>>>
static auto member(Observable&& o, Predicate&& p)
-> decltype(o.template lift<SourceValue>(TakeWhile(std::forward<Predicate>(p)))) {
return o.template lift<SourceValue>(TakeWhile(std::forward<Predicate>(p)));
}
template<class... AN>
static operators::detail::skip_while_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "skip_while takes (Predicate)");
}
};
}
#endif

View File

@ -0,0 +1,84 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-start_with.hpp
\brief Start with the supplied values, then concatenate this observable.
\tparam Value0 ...
\tparam ValueN the type of sending values
\param v0 ...
\param vn values to send
\return Observable that emits the specified items and then emits the items emitted by the source observable.
\sample
\snippet start_with.cpp short start_with sample
\snippet output.txt short start_with sample
Another form of this operator, rxcpp::observable<void, void>::start_with, gets the source observable as a parameter:
\snippet start_with.cpp full start_with sample
\snippet output.txt full start_with sample
*/
#if !defined(RXCPP_OPERATORS_RX_START_WITH_HPP)
#define RXCPP_OPERATORS_RX_START_WITH_HPP
#include "../rx-includes.hpp"
#include "./rx-concat.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct start_with_invalid_arguments {};
template<class... AN>
struct start_with_invalid : public rxo::operator_base<start_with_invalid_arguments<AN...>> {
using type = observable<start_with_invalid_arguments<AN...>, start_with_invalid<AN...>>;
};
template<class... AN>
using start_with_invalid_t = typename start_with_invalid<AN...>::type;
}
/*! @copydoc rx-start_with.hpp
*/
template<class... AN>
auto start_with(AN&&... an)
-> operator_factory<start_with_tag, AN...> {
return operator_factory<start_with_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<start_with_tag>
{
template<class Observable, class Value0, class... ValueN,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class From = decltype(rxs::from(rxu::decay_t<Value0>(std::declval<Value0>()), rxu::decay_t<ValueN>(std::declval<ValueN>())...))
>
static auto member(Observable&& o, Value0&& v0, ValueN&&... vn)
-> decltype(member_overload<concat_tag>::member(std::declval<From>(), std::forward<Observable>(o))) {
auto first = rxs::from(rxu::decay_t<Value0>(v0), rxu::decay_t<ValueN>(vn)...);
return member_overload<concat_tag>::member(first, std::forward<Observable>(o));
}
template<class... AN>
static operators::detail::start_with_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "start_with takes (Value0, optional ValueN...)");
}
};
}
#endif

View File

@ -0,0 +1,154 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-subscribe.hpp
\brief Subscribe will cause the source observable to emit values to the provided subscriber.
\tparam ArgN types of the subscriber parameters
\param an the parameters for making a subscriber
\return A subscription with which the observer can stop receiving items before the observable has finished sending them.
The arguments of subscribe are forwarded to rxcpp::make_subscriber function. Some possible alternatives are:
- Pass an already composed rxcpp::subscriber:
\snippet subscribe.cpp subscribe by subscriber
\snippet output.txt subscribe by subscriber
- Pass an rxcpp::observer. This allows subscribing the same subscriber to several observables:
\snippet subscribe.cpp subscribe by observer
\snippet output.txt subscribe by observer
- Pass an `on_next` handler:
\snippet subscribe.cpp subscribe by on_next
\snippet output.txt subscribe by on_next
- Pass `on_next` and `on_error` handlers:
\snippet subscribe.cpp subscribe by on_next and on_error
\snippet output.txt subscribe by on_next and on_error
- Pass `on_next` and `on_completed` handlers:
\snippet subscribe.cpp subscribe by on_next and on_completed
\snippet output.txt subscribe by on_next and on_completed
- Pass `on_next`, `on_error`, and `on_completed` handlers:
\snippet subscribe.cpp subscribe by on_next, on_error, and on_completed
\snippet output.txt subscribe by on_next, on_error, and on_completed
.
All the alternatives above also support passing rxcpp::composite_subscription instance. For example:
\snippet subscribe.cpp subscribe by subscription, on_next, and on_completed
\snippet output.txt subscribe by subscription, on_next, and on_completed
If neither subscription nor subscriber are provided, then a new subscription is created and returned as a result:
\snippet subscribe.cpp subscribe unsubscribe
\snippet output.txt subscribe unsubscribe
For more details, see rxcpp::make_subscriber function description.
*/
#if !defined(RXCPP_OPERATORS_RX_SUBSCRIBE_HPP)
#define RXCPP_OPERATORS_RX_SUBSCRIBE_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class Subscriber>
class subscribe_factory;
template<class T, class I>
class subscribe_factory<subscriber<T, I>>
{
subscriber<T, I> scrbr;
public:
subscribe_factory(subscriber<T, I> s)
: scrbr(std::move(s))
{}
template<class Observable>
auto operator()(Observable&& source)
-> decltype(std::forward<Observable>(source).subscribe(std::move(scrbr))) {
return std::forward<Observable>(source).subscribe(std::move(scrbr));
}
};
}
/*! @copydoc rx-subscribe.hpp
*/
template<class T, class... ArgN>
auto subscribe(ArgN&&... an)
-> detail::subscribe_factory<decltype (make_subscriber<T>(std::forward<ArgN>(an)...))> {
return detail::subscribe_factory<decltype (make_subscriber<T>(std::forward<ArgN>(an)...))>
(make_subscriber<T>(std::forward<ArgN>(an)...));
}
namespace detail {
class dynamic_factory
{
public:
template<class Observable>
auto operator()(Observable&& source)
-> observable<rxu::value_type_t<rxu::decay_t<Observable>>> {
return observable<rxu::value_type_t<rxu::decay_t<Observable>>>(std::forward<Observable>(source));
}
};
}
/*! Return a new observable that performs type-forgetting conversion of this observable.
\return The source observable converted to observable<T>.
\note This operator could be useful to workaround lambda deduction bug on msvc 2013.
\sample
\snippet as_dynamic.cpp as_dynamic sample
\snippet output.txt as_dynamic sample
*/
inline auto as_dynamic()
-> detail::dynamic_factory {
return detail::dynamic_factory();
}
namespace detail {
class blocking_factory
{
public:
template<class Observable>
auto operator()(Observable&& source)
-> decltype(std::forward<Observable>(source).as_blocking()) {
return std::forward<Observable>(source).as_blocking();
}
};
}
/*! Return a new observable that contains the blocking methods for this observable.
\return An observable that contains the blocking methods for this observable.
\sample
\snippet from.cpp threaded from sample
\snippet output.txt threaded from sample
*/
inline auto as_blocking()
-> detail::blocking_factory {
return detail::blocking_factory();
}
}
}
#endif

View File

@ -0,0 +1,182 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-subscribe_on.hpp
\brief Subscription and unsubscription are queued and delivered using the scheduler from the supplied coordination.
\tparam Coordination the type of the scheduler.
\param cn the scheduler to perform subscription actions on.
\return The source observable modified so that its subscriptions happen on the specified scheduler.
\sample
\snippet subscribe_on.cpp subscribe_on sample
\snippet output.txt subscribe_on sample
Invoking rxcpp::observable::observe_on operator, instead of subscribe_on, gives following results:
\snippet output.txt observe_on sample
*/
#if !defined(RXCPP_OPERATORS_RX_SUBSCRIBE_ON_HPP)
#define RXCPP_OPERATORS_RX_SUBSCRIBE_ON_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct subscribe_on_invalid_arguments {};
template<class... AN>
struct subscribe_on_invalid : public rxo::operator_base<subscribe_on_invalid_arguments<AN...>> {
using type = observable<subscribe_on_invalid_arguments<AN...>, subscribe_on_invalid<AN...>>;
};
template<class... AN>
using subscribe_on_invalid_t = typename subscribe_on_invalid<AN...>::type;
template<class T, class Observable, class Coordination>
struct subscribe_on : public operator_base<T>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct subscribe_on_values
{
~subscribe_on_values()
{
}
subscribe_on_values(source_type s, coordination_type sf)
: source(std::move(s))
, coordination(std::move(sf))
{
}
source_type source;
coordination_type coordination;
private:
subscribe_on_values& operator=(subscribe_on_values o) RXCPP_DELETE;
};
const subscribe_on_values initial;
~subscribe_on()
{
}
subscribe_on(source_type s, coordination_type sf)
: initial(std::move(s), std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber s) const {
typedef Subscriber output_type;
struct subscribe_on_state_type
: public std::enable_shared_from_this<subscribe_on_state_type>
, public subscribe_on_values
{
subscribe_on_state_type(const subscribe_on_values& i, const output_type& oarg)
: subscribe_on_values(i)
, out(oarg)
{
}
composite_subscription source_lifetime;
output_type out;
private:
subscribe_on_state_type& operator=(subscribe_on_state_type o) RXCPP_DELETE;
};
composite_subscription coordinator_lifetime;
auto coordinator = initial.coordination.create_coordinator(coordinator_lifetime);
auto controller = coordinator.get_worker();
// take a copy of the values for each subscription
auto state = std::make_shared<subscribe_on_state_type>(initial, std::move(s));
auto sl = state->source_lifetime;
auto ol = state->out.get_subscription();
auto disposer = [=](const rxsc::schedulable&){
sl.unsubscribe();
ol.unsubscribe();
coordinator_lifetime.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){return coordinator.act(disposer);},
state->out);
if (selectedDisposer.empty()) {
return;
}
state->source_lifetime.add([=](){
controller.schedule(selectedDisposer.get());
});
state->out.add([=](){
sl.unsubscribe();
ol.unsubscribe();
coordinator_lifetime.unsubscribe();
});
auto producer = [=](const rxsc::schedulable&){
state->source.subscribe(state->source_lifetime, state->out);
};
auto selectedProducer = on_exception(
[&](){return coordinator.act(producer);},
state->out);
if (selectedProducer.empty()) {
return;
}
controller.schedule(selectedProducer.get());
}
private:
subscribe_on& operator=(subscribe_on o) RXCPP_DELETE;
};
}
/*! @copydoc rx-subscribe_on.hpp
*/
template<class... AN>
auto subscribe_on(AN&&... an)
-> operator_factory<subscribe_on_tag, AN...> {
return operator_factory<subscribe_on_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<subscribe_on_tag>
{
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class SubscribeOn = rxo::detail::subscribe_on<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SubscribeOn>,
class Result = observable<Value, SubscribeOn>>
static Result member(Observable&& o, Coordination&& cn) {
return Result(SubscribeOn(std::forward<Observable>(o), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::subscribe_on_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "subscribe_on takes (Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,178 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-switch_if_empty.hpp
\brief If the source Observable terminates without emitting any items, emits items from a backup Observable.
\tparam BackupSource the type of the backup observable.
\param t a backup observable that is used if the source observable is empty.
\return Observable that emits items from a backup observable if the source observable is empty.
\sample
\snippet switch_if_empty.cpp switch_if_empty sample
\snippet output.txt switch_if_empty sample
*/
#if !defined(RXCPP_OPERATORS_RX_SWITCH_IF_EMPTY_HPP)
#define RXCPP_OPERATORS_RX_SWITCH_IF_EMPTY_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct switch_if_empty_invalid_arguments {};
template<class... AN>
struct switch_if_empty_invalid : public rxo::operator_base<switch_if_empty_invalid_arguments<AN...>> {
using type = observable<switch_if_empty_invalid_arguments<AN...>, switch_if_empty_invalid<AN...>>;
};
template<class... AN>
using switch_if_empty_invalid_t = typename switch_if_empty_invalid<AN...>::type;
template<class T, class BackupSource>
struct switch_if_empty
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<BackupSource> backup_source_type;
backup_source_type backup;
switch_if_empty(backup_source_type b)
: backup(std::move(b))
{
}
template<class Subscriber>
struct switch_if_empty_observer
{
typedef switch_if_empty_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
composite_subscription lifetime;
backup_source_type backup;
mutable bool is_empty;
switch_if_empty_observer(dest_type d, composite_subscription cs, backup_source_type b)
: dest(std::move(d))
, lifetime(std::move(cs))
, backup(std::move(b))
, is_empty(true)
{
dest.add(lifetime);
}
void on_next(source_value_type v) const {
is_empty = false;
dest.on_next(std::move(v));
}
void on_error(rxu::error_ptr e) const {
dest.on_error(std::move(e));
}
void on_completed() const {
if(!is_empty) {
dest.on_completed();
} else {
backup.subscribe(dest);
}
}
static subscriber<value_type, observer_type> make(dest_type d, backup_source_type b) {
auto cs = composite_subscription();
return make_subscriber<value_type>(cs, observer_type(this_type(std::move(d), cs, std::move(b))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(switch_if_empty_observer<Subscriber>::make(std::move(dest), std::move(backup))) {
return switch_if_empty_observer<Subscriber>::make(std::move(dest), std::move(backup));
}
};
}
/*! @copydoc rx-switch_if_empty.hpp
*/
template<class... AN>
auto switch_if_empty(AN&&... an)
-> operator_factory<switch_if_empty_tag, AN...> {
return operator_factory<switch_if_empty_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
/*! \brief If the source Observable terminates without emitting any items, emits a default item and completes.
\tparam Value the type of the value to emit.
\param v the default value to emit.
\return Observable that emits the specified default item if the source observable is empty.
\sample
\snippet default_if_empty.cpp default_if_empty sample
\snippet output.txt default_if_empty sample
*/
template<class... AN>
auto default_if_empty(AN&&... an)
-> operator_factory<default_if_empty_tag, AN...> {
return operator_factory<default_if_empty_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<switch_if_empty_tag>
{
template<class Observable, class BackupSource,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, BackupSource>>,
class SourceValue = rxu::value_type_t<Observable>,
class SwitchIfEmpty = rxo::detail::switch_if_empty<SourceValue, rxu::decay_t<BackupSource>>>
static auto member(Observable&& o, BackupSource&& b)
-> decltype(o.template lift<SourceValue>(SwitchIfEmpty(std::forward<BackupSource>(b)))) {
return o.template lift<SourceValue>(SwitchIfEmpty(std::forward<BackupSource>(b)));
}
template<class... AN>
static operators::detail::switch_if_empty_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "switch_if_empty takes (BackupSource)");
}
};
template<>
struct member_overload<default_if_empty_tag>
{
template<class Observable, class Value,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class BackupSource = decltype(rxs::from(std::declval<SourceValue>())),
class DefaultIfEmpty = rxo::detail::switch_if_empty<SourceValue, BackupSource>>
static auto member(Observable&& o, Value v)
-> decltype(o.template lift<SourceValue>(DefaultIfEmpty(rxs::from(std::move(v))))) {
return o.template lift<SourceValue>(DefaultIfEmpty(rxs::from(std::move(v))));
}
template<class... AN>
static operators::detail::switch_if_empty_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "default_if_empty takes (Value)");
}
};
}
#endif

View File

@ -0,0 +1,247 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-switch_on_next.hpp
\brief Return observable that emits the items emitted by the observable most recently emitted by the source observable.
\tparam Coordination the type of the scheduler (optional).
\param cn the scheduler to synchronize sources from different contexts (optional).
\return Observable that emits the items emitted by the observable most recently emitted by the source observable.
\sample
\snippet switch_on_next.cpp switch_on_next sample
\snippet output.txt switch_on_next sample
*/
#if !defined(RXCPP_OPERATORS_RX_SWITCH_ON_NEXT_HPP)
#define RXCPP_OPERATORS_RX_SWITCH_ON_NEXT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct switch_on_next_invalid_arguments {};
template<class... AN>
struct switch_on_next_invalid : public rxo::operator_base<switch_on_next_invalid_arguments<AN...>> {
using type = observable<switch_on_next_invalid_arguments<AN...>, switch_on_next_invalid<AN...>>;
};
template<class... AN>
using switch_on_next_invalid_t = typename switch_on_next_invalid<AN...>::type;
template<class T, class Observable, class Coordination>
struct switch_on_next
: public operator_base<rxu::value_type_t<rxu::decay_t<T>>>
{
//static_assert(is_observable<Observable>::value, "switch_on_next requires an observable");
//static_assert(is_observable<T>::value, "switch_on_next requires an observable that contains observables");
typedef switch_on_next<T, Observable, Coordination> this_type;
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Observable> source_type;
typedef typename source_type::source_operator_type source_operator_type;
typedef source_value_type collection_type;
typedef typename collection_type::value_type collection_value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values
{
values(source_operator_type o, coordination_type sf)
: source_operator(std::move(o))
, coordination(std::move(sf))
{
}
source_operator_type source_operator;
coordination_type coordination;
};
values initial;
switch_on_next(const source_type& o, coordination_type sf)
: initial(o.source_operator, std::move(sf))
{
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct switch_state_type
: public std::enable_shared_from_this<switch_state_type>
, public values
{
switch_state_type(values i, coordinator_type coor, output_type oarg)
: values(i)
, source(i.source_operator)
, pendingCompletions(0)
, coordinator(std::move(coor))
, out(std::move(oarg))
{
}
observable<source_value_type, source_operator_type> source;
// on_completed on the output must wait until all the
// subscriptions have received on_completed
int pendingCompletions;
coordinator_type coordinator;
composite_subscription inner_lifetime;
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<switch_state_type>(initial, std::move(coordinator), std::move(scbr));
composite_subscription outercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
state->out.add(outercs);
auto source = on_exception(
[&](){return state->coordinator.in(state->source);},
state->out);
if (source.empty()) {
return;
}
++state->pendingCompletions;
// 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<collection_type>(
state->out,
outercs,
// on_next
[state](collection_type st) {
state->inner_lifetime.unsubscribe();
state->inner_lifetime = composite_subscription();
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innerlifetimetoken = state->out.add(state->inner_lifetime);
state->inner_lifetime.add(make_subscription([state, innerlifetimetoken](){
state->out.remove(innerlifetimetoken);
--state->pendingCompletions;
}));
auto selectedSource = state->coordinator.in(st);
// this subscribe does not share the source subscription
// so that when it is unsubscribed the source will continue
auto sinkInner = make_subscriber<collection_value_type>(
state->out,
state->inner_lifetime,
// on_next
[state, st](collection_value_type ct) {
state->out.on_next(std::move(ct));
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
//on_completed
[state](){
if (state->pendingCompletions == 1) {
state->out.on_completed();
}
}
);
auto selectedSinkInner = state->coordinator.out(sinkInner);
++state->pendingCompletions;
selectedSource.subscribe(std::move(selectedSinkInner));
},
// on_error
[state](rxu::error_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()));
}
};
}
/*! @copydoc rx-switch_on_next.hpp
*/
template<class... AN>
auto switch_on_next(AN&&... an)
-> operator_factory<switch_on_next_tag, AN...> {
return operator_factory<switch_on_next_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<switch_on_next_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class SwitchOnNext = rxo::detail::switch_on_next<SourceValue, rxu::decay_t<Observable>, identity_one_worker>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, SwitchOnNext>
>
static Result member(Observable&& o) {
return Result(SwitchOnNext(std::forward<Observable>(o), identity_current_thread()));
}
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class SwitchOnNext = rxo::detail::switch_on_next<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<SourceValue>,
class Result = observable<Value, SwitchOnNext>
>
static Result member(Observable&& o, Coordination&& cn) {
return Result(SwitchOnNext(std::forward<Observable>(o), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::switch_on_next_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "switch_on_next takes (optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,165 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-take.hpp
\brief For the first count items from this observable emit them from the new observable that is returned.
\tparam Count the type of the items counter.
\param t the number of items to take.
\return An observable that emits only the first t items emitted by the source Observable, or all of the items from the source observable if that observable emits fewer than t items.
\sample
\snippet take.cpp take sample
\snippet output.txt take sample
*/
#if !defined(RXCPP_OPERATORS_RX_TAKE_HPP)
#define RXCPP_OPERATORS_RX_TAKE_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct take_invalid_arguments {};
template<class... AN>
struct take_invalid : public rxo::operator_base<take_invalid_arguments<AN...>> {
using type = observable<take_invalid_arguments<AN...>, take_invalid<AN...>>;
};
template<class... AN>
using take_invalid_t = typename take_invalid<AN...>::type;
template<class T, class Observable, class Count>
struct take : public operator_base<T>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Count> count_type;
struct values
{
values(source_type s, count_type t)
: source(std::move(s))
, count(std::move(t))
{
}
source_type source;
count_type count;
};
values initial;
take(source_type s, count_type t)
: initial(std::move(s), std::move(t))
{
}
struct mode
{
enum type {
taking, // capture messages
triggered, // ignore messages
errored, // error occured
stopped // observable completed
};
};
template<class Subscriber>
void on_subscribe(const Subscriber& s) const {
typedef Subscriber output_type;
struct state_type
: public std::enable_shared_from_this<state_type>
, public values
{
state_type(const values& i, const output_type& oarg)
: values(i)
, mode_value(mode::taking)
, out(oarg)
{
}
typename mode::type mode_value;
output_type out;
};
// take a copy of the values for each subscription
auto state = std::make_shared<state_type>(initial, s);
composite_subscription source_lifetime;
s.add(source_lifetime);
state->source.subscribe(
// split subscription lifetime
source_lifetime,
// on_next
[state, source_lifetime](T t) {
if (state->mode_value < mode::triggered) {
if (--state->count > 0) {
state->out.on_next(t);
} else {
state->mode_value = mode::triggered;
state->out.on_next(t);
// must shutdown source before signaling completion
source_lifetime.unsubscribe();
state->out.on_completed();
}
}
},
// on_error
[state](rxu::error_ptr e) {
state->mode_value = mode::errored;
state->out.on_error(e);
},
// on_completed
[state]() {
state->mode_value = mode::stopped;
state->out.on_completed();
}
);
}
};
}
/*! @copydoc rx-take.hpp
*/
template<class... AN>
auto take(AN&&... an)
-> operator_factory<take_tag, AN...> {
return operator_factory<take_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<take_tag>
{
template<class Observable,
class Count,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Take = rxo::detail::take<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Count>>,
class Value = rxu::value_type_t<Take>,
class Result = observable<Value, Take>>
static Result member(Observable&& o, Count&& c) {
return Result(Take(std::forward<Observable>(o), std::forward<Count>(c)));
}
template<class... AN>
static operators::detail::take_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "take takes (optional Count)");
}
};
}
#endif

View File

@ -0,0 +1,155 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-take_last.hpp
\brief Emit only the final t items emitted by the source Observable.
\tparam Count the type of the items counter.
\param t the number of last items to take.
\return An observable that emits only the last t items emitted by the source Observable, or all of the items from the source observable if that observable emits fewer than t items.
\sample
\snippet take_last.cpp take_last sample
\snippet output.txt take_last sample
*/
#if !defined(RXCPP_OPERATORS_RX_TAKE_LAST_HPP)
#define RXCPP_OPERATORS_RX_TAKE_LAST_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct take_last_invalid_arguments {};
template<class... AN>
struct take_last_invalid : public rxo::operator_base<take_last_invalid_arguments<AN...>> {
using type = observable<take_last_invalid_arguments<AN...>, take_last_invalid<AN...>>;
};
template<class... AN>
using take_last_invalid_t = typename take_last_invalid<AN...>::type;
template<class T, class Observable, class Count>
struct take_last : public operator_base<T>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<Count> count_type;
typedef std::queue<T> queue_type;
typedef typename queue_type::size_type queue_size_type;
struct values
{
values(source_type s, count_type t)
: source(std::move(s))
, count(static_cast<queue_size_type>(t))
{
}
source_type source;
queue_size_type count;
};
values initial;
take_last(source_type s, count_type t)
: initial(std::move(s), std::move(t))
{
}
template<class Subscriber>
void on_subscribe(const Subscriber& s) const {
typedef Subscriber output_type;
struct state_type
: public std::enable_shared_from_this<state_type>
, public values
{
state_type(const values& i, const output_type& oarg)
: values(i)
, out(oarg)
{
}
queue_type items;
output_type out;
};
// take a copy of the values for each subscription
auto state = std::make_shared<state_type>(initial, s);
composite_subscription source_lifetime;
s.add(source_lifetime);
state->source.subscribe(
// split subscription lifetime
source_lifetime,
// on_next
[state, source_lifetime](T t) {
if(state->count > 0) {
if (state->items.size() == state->count) {
state->items.pop();
}
state->items.push(t);
}
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state]() {
while(!state->items.empty()) {
state->out.on_next(std::move(state->items.front()));
state->items.pop();
}
state->out.on_completed();
}
);
}
};
}
/*! @copydoc rx-take_last.hpp
*/
template<class... AN>
auto take_last(AN&&... an)
-> operator_factory<take_last_tag, AN...> {
return operator_factory<take_last_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<take_last_tag>
{
template<class Observable,
class Count,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class TakeLast = rxo::detail::take_last<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<Count>>,
class Value = rxu::value_type_t<TakeLast>,
class Result = observable<Value, TakeLast>>
static Result member(Observable&& o, Count&& c) {
return Result(TakeLast(std::forward<Observable>(o), std::forward<Count>(c)));
}
template<class... AN>
static operators::detail::take_last_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "take_last takes (Count)");
}
};
}
#endif

View File

@ -0,0 +1,284 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-take_until.hpp
\brief For each item from this observable until on_next occurs on the trigger observable or until the specified time, emit them from the new observable that is returned.
take_until takes (TriggerObservable, optional Coordination) or (TimePoint, optional Coordination)
\tparam TriggerSource the type of the trigger observable.
\tparam TimePoint the type of the time interval.
\tparam Coordination the type of the scheduler (optional).
\param t an observable whose first emitted item will stop emitting items from the source observable.
\param when a time point when the returned observable will stop emitting items.
\param cn the scheduler to use for scheduling the items (optional).
\return An observable that emits the items emitted by the source observable until trigger observable emitted or the time runs out.
\sample
\snippet take_until.cpp take_until sample
\snippet output.txt take_until sample
\sample
\snippet take_until.cpp threaded take_until sample
\snippet output.txt threaded take_until sample
\sample
\snippet take_until.cpp take_until time sample
\snippet output.txt take_until time sample
\sample
\snippet take_until.cpp threaded take_until time sample
\snippet output.txt threaded take_until time sample
*/
#if !defined(RXCPP_OPERATORS_RX_TAKE_UNTIL_HPP)
#define RXCPP_OPERATORS_RX_TAKE_UNTIL_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct take_until_invalid_arguments {};
template<class... AN>
struct take_until_invalid : public rxo::operator_base<take_until_invalid_arguments<AN...>> {
using type = observable<take_until_invalid_arguments<AN...>, take_until_invalid<AN...>>;
};
template<class... AN>
using take_until_invalid_t = typename take_until_invalid<AN...>::type;
template<class T, class Observable, class TriggerObservable, class Coordination>
struct take_until : public operator_base<T>
{
typedef rxu::decay_t<Observable> source_type;
typedef rxu::decay_t<TriggerObservable> trigger_source_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
struct values
{
values(source_type s, trigger_source_type t, coordination_type sf)
: source(std::move(s))
, trigger(std::move(t))
, coordination(std::move(sf))
{
}
source_type source;
trigger_source_type trigger;
coordination_type coordination;
};
values initial;
take_until(source_type s, trigger_source_type t, coordination_type sf)
: initial(std::move(s), std::move(t), std::move(sf))
{
}
struct mode
{
enum type {
taking, // no messages from trigger
clear, // trigger completed
triggered, // trigger sent on_next
errored, // error either on trigger or on observable
stopped // observable completed
};
};
template<class Subscriber>
void on_subscribe(Subscriber s) const {
typedef Subscriber output_type;
struct take_until_state_type
: public std::enable_shared_from_this<take_until_state_type>
, public values
{
take_until_state_type(const values& i, coordinator_type coor, const output_type& oarg)
: values(i)
, mode_value(mode::taking)
, coordinator(std::move(coor))
, out(oarg)
{
out.add(trigger_lifetime);
out.add(source_lifetime);
}
typename mode::type mode_value;
composite_subscription trigger_lifetime;
composite_subscription source_lifetime;
coordinator_type coordinator;
output_type out;
};
auto coordinator = initial.coordination.create_coordinator(s.get_subscription());
// take a copy of the values for each subscription
auto state = std::make_shared<take_until_state_type>(initial, std::move(coordinator), std::move(s));
auto trigger = on_exception(
[&](){return state->coordinator.in(state->trigger);},
state->out);
if (trigger.empty()) {
return;
}
auto source = on_exception(
[&](){return state->coordinator.in(state->source);},
state->out);
if (source.empty()) {
return;
}
auto sinkTrigger = make_subscriber<typename trigger_source_type::value_type>(
// share parts of subscription
state->out,
// new lifetime
state->trigger_lifetime,
// on_next
[state](const typename trigger_source_type::value_type&) {
if (state->mode_value != mode::taking) {return;}
state->mode_value = mode::triggered;
state->out.on_completed();
},
// on_error
[state](rxu::error_ptr e) {
if (state->mode_value != mode::taking) {return;}
state->mode_value = mode::errored;
state->out.on_error(e);
},
// on_completed
[state]() {
if (state->mode_value != mode::taking) {return;}
state->mode_value = mode::clear;
}
);
auto selectedSinkTrigger = on_exception(
[&](){return state->coordinator.out(sinkTrigger);},
state->out);
if (selectedSinkTrigger.empty()) {
return;
}
trigger->subscribe(std::move(selectedSinkTrigger.get()));
auto sinkSource = make_subscriber<T>(
// split subscription lifetime
state->source_lifetime,
// on_next
[state](T t) {
//
// everything is crafted to minimize the overhead of this function.
//
if (state->mode_value < mode::triggered) {
state->out.on_next(t);
}
},
// on_error
[state](rxu::error_ptr e) {
if (state->mode_value > mode::clear) {return;}
state->mode_value = mode::errored;
state->out.on_error(e);
},
// on_completed
[state]() {
if (state->mode_value > mode::clear) {return;}
state->mode_value = mode::stopped;
state->out.on_completed();
}
);
auto selectedSinkSource = on_exception(
[&](){return state->coordinator.out(sinkSource);},
state->out);
if (selectedSinkSource.empty()) {
return;
}
source->subscribe(std::move(selectedSinkSource.get()));
}
};
}
/*! @copydoc rx-take_until.hpp
*/
template<class... AN>
auto take_until(AN&&... an)
-> operator_factory<take_until_tag, AN...> {
return operator_factory<take_until_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<take_until_tag>
{
template<class Observable, class TimePoint,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<TimePoint, rxsc::scheduler::clock_type::time_point>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timer = typename rxu::defer_type<rxs::detail::timer, identity_one_worker>::type,
class TimerValue = rxu::value_type_t<Timer>,
class TriggerObservable = observable<TimerValue, Timer>,
class TakeUntil = rxo::detail::take_until<SourceValue, rxu::decay_t<Observable>, TriggerObservable, identity_one_worker>,
class Value = rxu::value_type_t<TakeUntil>,
class Result = observable<Value, TakeUntil>>
static Result member(Observable&& o, TimePoint&& when) {
auto cn = identity_current_thread();
return Result(TakeUntil(std::forward<Observable>(o), rxs::timer(std::forward<TimePoint>(when), cn), cn));
}
template<class Observable, class TimePoint, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
std::is_convertible<TimePoint, rxsc::scheduler::clock_type::time_point>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timer = typename rxu::defer_type<rxs::detail::timer, rxu::decay_t<Coordination>>::type,
class TimerValue = rxu::value_type_t<Timer>,
class TriggerObservable = observable<TimerValue, Timer>,
class TakeUntil = rxo::detail::take_until<SourceValue, rxu::decay_t<Observable>, TriggerObservable, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<TakeUntil>,
class Result = observable<Value, TakeUntil>>
static Result member(Observable&& o, TimePoint&& when, Coordination cn) {
return Result(TakeUntil(std::forward<Observable>(o), rxs::timer(std::forward<TimePoint>(when), cn), cn));
}
template<class Observable, class TriggerObservable,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, TriggerObservable>>,
class SourceValue = rxu::value_type_t<Observable>,
class TakeUntil = rxo::detail::take_until<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<TriggerObservable>, identity_one_worker>,
class Value = rxu::value_type_t<TakeUntil>,
class Result = observable<Value, TakeUntil>>
static Result member(Observable&& o, TriggerObservable&& t) {
return Result(TakeUntil(std::forward<Observable>(o), std::forward<TriggerObservable>(t), identity_current_thread()));
}
template<class Observable, class TriggerObservable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, TriggerObservable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class TakeUntil = rxo::detail::take_until<SourceValue, rxu::decay_t<Observable>, rxu::decay_t<TriggerObservable>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<TakeUntil>,
class Result = observable<Value, TakeUntil>>
static Result member(Observable&& o, TriggerObservable&& t, Coordination&& cn) {
return Result(TakeUntil(std::forward<Observable>(o), std::forward<TriggerObservable>(t), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::take_until_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "take_until takes (TriggerObservable, optional Coordination) or (TimePoint, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,129 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-take_while.hpp
\brief For the first items fulfilling the predicate from this observable emit them from the new observable that is returned.
\tparam Predicate the type of the predicate
\param t the predicate
\return An observable that emits only the first items emitted by the source Observable fulfilling the predicate, or all of the items from the source observable if the predicate never returns false
\sample
\snippet take_while.cpp take_while sample
\snippet output.txt take_while sample
*/
#if !defined(RXCPP_OPERATORS_RX_TAKE_WHILE_HPP)
#define RXCPP_OPERATORS_RX_TAKE_WHILE_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct take_while_invalid_arguments {};
template<class... AN>
struct take_while_invalid : public rxo::operator_base<take_while_invalid_arguments<AN...>> {
using type = observable<take_while_invalid_arguments<AN...>, take_while_invalid<AN...>>;
};
template<class... AN>
using take_while_invalid_t = typename take_while_invalid<AN...>::type;
template<class T, class Predicate>
struct take_while
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Predicate> test_type;
test_type test;
take_while(test_type t)
: test(std::move(t))
{
}
template<class Subscriber>
struct take_while_observer
{
typedef take_while_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
test_type test;
take_while_observer(dest_type d, test_type t)
: dest(std::move(d))
, test(std::move(t))
{
}
void on_next(source_value_type v) const {
if (test(v)) {
dest.on_next(v);
} else {
dest.on_completed();
}
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer_type> make(dest_type d, test_type t) {
return make_subscriber<value_type>(d, this_type(d, std::move(t)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(take_while_observer<Subscriber>::make(std::move(dest), test)) {
return take_while_observer<Subscriber>::make(std::move(dest), test);
}
};
}
/*! @copydoc rx-take_while.hpp
*/
template<class... AN>
auto take_while(AN&&... an)
-> operator_factory<take_while_tag, AN...> {
return operator_factory<take_while_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<take_while_tag>
{
template<class Observable, class Predicate,
class SourceValue = rxu::value_type_t<Observable>,
class TakeWhile = rxo::detail::take_while<SourceValue, rxu::decay_t<Predicate>>>
static auto member(Observable&& o, Predicate&& p)
-> decltype(o.template lift<SourceValue>(TakeWhile(std::forward<Predicate>(p)))) {
return o.template lift<SourceValue>(TakeWhile(std::forward<Predicate>(p)));
}
template<class... AN>
static operators::detail::take_while_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "take_while takes (Predicate)");
}
};
}
#endif

View File

@ -0,0 +1,151 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-tap.hpp
\brief inspect calls to on_next, on_error and on_completed.
\tparam MakeObserverArgN... these args are passed to make_observer.
\param an these args are passed to make_observer.
\return Observable that emits the same items as the source observable to both the subscriber and the observer.
\note If an on_error method is not supplied the observer will ignore errors rather than call std::terminate()
\sample
\snippet tap.cpp tap sample
\snippet output.txt tap sample
If the source observable generates an error, the observer passed to tap is called:
\snippet tap.cpp error tap sample
\snippet output.txt error tap sample
*/
#if !defined(RXCPP_OPERATORS_RX_TAP_HPP)
#define RXCPP_OPERATORS_RX_TAP_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct tap_invalid_arguments {};
template<class... AN>
struct tap_invalid : public rxo::operator_base<tap_invalid_arguments<AN...>> {
using type = observable<tap_invalid_arguments<AN...>, tap_invalid<AN...>>;
};
template<class... AN>
using tap_invalid_t = typename tap_invalid<AN...>::type;
template<class T, class MakeObserverArgN>
struct tap_observer_factory;
template<class T, class... ArgN>
struct tap_observer_factory<T, std::tuple<ArgN...>>
{
using source_value_type = rxu::decay_t<T>;
using out_type = decltype(make_observer<source_value_type, rxcpp::detail::OnErrorIgnore>(*((ArgN*)nullptr)...));
auto operator()(ArgN&&... an) -> out_type const {
return make_observer<source_value_type, rxcpp::detail::OnErrorIgnore>(std::forward<ArgN>(an)...);
}
};
template<class T, class MakeObserverArgN, class Factory = tap_observer_factory<T, MakeObserverArgN>>
struct tap
{
using source_value_type = rxu::decay_t<T>;
using args_type = rxu::decay_t<MakeObserverArgN>;
using factory_type = Factory;
using out_type = typename factory_type::out_type;
out_type out;
tap(args_type a)
: out(rxu::apply(std::move(a), factory_type()))
{
}
template<class Subscriber>
struct tap_observer
{
using this_type = tap_observer<Subscriber>;
using value_type = source_value_type;
using dest_type = rxu::decay_t<Subscriber>;
using factory_type = Factory;
using out_type = typename factory_type::out_type;
using observer_type = observer<value_type, this_type>;
dest_type dest;
out_type out;
tap_observer(dest_type d, out_type o)
: dest(std::move(d))
, out(std::move(o))
{
}
void on_next(source_value_type v) const {
out.on_next(v);
dest.on_next(v);
}
void on_error(rxu::error_ptr e) const {
out.on_error(e);
dest.on_error(e);
}
void on_completed() const {
out.on_completed();
dest.on_completed();
}
static subscriber<value_type, observer<value_type, this_type>> make(dest_type d, out_type o) {
return make_subscriber<value_type>(d, this_type(d, std::move(o)));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(tap_observer<Subscriber>::make(std::move(dest), out)) {
return tap_observer<Subscriber>::make(std::move(dest), out);
}
};
}
/*! @copydoc rx-tap.hpp
*/
template<class... AN>
auto tap(AN&&... an)
-> operator_factory<tap_tag, AN...> {
return operator_factory<tap_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<tap_tag>
{
template<class Observable, class... MakeObserverArgN,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Tap = rxo::detail::tap<SourceValue, std::tuple<rxu::decay_t<MakeObserverArgN>...>>>
static auto member(Observable&& o, MakeObserverArgN&&... an)
-> decltype(o.template lift<SourceValue>(Tap(std::make_tuple(std::forward<MakeObserverArgN>(an)...)))) {
return o.template lift<SourceValue>(Tap(std::make_tuple(std::forward<MakeObserverArgN>(an)...)));
}
template<class... AN>
static operators::detail::tap_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "tap takes (MakeObserverArgN...)");
}
};
}
#endif

View File

@ -0,0 +1,154 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-time_interval.hpp
\brief Returns an observable that emits indications of the amount of time lapsed between consecutive emissions of the source observable.
The first emission from this new Observable indicates the amount of time lapsed between the time when the observer subscribed to the Observable and the time when the source Observable emitted its first item.
\tparam Coordination the type of the scheduler.
\param coordination the scheduler for time intervals.
\return Observable that emits a time_duration to indicate the amount of time lapsed between pairs of emissions.
\sample
\snippet time_interval.cpp time_interval sample
\snippet output.txt time_interval sample
*/
#if !defined(RXCPP_OPERATORS_RX_TIME_INTERVAL_HPP)
#define RXCPP_OPERATORS_RX_TIME_INTERVAL_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct time_interval_invalid_arguments {};
template<class... AN>
struct time_interval_invalid : public rxo::operator_base<time_interval_invalid_arguments<AN...>> {
using type = observable<time_interval_invalid_arguments<AN...>, time_interval_invalid<AN...>>;
};
template<class... AN>
using time_interval_invalid_t = typename time_interval_invalid<AN...>::type;
template<class T, class Coordination>
struct time_interval
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Coordination> coordination_type;
struct time_interval_values {
time_interval_values(coordination_type c)
: coordination(c)
{
}
coordination_type coordination;
};
time_interval_values initial;
time_interval(coordination_type coordination)
: initial(coordination)
{
}
template<class Subscriber>
struct time_interval_observer
{
typedef time_interval_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
typedef rxsc::scheduler::clock_type::time_point time_point;
dest_type dest;
coordination_type coord;
mutable time_point last;
time_interval_observer(dest_type d, coordination_type coordination)
: dest(std::move(d)),
coord(std::move(coordination)),
last(coord.now())
{
}
void on_next(source_value_type) const {
time_point now = coord.now();
dest.on_next(now - last);
last = now;
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer_type> make(dest_type d, time_interval_values v) {
return make_subscriber<value_type>(d, this_type(d, v.coordination));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(time_interval_observer<Subscriber>::make(std::move(dest), initial)) {
return time_interval_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-time_interval.hpp
*/
template<class... AN>
auto time_interval(AN&&... an)
-> operator_factory<time_interval_tag, AN...> {
return operator_factory<time_interval_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<time_interval_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class TimeInterval = rxo::detail::time_interval<SourceValue, identity_one_worker>,
class Value = typename rxsc::scheduler::clock_type::time_point::duration>
static auto member(Observable&& o)
-> decltype(o.template lift<Value>(TimeInterval(identity_current_thread()))) {
return o.template lift<Value>(TimeInterval(identity_current_thread()));
}
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class TimeInterval = rxo::detail::time_interval<SourceValue, rxu::decay_t<Coordination>>,
class Value = typename rxsc::scheduler::clock_type::time_point::duration>
static auto member(Observable&& o, Coordination&& cn)
-> decltype(o.template lift<Value>(TimeInterval(std::forward<Coordination>(cn)))) {
return o.template lift<Value>(TimeInterval(std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::time_interval_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "time_interval takes (optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,284 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-timeout.hpp
\brief Return an observable that terminates with timeout_error if a particular timespan has passed without emitting another item from the source observable.
\tparam Duration the type of time interval.
\tparam Coordination the type of the scheduler (optional).
\param period the period of time wait for another item from the source observable.
\param coordination the scheduler to manage timeout for each event (optional).
\return Observable that terminates with an error if a particular timespan has passed without emitting another item from the source observable.
\sample
\snippet timeout.cpp timeout sample
\snippet output.txt timeout sample
*/
#if !defined(RXCPP_OPERATORS_RX_TIMEOUT_HPP)
#define RXCPP_OPERATORS_RX_TIMEOUT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
class timeout_error: public std::runtime_error
{
public:
explicit timeout_error(const std::string& msg):
std::runtime_error(msg)
{}
};
namespace operators {
namespace detail {
template<class... AN>
struct timeout_invalid_arguments {};
template<class... AN>
struct timeout_invalid : public rxo::operator_base<timeout_invalid_arguments<AN...>> {
using type = observable<timeout_invalid_arguments<AN...>, timeout_invalid<AN...>>;
};
template<class... AN>
using timeout_invalid_t = typename timeout_invalid<AN...>::type;
template<class T, class Duration, class Coordination>
struct timeout
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef rxu::decay_t<Duration> duration_type;
struct timeout_values
{
timeout_values(duration_type p, coordination_type c)
: period(p)
, coordination(c)
{
}
duration_type period;
coordination_type coordination;
};
timeout_values initial;
timeout(duration_type period, coordination_type coordination)
: initial(period, coordination)
{
}
template<class Subscriber>
struct timeout_observer
{
typedef timeout_observer<Subscriber> this_type;
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
struct timeout_subscriber_values : public timeout_values
{
timeout_subscriber_values(composite_subscription cs, dest_type d, timeout_values v, coordinator_type c)
: timeout_values(v)
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
, index(0)
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
mutable std::size_t index;
};
typedef std::shared_ptr<timeout_subscriber_values> state_type;
state_type state;
timeout_observer(composite_subscription cs, dest_type d, timeout_values v, coordinator_type c)
: state(std::make_shared<timeout_subscriber_values>(timeout_subscriber_values(std::move(cs), std::move(d), v, std::move(c))))
{
auto localState = state;
auto disposer = [=](const rxsc::schedulable&){
localState->cs.unsubscribe();
localState->dest.unsubscribe();
localState->worker.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){ return localState->coordinator.act(disposer); },
localState->dest);
if (selectedDisposer.empty()) {
return;
}
localState->dest.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
localState->cs.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
auto work = [v, localState](const rxsc::schedulable&) {
auto new_id = ++localState->index;
auto produce_time = localState->worker.now() + localState->period;
localState->worker.schedule(produce_time, produce_timeout(new_id, localState));
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static std::function<void(const rxsc::schedulable&)> produce_timeout(std::size_t id, state_type state) {
auto produce = [id, state](const rxsc::schedulable&) {
if(id != state->index)
return;
state->dest.on_error(rxu::make_error_ptr(rxcpp::timeout_error("timeout has occurred")));
};
auto selectedProduce = on_exception(
[&](){ return state->coordinator.act(produce); },
state->dest);
if (selectedProduce.empty()) {
return std::function<void(const rxsc::schedulable&)>();
}
return std::function<void(const rxsc::schedulable&)>(selectedProduce.get());
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable&) {
auto new_id = ++localState->index;
auto produce_time = localState->worker.now() + localState->period;
localState->dest.on_next(v);
localState->worker.schedule(produce_time, produce_timeout(new_id, localState));
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&) {
localState->dest.on_error(e);
};
auto selectedWork = on_exception(
[&](){ return localState->coordinator.act(work); },
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&) {
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){ return localState->coordinator.act(work); },
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static subscriber<T, observer_type> make(dest_type d, timeout_values v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator();
return make_subscriber<T>(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(timeout_observer<Subscriber>::make(std::move(dest), initial)) {
return timeout_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-timeout.hpp
*/
template<class... AN>
auto timeout(AN&&... an)
-> operator_factory<timeout_tag, AN...> {
return operator_factory<timeout_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<timeout_tag>
{
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timeout = rxo::detail::timeout<SourceValue, rxu::decay_t<Duration>, identity_one_worker>>
static auto member(Observable&& o, Duration&& d)
-> decltype(o.template lift<SourceValue>(Timeout(std::forward<Duration>(d), identity_current_thread()))) {
return o.template lift<SourceValue>(Timeout(std::forward<Duration>(d), identity_current_thread()));
}
template<class Observable, class Coordination, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timeout = rxo::detail::timeout<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Coordination&& cn, Duration&& d)
-> decltype(o.template lift<SourceValue>(Timeout(std::forward<Duration>(d), std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(Timeout(std::forward<Duration>(d), std::forward<Coordination>(cn)));
}
template<class Observable, class Coordination, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>,
rxu::is_duration<Duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timeout = rxo::detail::timeout<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>>
static auto member(Observable&& o, Duration&& d, Coordination&& cn)
-> decltype(o.template lift<SourceValue>(Timeout(std::forward<Duration>(d), std::forward<Coordination>(cn)))) {
return o.template lift<SourceValue>(Timeout(std::forward<Duration>(d), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::timeout_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "timeout takes (optional Coordination, required Duration) or (required Duration, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,150 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-timestamp.hpp
\brief Returns an observable that attaches a timestamp to each item emitted by the source observable indicating when it was emitted.
\tparam Coordination the type of the scheduler (optional).
\param coordination the scheduler to manage timeout for each event (optional).
\return Observable that emits a pair: { item emitted by the source observable, time_point representing the current value of the clock }.
\sample
\snippet timestamp.cpp timestamp sample
\snippet output.txt timestamp sample
*/
#if !defined(RXCPP_OPERATORS_RX_TIMESTAMP_HPP)
#define RXCPP_OPERATORS_RX_TIMESTAMP_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct timestamp_invalid_arguments {};
template<class... AN>
struct timestamp_invalid : public rxo::operator_base<timestamp_invalid_arguments<AN...>> {
using type = observable<timestamp_invalid_arguments<AN...>, timestamp_invalid<AN...>>;
};
template<class... AN>
using timestamp_invalid_t = typename timestamp_invalid<AN...>::type;
template<class T, class Coordination>
struct timestamp
{
typedef rxu::decay_t<T> source_value_type;
typedef rxu::decay_t<Coordination> coordination_type;
struct timestamp_values {
timestamp_values(coordination_type c)
: coordination(c)
{
}
coordination_type coordination;
};
timestamp_values initial;
timestamp(coordination_type coordination)
: initial(coordination)
{
}
template<class Subscriber>
struct timestamp_observer
{
typedef timestamp_observer<Subscriber> this_type;
typedef source_value_type value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<value_type, this_type> observer_type;
dest_type dest;
coordination_type coord;
timestamp_observer(dest_type d, coordination_type coordination)
: dest(std::move(d)),
coord(std::move(coordination))
{
}
void on_next(source_value_type v) const {
dest.on_next(std::make_pair(v, coord.now()));
}
void on_error(rxu::error_ptr e) const {
dest.on_error(e);
}
void on_completed() const {
dest.on_completed();
}
static subscriber<value_type, observer_type> make(dest_type d, timestamp_values v) {
return make_subscriber<value_type>(d, this_type(d, v.coordination));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(timestamp_observer<Subscriber>::make(std::move(dest), initial)) {
return timestamp_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-timestamp.hpp
*/
template<class... AN>
auto timestamp(AN&&... an)
-> operator_factory<timestamp_tag, AN...> {
return operator_factory<timestamp_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<timestamp_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timestamp = rxo::detail::timestamp<SourceValue, identity_one_worker>,
class Clock = typename rxsc::scheduler::clock_type::time_point,
class Value = std::pair<SourceValue, Clock>>
static auto member(Observable&& o)
-> decltype(o.template lift<Value>(Timestamp(identity_current_thread()))) {
return o.template lift<Value>(Timestamp(identity_current_thread()));
}
template<class Observable, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class Timestamp = rxo::detail::timestamp<SourceValue, rxu::decay_t<Coordination>>,
class Clock = typename rxsc::scheduler::clock_type::time_point,
class Value = std::pair<SourceValue, Clock>>
static auto member(Observable&& o, Coordination&& cn)
-> decltype(o.template lift<Value>(Timestamp(std::forward<Coordination>(cn)))) {
return o.template lift<Value>(Timestamp(std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::timestamp_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "timestamp takes (optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,180 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-window.hpp
\brief Return an observable that emits connected, non-overlapping windows, each containing at most count items from the source observable.
If the skip parameter is set, return an observable that emits windows every skip items containing at most count items from the source observable.
\param count the maximum size of each window before it should be completed
\param skip how many items need to be skipped before starting a new window
\return Observable that emits connected, non-overlapping windows, each containing at most count items from the source observable.
If the skip parameter is set, return an Observable that emits windows every skip items containing at most count items from the source observable.
\sample
\snippet window.cpp window count+skip sample
\snippet output.txt window count+skip sample
\sample
\snippet window.cpp window count sample
\snippet output.txt window count sample
*/
#if !defined(RXCPP_OPERATORS_RX_WINDOW_HPP)
#define RXCPP_OPERATORS_RX_WINDOW_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct window_invalid_arguments {};
template<class... AN>
struct window_invalid : public rxo::operator_base<window_invalid_arguments<AN...>> {
using type = observable<window_invalid_arguments<AN...>, window_invalid<AN...>>;
};
template<class... AN>
using window_invalid_t = typename window_invalid<AN...>::type;
template<class T>
struct window
{
typedef rxu::decay_t<T> source_value_type;
typedef observable<source_value_type> value_type;
struct window_values
{
window_values(int c, int s)
: count(c)
, skip(s)
{
}
int count;
int skip;
};
window_values initial;
window(int count, int skip)
: initial(count, skip)
{
}
template<class Subscriber>
struct window_observer : public window_values
{
typedef window_observer<Subscriber> this_type;
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
dest_type dest;
mutable int cursor;
mutable std::deque<rxcpp::subjects::subject<T>> subj;
window_observer(dest_type d, window_values v)
: window_values(v)
, dest(std::move(d))
, cursor(0)
{
subj.push_back(rxcpp::subjects::subject<T>());
dest.on_next(subj[0].get_observable().as_dynamic());
}
void on_next(T v) const {
for (auto s : subj) {
s.get_subscriber().on_next(v);
}
int c = cursor - this->count + 1;
if (c >= 0 && c % this->skip == 0) {
subj[0].get_subscriber().on_completed();
subj.pop_front();
}
if (++cursor % this->skip == 0) {
subj.push_back(rxcpp::subjects::subject<T>());
dest.on_next(subj[subj.size() - 1].get_observable().as_dynamic());
}
}
void on_error(rxu::error_ptr e) const {
for (auto s : subj) {
s.get_subscriber().on_error(e);
}
dest.on_error(e);
}
void on_completed() const {
for (auto s : subj) {
s.get_subscriber().on_completed();
}
dest.on_completed();
}
static subscriber<T, observer_type> make(dest_type d, window_values v) {
auto cs = d.get_subscription();
return make_subscriber<T>(std::move(cs), observer_type(this_type(std::move(d), std::move(v))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(window_observer<Subscriber>::make(std::move(dest), initial)) {
return window_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-window.hpp
*/
template<class... AN>
auto window(AN&&... an)
-> operator_factory<window_tag, AN...> {
return operator_factory<window_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<window_tag>
{
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Window = rxo::detail::window<SourceValue>,
class Value = rxu::value_type_t<Window>>
static auto member(Observable&& o, int count, int skip)
-> decltype(o.template lift<Value>(Window(count, skip))) {
return o.template lift<Value>(Window(count, skip));
}
template<class Observable,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class SourceValue = rxu::value_type_t<Observable>,
class Window = rxo::detail::window<SourceValue>,
class Value = rxu::value_type_t<Window>>
static auto member(Observable&& o, int count)
-> decltype(o.template lift<Value>(Window(count, count))) {
return o.template lift<Value>(Window(count, count));
}
template<class... AN>
static operators::detail::window_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "window takes (Count, optional Skip)");
}
};
}
#endif

View File

@ -0,0 +1,322 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-window_time.hpp
\brief Return an observable that emits observables every period time interval and collects items from this observable for period of time into each produced observable, on the specified scheduler.
If the skip parameter is set, return an observable that emits observables every skip time interval and collects items from this observable for period of time into each produced observable, on the specified scheduler.
\tparam Duration the type of time intervals.
\tparam Coordination the type of the scheduler (optional).
\param period the period of time each window collects items before it is completed.
\param skip the period of time after which a new window will be created.
\param coordination the scheduler for the windows (optional).
\return Observable that emits observables every period time interval and collect items from this observable for period of time into each produced observable.
If the skip parameter is set, return an Observable that emits observables every skip time interval and collect items from this observable for period of time into each produced observable.
\sample
\snippet window.cpp window period+skip+coordination sample
\snippet output.txt window period+skip+coordination sample
\sample
\snippet window.cpp window period+skip sample
\snippet output.txt window period+skip sample
\sample
\snippet window.cpp window period+coordination sample
\snippet output.txt window period+coordination sample
\sample
\snippet window.cpp window period sample
\snippet output.txt window period sample
*/
#if !defined(RXCPP_OPERATORS_RX_WINDOW_WITH_TIME_HPP)
#define RXCPP_OPERATORS_RX_WINDOW_WITH_TIME_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct window_with_time_invalid_arguments {};
template<class... AN>
struct window_with_time_invalid : public rxo::operator_base<window_with_time_invalid_arguments<AN...>> {
using type = observable<window_with_time_invalid_arguments<AN...>, window_with_time_invalid<AN...>>;
};
template<class... AN>
using window_with_time_invalid_t = typename window_with_time_invalid<AN...>::type;
template<class T, class Duration, class Coordination>
struct window_with_time
{
typedef rxu::decay_t<T> source_value_type;
typedef observable<source_value_type> value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef rxu::decay_t<Duration> duration_type;
struct window_with_time_values
{
window_with_time_values(duration_type p, duration_type s, coordination_type c)
: period(p)
, skip(s)
, coordination(c)
{
}
duration_type period;
duration_type skip;
coordination_type coordination;
};
window_with_time_values initial;
window_with_time(duration_type period, duration_type skip, coordination_type coordination)
: initial(period, skip, coordination)
{
}
template<class Subscriber>
struct window_with_time_observer
{
typedef window_with_time_observer<Subscriber> this_type;
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
struct window_with_time_subscriber_values : public window_with_time_values
{
window_with_time_subscriber_values(composite_subscription cs, dest_type d, window_with_time_values v, coordinator_type c)
: window_with_time_values(v)
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
, expected(worker.now())
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
mutable std::deque<rxcpp::subjects::subject<T>> subj;
rxsc::scheduler::clock_type::time_point expected;
};
std::shared_ptr<window_with_time_subscriber_values> state;
window_with_time_observer(composite_subscription cs, dest_type d, window_with_time_values v, coordinator_type c)
: state(std::make_shared<window_with_time_subscriber_values>(window_with_time_subscriber_values(std::move(cs), std::move(d), v, std::move(c))))
{
auto localState = state;
auto disposer = [=](const rxsc::schedulable&){
localState->cs.unsubscribe();
localState->dest.unsubscribe();
localState->worker.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){return localState->coordinator.act(disposer);},
localState->dest);
if (selectedDisposer.empty()) {
return;
}
localState->dest.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
localState->cs.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
//
// The scheduler is FIFO for any time T. Since the observer is scheduling
// on_next/on_error/oncompleted the timed schedule calls must be resheduled
// when they occur to ensure that production happens after on_next/on_error/oncompleted
//
auto release_window = [localState](const rxsc::schedulable&) {
localState->worker.schedule([localState](const rxsc::schedulable&) {
localState->subj[0].get_subscriber().on_completed();
localState->subj.pop_front();
});
};
auto selectedRelease = on_exception(
[&](){return localState->coordinator.act(release_window);},
localState->dest);
if (selectedRelease.empty()) {
return;
}
auto create_window = [localState, selectedRelease](const rxsc::schedulable&) {
localState->subj.push_back(rxcpp::subjects::subject<T>());
localState->dest.on_next(localState->subj[localState->subj.size() - 1].get_observable().as_dynamic());
auto produce_at = localState->expected + localState->period;
localState->expected += localState->skip;
localState->worker.schedule(produce_at, [localState, selectedRelease](const rxsc::schedulable&) {
localState->worker.schedule(selectedRelease.get());
});
};
auto selectedCreate = on_exception(
[&](){return localState->coordinator.act(create_window);},
localState->dest);
if (selectedCreate.empty()) {
return;
}
state->worker.schedule_periodically(
state->expected,
state->skip,
[localState, selectedCreate](const rxsc::schedulable&) {
localState->worker.schedule(selectedCreate.get());
});
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable&){
for (auto s : localState->subj) {
s.get_subscriber().on_next(v);
}
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&){
for (auto s : localState->subj) {
s.get_subscriber().on_error(e);
}
localState->dest.on_error(e);
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&){
for (auto s : localState->subj) {
s.get_subscriber().on_completed();
}
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static subscriber<T, observer_type> make(dest_type d, window_with_time_values v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator();
return make_subscriber<T>(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(window_with_time_observer<Subscriber>::make(std::move(dest), initial)) {
return window_with_time_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-window_time.hpp
*/
template<class... AN>
auto window_with_time(AN&&... an)
-> operator_factory<window_with_time_tag, AN...> {
return operator_factory<window_with_time_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<window_with_time_tag>
{
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class WindowWithTime = rxo::detail::window_with_time<SourceValue, rxu::decay_t<Duration>, identity_one_worker>,
class Value = rxu::value_type_t<WindowWithTime>>
static auto member(Observable&& o, Duration period)
-> decltype(o.template lift<Value>(WindowWithTime(period, period, identity_current_thread()))) {
return o.template lift<Value>(WindowWithTime(period, period, identity_current_thread()));
}
template<class Observable, class Duration, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class WindowWithTime = rxo::detail::window_with_time<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<WindowWithTime>>
static auto member(Observable&& o, Duration period, Coordination&& cn)
-> decltype(o.template lift<Value>(WindowWithTime(period, period, std::forward<Coordination>(cn)))) {
return o.template lift<Value>(WindowWithTime(period, period, std::forward<Coordination>(cn)));
}
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class WindowWithTime = rxo::detail::window_with_time<SourceValue, rxu::decay_t<Duration>, identity_one_worker>,
class Value = rxu::value_type_t<WindowWithTime>>
static auto member(Observable&& o, Duration&& period, Duration&& skip)
-> decltype(o.template lift<Value>(WindowWithTime(std::forward<Duration>(period), std::forward<Duration>(skip), identity_current_thread()))) {
return o.template lift<Value>(WindowWithTime(std::forward<Duration>(period), std::forward<Duration>(skip), identity_current_thread()));
}
template<class Observable, class Duration, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class WindowWithTime = rxo::detail::window_with_time<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<WindowWithTime>>
static auto member(Observable&& o, Duration&& period, Duration&& skip, Coordination&& cn)
-> decltype(o.template lift<Value>(WindowWithTime(std::forward<Duration>(period), std::forward<Duration>(skip), std::forward<Coordination>(cn)))) {
return o.template lift<Value>(WindowWithTime(std::forward<Duration>(period), std::forward<Duration>(skip), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::window_with_time_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "window_with_time takes (Duration, optional Duration, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,281 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-window_time_count.hpp
\brief Return an observable that emits connected, non-overlapping windows of items from the source observable that were emitted during a fixed duration of time or when the window has reached maximum capacity (whichever occurs first), on the specified scheduler.
\tparam Duration the type of time intervals.
\tparam Coordination the type of the scheduler (optional).
\param period the period of time each window collects items before it is completed and replaced with a new window.
\param count the maximum size of each window before it is completed and new window is created.
\param coordination the scheduler for the windows (optional).
\return Observable that emits connected, non-overlapping windows of items from the source observable that were emitted during a fixed duration of time or when the window has reached maximum capacity (whichever occurs first).
\sample
\snippet window.cpp window period+count+coordination sample
\snippet output.txt window period+count+coordination sample
\sample
\snippet window.cpp window period+count sample
\snippet output.txt window period+count sample
*/
#if !defined(RXCPP_OPERATORS_RX_WINDOW_WITH_TIME_OR_COUNT_HPP)
#define RXCPP_OPERATORS_RX_WINDOW_WITH_TIME_OR_COUNT_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct window_with_time_or_count_invalid_arguments {};
template<class... AN>
struct window_with_time_or_count_invalid : public rxo::operator_base<window_with_time_or_count_invalid_arguments<AN...>> {
using type = observable<window_with_time_or_count_invalid_arguments<AN...>, window_with_time_or_count_invalid<AN...>>;
};
template<class... AN>
using window_with_time_or_count_invalid_t = typename window_with_time_or_count_invalid<AN...>::type;
template<class T, class Duration, class Coordination>
struct window_with_time_or_count
{
typedef rxu::decay_t<T> source_value_type;
typedef observable<source_value_type> value_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename coordination_type::coordinator_type coordinator_type;
typedef rxu::decay_t<Duration> duration_type;
struct window_with_time_or_count_values
{
window_with_time_or_count_values(duration_type p, int n, coordination_type c)
: period(p)
, count(n)
, coordination(c)
{
}
duration_type period;
int count;
coordination_type coordination;
};
window_with_time_or_count_values initial;
window_with_time_or_count(duration_type period, int count, coordination_type coordination)
: initial(period, count, coordination)
{
}
template<class Subscriber>
struct window_with_time_or_count_observer
{
typedef window_with_time_or_count_observer<Subscriber> this_type;
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
struct window_with_time_or_count_subscriber_values : public window_with_time_or_count_values
{
window_with_time_or_count_subscriber_values(composite_subscription cs, dest_type d, window_with_time_or_count_values v, coordinator_type c)
: window_with_time_or_count_values(std::move(v))
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
, cursor(0)
, subj_id(0)
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
mutable int cursor;
mutable int subj_id;
mutable rxcpp::subjects::subject<T> subj;
};
typedef std::shared_ptr<window_with_time_or_count_subscriber_values> state_type;
state_type state;
window_with_time_or_count_observer(composite_subscription cs, dest_type d, window_with_time_or_count_values v, coordinator_type c)
: state(std::make_shared<window_with_time_or_count_subscriber_values>(window_with_time_or_count_subscriber_values(std::move(cs), std::move(d), std::move(v), std::move(c))))
{
auto new_id = state->subj_id;
auto produce_time = state->worker.now();
auto localState = state;
auto disposer = [=](const rxsc::schedulable&){
localState->cs.unsubscribe();
localState->dest.unsubscribe();
localState->worker.unsubscribe();
};
auto selectedDisposer = on_exception(
[&](){return localState->coordinator.act(disposer);},
localState->dest);
if (selectedDisposer.empty()) {
return;
}
localState->dest.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
localState->cs.add([=](){
localState->worker.schedule(selectedDisposer.get());
});
//
// The scheduler is FIFO for any time T. Since the observer is scheduling
// on_next/on_error/oncompleted the timed schedule calls must be resheduled
// when they occur to ensure that production happens after on_next/on_error/oncompleted
//
localState->worker.schedule(produce_time, [new_id, produce_time, localState](const rxsc::schedulable&){
localState->worker.schedule(release_window(new_id, produce_time, localState));
});
}
static std::function<void(const rxsc::schedulable&)> release_window(int id, rxsc::scheduler::clock_type::time_point expected, state_type state) {
auto release = [id, expected, state](const rxsc::schedulable&) {
if (id != state->subj_id)
return;
state->subj.get_subscriber().on_completed();
state->subj = rxcpp::subjects::subject<T>();
state->dest.on_next(state->subj.get_observable().as_dynamic());
state->cursor = 0;
auto new_id = ++state->subj_id;
auto produce_time = expected + state->period;
state->worker.schedule(produce_time, [new_id, produce_time, state](const rxsc::schedulable&){
state->worker.schedule(release_window(new_id, produce_time, state));
});
};
auto selectedRelease = on_exception(
[&](){return state->coordinator.act(release);},
state->dest);
if (selectedRelease.empty()) {
return std::function<void(const rxsc::schedulable&)>();
}
return std::function<void(const rxsc::schedulable&)>(selectedRelease.get());
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable& self){
localState->subj.get_subscriber().on_next(v);
if (++localState->cursor == localState->count) {
release_window(localState->subj_id, localState->worker.now(), localState)(self);
}
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&){
localState->subj.get_subscriber().on_error(e);
localState->dest.on_error(e);
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&){
localState->subj.get_subscriber().on_completed();
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static subscriber<T, observer_type> make(dest_type d, window_with_time_or_count_values v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator();
return make_subscriber<T>(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(window_with_time_or_count_observer<Subscriber>::make(std::move(dest), initial)) {
return window_with_time_or_count_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-window_time_count.hpp
*/
template<class... AN>
auto window_with_time_or_count(AN&&... an)
-> operator_factory<window_with_time_or_count_tag, AN...> {
return operator_factory<window_with_time_or_count_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<window_with_time_or_count_tag>
{
template<class Observable, class Duration,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>>,
class SourceValue = rxu::value_type_t<Observable>,
class WindowTimeCount = rxo::detail::window_with_time_or_count<SourceValue, rxu::decay_t<Duration>, identity_one_worker>,
class Value = rxu::value_type_t<WindowTimeCount>>
static auto member(Observable&& o, Duration&& period, int count)
-> decltype(o.template lift<Value>(WindowTimeCount(std::forward<Duration>(period), count, identity_current_thread()))) {
return o.template lift<Value>(WindowTimeCount(std::forward<Duration>(period), count, identity_current_thread()));
}
template<class Observable, class Duration, class Coordination,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>,
std::is_convertible<Duration, rxsc::scheduler::clock_type::duration>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class WindowTimeCount = rxo::detail::window_with_time_or_count<SourceValue, rxu::decay_t<Duration>, rxu::decay_t<Coordination>>,
class Value = rxu::value_type_t<WindowTimeCount>>
static auto member(Observable&& o, Duration&& period, int count, Coordination&& cn)
-> decltype(o.template lift<Value>(WindowTimeCount(std::forward<Duration>(period), count, std::forward<Coordination>(cn)))) {
return o.template lift<Value>(WindowTimeCount(std::forward<Duration>(period), count, std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::window_with_time_or_count_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "window_with_time_or_count takes (Duration, Count, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,324 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
/*! \file rx-window_toggle.hpp
\brief Return an observable that emits observables every period time interval and collects items from this observable for period of time into each produced observable, on the specified scheduler.
\tparam Openings observable<OT>
\tparam ClosingSelector a function of type observable<CT>(OT)
\tparam Coordination the type of the scheduler (optional).
\param opens each value from this observable opens a new window.
\param closes this function is called for each opened window and returns an observable. the first value from the returned observable will close the window.
\param coordination the scheduler for the windows (optional).
\return Observable that emits an observable for each opened window.
\sample
\snippet window.cpp window toggle+coordination sample
\snippet output.txt window toggle+coordination sample
\sample
\snippet window.cpp window toggle sample
\snippet output.txt window toggle sample
*/
#if !defined(RXCPP_OPERATORS_RX_WINDOW_TOGGLE_HPP)
#define RXCPP_OPERATORS_RX_WINDOW_TOGGLE_HPP
#include "../rx-includes.hpp"
namespace rxcpp {
namespace operators {
namespace detail {
template<class... AN>
struct window_toggle_invalid_arguments {};
template<class... AN>
struct window_toggle_invalid : public rxo::operator_base<window_toggle_invalid_arguments<AN...>> {
using type = observable<window_toggle_invalid_arguments<AN...>, window_toggle_invalid<AN...>>;
};
template<class... AN>
using window_toggle_invalid_t = typename window_toggle_invalid<AN...>::type;
template<class T, class Openings, class ClosingSelector, class Coordination>
struct window_toggle
{
typedef window_toggle<T, Openings, ClosingSelector, Coordination> this_type;
using source_value_type = rxu::decay_t<T>;
using coordination_type = rxu::decay_t<Coordination>;
using coordinator_type = typename coordination_type::coordinator_type;
using openings_type = rxu::decay_t<Openings>;
using openings_value_type = typename openings_type::value_type;
using closing_selector_type = rxu::decay_t<ClosingSelector>;
using closings_type = rxu::result_of_t<closing_selector_type(openings_value_type)>;
using closings_value_type = typename closings_type::value_type;
struct window_toggle_values
{
window_toggle_values(openings_type opens, closing_selector_type closes, coordination_type c)
: openings(opens)
, closingSelector(closes)
, coordination(c)
{
}
openings_type openings;
mutable closing_selector_type closingSelector;
coordination_type coordination;
};
window_toggle_values initial;
window_toggle(openings_type opens, closing_selector_type closes, coordination_type coordination)
: initial(opens, closes, coordination)
{
}
template<class Subscriber>
struct window_toggle_observer
{
typedef window_toggle_observer<Subscriber> this_type;
typedef rxu::decay_t<T> value_type;
typedef rxu::decay_t<Subscriber> dest_type;
typedef observer<T, this_type> observer_type;
struct window_toggle_subscriber_values : public window_toggle_values
{
window_toggle_subscriber_values(composite_subscription cs, dest_type d, window_toggle_values v, coordinator_type c)
: window_toggle_values(v)
, cs(std::move(cs))
, dest(std::move(d))
, coordinator(std::move(c))
, worker(coordinator.get_worker())
{
}
composite_subscription cs;
dest_type dest;
coordinator_type coordinator;
rxsc::worker worker;
mutable std::list<rxcpp::subjects::subject<T>> subj;
};
std::shared_ptr<window_toggle_subscriber_values> state;
window_toggle_observer(composite_subscription cs, dest_type d, window_toggle_values v, coordinator_type c)
: state(std::make_shared<window_toggle_subscriber_values>(window_toggle_subscriber_values(std::move(cs), std::move(d), v, std::move(c))))
{
auto localState = state;
composite_subscription innercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innerscope = localState->dest.add(innercs);
innercs.add([=](){
localState->dest.remove(innerscope);
});
localState->dest.add(localState->cs);
auto source = on_exception(
[&](){return localState->coordinator.in(localState->openings);},
localState->dest);
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<openings_value_type>(
localState->dest,
innercs,
// on_next
[localState](const openings_value_type& ov) {
auto closer = localState->closingSelector(ov);
auto it = localState->subj.insert(localState->subj.end(), rxcpp::subjects::subject<T>());
localState->dest.on_next(it->get_observable().as_dynamic());
composite_subscription innercs;
// when the out observer is unsubscribed all the
// inner subscriptions are unsubscribed as well
auto innerscope = localState->dest.add(innercs);
innercs.add([=](){
localState->dest.remove(innerscope);
});
auto source = localState->coordinator.in(closer);
auto sit = std::make_shared<decltype(it)>(it);
auto close = [localState, sit]() {
auto it = *sit;
*sit = localState->subj.end();
if (it != localState->subj.end()) {
it->get_subscriber().on_completed();
localState->subj.erase(it);
}
};
// 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<closings_value_type>(
localState->dest,
innercs,
// on_next
[close, innercs](closings_value_type) {
close();
innercs.unsubscribe();
},
// on_error
[localState](rxu::error_ptr e) {
localState->dest.on_error(e);
},
// on_completed
close
);
auto selectedSink = localState->coordinator.out(sink);
source.subscribe(std::move(selectedSink));
},
// on_error
[localState](rxu::error_ptr e) {
localState->dest.on_error(e);
},
// on_completed
[]() {
}
);
auto selectedSink = on_exception(
[&](){return localState->coordinator.out(sink);},
localState->dest);
if (selectedSink.empty()) {
return;
}
source->subscribe(std::move(selectedSink.get()));
}
void on_next(T v) const {
auto localState = state;
auto work = [v, localState](const rxsc::schedulable&){
for (auto s : localState->subj) {
s.get_subscriber().on_next(v);
}
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_error(rxu::error_ptr e) const {
auto localState = state;
auto work = [e, localState](const rxsc::schedulable&){
for (auto s : localState->subj) {
s.get_subscriber().on_error(e);
}
localState->dest.on_error(e);
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
void on_completed() const {
auto localState = state;
auto work = [localState](const rxsc::schedulable&){
for (auto s : localState->subj) {
s.get_subscriber().on_completed();
}
localState->dest.on_completed();
};
auto selectedWork = on_exception(
[&](){return localState->coordinator.act(work);},
localState->dest);
if (selectedWork.empty()) {
return;
}
localState->worker.schedule(selectedWork.get());
}
static subscriber<T, observer_type> make(dest_type d, window_toggle_values v) {
auto cs = composite_subscription();
auto coordinator = v.coordination.create_coordinator(d.get_subscription());
return make_subscriber<T>(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator))));
}
};
template<class Subscriber>
auto operator()(Subscriber dest) const
-> decltype(window_toggle_observer<Subscriber>::make(std::move(dest), initial)) {
return window_toggle_observer<Subscriber>::make(std::move(dest), initial);
}
};
}
/*! @copydoc rx-window_toggle.hpp
*/
template<class... AN>
auto window_toggle(AN&&... an)
-> operator_factory<window_toggle_tag, AN...> {
return operator_factory<window_toggle_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<window_toggle_tag>
{
template<class Observable, class Openings, class ClosingSelector,
class ClosingSelectorType = rxu::decay_t<ClosingSelector>,
class OpeningsType = rxu::decay_t<Openings>,
class OpeningsValueType = typename OpeningsType::value_type,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Openings, rxu::result_of_t<ClosingSelectorType(OpeningsValueType)>>>,
class SourceValue = rxu::value_type_t<Observable>,
class WindowToggle = rxo::detail::window_toggle<SourceValue, rxu::decay_t<Openings>, rxu::decay_t<ClosingSelector>, identity_one_worker>,
class Value = observable<SourceValue>>
static auto member(Observable&& o, Openings&& openings, ClosingSelector&& closingSelector)
-> decltype(o.template lift<Value>(WindowToggle(std::forward<Openings>(openings), std::forward<ClosingSelector>(closingSelector), identity_immediate()))) {
return o.template lift<Value>(WindowToggle(std::forward<Openings>(openings), std::forward<ClosingSelector>(closingSelector), identity_immediate()));
}
template<class Observable, class Openings, class ClosingSelector, class Coordination,
class ClosingSelectorType = rxu::decay_t<ClosingSelector>,
class OpeningsType = rxu::decay_t<Openings>,
class OpeningsValueType = typename OpeningsType::value_type,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, Openings, rxu::result_of_t<ClosingSelectorType(OpeningsValueType)>>,
is_coordination<Coordination>>,
class SourceValue = rxu::value_type_t<Observable>,
class WindowToggle = rxo::detail::window_toggle<SourceValue, rxu::decay_t<Openings>, rxu::decay_t<ClosingSelector>, rxu::decay_t<Coordination>>,
class Value = observable<SourceValue>>
static auto member(Observable&& o, Openings&& openings, ClosingSelector&& closingSelector, Coordination&& cn)
-> decltype(o.template lift<Value>(WindowToggle(std::forward<Openings>(openings), std::forward<ClosingSelector>(closingSelector), std::forward<Coordination>(cn)))) {
return o.template lift<Value>(WindowToggle(std::forward<Openings>(openings), std::forward<ClosingSelector>(closingSelector), std::forward<Coordination>(cn)));
}
template<class... AN>
static operators::detail::window_toggle_invalid_t<AN...> member(AN...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "window_toggle takes (Openings, ClosingSelector, optional Coordination)");
}
};
}
#endif

View File

@ -0,0 +1,312 @@
// 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<class... AN>
struct with_latest_from_invalid_arguments {};
template<class... AN>
struct with_latest_from_invalid : public rxo::operator_base<with_latest_from_invalid_arguments<AN...>> {
using type = observable<with_latest_from_invalid_arguments<AN...>, with_latest_from_invalid<AN...>>;
};
template<class... AN>
using with_latest_from_invalid_t = typename with_latest_from_invalid<AN...>::type;
template<class Selector, class... ObservableN>
struct is_with_latest_from_selector_check {
typedef rxu::decay_t<Selector> selector_type;
struct tag_not_valid;
template<class CS, class... CON>
static auto check(int) -> decltype((*(CS*)nullptr)((*(typename CON::value_type*)nullptr)...));
template<class CS, class... CON>
static tag_not_valid check(...);
using type = decltype(check<selector_type, rxu::decay_t<ObservableN>...>(0));
static const bool value = !std::is_same<type, tag_not_valid>::value;
};
template<class Selector, class... ObservableN>
struct invalid_with_latest_from_selector {
static const bool value = false;
};
template<class Selector, class... ObservableN>
struct is_with_latest_from_selector : public std::conditional<
is_with_latest_from_selector_check<Selector, ObservableN...>::value,
is_with_latest_from_selector_check<Selector, ObservableN...>,
invalid_with_latest_from_selector<Selector, ObservableN...>>::type {
};
template<class Selector, class... ON>
using result_with_latest_from_selector_t = typename is_with_latest_from_selector<Selector, ON...>::type;
template<class Coordination, class Selector, class... ObservableN>
struct with_latest_from_traits {
typedef std::tuple<ObservableN...> tuple_source_type;
typedef std::tuple<rxu::detail::maybe<typename ObservableN::value_type>...> tuple_source_value_type;
typedef rxu::decay_t<Selector> selector_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename is_with_latest_from_selector<selector_type, ObservableN...>::type value_type;
};
template<class Coordination, class Selector, class... ObservableN>
struct with_latest_from : public operator_base<rxu::value_type_t<with_latest_from_traits<Coordination, Selector, ObservableN...>>>
{
typedef with_latest_from<Coordination, Selector, ObservableN...> this_type;
typedef with_latest_from_traits<Coordination, Selector, ObservableN...> 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<int Index, class State>
void subscribe_one(std::shared_ptr<State> state) const {
typedef typename std::tuple_element<Index, tuple_source_type>::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<Index>(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<source_value_type>(
state->out,
innercs,
// on_next
[state](source_value_type st) {
auto& value = std::get<Index>(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](rxu::error_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<class State, int... IndexN>
void subscribe_all(std::shared_ptr<State> state, rxu::values<int, IndexN...>) const {
bool subscribed[] = {(subscribe_one<(sizeof...(IndexN)) - 1 - IndexN>(state), true)...};
subscribed[0] = (*subscribed); // silence warning
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct with_latest_from_state_type
: public std::enable_shared_from_this<with_latest_from_state_type>
, 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<with_latest_from_state_type>(initial, std::move(coordinator), std::move(scbr));
subscribe_all(state, typename rxu::values_from<int, sizeof...(ObservableN)>::type());
}
};
}
/*! @copydoc rx-with_latest_from.hpp
*/
template<class... AN>
auto with_latest_from(AN&&... an)
-> operator_factory<with_latest_from_tag, AN...> {
return operator_factory<with_latest_from_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<with_latest_from_tag>
{
template<class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, ObservableN...>>,
class with_latest_from = rxo::detail::with_latest_from<identity_one_worker, rxu::detail::pack, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<with_latest_from>,
class Result = observable<Value, with_latest_from>>
static Result member(Observable&& o, ObservableN&&... on)
{
return Result(with_latest_from(identity_current_thread(), rxu::pack(), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Observable, class Selector, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
operators::detail::is_with_latest_from_selector<Selector, Observable, ObservableN...>,
all_observables<Observable, ObservableN...>>,
class ResolvedSelector = rxu::decay_t<Selector>,
class with_latest_from = rxo::detail::with_latest_from<identity_one_worker, ResolvedSelector, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<with_latest_from>,
class Result = observable<Value, with_latest_from>>
static Result member(Observable&& o, Selector&& s, ObservableN&&... on)
{
return Result(with_latest_from(identity_current_thread(), std::forward<Selector>(s), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Coordination, class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
is_coordination<Coordination>,
all_observables<Observable, ObservableN...>>,
class with_latest_from = rxo::detail::with_latest_from<Coordination, rxu::detail::pack, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<with_latest_from>,
class Result = observable<Value, with_latest_from>>
static Result member(Observable&& o, Coordination&& cn, ObservableN&&... on)
{
return Result(with_latest_from(std::forward<Coordination>(cn), rxu::pack(), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Coordination, class Selector, class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
is_coordination<Coordination>,
operators::detail::is_with_latest_from_selector<Selector, Observable, ObservableN...>,
all_observables<Observable, ObservableN...>>,
class ResolvedSelector = rxu::decay_t<Selector>,
class with_latest_from = rxo::detail::with_latest_from<Coordination, ResolvedSelector, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<with_latest_from>,
class Result = observable<Value, with_latest_from>>
static Result member(Observable&& o, Coordination&& cn, Selector&& s, ObservableN&&... on)
{
return Result(with_latest_from(std::forward<Coordination>(cn), std::forward<Selector>(s), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class... AN>
static operators::detail::with_latest_from_invalid_t<AN...> 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

View File

@ -0,0 +1,344 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#pragma once
#if !defined(RXCPP_OPERATORS_RX_ZIP_HPP)
#define RXCPP_OPERATORS_RX_ZIP_HPP
#include "../rx-includes.hpp"
/*! \file rx-zip.hpp
\brief Bring by one item from all given observables and select a value 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 the result of combining the items emitted and brought by one from each of 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 zip.cpp zip sample
\snippet output.txt zip sample
Only scheduler is present:
\snippet zip.cpp Coordination zip sample
\snippet output.txt Coordination zip sample
Only aggregation function is present:
\snippet zip.cpp Selector zip sample
\snippet output.txt Selector zip sample
Both scheduler and aggregation function are present:
\snippet zip.cpp Coordination+Selector zip sample
\snippet output.txt Coordination+Selector zip sample
*/
namespace rxcpp {
namespace operators {
namespace detail {
template<class Observable>
struct zip_source_state
{
using value_type = rxu::value_type_t<Observable>;
zip_source_state()
: completed(false)
{
}
std::list<value_type> values;
bool completed;
};
struct values_not_empty {
template<class Observable>
bool operator()(zip_source_state<Observable>& source) const {
return !source.values.empty();
}
};
struct source_completed_values_empty {
template<class Observable>
bool operator()(zip_source_state<Observable>& source) const {
return source.completed && source.values.empty();
}
};
struct extract_value_front {
template<class Observable, class Value = rxu::value_type_t<Observable>>
Value operator()(zip_source_state<Observable>& source) const {
auto val = std::move(source.values.front());
source.values.pop_front();
return val;
}
};
template<class... AN>
struct zip_invalid_arguments {};
template<class... AN>
struct zip_invalid : public rxo::operator_base<zip_invalid_arguments<AN...>> {
using type = observable<zip_invalid_arguments<AN...>, zip_invalid<AN...>>;
};
template<class... AN>
using zip_invalid_t = typename zip_invalid<AN...>::type;
template<class Selector, class... ObservableN>
struct is_zip_selector_check {
typedef rxu::decay_t<Selector> selector_type;
struct tag_not_valid;
template<class CS, class... CON>
static auto check(int) -> decltype((*(CS*)nullptr)((*(typename CON::value_type*)nullptr)...));
template<class CS, class... CON>
static tag_not_valid check(...);
using type = decltype(check<selector_type, rxu::decay_t<ObservableN>...>(0));
static const bool value = !std::is_same<type, tag_not_valid>::value;
};
template<class Selector, class... ObservableN>
struct invalid_zip_selector {
static const bool value = false;
};
template<class Selector, class... ObservableN>
struct is_zip_selector : public std::conditional<
is_zip_selector_check<Selector, ObservableN...>::value,
is_zip_selector_check<Selector, ObservableN...>,
invalid_zip_selector<Selector, ObservableN...>>::type {
};
template<class Selector, class... ON>
using result_zip_selector_t = typename is_zip_selector<Selector, ON...>::type;
template<class Coordination, class Selector, class... ObservableN>
struct zip_traits {
typedef std::tuple<rxu::decay_t<ObservableN>...> tuple_source_type;
typedef std::tuple<zip_source_state<ObservableN>...> tuple_source_values_type;
typedef rxu::decay_t<Selector> selector_type;
typedef rxu::decay_t<Coordination> coordination_type;
typedef typename is_zip_selector<selector_type, ObservableN...>::type value_type;
};
template<class Coordination, class Selector, class... ObservableN>
struct zip : public operator_base<rxu::value_type_t<zip_traits<Coordination, Selector, ObservableN...>>>
{
typedef zip<Coordination, Selector, ObservableN...> this_type;
typedef zip_traits<Coordination, Selector, ObservableN...> traits;
typedef typename traits::tuple_source_type tuple_source_type;
typedef typename traits::tuple_source_values_type tuple_source_values_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;
zip(coordination_type sf, selector_type s, tuple_source_type ts)
: initial(std::move(ts), std::move(s), std::move(sf))
{
}
template<int Index, class State>
void subscribe_one(std::shared_ptr<State> state) const {
typedef typename std::tuple_element<Index, tuple_source_type>::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<Index>(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<source_value_type>(
state->out,
innercs,
// on_next
[state](source_value_type st) {
auto& values = std::get<Index>(state->pending).values;
values.push_back(st);
if (rxu::apply_to_each(state->pending, values_not_empty(), rxu::all_values_true())) {
auto selectedResult = rxu::apply_to_each(state->pending, extract_value_front(), state->selector);
state->out.on_next(selectedResult);
}
if (rxu::apply_to_each(state->pending, source_completed_values_empty(), rxu::any_value_true())) {
state->out.on_completed();
}
},
// on_error
[state](rxu::error_ptr e) {
state->out.on_error(e);
},
// on_completed
[state]() {
auto& completed = std::get<Index>(state->pending).completed;
completed = true;
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<class State, int... IndexN>
void subscribe_all(std::shared_ptr<State> state, rxu::values<int, IndexN...>) const {
bool subscribed[] = {(subscribe_one<IndexN>(state), true)...};
subscribed[0] = (*subscribed); // silence warning
}
template<class Subscriber>
void on_subscribe(Subscriber scbr) const {
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
typedef Subscriber output_type;
struct zip_state_type
: public std::enable_shared_from_this<zip_state_type>
, public values
{
zip_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_values_type pending;
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<zip_state_type>(initial, std::move(coordinator), std::move(scbr));
subscribe_all(state, typename rxu::values_from<int, sizeof...(ObservableN)>::type());
}
};
}
/*! @copydoc rx-zip.hpp
*/
template<class... AN>
auto zip(AN&&... an)
-> operator_factory<zip_tag, AN...> {
return operator_factory<zip_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}
}
template<>
struct member_overload<zip_tag>
{
template<class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
all_observables<Observable, ObservableN...>>,
class Zip = rxo::detail::zip<identity_one_worker, rxu::detail::pack, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<Zip>,
class Result = observable<Value, Zip>>
static Result member(Observable&& o, ObservableN&&... on)
{
return Result(Zip(identity_current_thread(), rxu::pack(), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Observable, class Selector, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
operators::detail::is_zip_selector<Selector, Observable, ObservableN...>,
all_observables<Observable, ObservableN...>>,
class ResolvedSelector = rxu::decay_t<Selector>,
class Zip = rxo::detail::zip<identity_one_worker, ResolvedSelector, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<Zip>,
class Result = observable<Value, Zip>>
static Result member(Observable&& o, Selector&& s, ObservableN&&... on)
{
return Result(Zip(identity_current_thread(), std::forward<Selector>(s), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Coordination, class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
is_coordination<Coordination>,
all_observables<Observable, ObservableN...>>,
class Zip = rxo::detail::zip<Coordination, rxu::detail::pack, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<Zip>,
class Result = observable<Value, Zip>>
static Result member(Observable&& o, Coordination&& cn, ObservableN&&... on)
{
return Result(Zip(std::forward<Coordination>(cn), rxu::pack(), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class Coordination, class Selector, class Observable, class... ObservableN,
class Enabled = rxu::enable_if_all_true_type_t<
is_coordination<Coordination>,
operators::detail::is_zip_selector<Selector, Observable, ObservableN...>,
all_observables<Observable, ObservableN...>>,
class ResolvedSelector = rxu::decay_t<Selector>,
class Zip = rxo::detail::zip<Coordination, ResolvedSelector, rxu::decay_t<Observable>, rxu::decay_t<ObservableN>...>,
class Value = rxu::value_type_t<Zip>,
class Result = observable<Value, Zip>>
static Result member(Observable&& o, Coordination&& cn, Selector&& s, ObservableN&&... on)
{
return Result(Zip(std::forward<Coordination>(cn), std::forward<Selector>(s), std::make_tuple(std::forward<Observable>(o), std::forward<ObservableN>(on)...)));
}
template<class... AN>
static operators::detail::zip_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "zip takes (optional Coordination, optional Selector, required Observable, optional Observable...), Selector takes (Observable::value_type...)");
}
};
}
#endif