326 lines
12 KiB
C++
326 lines
12 KiB
C++
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#if !defined(RXCPP_SOURCES_RX_ITERATE_HPP)
|
||
|
#define RXCPP_SOURCES_RX_ITERATE_HPP
|
||
|
|
||
|
#include "../rx-includes.hpp"
|
||
|
|
||
|
/*! \file rx-iterate.hpp
|
||
|
|
||
|
\brief Returns an observable that sends each value in the collection, on the specified scheduler.
|
||
|
|
||
|
\tparam Collection the type of the collection of values that this observable emits
|
||
|
\tparam Coordination the type of the scheduler (optional)
|
||
|
|
||
|
\param c collection containing values to send
|
||
|
\param cn the scheduler to use for scheduling the items (optional)
|
||
|
|
||
|
\return Observable that sends each value in the collection.
|
||
|
|
||
|
\sample
|
||
|
\snippet iterate.cpp iterate sample
|
||
|
\snippet output.txt iterate sample
|
||
|
|
||
|
\sample
|
||
|
\snippet iterate.cpp threaded iterate sample
|
||
|
\snippet output.txt threaded iterate sample
|
||
|
|
||
|
*/
|
||
|
|
||
|
namespace rxcpp {
|
||
|
|
||
|
namespace sources {
|
||
|
|
||
|
namespace detail {
|
||
|
|
||
|
template<class Collection>
|
||
|
struct is_iterable
|
||
|
{
|
||
|
typedef rxu::decay_t<Collection> collection_type;
|
||
|
|
||
|
struct not_void {};
|
||
|
template<class CC>
|
||
|
static auto check(int) -> decltype(std::begin(*(CC*)nullptr));
|
||
|
template<class CC>
|
||
|
static not_void check(...);
|
||
|
|
||
|
static const bool value = !std::is_same<decltype(check<collection_type>(0)), not_void>::value;
|
||
|
};
|
||
|
|
||
|
template<class Collection>
|
||
|
struct iterate_traits
|
||
|
{
|
||
|
typedef rxu::decay_t<Collection> collection_type;
|
||
|
typedef rxu::decay_t<decltype(std::begin(*(collection_type*)nullptr))> iterator_type;
|
||
|
typedef rxu::value_type_t<std::iterator_traits<iterator_type>> value_type;
|
||
|
};
|
||
|
|
||
|
template<class Collection, class Coordination>
|
||
|
struct iterate : public source_base<rxu::value_type_t<iterate_traits<Collection>>>
|
||
|
{
|
||
|
typedef iterate<Collection, Coordination> this_type;
|
||
|
typedef iterate_traits<Collection> traits;
|
||
|
|
||
|
typedef rxu::decay_t<Coordination> coordination_type;
|
||
|
typedef typename coordination_type::coordinator_type coordinator_type;
|
||
|
|
||
|
typedef typename traits::collection_type collection_type;
|
||
|
typedef typename traits::iterator_type iterator_type;
|
||
|
|
||
|
struct iterate_initial_type
|
||
|
{
|
||
|
iterate_initial_type(collection_type c, coordination_type cn)
|
||
|
: collection(std::move(c))
|
||
|
, coordination(std::move(cn))
|
||
|
{
|
||
|
}
|
||
|
collection_type collection;
|
||
|
coordination_type coordination;
|
||
|
};
|
||
|
iterate_initial_type initial;
|
||
|
|
||
|
iterate(collection_type c, coordination_type cn)
|
||
|
: initial(std::move(c), std::move(cn))
|
||
|
{
|
||
|
}
|
||
|
template<class Subscriber>
|
||
|
void on_subscribe(Subscriber o) const {
|
||
|
static_assert(is_subscriber<Subscriber>::value, "subscribe must be passed a subscriber");
|
||
|
|
||
|
typedef typename coordinator_type::template get<Subscriber>::type output_type;
|
||
|
|
||
|
struct iterate_state_type
|
||
|
: public iterate_initial_type
|
||
|
{
|
||
|
iterate_state_type(const iterate_initial_type& i, output_type o)
|
||
|
: iterate_initial_type(i)
|
||
|
, cursor(std::begin(iterate_initial_type::collection))
|
||
|
, end(std::end(iterate_initial_type::collection))
|
||
|
, out(std::move(o))
|
||
|
{
|
||
|
}
|
||
|
iterate_state_type(const iterate_state_type& o)
|
||
|
: iterate_initial_type(o)
|
||
|
, cursor(std::begin(iterate_initial_type::collection))
|
||
|
, end(std::end(iterate_initial_type::collection))
|
||
|
, out(std::move(o.out)) // since lambda capture does not yet support move
|
||
|
{
|
||
|
}
|
||
|
mutable iterator_type cursor;
|
||
|
iterator_type end;
|
||
|
mutable output_type out;
|
||
|
};
|
||
|
|
||
|
// creates a worker whose lifetime is the same as this subscription
|
||
|
auto coordinator = initial.coordination.create_coordinator(o.get_subscription());
|
||
|
|
||
|
iterate_state_type state(initial, o);
|
||
|
|
||
|
auto controller = coordinator.get_worker();
|
||
|
|
||
|
auto producer = [state](const rxsc::schedulable& self){
|
||
|
if (!state.out.is_subscribed()) {
|
||
|
// terminate loop
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (state.cursor != state.end) {
|
||
|
// send next value
|
||
|
state.out.on_next(*state.cursor);
|
||
|
++state.cursor;
|
||
|
}
|
||
|
|
||
|
if (state.cursor == state.end) {
|
||
|
state.out.on_completed();
|
||
|
// o is unsubscribed
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// tail recurse this same action to continue loop
|
||
|
self();
|
||
|
};
|
||
|
auto selectedProducer = on_exception(
|
||
|
[&](){return coordinator.act(producer);},
|
||
|
o);
|
||
|
if (selectedProducer.empty()) {
|
||
|
return;
|
||
|
}
|
||
|
controller.schedule(selectedProducer.get());
|
||
|
|
||
|
}
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
/*! @copydoc rx-iterate.hpp
|
||
|
*/
|
||
|
template<class Collection>
|
||
|
auto iterate(Collection c)
|
||
|
-> observable<rxu::value_type_t<detail::iterate_traits<Collection>>, detail::iterate<Collection, identity_one_worker>> {
|
||
|
return observable<rxu::value_type_t<detail::iterate_traits<Collection>>, detail::iterate<Collection, identity_one_worker>>(
|
||
|
detail::iterate<Collection, identity_one_worker>(std::move(c), identity_immediate()));
|
||
|
}
|
||
|
/*! @copydoc rx-iterate.hpp
|
||
|
*/
|
||
|
template<class Collection, class Coordination>
|
||
|
auto iterate(Collection c, Coordination cn)
|
||
|
-> observable<rxu::value_type_t<detail::iterate_traits<Collection>>, detail::iterate<Collection, Coordination>> {
|
||
|
return observable<rxu::value_type_t<detail::iterate_traits<Collection>>, detail::iterate<Collection, Coordination>>(
|
||
|
detail::iterate<Collection, Coordination>(std::move(c), std::move(cn)));
|
||
|
}
|
||
|
|
||
|
/*! Returns an observable that sends an empty set of values and then completes.
|
||
|
|
||
|
\tparam T the type of elements (not) to be sent
|
||
|
|
||
|
\return Observable that sends an empty set of values and then completes.
|
||
|
|
||
|
This is a degenerate case of rxcpp::observable<void,void>#from(Value0,ValueN...) operator.
|
||
|
|
||
|
\note This is a degenerate case of ```from(Value0 v0, ValueN... vn)``` operator.
|
||
|
*/
|
||
|
template<class T>
|
||
|
auto from()
|
||
|
-> decltype(iterate(std::initializer_list<T>(), identity_immediate())) {
|
||
|
return iterate(std::initializer_list<T>(), identity_immediate());
|
||
|
}
|
||
|
/*! Returns an observable that sends an empty set of values and then completes, on the specified scheduler.
|
||
|
|
||
|
\tparam T the type of elements (not) to be sent
|
||
|
\tparam Coordination the type of the scheduler
|
||
|
|
||
|
\return Observable that sends an empty set of values and then completes.
|
||
|
|
||
|
\note This is a degenerate case of ```from(Coordination cn, Value0 v0, ValueN... vn)``` operator.
|
||
|
*/
|
||
|
template<class T, class Coordination>
|
||
|
auto from(Coordination cn)
|
||
|
-> typename std::enable_if<is_coordination<Coordination>::value,
|
||
|
decltype( iterate(std::initializer_list<T>(), std::move(cn)))>::type {
|
||
|
return iterate(std::initializer_list<T>(), std::move(cn));
|
||
|
}
|
||
|
/*! Returns an observable that sends each value from its arguments list.
|
||
|
|
||
|
\tparam Value0 ...
|
||
|
\tparam ValueN the type of sending values
|
||
|
|
||
|
\param v0 ...
|
||
|
\param vn values to send
|
||
|
|
||
|
\return Observable that sends each value from its arguments list.
|
||
|
|
||
|
\sample
|
||
|
\snippet from.cpp from sample
|
||
|
\snippet output.txt from sample
|
||
|
|
||
|
\note This operator is useful to send separated values. If they are stored as a collection, use observable<void,void>::iterate instead.
|
||
|
*/
|
||
|
template<class Value0, class... ValueN>
|
||
|
auto from(Value0 v0, ValueN... vn)
|
||
|
-> typename std::enable_if<!is_coordination<Value0>::value,
|
||
|
decltype(iterate(*(std::array<Value0, sizeof...(ValueN) + 1>*)nullptr, identity_immediate()))>::type {
|
||
|
std::array<Value0, sizeof...(ValueN) + 1> c{{v0, vn...}};
|
||
|
return iterate(std::move(c), identity_immediate());
|
||
|
}
|
||
|
/*! Returns an observable that sends each value from its arguments list, on the specified scheduler.
|
||
|
|
||
|
\tparam Coordination the type of the scheduler
|
||
|
\tparam Value0 ...
|
||
|
\tparam ValueN the type of sending values
|
||
|
|
||
|
\param cn the scheduler to use for scheduling the items
|
||
|
\param v0 ...
|
||
|
\param vn values to send
|
||
|
|
||
|
\return Observable that sends each value from its arguments list.
|
||
|
|
||
|
\sample
|
||
|
\snippet from.cpp threaded from sample
|
||
|
\snippet output.txt threaded from sample
|
||
|
|
||
|
\note This operator is useful to send separated values. If they are stored as a collection, use observable<void,void>::iterate instead.
|
||
|
*/
|
||
|
template<class Coordination, class Value0, class... ValueN>
|
||
|
auto from(Coordination cn, Value0 v0, ValueN... vn)
|
||
|
-> typename std::enable_if<is_coordination<Coordination>::value,
|
||
|
decltype(iterate(*(std::array<Value0, sizeof...(ValueN) + 1>*)nullptr, std::move(cn)))>::type {
|
||
|
std::array<Value0, sizeof...(ValueN) + 1> c{{v0, vn...}};
|
||
|
return iterate(std::move(c), std::move(cn));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*! Returns an observable that sends the specified item to observer and then completes.
|
||
|
|
||
|
\tparam T the type of the emitted item
|
||
|
|
||
|
\param v the value to send
|
||
|
|
||
|
\return Observable that sends the specified item to observer and then completes.
|
||
|
|
||
|
\sample
|
||
|
\snippet just.cpp just sample
|
||
|
\snippet output.txt just sample
|
||
|
*/
|
||
|
template<class Value0>
|
||
|
auto just(Value0 v0)
|
||
|
-> typename std::enable_if<!is_coordination<Value0>::value,
|
||
|
decltype(iterate(*(std::array<Value0, 1>*)nullptr, identity_immediate()))>::type {
|
||
|
std::array<Value0, 1> c{{v0}};
|
||
|
return iterate(std::move(c), identity_immediate());
|
||
|
}
|
||
|
/*! Returns an observable that sends the specified item to observer and then completes, on the specified scheduler.
|
||
|
|
||
|
\tparam T the type of the emitted item
|
||
|
\tparam Coordination the type of the scheduler
|
||
|
|
||
|
\param v the value to send
|
||
|
\param cn the scheduler to use for scheduling the items
|
||
|
|
||
|
\return Observable that sends the specified item to observer and then completes.
|
||
|
|
||
|
\sample
|
||
|
\snippet just.cpp threaded just sample
|
||
|
\snippet output.txt threaded just sample
|
||
|
*/
|
||
|
template<class Value0, class Coordination>
|
||
|
auto just(Value0 v0, Coordination cn)
|
||
|
-> typename std::enable_if<is_coordination<Coordination>::value,
|
||
|
decltype(iterate(*(std::array<Value0, 1>*)nullptr, std::move(cn)))>::type {
|
||
|
std::array<Value0, 1> c{{v0}};
|
||
|
return iterate(std::move(c), std::move(cn));
|
||
|
}
|
||
|
|
||
|
/*! Returns an observable that sends the specified values before it begins to send items emitted by the given observable.
|
||
|
|
||
|
\tparam Observable the type of the observable that emits values for resending
|
||
|
\tparam Value0 ...
|
||
|
\tparam ValueN the type of sending values
|
||
|
|
||
|
\param o the observable that emits values for resending
|
||
|
\param v0 ...
|
||
|
\param vn values to send
|
||
|
|
||
|
\return Observable that sends the specified values before it begins to send items emitted by the given observable.
|
||
|
|
||
|
\sample
|
||
|
\snippet start_with.cpp full start_with sample
|
||
|
\snippet output.txt full start_with sample
|
||
|
|
||
|
Instead of passing the observable as a parameter, you can use rxcpp::observable<T, SourceOperator>::start_with method of the existing observable:
|
||
|
\snippet start_with.cpp short start_with sample
|
||
|
\snippet output.txt short start_with sample
|
||
|
*/
|
||
|
template<class Observable, class Value0, class... ValueN>
|
||
|
auto start_with(Observable o, Value0 v0, ValueN... vn)
|
||
|
-> decltype(from(rxu::value_type_t<Observable>(v0), rxu::value_type_t<Observable>(vn)...).concat(o)) {
|
||
|
return from(rxu::value_type_t<Observable>(v0), rxu::value_type_t<Observable>(vn)...).concat(o);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
#endif
|