diff --git a/3party/cpplinq/linq.hpp b/3party/cpplinq/linq.hpp new file mode 100644 index 0000000..6552f79 --- /dev/null +++ b/3party/cpplinq/linq.hpp @@ -0,0 +1,584 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +/// +/// namespace cpplinq +/// ----------------- +/// +/// Defines a number of range-based composable operators for enumerating and modifying collections +/// +/// The general design philosophy is to +/// (1) emulate the composable query patterns introduced in C# Linq +/// (2) preserve iterator category and writability where possible +/// For instance, like C# Linq we have a select operator to project one sequence into a new one. +/// Unlike Linq, invoking Select on a random access sequence will yield you a _random_ access sequence. +/// +/// The general workflow begins with 'from()' which normally only takes a reference +/// the the collection in question. However, from that point on, all operators function +/// by value, so that the range can store any necessary state, rather than duplicating it +/// onto iterator pairs. +/// +/// In subsequent documentation, "powers" lists which powers that the operator attempts to preserve if +/// available on on the input sequence. Some iterator powers may be skipped - in such a case, round down +/// to the next supported power (e.g. if 'fwd' and 'rnd', an input of 'bidi' will round down to a 'fwd' result). +/// +/// +/// +/// class linq_query +/// ---------------- +/// +/// from(container&) +/// ================ +/// - Result: Query +/// +/// Construct a new query, from an lvalue reference to a collection. Does not copy the collection +/// +/// +/// +/// from(iter, iter) +/// ================ +/// - Result: Query +/// +/// Construct a new query, from an iterator pair. +/// +/// +/// +/// query.select(map) +/// ========================== +/// - Result: Query +/// - Powers: input, forward, bidirectional, random access +/// +/// For each element `x` in the input sequences, computes `map(x)` for the result sequence. +/// +/// +/// +/// query.where(pred) -> query +/// ========================== +/// - Result: Query +/// - Powers: input, forward, bidirectional +/// +/// Each element `x` in the input appears in the output if `pred(x)` is true. +/// +/// The expression `pred(x)` is evaluated only when moving iterators (op++, op--). +/// Dereferencing (op*) does not invoke the predicate. +/// +/// +/// +/// query.groupby(keymap [, keyequal]) +/// ==================================== +/// Result: Query of groups. Each group has a 'key' field, and is a query of elements from the input. +/// Powers: forward +/// +/// +/// +/// query.any([pred]) +/// ================= +/// - Result: bool +/// +/// (No argument) Returns true if sequence is non-empty. Equivalent to `query.begin()!=query.end()` +/// +/// (One argument) Returns true if the sequence contains any elements for which `pred(element)` is true. +/// Equivalent to `query.where(pred).any()`. +/// +/// +/// +/// query.all(pred) +/// =============== +/// - Result: bool +/// +/// Returns true if `pred` holds for all elements in the sequence. Equivalent to `!query.any(std::not1(pred))` +/// +/// +/// +/// query.take(n) +/// ============= +/// - Result: query +/// - Powers: input, forward, random access (not bidirectional) +/// +/// Returns a sequence that contains up to `n` items from the original sequence. +/// +/// +/// +/// query.skip(n) +/// ============= +/// - Result: query +/// - Powers: input, forward, random access (not bidirectional) +/// +/// Returns a sequence that skips the first `n` items from the original sequence, or an empty sequence if +/// fewer than `n` were available on input. +/// +/// Note: begin() takes O(n) time when input iteration power is weaker than random access. +/// +/// +/// +/// query.count([pred]) +/// =================== +/// - Result: std::size_t +/// +/// _TODO: should use inner container's iterator distance type instead._ +/// +/// (Zero-argument) Returns the number of elements in the range. +/// Equivalent to `std::distance(query.begin(), query.end())` +/// +/// (One-argument) Returns the number of elements for whicht `pred(element)` is true. +/// Equivalent to `query.where(pred).count()` +/// + + + +#if !defined(CPPLINQ_LINQ_HPP) +#define CPPLINQ_LINQ_HPP +#pragma once + +#pragma push_macro("min") +#pragma push_macro("max") +#undef min +#undef max + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +// some configuration macros +#if _MSC_VER > 1600 || __cplusplus > 199711L +#define LINQ_USE_RVALUEREF 1 +#endif + +#if (defined(_MSC_VER) && _CPPRTTI) || !defined(_MSC_VER) +#define LINQ_USE_RTTI 1 +#endif + +#if defined(__clang__) +#if __has_feature(cxx_rvalue_references) +#define LINQ_USE_RVALUEREF 1 +#endif +#if __has_feature(cxx_rtti) +#define LINQ_USE_RTTI 1 +#endif +#endif + + +// individual features +#include "util.hpp" +#include "linq_cursor.hpp" +#include "linq_iterators.hpp" +#include "linq_select.hpp" +#include "linq_take.hpp" +#include "linq_skip.hpp" +#include "linq_groupby.hpp" +#include "linq_where.hpp" +#include "linq_last.hpp" +#include "linq_selectmany.hpp" + + + + +namespace cpplinq +{ + +namespace detail +{ + template + struct not1_{ + Pred pred; + not1_(Pred p) : pred(p) + {} + template + bool operator()(const T& value) + { + return !pred(value); + } + }; + // note: VC2010's std::not1 doesn't support lambda expressions. provide our own. + template + not1_ not1(Pred p) { return not1_(p); } +} + +namespace detail { + template + struct cast_to { + template + U operator()(const T& value) const { + return static_cast(value); + } + }; +} + +template +class linq_driver +{ + typedef typename Collection::cursor::element_type + element_type; + typedef typename Collection::cursor::reference_type + reference_type; +public: + typedef cursor_iterator + iterator; + + linq_driver(Collection c) : c(c) {} + + + // -------------------- linq core methods -------------------- + + template + linq_driver< linq_groupby > groupby(KeyFn fn) + { + return linq_groupby(c, std::move(fn) ); + } + + // TODO: groupby(keyfn, eq) + + // TODO: join... + + template + linq_driver< linq_select > select(Selector sel) const { + return linq_select(c, std::move(sel) ); + } + + template + linq_driver< linq_select_many > + select_many(Fn fn) const + { + return linq_select_many(c, fn, detail::default_select_many_selector()); + } + + template + linq_driver< linq_select_many > select_many(Fn fn, Fn2 fn2) const + { + return linq_select_many(c, fn, fn2); + } + + template + linq_driver< linq_where > where(Predicate p) const { + return linq_where(c, std::move(p) ); + } + + + // -------------------- linq peripheral methods -------------------- + + template + element_type aggregate(Fn fn) const + { + auto it = begin(); + if (it == end()) { + return element_type(); + } + + reference_type first = *it; + return std::accumulate(++it, end(), first, fn); + } + + template + T aggregate(T initialValue, Fn fn) const + { + return std::accumulate(begin(), end(), initialValue, fn); + } + + bool any() const { auto cur = c.get_cursor(); return !cur.empty(); } + + template + bool any(Predicate p) const { + auto it = std::find_if(begin(), end(), p); + return it != end(); + } + + template + bool all(Predicate p) const { + auto it = std::find_if(begin(), end(), detail::not1(p)); + return it == end(); + } + + // TODO: average + +#if !defined(__clang__) + // Clang complains that linq_driver is not complete until the closing brace + // so (linq_driver*)->select() cannot be resolved. + template + auto cast() + -> decltype(static_cast(0)->select(detail::cast_to())) + { + return this->select(detail::cast_to()); + } +#endif + + // TODO: concat + + bool contains(const typename Collection::cursor::element_type& value) const { + return std::find(begin(), end(), value) != end(); + } + + typename std::iterator_traits::difference_type count() const { + return std::distance(begin(), end()); + } + + template + typename std::iterator_traits::difference_type count(Predicate p) const { + auto filtered = this->where(p); + return std::distance(begin(filtered), end(filtered)); + } + + // TODO: default_if_empty + + // TODO: distinct() + // TODO: distinct(cmp) + + reference_type element_at(std::size_t ix) const { + auto cur = c.get_cursor(); + while(ix && !cur.empty()) { + cur.inc(); + --ix; + } + if (cur.empty()) { throw std::logic_error("index out of bounds"); } + else { return cur.get(); } + } + + element_type element_at_or_default(std::size_t ix) const { + auto cur = c.get_cursor(); + while(ix && !cur.empty()) { + cur.inc(); + -- ix; + } + if (cur.empty()) { return element_type(); } + else { return cur.get(); } + } + + bool empty() const { + return !this->any(); + } + + // TODO: except(second) + // TODO: except(second, eq) + + reference_type first() const { + auto cur = c.get_cursor(); + if (cur.empty()) { throw std::logic_error("index out of bounds"); } + else { return cur.get(); } + } + + template + reference_type first(Predicate pred) const { + auto cur = c.get_cursor(); + while (!cur.empty() && !pred(cur.get())) { + cur.inc(); + } + if (cur.empty()) { throw std::logic_error("index out of bounds"); } + else { return cur.get(); } + } + + element_type first_or_default() const { + auto cur = c.get_cursor(); + if (cur.empty()) { return element_type(); } + else { return cur.get(); } + } + + template + element_type first_or_default(Predicate pred) const { + auto cur = c.get_cursor(); + while (!cur.empty() && !pred(cur.get())) { + cur.inc(); + } + if (cur.empty()) { return element_type(); } + else { return cur.get(); } + } + + // TODO: intersect(second) + // TODO: intersect(second, eq) + + // note: forward cursors and beyond can provide a clone, so we can refer to the element directly + typename std::conditional< + std::is_convertible< + typename Collection::cursor::cursor_category, + forward_cursor_tag>::value, + reference_type, + element_type>::type + last() const + { + return linq_last_(c.get_cursor(), typename Collection::cursor::cursor_category()); + } + + template + reference_type last(Predicate pred) const + { + auto cur = c.where(pred).get_cursor(); + return linq_last_(cur, typename decltype(cur)::cursor_category()); + } + + element_type last_or_default() const + { + return linq_last_or_default_(c.get_cursor(), typename Collection::cursor::cursor_category()); + } + + template + element_type last_or_default(Predicate pred) const + { + auto cur = c.where(pred).get_cursor(); + return linq_last_or_default_(cur, typename decltype(cur)::cursor_category()); + } + + reference_type max() const + { + return max(std::less()); + } + + template + reference_type max(Compare less) const + { + auto it = std::max_element(begin(), end(), less); + if (it == end()) + throw std::logic_error("max performed on empty range"); + + return *it; + } + + reference_type min() const + { + return min(std::less()); + } + + template + reference_type min(Compare less) const + { + auto it = std::min_element(begin(), end(), less); + if (it == end()) + throw std::logic_error("max performed on empty range"); + + return *it; + } + + // TODO: order_by(sel) + // TODO: order_by(sel, less) + // TODO: order_by_descending(sel) + // TODO: order_by_descending(sel, less) + + // TODO: sequence_equal(second) + // TODO: sequence_equal(second, eq) + + // TODO: single / single_or_default + + linq_driver> skip(std::size_t n) const { + return linq_skip(c, n); + } + + // TODO: skip_while(pred) + + template + typename std::enable_if::value, ITEM>::type sum() const { + ITEM seed{}; + return sum(seed); + } + + element_type sum(element_type seed) const { + return std::accumulate(begin(), end(), seed); + } + + template ::type> + typename std::enable_if::value, Result>::type sum(Selector sel) const { + return from(begin(), end()).select(sel).sum(); + } + + template ::type> + Result sum(Selector sel, Result seed) const { + return from(begin(), end()).select(sel).sum(seed); + } + + linq_driver> take(std::size_t n) const { + return linq_take(c, n); + } + + // TODO: take_while + + // TODO: then_by / then_by_descending ? + + // TODO: to_... + + // TODO: union(second) + // TODO: union(eq) + + // TODO: zip + + // -------------------- conversion methods -------------------- + + std::vector to_vector() const + { + return std::vector(begin(), end()); + } + + std::list to_list() const + { + return std::list(begin(), end()); + } + + std::set to_set() const + { + return std::set(begin(), end()); + } + + // -------------------- container/range methods -------------------- + + iterator begin() const { auto cur = c.get_cursor(); return !cur.empty() ? iterator(cur) : iterator(); } + iterator end() const { return iterator(); } + linq_driver& operator=(const linq_driver& other) { c = other.c; return *this; } + template + linq_driver& operator=(const linq_driver& other) { c = other.c; return *this; } + + typename std::iterator_traits::reference + operator[](std::size_t ix) const { + return *(begin()+=ix); + } + + // -------------------- collection methods (leaky abstraction) -------------------- + + typedef typename Collection::cursor cursor; + cursor get_cursor() { return c.get_cursor(); } + + linq_driver< dynamic_collection > + late_bind() const + { + return dynamic_collection(c); + } + +private: + Collection c; +}; + +// TODO: should probably use reference-wrapper instead? +template +linq_driver::iterator>> from(TContainer& c) +{ + auto cur = iter_cursor::iterator>(std::begin(c), std::end(c)); + return cur; +} +template +const linq_driver& from(const linq_driver& c) +{ + return c; +} +template +linq_driver> from(Iter start, Iter finish) +{ + return iter_cursor(start, finish); +} + +template +linq_driver from_value(const TContainer& c) +{ + return linq_driver(c); +} + +} + +#pragma pop_macro("min") +#pragma pop_macro("max") + +#endif // defined(CPPLINQ_LINQ_HPP) + diff --git a/3party/cpplinq/linq_cursor.hpp b/3party/cpplinq/linq_cursor.hpp new file mode 100644 index 0000000..4c5f5b5 --- /dev/null +++ b/3party/cpplinq/linq_cursor.hpp @@ -0,0 +1,342 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_CURSOR_HPP) +#define CPPLINQ_LINQ_CURSOR_HPP +#pragma once + +#include + +/// cursors +/// ---------- +/// It should be noted that CppLinq uses a slightly different iterator concept, one where iterators +/// know their extents. This sacrificed some generality, but it adds convenience and improves +/// some performance in some cases. Notably, captures need only be stored once instead of twice in +/// most use cases. +/// +/// Cursors and Ranges are always classes. +/// +/// To get a cursor from a range: +/// +/// get_cursor(range) -> cur +/// +/// Unlike boost ranges, CppLinq cursors are mutated directly, and may "shed state" as they are +/// mutated. For example, a GroupBy range will drop references to earlier groups, possibly +/// permitting freeing them. +/// +/// Onepass cursor +/// =========== +/// - empty(cur) -> bool : at end of sequence +/// - inc(cur) +/// - get(cur) -> T +/// - copy ctor : duplicate reference to seek position +/// +/// Forward cursor +/// ============= +/// - copy ctor : true duplicate of seek position +/// +/// Bidirectional cursor +/// ==================== +/// - forget() : notes the current element as the new 'begin' point +/// - atbegin(cur) -> bool +/// - dec(cur) +/// +/// Random access cursor +/// ==================== +/// - skip(cur, n) +/// - position(cur) -> n +/// - size(cur) -> n +/// - truncate(n) : keep only n more elements +/// +/// As well, cursors must define the appropriate type/typedefs: +/// - cursor_category :: { onepass_cursor_tag, forward_cursor_tag, bidirectional_cursor_tag, random_access_cursor_tag } +/// - element_type +/// - reference_type : if writable, element_type& or such. else, == element_type +/// - + + + +namespace cpplinq { + + // used to identify cursor-based collections + struct collection_tag {}; + + struct onepass_cursor_tag {}; + struct forward_cursor_tag : onepass_cursor_tag {}; + struct bidirectional_cursor_tag : forward_cursor_tag {}; + struct random_access_cursor_tag : bidirectional_cursor_tag {}; + + struct noread_cursor_tag {}; // TODO: remove if not used + struct readonly_cursor_tag : noread_cursor_tag {}; + struct readwrite_cursor_tag : readonly_cursor_tag {}; + + + + // standard cursor adaptors + + namespace util + { + namespace detail + { + template struct type_to_size { char value[n]; }; + + type_to_size<1> get_category_from_iterator(std::input_iterator_tag); + type_to_size<2> get_category_from_iterator(std::forward_iterator_tag); + type_to_size<3> get_category_from_iterator(std::bidirectional_iterator_tag); + type_to_size<4> get_category_from_iterator(std::random_access_iterator_tag); + } + + template + struct iter_to_cursor_category_; + + template + struct iter_to_cursor_category + { + static const std::size_t catIx = sizeof(detail::get_category_from_iterator(typename std::iterator_traits::iterator_category()) /*.value*/ ); + typedef typename iter_to_cursor_category_::type type; + }; + + template <> struct iter_to_cursor_category_<1> { typedef onepass_cursor_tag type; }; + template <> struct iter_to_cursor_category_<2> { typedef forward_cursor_tag type; }; + template <> struct iter_to_cursor_category_<3> { typedef bidirectional_cursor_tag type; }; + template <> struct iter_to_cursor_category_<4> { typedef random_access_cursor_tag type; }; + + + // Note: returns false if no partial order exists between two + // particular iterator categories, such as with some of the boost categories + template + struct less_or_equal_cursor_category + { + private: + typedef char yes; + typedef struct { char c1,c2; } no; + static yes invoke(Cat1); + static no invoke(...); + public: + enum { value = (sizeof(invoke(Cat2())) == sizeof(yes)) }; + }; + + // Return the weaker of the two iterator categories. Make sure + // a non-standard category is in the second argument position, as + // this metafunction will default to the first value if the order is undefined + template + struct min_cursor_category : min_cursor_category::type, Cat3> + { + }; + + template + struct min_cursor_category + : std::conditional< + less_or_equal_cursor_category::value, + Cat2, + Cat1> + { + }; + + template + struct cursor_type { + typedef decltype(cursor(*static_cast(0))) type; + }; + } + + // simultaniously models a cursor and a cursor-collection + template + class iter_cursor : collection_tag { + public: + + typedef iter_cursor cursor; + + typedef typename std::remove_reference::value_type>::type + element_type; + typedef typename std::iterator_traits::reference + reference_type; + typedef typename util::iter_to_cursor_category::type + cursor_category; + + void forget() { start = current; } + bool empty() const { return current == fin; } + void inc() { + if (current == fin) + throw std::logic_error("inc past end"); + ++current; + } + typename std::iterator_traits::reference get() const { return *current; } + + bool atbegin() const { return current == start; } + void dec() { + if (current == start) + throw std::logic_error("dec past begin"); + --current; + } + + void skip(std::ptrdiff_t n) { current += n; } + std::size_t size() { return fin-start; } + void position() { return current-start; } + void truncate(std::size_t n) { + if (n > fin-current) { + fin = current + n; + } + } + + + iter_cursor(Iterator start, Iterator fin) + : current(start) + , start(start) + , fin(std::move(fin)) + { + } + + iter_cursor(Iterator start, Iterator fin, Iterator current) + : current(std::move(current)) + , start(std::move(start)) + , fin(std::move(fin)) + { + } + + iter_cursor get_cursor() const { return *this; } + + private: + Iterator current; + Iterator start, fin; + }; + + + template + struct cursor_interface + { + virtual bool empty() const = 0; + virtual void inc() = 0; + virtual cursor_interface* copy() const = 0; + + virtual T get() const = 0; + + virtual ~cursor_interface() {} + }; + + template + class dynamic_cursor : collection_tag + { + template + struct instance : cursor_interface + { + Cur innerCursor; + + instance(Cur cursor) : innerCursor(std::move(cursor)) + { + } + virtual bool empty() const + { + return innerCursor.empty(); + } + virtual void inc() + { + innerCursor.inc(); + } + virtual T get() const + { + return innerCursor.get(); + } + virtual cursor_interface* copy() const + { + return new instance(*this); + } + }; + + std::unique_ptr> myCur; + + public: + typedef forward_cursor_tag cursor_category; // TODO: not strictly true! + typedef typename std::remove_reference::type element_type; + typedef T reference_type; + + dynamic_cursor() {} + + dynamic_cursor(const dynamic_cursor& other) + : myCur(other.myCur ? other.myCur->copy() : nullptr) + { + } + + dynamic_cursor(dynamic_cursor&& other) + : myCur(other.myCur.release()) + { + } + + template + dynamic_cursor(Cursor cursor) + : myCur(new instance(std::move(cursor))) + { + } + + template + dynamic_cursor(Iterator start, Iterator end) + { + *this = iter_cursor(start, end); + } + + bool empty() const { return !myCur || myCur->empty(); } + void inc() { myCur->inc(); } + T get() const { return myCur->get(); } + + dynamic_cursor& operator=(dynamic_cursor other) + { + std::swap(myCur, other.myCur); + return *this; + } + }; + + template + struct container_interface + { + virtual dynamic_cursor get_cursor() const = 0; + }; + + template + class dynamic_collection + { + std::shared_ptr< container_interface > container; + + template + struct instance : container_interface + { + Container c; + + instance(Container c) : c(c) + { + } + + dynamic_cursor get_cursor() const + { + return c.get_cursor(); + } + }; + + public: + typedef dynamic_cursor cursor; + + dynamic_collection() {} + + dynamic_collection(const dynamic_collection& other) + : container(other.container) + { + } + + // container or query + template + dynamic_collection(Container c) + : container(new instance(c)) + { + } + + // container or query + template + dynamic_collection(Iterator begin, Iterator end) + : container(new instance< iter_cursor >(iter_cursor(begin, end))) + { + } + + dynamic_cursor get_cursor() const { + return container ? container->get_cursor() : dynamic_cursor(); + } + }; +} + +#endif // !defined(CPPLINQ_LINQ_CURSOR_HPP diff --git a/3party/cpplinq/linq_groupby.hpp b/3party/cpplinq/linq_groupby.hpp new file mode 100644 index 0000000..c521e5e --- /dev/null +++ b/3party/cpplinq/linq_groupby.hpp @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_GROUPBY_HPP) +#define CPPLINQ_LINQ_GROUPBY_HPP +#pragma once + +namespace cpplinq +{ + +template +struct group +{ + Key key; + Iter start; + Iter fin; + + typedef Iter iterator; + typedef Iter const_iterator; + + group(){} + + group(const Key& key) : key(key) + { + } + + Iter begin() const { return start; } + Iter end() const { return fin; } +}; + +struct default_equality +{ + template + bool operator()(const T& a, const T& b) const { + return a == b; + } +}; +struct default_less +{ + template + bool operator()(const T& a, const T& b) const { + return a < b; + } +}; + +// progressively constructs grouping as user iterates over groups and elements +// within each group. Performs this task by building a std::list of element +// iterators with equal elements within each group. +// +// invariants: +// - relative order of groups corresponds to relative order of each group's first +// element, as they appeared in the input sequence. +// - relative order of elements within a group correspond to relative order +// as they appeared in the input sequence. +// +// requires: +// Iter must be a forward iterator. +template +class linq_groupby +{ + typedef typename Collection::cursor + inner_cursor; + + typedef typename util::result_of::type + key_type; + + typedef std::list + element_list_type; + + typedef group + group_type; + + typedef std::list + group_list_type; + +private: + struct impl_t + { + // TODO: would be faster to use a chunked list, where + // pointers are invalidated but iterators are not. Need + // benchmarks first + + element_list_type elements; + std::list groups; + std::map groupIndex; + + + + KeyFn keySelector; + Compare comp; + + impl_t(inner_cursor cur, + KeyFn keySelector, + Compare comp = Compare()) + : keySelector(keySelector) + , groupIndex(comp) + { + // TODO: make lazy + insert_all(std::move(cur)); + } + + void insert_all(inner_cursor cur) + { + while(!cur.empty()) { + insert(cur.get()); + cur.inc(); + } + } + void insert(typename inner_cursor::reference_type element) + { + key_type key = keySelector(element); + auto groupPos = groupIndex.find(key); + if(groupPos == groupIndex.end()) { + // new group + bool firstGroup = groups.empty(); + + elements.push_back(element); + if(!firstGroup) { + // pop new element out of previous group + --groups.back().fin; + } + + // build new group + groups.push_back(group_type(key)); + group_type& newGroup = groups.back(); + + groupIndex.insert( std::make_pair(key, &newGroup) ); + + newGroup.fin = elements.end(); + --(newGroup.start = newGroup.fin); + } else { + // add to existing group at end + elements.insert(groupPos->second->end(), element); + } + } + }; + +public: + struct cursor { + typedef group_type + element_type; + + typedef element_type + reference_type; + + typedef forward_cursor_tag + cursor_category; + + cursor(inner_cursor cur, + KeyFn keyFn, + Compare comp = Compare()) + { + impl.reset(new impl_t(cur, keyFn, comp)); + inner = impl->groups.begin(); + fin = impl->groups.end(); + } + + void forget() { } // nop on forward-only cursors + bool empty() const { + return inner == fin; + } + void inc() { + if (inner == fin) { + throw std::logic_error("attempt to iterate past end of range"); + } + ++inner; + } + reference_type get() const { + return *inner; + } + + private: + std::shared_ptr impl; + typename std::list::iterator inner; + typename std::list::iterator fin; + }; + + linq_groupby(Collection c, + KeyFn keyFn, + Compare comp = Compare()) + : c(c), keyFn(keyFn), comp(comp) + { + } + + cursor get_cursor() const { return cursor(c.get_cursor(), keyFn, comp); } + +private: + Collection c; + KeyFn keyFn; + Compare comp; +}; + +} + +#endif // !defined(CPPLINQ_LINQ_GROUPBY_HPP) + diff --git a/3party/cpplinq/linq_iterators.hpp b/3party/cpplinq/linq_iterators.hpp new file mode 100644 index 0000000..3472281 --- /dev/null +++ b/3party/cpplinq/linq_iterators.hpp @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_ITERATORS_HPP) +#define CPPLINQ_LINQ_ITERATORS_HPP +#pragma once + +#include + +namespace cpplinq { + + // if a member, provides the straightforward implementation of various redundant operators. For example, + // providing -> for any iterator providing *, and so forth. + struct use_default_iterator_operators {}; + + #define CPPLINQ_USE_DEFAULT_ITERATOR_OPERATORS \ + operator ::cpplinq::use_default_iterator_operators() const { return ::cpplinq::use_default_iterator_operators(); } + + template + typename std::enable_if< + std::is_convertible::value, + Iter + >::type + operator+(const Iter& it, typename std::iterator_traits::distance_type n) { + return it += n; + } + template + typename std::enable_if< + std::is_convertible::value, + Iter + >::type + operator-(const Iter& it, typename std::iterator_traits::distance_type n) { + return it -= n; + } + template + typename std::enable_if< + std::is_convertible::value, + Iter + >::type + operator-=(const Iter& it, typename std::iterator_traits::distance_type n) { + return it += (-n); + } + + template + typename std::enable_if< + std::is_convertible::value, + bool + >::type + operator!=(const Iter& it, const Iter& it2) { + return !(it == it2); + } + template + typename std::enable_if< + std::is_convertible::value, + bool + >::type + operator>(const Iter& it, const Iter& it2) { + return it2 < it; + } + template + typename std::enable_if< + std::is_convertible::value, + bool + >::type + operator<=(const Iter& it, const Iter& it2) { + return !(it2 < it); + } + template + typename std::enable_if< + std::is_convertible::value, + bool + >::type + operator>=(const Iter& it, const Iter& it2) { + return !(it < it2); + } + + namespace util { + template + typename std::iterator_traits::pointer deref_iterator(const Iter& it) { + return deref_iterator(it, util::identity::reference>()); + } + + template + T* deref_iterator(const Iter& it, util::identity) { + return &*it; + } + + template + util::value_ptr deref_iterator(const Iter& it, util::identity) { + return util::value_ptr(*it); + } + } + + + template + class iter_range + { + Iter start, finish; + public: + + CPPLINQ_USE_DEFAULT_ITERATOR_OPERATORS + + typedef Iter iterator; + typedef typename iterator::value_type value_type; + + explicit iter_range(Iter start, Iter finish) : start(start), finish(finish) {} + iterator begin() const { return start; } + iterator end() const { return finish; } + }; + template + iter_range make_range(Iter start, Iter finish) { + return iter_range(start, finish); + } + + // decays into a onepass/forward iterator + template + class cursor_iterator + : public std::iterator::value, + typename std::add_pointer::type, + util::value_ptr>::type, + typename Cursor::reference_type> + { + public: + CPPLINQ_USE_DEFAULT_ITERATOR_OPERATORS; + + cursor_iterator(Cursor cur) : cur(cur) { + } + + cursor_iterator() : cur() { + } + + bool operator==(const cursor_iterator& other) const { + return !cur && !other.cur; + } + + typename Cursor::reference_type operator*() const { + return cur->get(); + } + + typename cursor_iterator::pointer operator->() const { + auto& v = **this; + return &v; + } + + cursor_iterator& operator++() { + cur->inc(); + + if (cur->empty()) { cur.reset(); } + return *this; + } + + cursor_iterator& operator+=(std::ptrdiff_t n) { + cur->skip(n); + + if (cur->empty()) { cur.reset(); } + return *this; + } + + + + private: + bool empty() const { + !cur || cur->empty(); + } + + util::maybe cur; + }; + + template + class container_range + { + Container c; + + public: + typedef cursor_iterator iterator; + + container_range(Container c) : c(c) + { + } + + iterator begin() const + { + return iterator(c.get_cursor()); + } + + iterator end() const + { + return iterator(); + } + }; + +} + +#endif diff --git a/3party/cpplinq/linq_last.hpp b/3party/cpplinq/linq_last.hpp new file mode 100644 index 0000000..cb2bf76 --- /dev/null +++ b/3party/cpplinq/linq_last.hpp @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_LAST_HPP) +#define CPPLINQ_LINQ_LAST_HPP +#pragma once + +namespace cpplinq { + + template + typename Cursor::element_type + linq_last_(Cursor c, onepass_cursor_tag) + { + if (c.empty()) { throw std::logic_error("last() out of bounds"); } + typename Cursor::element_type elem = c.get(); + for(;;) { + c.inc(); + if (c.empty()) break; + elem = c.get(); + } + return elem; + } + + // TODO: bidirectional iterator in constant time + + template + typename Cursor::reference_type + linq_last_(Cursor c, forward_cursor_tag) + { + if (c.empty()) { throw std::logic_error("last() out of bounds"); } + Cursor best = c; + for(;;) { + c.inc(); + if (c.empty()) break; + best = c; + } + return best.get(); + } + + template + typename Cursor::reference_type + linq_last_(Cursor c, random_access_cursor_tag) + { + if (c.empty()) { throw std::logic_error("last() out of bounds"); } + c.skip(c.size()-1); + return c.get(); + } + + template + typename Cursor::element_type + linq_last_or_default_(Cursor c, onepass_cursor_tag) + { + typename Cursor::element_type elem; + while(!c.empty()) { + elem = c.get(); + c.inc(); + } + return elem; + } + + template + typename Cursor::element_type + linq_last_or_default_(Cursor c, forward_cursor_tag) + { + if (c.empty()) { throw std::logic_error("last() out of bounds"); } + Cursor best = c; + for(;;) { + c.inc(); + if (c.empty()) break; + best = c; + } + return best.get(); + } + + template + typename Cursor::element_type + linq_last_or_default_(Cursor c, random_access_cursor_tag) + { + if (c.empty()) { return typename Cursor::element_type(); } + c.skip(c.size()-1); + return c.get(); + } + +} + +#endif // CPPLINQ_LINQ_LAST_HPP diff --git a/3party/cpplinq/linq_select.hpp b/3party/cpplinq/linq_select.hpp new file mode 100644 index 0000000..d505284 --- /dev/null +++ b/3party/cpplinq/linq_select.hpp @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_SELECT_HPP) +#define CPPLINQ_LINQ_SELECT_HPP +#pragma once + +#include + +namespace cpplinq +{ + template + class linq_select + { + typedef typename Collection::cursor + inner_cursor; + public: + struct cursor { + typedef typename util::result_of::type + reference_type; + typedef typename std::remove_reference::type + element_type; + typedef typename inner_cursor::cursor_category + cursor_category; + + cursor(const inner_cursor& cur, Selector sel) : cur(cur), sel(std::move(sel)) {} + + void forget() { cur.forget(); } + bool empty() const { return cur.empty(); } + void inc() { cur.inc(); } + reference_type get() const { return sel(cur.get()); } + + bool atbegin() const { return cur.atbegin(); } + void dec() { cur.dec(); } + + void skip(std::size_t n) { cur.skip(n); } + std::size_t position() const { return cur.position(); } + std::size_t size() const { return cur.size(); } + private: + inner_cursor cur; + Selector sel; + }; + + linq_select(const Collection& c, Selector sel) : c(c), sel(sel) {} + + cursor get_cursor() const { return cursor(c.get_cursor(), sel); } + + private: + Collection c; + Selector sel; + }; + +} + +#endif // defined(CPPLINQ_LINQ_SELECT_HPP) diff --git a/3party/cpplinq/linq_selectmany.hpp b/3party/cpplinq/linq_selectmany.hpp new file mode 100644 index 0000000..85f0d42 --- /dev/null +++ b/3party/cpplinq/linq_selectmany.hpp @@ -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 + +#include "util.hpp" +#include "linq_cursor.hpp" + +#include + +namespace cpplinq +{ + namespace detail + { + struct default_select_many_selector + { + template + auto operator()(T1&& t1, T2&& t2) const + -> decltype(std::forward(t2)) + { + return std::forward(t2); + } + }; + } + + namespace detail + { + template + struct resolve_select_many_fn_return_type + { + typedef decltype(std::declval()(std::declval())) value; + }; + + template + struct value_collection_adapter + { + value_collection_adapter(const TCol& col) + : _collection(col){} + + value_collection_adapter(const value_collection_adapter& src) + : _collection(src._collection) {} + + value_collection_adapter(value_collection_adapter && src) + : _collection(std::move(src._collection)) {} + + const TCol& get() const + { + return _collection; + } + + TCol& get() + { + return _collection; + } + + private: + TCol _collection; + }; + + template + struct collection_store_type + { + typedef typename std::remove_reference::type collection_type; + typedef std::reference_wrapper reference_store_type; + typedef value_collection_adapter value_store_type; + + typedef typename std::conditional::value, reference_store_type, value_store_type>::type store; + }; + } + + // cur -> (T -> cur) -> cur + template + class linq_select_many + { + template static T instance(); // for type inference + + Container1 c1; + Fn fn; + Fn2 fn2; + + typedef typename Container1::cursor Cur1; + typedef decltype(from(instance()(instance().get()))) Container2; + typedef typename Container2::cursor Cur2; + + typedef typename detail::resolve_select_many_fn_return_type::value inner_collection; + + public: + class cursor + { + public: + typedef typename util::min_cursor_category::type + cursor_category; + typedef typename Cur2::reference_type reference_type; + typedef typename Cur2::element_type element_type; + + typedef detail::collection_store_type collection_store_type; + typedef typename collection_store_type::store collection_store; + typedef std::shared_ptr collection_store_ptr; + + private: + // TODO: we need to lazy eval somehow, but this feels wrong. + Cur1 cur1; + dynamic_cursor cur2; + Fn fn; + Fn2 fn2; + collection_store_ptr store; + + public: + cursor(Cur1 cur1, const Fn& fn, const Fn2& fn2) + : cur1(std::move(cur1)), fn(fn), fn2(fn2) + { + if (!cur1.empty()) + { + store = std::make_shared(fn(cur1.get())); + cur2 = from(store->get()).get_cursor(); + } + } + + bool empty() const + { + return cur2.empty(); + } + + void inc() + { + cur2.inc(); + thunk(); + } + + reference_type get() const + { + return fn2(cur1.get(), cur2.get()); + } + + private: + void thunk() + { + // refill cur2 + while (cur2.empty() && !cur1.empty()) { + cur1.inc(); + if (cur1.empty()) + break; + + store = std::make_shared(fn(cur1.get())); + cur2 = from(store->get()).get_cursor(); + } + } + }; + + linq_select_many(Container1 c1, Fn fn, Fn2 fn2) + : c1(std::move(c1)), fn(std::move(fn)), fn2(std::move(fn2)) + { + } + + cursor get_cursor() const + { + return cursor(c1.get_cursor(), fn, fn2); + } + }; +} + + + diff --git a/3party/cpplinq/linq_skip.hpp b/3party/cpplinq/linq_skip.hpp new file mode 100644 index 0000000..5b1624f --- /dev/null +++ b/3party/cpplinq/linq_skip.hpp @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_SKIP_HPP) +#define CPPLINQ_LINQ_SKIP_HPP +#pragma once + +#include + +namespace cpplinq +{ + template + struct linq_skip + { + public: + typedef typename Collection::cursor cursor; + + linq_skip(const Collection& c, std::size_t n) : c(c), n(n) {} + + cursor get_cursor() const { + std::size_t rem = n; + + auto cur = c.get_cursor(); + while(rem-- && !cur.empty()) { + cur.inc(); + } + cur.forget(); + return cur; + } + + private: + Collection c; + std::size_t n; + }; +} +#endif // !defined(CPPLINQ_LINQ_SKIP_HPP) + + diff --git a/3party/cpplinq/linq_take.hpp b/3party/cpplinq/linq_take.hpp new file mode 100644 index 0000000..a7093ef --- /dev/null +++ b/3party/cpplinq/linq_take.hpp @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_TAKE_HPP) +#define CPPLINQ_LINQ_TAKE_HPP +#pragma once + +#include + +namespace cpplinq +{ + template + struct linq_take_cursor + { + typedef typename InnerCursor::element_type element_type; + typedef typename InnerCursor::reference_type reference_type; + typedef typename InnerCursor::cursor_category cursor_category; + + linq_take_cursor(const InnerCursor& cur, std::size_t rem) : cur(cur), rem(rem) {} + + void forget() { cur.forget(); } + bool empty() const { return cur.empty() || rem == 0; } + void inc() { cur.inc(); --rem; } + reference_type get() const { return cur.get(); } + + bool atbegin() const { return cur.atbegin(); } + void dec() { cur.dec(); --rem; } + + void skip(std::size_t n) { cur.skip(n); rem -= n; } + std::size_t position() const { return cur.position(); } + std::size_t size() const { return cur.size(); } + + private: + InnerCursor cur; + std::size_t rem; + }; + + namespace detail { + template + linq_take_cursor + take_get_cursor_( + const Collection& c, + std::size_t n, + onepass_cursor_tag + ) + { + return linq_take_cursor(c.get_cursor(), n); + } + + template + typename Collection::cursor + take_get_cursor_( + const Collection& c, + std::size_t n, + random_access_cursor_tag + ) + { + auto cur = c.get_cursor(); + if (cur.size() > n) { + cur.truncate(n); + } + return cur; + } + } + + template + struct linq_take + { + typedef typename std::conditional< + util::less_or_equal_cursor_category< + random_access_cursor_tag, + typename Collection::cursor::cursor_category>::value, + typename Collection::cursor, + linq_take_cursor>::type + cursor; + + linq_take(const Collection& c, std::size_t n) : c(c), n(n) {} + + cursor get_cursor() const { + return detail::take_get_cursor_(c, n, typename Collection::cursor::cursor_category()); + } + + Collection c; + std::size_t n; + }; + + template + auto get_cursor( + const linq_take& take + ) + -> decltype(get_cursor_(take, typename Collection::cursor::cursor_category())) + { + return get_cursor_(take, typename Collection::cursor::cursor_category()); + } + + +} +#endif // !defined(CPPLINQ_LINQ_TAKE_HPP) + diff --git a/3party/cpplinq/linq_where.hpp b/3party/cpplinq/linq_where.hpp new file mode 100644 index 0000000..9b3d936 --- /dev/null +++ b/3party/cpplinq/linq_where.hpp @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_WHERE_HPP) +#define CPPLINQ_LINQ_WHERE_HPP +#pragma once + +namespace cpplinq +{ + template + class linq_where + { + typedef typename Collection::cursor + inner_cursor; + public: + struct cursor { + typedef typename util::min_iterator_category< + bidirectional_cursor_tag, + typename inner_cursor::cursor_category>::type + cursor_category; + typedef typename inner_cursor::element_type + element_type; + typedef typename inner_cursor::reference_type + reference_type; + + cursor(const inner_cursor& cur, const Predicate& p) : cur(cur), pred(p) + { + if (!cur.empty() && !pred(cur.get())) { + this->inc(); + } + } + + void forget() { cur.forget(); } + bool empty() const { return cur.empty(); } + void inc() { + for (;;) { + cur.inc(); + if (cur.empty() || pred(cur.get())) break; + } + } + reference_type get() const { + return cur.get(); + } + + bool atbegin() const { return atbegin(cur); } + void dec() { + for (;;) { + cur.dec(); + if (pred(cur.get())) break; + } + } + private: + inner_cursor cur; + Predicate pred; + }; + + linq_where(const Collection& c, Predicate pred) : c(c), pred(pred) {} + + cursor get_cursor() const { + return cursor(c.get_cursor(), pred); + } + + private: + Collection c; + Predicate pred; + }; +} + +#endif // !defined(CPPLINQ_LINQ_WHERE_HPP) + diff --git a/3party/cpplinq/util.hpp b/3party/cpplinq/util.hpp new file mode 100644 index 0000000..ee3d7eb --- /dev/null +++ b/3party/cpplinq/util.hpp @@ -0,0 +1,232 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#if !defined(CPPLINQ_LINQ_UTIL_HPP) +#define CPPLINQ_LINQ_UTIL_HPP +#pragma once + +namespace cpplinq { namespace util { + + template + struct container_traits { + typedef typename Container::iterator iterator; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::iterator_category iterator_category; + + // TODO: conservative definition for now. + enum { is_writable_iterator = + std::is_reference::reference>::value + && std::is_same::type, + typename std::remove_cv::reference>::type>::type>::value + }; + }; + + template <> + struct container_traits; + + template + struct container_traits + : container_traits + {}; + template + struct container_traits + : container_traits + { + typedef typename Container::const_iterator iterator; + }; + + // Note: returns false if no partial order exists between two + // particular iterator categories, such as with some of the boost categories + template + struct less_or_equal_iterator_category + { + private: + typedef char yes; + typedef struct { char c1,c2; } no; + static yes invoke(Cat1); + static no invoke(...); + public: + enum { value = (sizeof(invoke(Cat2())) == sizeof(yes)) }; + }; + + // Return the weaker of the two iterator categories. Make sure + // a non-standard category is in the second argument position, as + // this metafunction will default to the first value if the order is undefined + template + struct min_iterator_category + : std::conditional< + less_or_equal_iterator_category::value, + Cat2, + Cat1> + { + }; + +#if 0 +#define CppLinq_GET_ITERATOR_TYPE(TContainer) \ + decltype(begin(static_cast(0))) +#define CppLinq_GET_CONST_ITERATOR_TYPE(TContainer) \ + decltype(begin(static_cast(0))) +#else +#define CppLinq_GET_ITERATOR_TYPE(TContainer) \ + typename ::cpplinq::util::container_traits::iterator +#define CppLinq_GET_CONST_ITERATOR_TYPE(TContainer) \ + typename ::cpplinq::util::container_traits::const_iterator +#endif + + // VC10's std::tr1::result_of is busted with lambdas. use decltype instead on vc10 and later +#if defined(_MSC_VER) && _MSC_VER >= 1600 + namespace detail { + template T instance(); + }; + template struct result_of; + template + struct result_of { + typedef decltype(detail::instance()()) type; + }; + template + struct result_of { + typedef decltype(detail::instance()(detail::instance())) type; + }; + template + struct result_of { + typedef decltype(detail::instance()(detail::instance(), + detail::instance())) type; + }; + template + struct result_of { + typedef decltype(detail::instance()(detail::instance(), + detail::instance(), + detail::instance())) type; + }; + template + struct result_of { + typedef decltype(detail::instance()(detail::instance(), + detail::instance(), + detail::instance(), + detail::instance())) type; + }; +#elif defined(_MSC_VER) + template + struct result_of : std::tr1::result_of {}; +#else + using std::result_of; +#endif + + template + struct identity + { + typedef Type type; + Type operator()(const Type& left) const {return left;} + }; + + // faux pointer proxy for iterators that dereference to a value rather than reference, such as selectors + template + struct value_ptr + { + T value; + value_ptr(const T& value) : value(value) + {} + value_ptr(const T* pvalue) : value(*pvalue) + {} + const T* operator->() + { + return &value; + } + }; + + + template + class maybe + { + bool is_set; + typename std::aligned_storage::value>::type + storage; + public: + maybe() + : is_set(false) + { + } + + maybe(T value) + : is_set(false) + { + new (reinterpret_cast(&storage)) T(value); + is_set = true; + } + + maybe(const maybe& other) + : is_set(false) + { + if (other.is_set) { + new (reinterpret_cast(&storage)) T(*other.get()); + is_set = true; + } + } + maybe(maybe&& other) + : is_set(false) + { + if (other.is_set) { + new (reinterpret_cast(&storage)) T(std::move(*other.get())); + is_set = true; + other.reset(); + } + } + + ~maybe() + { + reset(); + } + + void reset() + { + if (is_set) { + is_set = false; + reinterpret_cast(&storage)->~T(); + } + } + + T* get() { + return is_set ? reinterpret_cast(&storage) : 0; + } + + const T* get() const { + return is_set ? reinterpret_cast(&storage) : 0; + } + + void set(T value) { + if (is_set) { + *reinterpret_cast(&storage) = std::move(value); + } else { + new (reinterpret_cast(&storage)) T(std::move(value)); + is_set = true; + } + } + + T& operator*() { return *get(); } + const T& operator*() const { return *get(); } + T* operator->() { return get(); } + const T* operator->() const { return get(); } + + maybe& operator=(const T& other) { + set(other); + } + maybe& operator=(const maybe& other) { + if (const T* pother = other.get()) { + set(*pother); + } else { + reset(); + } + return *this; + } + + // boolean-like operators + operator T*() { return get(); } + operator const T*() const { return get(); } + + private: + + }; +}} + + +#endif //CPPLINQ_UTIL_HPP + diff --git a/3party/rxcpp/operators/rx-all.hpp b/3party/rxcpp/operators/rx-all.hpp new file mode 100644 index 0000000..a0f6a3e --- /dev/null +++ b/3party/rxcpp/operators/rx-all.hpp @@ -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 +struct all_invalid_arguments {}; + +template +struct all_invalid : public rxo::operator_base> { + using type = observable, all_invalid>; +}; +template +using all_invalid_t = typename all_invalid::type; + +template +struct all +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t test_type; + test_type test; + + typedef bool value_type; + + all(test_type t) + : test(std::move(t)) + { + } + + template + struct all_observer + { + typedef all_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, test_type t) { + return make_subscriber(d, this_type(d, std::move(t))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(all_observer::make(std::move(dest), test)) { + return all_observer::make(std::move(dest), test); + } +}; + +} + +/*! @copydoc rx-all.hpp +*/ +template +auto all(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(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 +auto is_empty(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable>, + class All = rxo::detail::all>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Predicate&& p) + -> decltype(o.template lift(All(std::forward(p)))) { + return o.template lift(All(std::forward(p))); + } + + template + static operators::detail::all_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "all takes (Predicate)"); + } +}; + +template<> +struct member_overload +{ + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable>, + class Predicate = std::function, + class IsEmpty = rxo::detail::all>, + class Value = rxu::value_type_t> + static auto member(Observable&& o) + -> decltype(o.template lift(IsEmpty(nullptr))) { + return o.template lift(IsEmpty([](SourceValue) { return false; })); + } + + template + static operators::detail::all_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "is_empty takes no arguments"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-amb.hpp b/3party/rxcpp/operators/rx-amb.hpp new file mode 100644 index 0000000..56bfbe9 --- /dev/null +++ b/3party/rxcpp/operators/rx-amb.hpp @@ -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 +struct amb_invalid_arguments {}; + +template +struct amb_invalid : public rxo::operator_base> { + using type = observable, amb_invalid>; +}; +template +using amb_invalid_t = typename amb_invalid::type; + +template +struct amb + : public operator_base> +{ + //static_assert(is_observable::value, "amb requires an observable"); + //static_assert(is_observable::value, "amb requires an observable that contains observables"); + + typedef amb this_type; + + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t 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_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 + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct amb_state_type + : public std::enable_shared_from_this + , 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; + coordinator_type coordinator; + output_type out; + int pendingObservables; + bool firstEmitted; + std::vector innerSubscriptions; + }; + + auto coordinator = initial.coordination.create_coordinator(scbr.get_subscription()); + + // take a copy of the values for each subscription + auto state = std::make_shared(initial, std::move(coordinator), std::move(scbr)); + + 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( + 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( + 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 +auto amb(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Amb = rxo::detail::amb, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o) { + return Result(Amb(std::forward(o), identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Amb = rxo::detail::amb, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o, Coordination&& cn) { + return Result(Amb(std::forward(o), std::forward(cn))); + } + + template>, + class EmittedValue = rxu::value_type_t, + class SourceValue = observable, + class ObservableObservable = observable, + class Amb = typename rxu::defer_type::type, + class Value = rxu::value_type_t, + class Result = observable + > + 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, + is_coordination>, + class EmittedValue = rxu::value_type_t, + class SourceValue = observable, + class ObservableObservable = observable, + class Amb = typename rxu::defer_type>::type, + class Value = rxu::value_type_t, + class Result = observable + > + 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(cn))); + } + + template + static operators::detail::amb_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "amb takes (optional Coordination, optional Value0, optional ValueN...)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-any.hpp b/3party/rxcpp/operators/rx-any.hpp new file mode 100644 index 0000000..19fca6e --- /dev/null +++ b/3party/rxcpp/operators/rx-any.hpp @@ -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 +struct any_invalid_arguments {}; + +template +struct any_invalid : public rxo::operator_base> { + using type = observable, any_invalid>; +}; +template +using any_invalid_t = typename any_invalid::type; + +template +struct any +{ + typedef rxu::decay_t source_value_type; + typedef bool value_type; + typedef rxu::decay_t test_type; + test_type test; + + any(test_type t) + : test(std::move(t)) + { + } + + template + struct any_observer + { + typedef any_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, test_type t) { + return make_subscriber(d, this_type(d, std::move(t))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(any_observer::make(std::move(dest), test)) { + return any_observer::make(std::move(dest), test); + } +}; + +} + +/*! @copydoc rx-any.hpp +*/ +template +auto any(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(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 +auto exists(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(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 +auto contains(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable>, + class Any = rxo::detail::any>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Predicate&& p) + -> decltype(o.template lift(Any(std::forward(p)))) { + return o.template lift(Any(std::forward(p))); + } + + template + static operators::detail::any_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "any takes (Predicate)"); + } +}; + +template<> +struct member_overload + : member_overload +{ + using member_overload::member; + + template + static operators::detail::any_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "exists takes (Predicate)"); + } +}; + +template<> +struct member_overload +{ + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable>, + class Predicate = std::function, + class Any = rxo::detail::any>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, T&& value) + -> decltype(o.template lift(Any(nullptr))) { + return o.template lift(Any([value](T n) { return n == value; })); + } + + template + static operators::detail::any_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "contains takes (T)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-buffer_count.hpp b/3party/rxcpp/operators/rx-buffer_count.hpp new file mode 100644 index 0000000..79eb30e --- /dev/null +++ b/3party/rxcpp/operators/rx-buffer_count.hpp @@ -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 +struct buffer_count_invalid_arguments {}; + +template +struct buffer_count_invalid : public rxo::operator_base> { + using type = observable, buffer_count_invalid>; +}; +template +using buffer_count_invalid_t = typename buffer_count_invalid::type; + +template +struct buffer_count +{ + typedef rxu::decay_t source_value_type; + typedef std::vector 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 + struct buffer_count_observer : public buffer_count_values + { + typedef buffer_count_observer this_type; + typedef std::vector value_type; + typedef rxu::decay_t dest_type; + typedef observer observer_type; + dest_type dest; + mutable int cursor; + mutable std::deque 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> make(dest_type d, buffer_count_values v) { + auto cs = d.get_subscription(); + return make_subscriber(std::move(cs), this_type(std::move(d), std::move(v))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(buffer_count_observer::make(std::move(dest), initial)) { + return buffer_count_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-buffer_count.hpp +*/ +template +auto buffer(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class BufferCount = rxo::detail::buffer_count, + class Value = rxu::value_type_t> + static auto member(Observable&& o, int count, int skip) + -> decltype(o.template lift(BufferCount(count, skip))) { + return o.template lift(BufferCount(count, skip)); + } + + template>, + class SourceValue = rxu::value_type_t, + class BufferCount = rxo::detail::buffer_count, + class Value = rxu::value_type_t> + static auto member(Observable&& o, int count) + -> decltype(o.template lift(BufferCount(count, count))) { + return o.template lift(BufferCount(count, count)); + } + + template + static operators::detail::buffer_count_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "buffer takes (Count, optional Skip)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-buffer_time.hpp b/3party/rxcpp/operators/rx-buffer_time.hpp new file mode 100644 index 0000000..aa94f7b --- /dev/null +++ b/3party/rxcpp/operators/rx-buffer_time.hpp @@ -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 +struct buffer_with_time_invalid_arguments {}; + +template +struct buffer_with_time_invalid : public rxo::operator_base> { + using type = observable, buffer_with_time_invalid>; +}; +template +using buffer_with_time_invalid_t = typename buffer_with_time_invalid::type; + +template +struct buffer_with_time +{ + typedef rxu::decay_t source_value_type; + typedef std::vector value_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxu::decay_t 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 + struct buffer_with_time_observer + { + typedef buffer_with_time_observer this_type; + typedef std::vector value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 chunks; + rxsc::scheduler::clock_type::time_point expected; + }; + std::shared_ptr 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(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> make(dest_type d, buffer_with_time_values v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return make_subscriber(cs, this_type(cs, std::move(d), std::move(v), std::move(coordinator))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(buffer_with_time_observer::make(std::move(dest), initial)) { + return buffer_with_time_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-buffer_time.hpp +*/ +template +auto buffer_with_time(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class BufferWithTime = rxo::detail::buffer_with_time, identity_one_worker>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration period) + -> decltype(o.template lift(BufferWithTime(period, period, identity_current_thread()))) { + return o.template lift(BufferWithTime(period, period, identity_current_thread())); + } + + template, + std::is_convertible, + is_coordination>, + class SourceValue = rxu::value_type_t, + class BufferWithTime = rxo::detail::buffer_with_time, rxu::decay_t>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration period, Coordination&& cn) + -> decltype(o.template lift(BufferWithTime(period, period, std::forward(cn)))) { + return o.template lift(BufferWithTime(period, period, std::forward(cn))); + } + + template, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class BufferWithTime = rxo::detail::buffer_with_time, identity_one_worker>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration&& period, Duration&& skip) + -> decltype(o.template lift(BufferWithTime(std::forward(period), std::forward(skip), identity_current_thread()))) { + return o.template lift(BufferWithTime(std::forward(period), std::forward(skip), identity_current_thread())); + } + + template, + std::is_convertible, + is_coordination>, + class SourceValue = rxu::value_type_t, + class BufferWithTime = rxo::detail::buffer_with_time, rxu::decay_t>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration&& period, Duration&& skip, Coordination&& cn) + -> decltype(o.template lift(BufferWithTime(std::forward(period), std::forward(skip), std::forward(cn)))) { + return o.template lift(BufferWithTime(std::forward(period), std::forward(skip), std::forward(cn))); + } + + template + static operators::detail::buffer_with_time_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "buffer_with_time takes (Duration, optional Duration, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-buffer_time_count.hpp b/3party/rxcpp/operators/rx-buffer_time_count.hpp new file mode 100644 index 0000000..6d4b9a4 --- /dev/null +++ b/3party/rxcpp/operators/rx-buffer_time_count.hpp @@ -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 +struct buffer_with_time_or_count_invalid_arguments {}; + +template +struct buffer_with_time_or_count_invalid : public rxo::operator_base> { + using type = observable, buffer_with_time_or_count_invalid>; +}; +template +using buffer_with_time_or_count_invalid_t = typename buffer_with_time_or_count_invalid::type; + +template +struct buffer_with_time_or_count +{ + typedef rxu::decay_t source_value_type; + typedef std::vector value_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxu::decay_t 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 + struct buffer_with_time_or_count_observer + { + typedef buffer_with_time_or_count_observer this_type; + typedef std::vector value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 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(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 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(); + } + + return std::function(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> make(dest_type d, buffer_with_time_or_count_values v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return make_subscriber(cs, this_type(cs, std::move(d), std::move(v), std::move(coordinator))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(buffer_with_time_or_count_observer::make(std::move(dest), initial)) { + return buffer_with_time_or_count_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-buffer_time_count.hpp +*/ +template +auto buffer_with_time_or_count(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class BufferTimeCount = rxo::detail::buffer_with_time_or_count, identity_one_worker>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration&& period, int count) + -> decltype(o.template lift(BufferTimeCount(std::forward(period), count, identity_current_thread()))) { + return o.template lift(BufferTimeCount(std::forward(period), count, identity_current_thread())); + } + + template, + std::is_convertible, + is_coordination>, + class SourceValue = rxu::value_type_t, + class BufferTimeCount = rxo::detail::buffer_with_time_or_count, rxu::decay_t>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration&& period, int count, Coordination&& cn) + -> decltype(o.template lift(BufferTimeCount(std::forward(period), count, std::forward(cn)))) { + return o.template lift(BufferTimeCount(std::forward(period), count, std::forward(cn))); + } + + template + static operators::detail::buffer_with_time_or_count_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "buffer_with_time_or_count takes (Duration, Count, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-combine_latest.hpp b/3party/rxcpp/operators/rx-combine_latest.hpp new file mode 100644 index 0000000..06ff3d1 --- /dev/null +++ b/3party/rxcpp/operators/rx-combine_latest.hpp @@ -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 +struct combine_latest_invalid_arguments {}; + +template +struct combine_latest_invalid : public rxo::operator_base> { + using type = observable, combine_latest_invalid>; +}; +template +using combine_latest_invalid_t = typename combine_latest_invalid::type; + +template +struct is_combine_latest_selector_check { + typedef rxu::decay_t selector_type; + + struct tag_not_valid; + template + static auto check(int) -> decltype((*(CS*)nullptr)((*(typename CON::value_type*)nullptr)...)); + template + static tag_not_valid check(...); + + using type = decltype(check...>(0)); + + static const bool value = !std::is_same::value; +}; + +template +struct invalid_combine_latest_selector { + static const bool value = false; +}; + +template +struct is_combine_latest_selector : public std::conditional< + is_combine_latest_selector_check::value, + is_combine_latest_selector_check, + invalid_combine_latest_selector>::type { +}; + +template +using result_combine_latest_selector_t = typename is_combine_latest_selector::type; + +template +struct combine_latest_traits { + + typedef std::tuple tuple_source_type; + typedef std::tuple...> tuple_source_value_type; + + typedef rxu::decay_t selector_type; + typedef rxu::decay_t coordination_type; + + typedef typename is_combine_latest_selector::type value_type; +}; + +template +struct combine_latest : public operator_base>> +{ + typedef combine_latest this_type; + + typedef combine_latest_traits traits; + + typedef typename traits::tuple_source_type tuple_source_type; + typedef typename traits::tuple_source_value_type tuple_source_value_type; + + typedef typename traits::selector_type selector_type; + + typedef typename traits::coordination_type coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + + struct values + { + values(tuple_source_type o, selector_type s, coordination_type sf) + : source(std::move(o)) + , selector(std::move(s)) + , coordination(std::move(sf)) + { + } + tuple_source_type source; + selector_type selector; + coordination_type coordination; + }; + values initial; + + combine_latest(coordination_type sf, selector_type s, tuple_source_type ts) + : initial(std::move(ts), std::move(s), std::move(sf)) + { + } + + template + void subscribe_one(std::shared_ptr state) const { + + typedef typename std::tuple_element::type::value_type source_value_type; + + composite_subscription innercs; + + // when the out observer is unsubscribed all the + // inner subscriptions are unsubscribed as well + state->out.add(innercs); + + auto source = on_exception( + [&](){return state->coordinator.in(std::get(state->source));}, + state->out); + if (source.empty()) { + return; + } + + // this subscribe does not share the observer subscription + // so that when it is unsubscribed the observer can be called + // until the inner subscriptions have finished + auto sink = make_subscriber( + state->out, + innercs, + // on_next + [state](source_value_type st) { + auto& value = std::get(state->latest); + + if (value.empty()) { + ++state->valuesSet; + } + + value.reset(st); + + if (state->valuesSet == sizeof... (ObservableN)) { + 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 + void subscribe_all(std::shared_ptr state, rxu::values) const { + bool subscribed[] = {(subscribe_one(state), true)...}; + subscribed[0] = (*subscribed); // silence warning + } + + template + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct combine_latest_state_type + : public std::enable_shared_from_this + , 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(initial, std::move(coordinator), std::move(scbr)); + + subscribe_all(state, typename rxu::values_from::type()); + } +}; + +} + +/*! @copydoc rx-combine_latest.hpp +*/ +template +auto combine_latest(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class combine_latest = rxo::detail::combine_latest, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, ObservableN&&... on) + { + return Result(combine_latest(identity_current_thread(), rxu::pack(), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + all_observables>, + class ResolvedSelector = rxu::decay_t, + class combine_latest = rxo::detail::combine_latest, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Selector&& s, ObservableN&&... on) + { + return Result(combine_latest(identity_current_thread(), std::forward(s), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + all_observables>, + class combine_latest = rxo::detail::combine_latest, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Coordination&& cn, ObservableN&&... on) + { + return Result(combine_latest(std::forward(cn), rxu::pack(), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + operators::detail::is_combine_latest_selector, + all_observables>, + class ResolvedSelector = rxu::decay_t, + class combine_latest = rxo::detail::combine_latest, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Coordination&& cn, Selector&& s, ObservableN&&... on) + { + return Result(combine_latest(std::forward(cn), std::forward(s), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template + static operators::detail::combine_latest_invalid_t 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 diff --git a/3party/rxcpp/operators/rx-concat.hpp b/3party/rxcpp/operators/rx-concat.hpp new file mode 100644 index 0000000..457b9cf --- /dev/null +++ b/3party/rxcpp/operators/rx-concat.hpp @@ -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 +struct concat_invalid_arguments {}; + +template +struct concat_invalid : public rxo::operator_base> { + using type = observable, concat_invalid>; +}; +template +using concat_invalid_t = typename concat_invalid::type; + +template +struct concat + : public operator_base>> +{ + typedef concat this_type; + + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t source_type; + typedef rxu::decay_t 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 + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct concat_state_type + : public std::enable_shared_from_this + , 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( + 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; + composite_subscription sourceLifetime; + composite_subscription collectionLifetime; + std::deque 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(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( + 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 +auto concat(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Concat = rxo::detail::concat, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o) { + return Result(Concat(std::forward(o), identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Concat = rxo::detail::concat, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o, Coordination&& cn) { + return Result(Concat(std::forward(o), std::forward(cn))); + } + + template>, + class EmittedValue = rxu::value_type_t, + class SourceValue = observable, + class ObservableObservable = observable, + class Concat = typename rxu::defer_type::type, + class Value = rxu::value_type_t, + class Result = observable + > + 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, + is_coordination>, + class EmittedValue = rxu::value_type_t, + class SourceValue = observable, + class ObservableObservable = observable, + class Concat = typename rxu::defer_type>::type, + class Value = rxu::value_type_t, + class Result = observable + > + 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(cn))); + } + + template + static operators::detail::concat_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "concat takes (optional Coordination, optional Value0, optional ValueN...)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-concat_map.hpp b/3party/rxcpp/operators/rx-concat_map.hpp new file mode 100644 index 0000000..546c1eb --- /dev/null +++ b/3party/rxcpp/operators/rx-concat_map.hpp @@ -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::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 +struct concat_map_invalid_arguments {}; + +template +struct concat_map_invalid : public rxo::operator_base> { + using type = observable, concat_map_invalid>; +}; +template +using concat_map_invalid_t = typename concat_map_invalid::type; + +template +struct concat_traits { + typedef rxu::decay_t source_type; + typedef rxu::decay_t collection_selector_type; + typedef rxu::decay_t result_selector_type; + typedef rxu::decay_t coordination_type; + + typedef typename source_type::value_type source_value_type; + + struct tag_not_valid {}; + template + static auto collection_check(int) -> decltype((*(CCS*)nullptr)(*(CV*)nullptr)); + template + static tag_not_valid collection_check(...); + + static_assert(!std::is_same(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::value, "concat_map CollectionSelector must return an observable"); +//#endif + + typedef typename collection_type::value_type collection_value_type; + + template + static auto result_check(int) -> decltype((*(CRS*)nullptr)(*(CV*)nullptr, *(CCV*)nullptr)); + template + static tag_not_valid result_check(...); + + static_assert(!std::is_same(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 value_type; +}; + +template +struct concat_map + : public operator_base>> +{ + typedef concat_map this_type; + typedef concat_traits 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 + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct concat_map_state_type + : public std::enable_shared_from_this + , 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( + 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 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(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( + 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 +auto concat_map(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +/*! @copydoc rx-concat_map.hpp +*/ +template +auto concat_transform(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class SourceValue = rxu::value_type_t, + class CollectionType = rxu::result_of_t, + class ResultSelectorType = rxu::detail::take_at<1>, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables>, + class ConcatMap = rxo::detail::concat_map, rxu::decay_t, ResultSelectorType, identity_one_worker>, + class CollectionValueType = rxu::value_type_t, + class Value = rxu::result_of_t, + class Result = observable + > + static Result member(Observable&& o, CollectionSelector&& s) { + return Result(ConcatMap(std::forward(o), std::forward(s), ResultSelectorType(), identity_current_thread())); + } + + template, + class SourceValue = rxu::value_type_t, + class CollectionType = rxu::result_of_t, + class ResultSelectorType = rxu::detail::take_at<1>, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables, + is_coordination>, + class ConcatMap = rxo::detail::concat_map, rxu::decay_t, ResultSelectorType, rxu::decay_t>, + class CollectionValueType = rxu::value_type_t, + class Value = rxu::result_of_t, + class Result = observable + > + static Result member(Observable&& o, CollectionSelector&& s, Coordination&& cn) { + return Result(ConcatMap(std::forward(o), std::forward(s), ResultSelectorType(), std::forward(cn))); + } + + template, + class CollectionSelectorType = rxu::decay_t, + class SourceValue = rxu::value_type_t, + class CollectionType = rxu::result_of_t, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables, + rxu::negation>, + class ConcatMap = rxo::detail::concat_map, rxu::decay_t, rxu::decay_t, identity_one_worker>, + class CollectionValueType = rxu::value_type_t, + class ResultSelectorType = rxu::decay_t, + class Value = rxu::result_of_t, + class Result = observable + > + static Result member(Observable&& o, CollectionSelector&& s, ResultSelector&& rs) { + return Result(ConcatMap(std::forward(o), std::forward(s), std::forward(rs), identity_current_thread())); + } + + template, + class SourceValue = rxu::value_type_t, + class CollectionType = rxu::result_of_t, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables, + is_coordination>, + class ConcatMap = rxo::detail::concat_map, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class CollectionValueType = rxu::value_type_t, + class ResultSelectorType = rxu::decay_t, + class Value = rxu::result_of_t, + class Result = observable + > + static Result member(Observable&& o, CollectionSelector&& s, ResultSelector&& rs, Coordination&& cn) { + return Result(ConcatMap(std::forward(o), std::forward(s), std::forward(rs), std::forward(cn))); + } + + template + static operators::detail::concat_map_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "concat_map takes (CollectionSelector, optional ResultSelector, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-connect_forever.hpp b/3party/rxcpp/operators/rx-connect_forever.hpp new file mode 100644 index 0000000..bcbe830 --- /dev/null +++ b/3party/rxcpp/operators/rx-connect_forever.hpp @@ -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 +struct connect_forever_invalid_arguments {}; + +template +struct connect_forever_invalid : public rxo::operator_base> { + using type = observable, connect_forever_invalid>; +}; +template +using connect_forever_invalid_t = typename connect_forever_invalid::type; + +template +struct connect_forever : public operator_base +{ + typedef rxu::decay_t source_type; + + source_type source; + + explicit connect_forever(source_type o) + : source(std::move(o)) + { + source.connect(); + } + + template + void on_subscribe(Subscriber&& o) const { + source.subscribe(std::forward(o)); + } +}; + +} + +/*! @copydoc rx-connect_forever.hpp +*/ +template +auto connect_forever(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class ConnectForever = rxo::detail::connect_forever>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(ConnectableObservable&& o) { + return Result(ConnectForever(std::forward(o))); + } + + template + static operators::detail::connect_forever_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "connect_forever takes no arguments"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-debounce.hpp b/3party/rxcpp/operators/rx-debounce.hpp new file mode 100644 index 0000000..6fbedd7 --- /dev/null +++ b/3party/rxcpp/operators/rx-debounce.hpp @@ -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 +struct debounce_invalid_arguments {}; + +template +struct debounce_invalid : public rxo::operator_base> { + using type = observable, debounce_invalid>; +}; +template +using debounce_invalid_t = typename debounce_invalid::type; + +template +struct debounce +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxu::decay_t 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 + struct debounce_observer + { + typedef debounce_observer this_type; + typedef rxu::decay_t value_type; + typedef rxu::decay_t dest_type; + typedef observer 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; + }; + typedef std::shared_ptr 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(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 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(); + } + + return std::function(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 make(dest_type d, debounce_values v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return make_subscriber(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(debounce_observer::make(std::move(dest), initial)) { + return debounce_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-debounce.hpp +*/ +template +auto debounce(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class Debounce = rxo::detail::debounce, identity_one_worker>> + static auto member(Observable&& o, Duration&& d) + -> decltype(o.template lift(Debounce(std::forward(d), identity_current_thread()))) { + return o.template lift(Debounce(std::forward(d), identity_current_thread())); + } + + template, + is_coordination, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class Debounce = rxo::detail::debounce, rxu::decay_t>> + static auto member(Observable&& o, Coordination&& cn, Duration&& d) + -> decltype(o.template lift(Debounce(std::forward(d), std::forward(cn)))) { + return o.template lift(Debounce(std::forward(d), std::forward(cn))); + } + + template, + is_coordination, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class Debounce = rxo::detail::debounce, rxu::decay_t>> + static auto member(Observable&& o, Duration&& d, Coordination&& cn) + -> decltype(o.template lift(Debounce(std::forward(d), std::forward(cn)))) { + return o.template lift(Debounce(std::forward(d), std::forward(cn))); + } + + template + static operators::detail::debounce_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "debounce takes (optional Coordination, required Duration) or (required Duration, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-delay.hpp b/3party/rxcpp/operators/rx-delay.hpp new file mode 100644 index 0000000..5986f79 --- /dev/null +++ b/3party/rxcpp/operators/rx-delay.hpp @@ -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 +struct delay_invalid_arguments {}; + +template +struct delay_invalid : public rxo::operator_base> { + using type = observable, delay_invalid>; +}; +template +using delay_invalid_t = typename delay_invalid::type; + +template +struct delay +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxu::decay_t 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 + struct delay_observer + { + typedef delay_observer this_type; + typedef rxu::decay_t value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 state; + + delay_observer(composite_subscription cs, dest_type d, delay_values v, coordinator_type c) + : state(std::make_shared(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 make(dest_type d, delay_values v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return make_subscriber(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(delay_observer::make(std::move(dest), initial)) { + return delay_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-delay.hpp +*/ +template +auto delay(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class delay = rxo::detail::delay, identity_one_worker>> + static auto member(Observable&& o, Duration&& d) + -> decltype(o.template lift(delay(std::forward(d), identity_current_thread()))) { + return o.template lift(delay(std::forward(d), identity_current_thread())); + } + + template, + is_coordination, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class delay = rxo::detail::delay, rxu::decay_t>> + static auto member(Observable&& o, Coordination&& cn, Duration&& d) + -> decltype(o.template lift(delay(std::forward(d), std::forward(cn)))) { + return o.template lift(delay(std::forward(d), std::forward(cn))); + } + + template, + is_coordination, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class delay = rxo::detail::delay, rxu::decay_t>> + static auto member(Observable&& o, Duration&& d, Coordination&& cn) + -> decltype(o.template lift(delay(std::forward(d), std::forward(cn)))) { + return o.template lift(delay(std::forward(d), std::forward(cn))); + } + + template + static operators::detail::delay_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "delay takes (optional Coordination, required Duration) or (required Duration, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-distinct.hpp b/3party/rxcpp/operators/rx-distinct.hpp new file mode 100644 index 0000000..c90ebdb --- /dev/null +++ b/3party/rxcpp/operators/rx-distinct.hpp @@ -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 of past values. Due to an issue in multiple implementations of std::hash, rxcpp maintains a whitelist of hashable types. new types can be added by specializing rxcpp::filtered_hash + + \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 +struct distinct_invalid_arguments {}; + +template +struct distinct_invalid : public rxo::operator_base> { + using type = observable, distinct_invalid>; +}; +template +using distinct_invalid_t = typename distinct_invalid::type; + +template +struct distinct +{ + typedef rxu::decay_t source_value_type; + + template + struct distinct_observer + { + typedef distinct_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer observer_type; + dest_type dest; + mutable std::unordered_set> 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> make(dest_type d) { + return make_subscriber(d, this_type(d)); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(distinct_observer::make(std::move(dest))) { + return distinct_observer::make(std::move(dest)); + } +}; + +} + +/*! @copydoc rx-distinct.hpp +*/ +template +auto distinct(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable, + is_hashable>, + class Distinct = rxo::detail::distinct> + static auto member(Observable&& o) + -> decltype(o.template lift(Distinct())) { + return o.template lift(Distinct()); + } + + template + static operators::detail::distinct_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "distinct takes no arguments"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-distinct_until_changed.hpp b/3party/rxcpp/operators/rx-distinct_until_changed.hpp new file mode 100644 index 0000000..3702185 --- /dev/null +++ b/3party/rxcpp/operators/rx-distinct_until_changed.hpp @@ -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 +struct distinct_until_changed_invalid_arguments {}; + +template +struct distinct_until_changed_invalid : public rxo::operator_base> { + using type = observable, distinct_until_changed_invalid>; +}; +template +using distinct_until_changed_invalid_t = typename distinct_until_changed_invalid::type; + +template +struct distinct_until_changed +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t predicate_type; + + predicate_type pred; + + distinct_until_changed(predicate_type p) + : pred(std::move(p)) + { + } + + template + struct distinct_until_changed_observer + { + typedef distinct_until_changed_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer observer_type; + + dest_type dest; + predicate_type pred; + mutable rxu::detail::maybe 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 make(dest_type d, predicate_type p) { + return make_subscriber(d, this_type(d, std::move(p))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(distinct_until_changed_observer::make(std::move(dest), pred)) { + return distinct_until_changed_observer::make(std::move(dest), pred); + } +}; + +} + +/*! @copydoc rx-distinct_until_changed.hpp +*/ +template +auto distinct_until_changed(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable>, + class DistinctUntilChanged = rxo::detail::distinct_until_changed>> + static auto member(Observable&& o) + -> decltype(o.template lift(DistinctUntilChanged(rxu::equal_to<>()))) { + return o.template lift(DistinctUntilChanged(rxu::equal_to<>())); + } + + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable>, + class DistinctUntilChanged = rxo::detail::distinct_until_changed> + static auto member(Observable&& o, BinaryPredicate&& pred) + -> decltype(o.template lift(DistinctUntilChanged(std::forward(pred)))) { + return o.template lift(DistinctUntilChanged(std::forward(pred))); + } + + template + static operators::detail::distinct_until_changed_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "distinct_until_changed takes (optional BinaryPredicate)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-element_at.hpp b/3party/rxcpp/operators/rx-element_at.hpp new file mode 100644 index 0000000..1d773ac --- /dev/null +++ b/3party/rxcpp/operators/rx-element_at.hpp @@ -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 +struct element_at_invalid_arguments {}; + +template +struct element_at_invalid : public rxo::operator_base> { + using type = observable, element_at_invalid>; +}; +template +using element_at_invalid_t = typename element_at_invalid::type; + +template +struct element_at { + typedef rxu::decay_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 + struct element_at_observer : public element_at_values + { + typedef element_at_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, element_at_values v) { + return make_subscriber(d, this_type(d, v)); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(element_at_observer::make(std::move(dest), initial)) { + return element_at_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-element_at.hpp +*/ +template +auto element_at(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template + >, + class SourceValue = rxu::value_type_t, + class element_at = rxo::detail::element_at> + static auto member(Observable&& o, int index) + -> decltype(o.template lift(element_at(index))) { + return o.template lift(element_at(index)); + } + + template + static operators::detail::element_at_invalid_t member(const AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "element_at takes (required int)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-filter.hpp b/3party/rxcpp/operators/rx-filter.hpp new file mode 100644 index 0000000..3a622bb --- /dev/null +++ b/3party/rxcpp/operators/rx-filter.hpp @@ -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 +struct filter_invalid_arguments {}; + +template +struct filter_invalid : public rxo::operator_base> { + using type = observable, filter_invalid>; +}; +template +using filter_invalid_t = typename filter_invalid::type; + +template +struct filter +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t test_type; + test_type test; + + filter(test_type t) + : test(std::move(t)) + { + } + + template + struct filter_observer + { + typedef filter_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 + 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(v)); + } + } + void on_error(rxu::error_ptr e) const { + dest.on_error(e); + } + void on_completed() const { + dest.on_completed(); + } + + static subscriber make(dest_type d, test_type t) { + return make_subscriber(d, this_type(d, std::move(t))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(filter_observer::make(std::move(dest), test)) { + return filter_observer::make(std::move(dest), test); + } +}; + +} + +/*! @copydoc rx-filter.hpp +*/ +template +auto filter(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class Filter = rxo::detail::filter>> + static auto member(Observable&& o, Predicate&& p) + -> decltype(o.template lift(Filter(std::forward(p)))) { + return o.template lift(Filter(std::forward(p))); + } + + template + static operators::detail::filter_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "filter takes (Predicate)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-finally.hpp b/3party/rxcpp/operators/rx-finally.hpp new file mode 100644 index 0000000..d6f5487 --- /dev/null +++ b/3party/rxcpp/operators/rx-finally.hpp @@ -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 +struct finally_invalid_arguments {}; + +template +struct finally_invalid : public rxo::operator_base> { + using type = observable, finally_invalid>; +}; +template +using finally_invalid_t = typename finally_invalid::type; + +template +struct finally +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t last_call_type; + last_call_type last_call; + + finally(last_call_type lc) + : last_call(std::move(lc)) + { + } + + template + struct finally_observer + { + typedef finally_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 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(cs, this_type(d)); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(finally_observer::make(std::move(dest), last_call)) { + return finally_observer::make(std::move(dest), last_call); + } +}; + +} + +/*! @copydoc rx-finally.hpp +*/ +template +auto finally(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable>, + class Finally = rxo::detail::finally>> + static auto member(Observable&& o, LastCall&& lc) + -> decltype(o.template lift(Finally(std::forward(lc)))) { + return o.template lift(Finally(std::forward(lc))); + } + + template + static operators::detail::finally_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "finally takes (LastCall)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-flat_map.hpp b/3party/rxcpp/operators/rx-flat_map.hpp new file mode 100644 index 0000000..eb76198 --- /dev/null +++ b/3party/rxcpp/operators/rx-flat_map.hpp @@ -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::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 +struct flat_map_invalid_arguments {}; + +template +struct flat_map_invalid : public rxo::operator_base> { + using type = observable, flat_map_invalid>; +}; +template +using flat_map_invalid_t = typename flat_map_invalid::type; + +template +struct flat_map_traits { + typedef rxu::decay_t source_type; + typedef rxu::decay_t collection_selector_type; + typedef rxu::decay_t result_selector_type; + typedef rxu::decay_t coordination_type; + + typedef typename source_type::value_type source_value_type; + + struct tag_not_valid {}; + template + static auto collection_check(int) -> decltype((*(CCS*)nullptr)(*(CV*)nullptr)); + template + static tag_not_valid collection_check(...); + + static_assert(!std::is_same(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 collection_type; + + static_assert(is_observable::value, "flat_map CollectionSelector must return an observable"); + + typedef typename collection_type::value_type collection_value_type; + + template + static auto result_check(int) -> decltype((*(CRS*)nullptr)(*(CV*)nullptr, *(CCV*)nullptr)); + template + static tag_not_valid result_check(...); + + static_assert(!std::is_same(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 value_type; +}; + +template +struct flat_map + : public operator_base>> +{ + typedef flat_map this_type; + typedef flat_map_traits 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 + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct state_type + : public std::enable_shared_from_this + , 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(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( + 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( + 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 +auto flat_map(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +/*! @copydoc rx-flat_map.hpp +*/ +template +auto merge_transform(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class SourceValue = rxu::value_type_t, + class CollectionType = rxu::result_of_t, + class ResultSelectorType = rxu::detail::take_at<1>, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables>, + class FlatMap = rxo::detail::flat_map, rxu::decay_t, ResultSelectorType, identity_one_worker>, + class CollectionValueType = rxu::value_type_t, + class Value = rxu::result_of_t, + class Result = observable + > + static Result member(Observable&& o, CollectionSelector&& s) { + return Result(FlatMap(std::forward(o), std::forward(s), ResultSelectorType(), identity_current_thread())); + } + + template, + class SourceValue = rxu::value_type_t, + class CollectionType = rxu::result_of_t, + class ResultSelectorType = rxu::detail::take_at<1>, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables, + is_coordination>, + class FlatMap = rxo::detail::flat_map, rxu::decay_t, ResultSelectorType, rxu::decay_t>, + class CollectionValueType = rxu::value_type_t, + class Value = rxu::result_of_t, + class Result = observable + > + static Result member(Observable&& o, CollectionSelector&& s, Coordination&& cn) { + return Result(FlatMap(std::forward(o), std::forward(s), ResultSelectorType(), std::forward(cn))); + } + + template, + class CollectionSelectorType = rxu::decay_t, + class SourceValue = rxu::value_type_t, + class CollectionType = rxu::result_of_t, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables, + rxu::negation>, + class FlatMap = rxo::detail::flat_map, rxu::decay_t, rxu::decay_t, identity_one_worker>, + class CollectionValueType = rxu::value_type_t, + class ResultSelectorType = rxu::decay_t, + class Value = rxu::result_of_t, + class Result = observable + > + static Result member(Observable&& o, CollectionSelector&& s, ResultSelector&& rs) { + return Result(FlatMap(std::forward(o), std::forward(s), std::forward(rs), identity_current_thread())); + } + + template, + class SourceValue = rxu::value_type_t, + class CollectionType = rxu::result_of_t, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables, + is_coordination>, + class FlatMap = rxo::detail::flat_map, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class CollectionValueType = rxu::value_type_t, + class ResultSelectorType = rxu::decay_t, + class Value = rxu::result_of_t, + class Result = observable + > + static Result member(Observable&& o, CollectionSelector&& s, ResultSelector&& rs, Coordination&& cn) { + return Result(FlatMap(std::forward(o), std::forward(s), std::forward(rs), std::forward(cn))); + } + + template + static operators::detail::flat_map_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "flat_map takes (CollectionSelector, optional ResultSelector, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-group_by.hpp b/3party/rxcpp/operators/rx-group_by.hpp new file mode 100644 index 0000000..d1c4ea4 --- /dev/null +++ b/3party/rxcpp/operators/rx-group_by.hpp @@ -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 +struct group_by_invalid_arguments {}; + +template +struct group_by_invalid : public rxo::operator_base> { + using type = observable, group_by_invalid>; +}; +template +using group_by_invalid_t = typename group_by_invalid::type; + +template +struct is_group_by_selector_for { + + typedef rxu::decay_t selector_type; + typedef T source_value_type; + + struct tag_not_valid {}; + template + static auto check(int) -> decltype((*(CS*)nullptr)(*(CV*)nullptr)); + template + static tag_not_valid check(...); + + typedef decltype(check(0)) type; + static const bool value = !std::is_same::value; +}; + +template +struct group_by_traits +{ + typedef T source_value_type; + typedef rxu::decay_t source_type; + typedef rxu::decay_t key_selector_type; + typedef rxu::decay_t marble_selector_type; + typedef rxu::decay_t predicate_type; + typedef rxu::decay_t duration_selector_type; + + static_assert(is_group_by_selector_for::value, "group_by KeySelector must be a function with the signature key_type(source_value_type)"); + + typedef typename is_group_by_selector_for::type key_type; + + static_assert(is_group_by_selector_for::value, "group_by MarbleSelector must be a function with the signature marble_type(source_value_type)"); + + typedef typename is_group_by_selector_for::type marble_type; + + typedef rxsub::subject subject_type; + + typedef std::map key_subscriber_map_type; + + typedef grouped_observable grouped_observable_type; +}; + +template +struct group_by +{ + typedef group_by_traits 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 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 observers; + }; + + template + static void stopsource(Subscriber&& dest, std::shared_ptr& 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 + { + mutable std::shared_ptr state; + subject_type subject; + key_type key; + + group_by_observable(std::shared_ptr st, subject_type s, key_type k) + : state(std::move(st)) + , subject(std::move(s)) + , key(k) + { + } + + template + void on_subscribe(Subscriber&& o) const { + group_by::stopsource(o, state); + subject.get_observable().subscribe(std::forward(o)); + } + + key_type on_get_key() { + return key; + } + }; + + template + struct group_by_observer : public group_by_values + { + typedef group_by_observer this_type; + typedef typename traits_type::grouped_observable_type value_type; + typedef rxu::decay_t dest_type; + typedef observer observer_type; + + dest_type dest; + + mutable std::shared_ptr 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(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(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 make(dest_type d, group_by_values v) { + auto cs = composite_subscription(); + return make_subscriber(cs, observer_type(this_type(cs, std::move(d), std::move(v)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(group_by_observer::make(std::move(dest), initial)) { + return group_by_observer::make(std::move(dest), initial); + } +}; + +template +class group_by_factory +{ + typedef rxu::decay_t key_selector_type; + typedef rxu::decay_t marble_selector_type; + typedef rxu::decay_t predicate_type; + typedef rxu::decay_t 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 + struct group_by_factory_traits + { + typedef rxu::value_type_t> value_type; + typedef detail::group_by_traits traits_type; + typedef detail::group_by group_by_type; + }; + template + auto operator()(Observable&& source) + -> decltype(source.template lift::traits_type::grouped_observable_type>(typename group_by_factory_traits::group_by_type(std::move(keySelector), std::move(marbleSelector), std::move(predicate), std::move(durationSelector)))) { + return source.template lift::traits_type::grouped_observable_type>(typename group_by_factory_traits::group_by_type(std::move(keySelector), std::move(marbleSelector), std::move(predicate), std::move(durationSelector))); + } +}; + +} + +/*! @copydoc rx-group_by.hpp +*/ +template +auto group_by(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class Traits = rxo::detail::group_by_traits, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>, + class GroupBy = rxo::detail::group_by, rxu::decay_t, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class Value = typename Traits::grouped_observable_type> + static auto member(Observable&& o, KeySelector&& ks, MarbleSelector&& ms, BinaryPredicate&& p, DurationSelector&& ds) + -> decltype(o.template lift(GroupBy(std::forward(ks), std::forward(ms), std::forward(p), std::forward(ds)))) { + return o.template lift(GroupBy(std::forward(ks), std::forward(ms), std::forward(p), std::forward(ds))); + } + + template>>, + class SourceValue = rxu::value_type_t, + class Traits = rxo::detail::group_by_traits, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>, + class GroupBy = rxo::detail::group_by, rxu::decay_t, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class Value = typename Traits::grouped_observable_type> + static auto member(Observable&& o, KeySelector&& ks, MarbleSelector&& ms, BinaryPredicate&& p) + -> decltype(o.template lift(GroupBy(std::forward(ks), std::forward(ms), std::forward(p), rxu::ret>>()))) { + return o.template lift(GroupBy(std::forward(ks), std::forward(ms), std::forward(p), rxu::ret>>())); + } + + template>>, + class SourceValue = rxu::value_type_t, + class Traits = rxo::detail::group_by_traits, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>, + class GroupBy = rxo::detail::group_by, rxu::decay_t, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class Value = typename Traits::grouped_observable_type> + static auto member(Observable&& o, KeySelector&& ks, MarbleSelector&& ms) + -> decltype(o.template lift(GroupBy(std::forward(ks), std::forward(ms), rxu::less(), rxu::ret>>()))) { + return o.template lift(GroupBy(std::forward(ks), std::forward(ms), rxu::less(), rxu::ret>>())); + } + + + template, + class BinaryPredicate=rxu::less, + class DurationSelector=rxu::ret>>, + class SourceValue = rxu::value_type_t, + class Traits = rxo::detail::group_by_traits, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>, + class GroupBy = rxo::detail::group_by, rxu::decay_t, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class Value = typename Traits::grouped_observable_type> + static auto member(Observable&& o, KeySelector&& ks) + -> decltype(o.template lift(GroupBy(std::forward(ks), rxu::detail::take_at<0>(), rxu::less(), rxu::ret>>()))) { + return o.template lift(GroupBy(std::forward(ks), rxu::detail::take_at<0>(), rxu::less(), rxu::ret>>())); + } + + template, + class MarbleSelector=rxu::detail::take_at<0>, + class BinaryPredicate=rxu::less, + class DurationSelector=rxu::ret>>, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables>, + class SourceValue = rxu::value_type_t, + class Traits = rxo::detail::group_by_traits, KeySelector, MarbleSelector, BinaryPredicate, DurationSelector>, + class GroupBy = rxo::detail::group_by, rxu::decay_t, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class Value = typename Traits::grouped_observable_type> + static auto member(Observable&& o) + -> decltype(o.template lift(GroupBy(rxu::detail::take_at<0>(), rxu::detail::take_at<0>(), rxu::less(), rxu::ret>>()))) { + return o.template lift(GroupBy(rxu::detail::take_at<0>(), rxu::detail::take_at<0>(), rxu::less(), rxu::ret>>())); + } + + template + static operators::detail::group_by_invalid_t 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 + diff --git a/3party/rxcpp/operators/rx-ignore_elements.hpp b/3party/rxcpp/operators/rx-ignore_elements.hpp new file mode 100644 index 0000000..00b10a6 --- /dev/null +++ b/3party/rxcpp/operators/rx-ignore_elements.hpp @@ -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 +struct ignore_elements_invalid_arguments {}; + +template +struct ignore_elements_invalid : public rxo::operator_base> { + using type = observable, ignore_elements_invalid>; +}; +template +using ignore_elements_invalid_t = typename ignore_elements_invalid::type; + +template +struct ignore_elements { + typedef rxu::decay_t source_value_type; + + template + struct ignore_elements_observer + { + typedef ignore_elements_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d) { + return make_subscriber(d, this_type(d)); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(ignore_elements_observer::make(std::move(dest))) { + return ignore_elements_observer::make(std::move(dest)); + } +}; + +} + +/*! @copydoc rx-ignore_elements.hpp +*/ +template +auto ignore_elements(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable>, + class IgnoreElements = rxo::detail::ignore_elements> + static auto member(Observable&& o) + -> decltype(o.template lift(IgnoreElements())) { + return o.template lift(IgnoreElements()); + } + + template + static operators::detail::ignore_elements_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "ignore_elements takes no arguments"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-lift.hpp b/3party/rxcpp/operators/rx-lift.hpp new file mode 100644 index 0000000..bcbf93f --- /dev/null +++ b/3party/rxcpp/operators/rx-lift.hpp @@ -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 +struct is_lift_function_for { + + struct tag_not_valid {}; + template + static auto check(int) -> decltype((*(CF*)nullptr)(*(CS*)nullptr)); + template + static tag_not_valid check(...); + + using for_type = rxu::decay_t; + using func_type = rxu::decay_t; + using detail_result = decltype(check(0)); + + static const bool value = rxu::all_true_type< + is_subscriber, + is_subscriber, + std::is_convertible::type>>::value; +}; + +} + +namespace operators { + +namespace detail { + +template +struct lift_traits +{ + typedef rxu::decay_t result_value_type; + typedef rxu::decay_t source_operator_type; + typedef rxu::decay_t operator_type; + + typedef typename source_operator_type::value_type source_value_type; +}; + +template +struct lift_operator : public operator_base::result_value_type> +{ + typedef lift_traits 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 + 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 lift_factory +{ + typedef rxu::decay_t operator_type; + operator_type chain; +public: + lift_factory(operator_type op) : chain(std::move(op)) {} + template + auto operator()(const Observable& source) + -> decltype(source.template lift(chain)) { + return source.template lift(chain); + static_assert(rxcpp::detail::is_lift_function_for, subscriber, Operator>::value, "Function passed for lift() must have the signature subscriber<...>(subscriber)"); + } +}; + +} + +template +auto lift(Operator&& op) + -> detail::lift_factory { + return detail::lift_factory(std::forward(op)); +} + +} + +} + +#endif diff --git a/3party/rxcpp/operators/rx-map.hpp b/3party/rxcpp/operators/rx-map.hpp new file mode 100644 index 0000000..7570376 --- /dev/null +++ b/3party/rxcpp/operators/rx-map.hpp @@ -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 +struct map_invalid_arguments {}; + +template +struct map_invalid : public rxo::operator_base> { + using type = observable, map_invalid>; +}; +template +using map_invalid_t = typename map_invalid::type; + +template +struct map +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t 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 + struct map_observer + { + typedef map_observer this_type; + typedef decltype((*(select_type*)nullptr)(*(source_value_type*)nullptr)) value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 + void on_next(Value&& v) const { + auto selected = on_exception( + [&](){ + return this->selector(std::forward(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 make(dest_type d, select_type s) { + auto cs = d.get_subscription(); + return make_subscriber(std::move(cs), observer_type(this_type(std::move(d), std::move(s)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(map_observer::make(std::move(dest), selector)) { + return map_observer::make(std::move(dest), selector); + } +}; + +} + +/*! @copydoc rx-map.hpp +*/ +template +auto map(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +/*! @copydoc rx-map.hpp +*/ +template +auto transform(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class ResolvedSelector = rxu::decay_t, + class SourceValue = rxu::value_type_t, + class Map = rxo::detail::map, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Selector&& s) + -> decltype(o.template lift(Map(std::forward(s)))) { + return o.template lift(Map(std::forward(s))); + } + + template + static operators::detail::map_invalid_t member(const AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "map takes Selector"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-merge.hpp b/3party/rxcpp/operators/rx-merge.hpp new file mode 100644 index 0000000..cc1ea77 --- /dev/null +++ b/3party/rxcpp/operators/rx-merge.hpp @@ -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 +struct merge_invalid_arguments {}; + +template +struct merge_invalid : public rxo::operator_base> { + using type = observable, merge_invalid>; +}; +template +using merge_invalid_t = typename merge_invalid::type; + +template +struct merge + : public operator_base>> +{ + //static_assert(is_observable::value, "merge requires an observable"); + //static_assert(is_observable::value, "merge requires an observable that contains observables"); + + typedef merge this_type; + + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t 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_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 + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct merge_state_type + : public std::enable_shared_from_this + , 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; + // 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(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( + 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( + 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 +auto merge(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Merge = rxo::detail::merge, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o) { + return Result(Merge(std::forward(o), identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Merge = rxo::detail::merge, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o, Coordination&& cn) { + return Result(Merge(std::forward(o), std::forward(cn))); + } + + template>, + class EmittedValue = rxu::value_type_t, + class SourceValue = observable, + class ObservableObservable = observable, + class Merge = typename rxu::defer_type::type, + class Value = rxu::value_type_t, + class Result = observable + > + 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, + is_coordination>, + class EmittedValue = rxu::value_type_t, + class SourceValue = observable, + class ObservableObservable = observable, + class Merge = typename rxu::defer_type>::type, + class Value = rxu::value_type_t, + class Result = observable + > + 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(cn))); + } + + template + static operators::detail::merge_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "merge takes (optional Coordination, optional Value0, optional ValueN...)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-merge_delay_error.hpp b/3party/rxcpp/operators/rx-merge_delay_error.hpp new file mode 100644 index 0000000..f1e72eb --- /dev/null +++ b/3party/rxcpp/operators/rx-merge_delay_error.hpp @@ -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 +struct merge_delay_error + : public operator_base>> +{ + //static_assert(is_observable::value, "merge requires an observable"); + //static_assert(is_observable::value, "merge requires an observable that contains observables"); + + typedef merge_delay_error this_type; + + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t 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_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 + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct merge_state_type + : public std::enable_shared_from_this + , 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; + // 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(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( + 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( + 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 +auto merge_delay_error(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Merge = rxo::detail::merge_delay_error, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o) { + return Result(Merge(std::forward(o), identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Merge = rxo::detail::merge_delay_error, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o, Coordination&& cn) { + return Result(Merge(std::forward(o), std::forward(cn))); + } + + template>, + class EmittedValue = rxu::value_type_t, + class SourceValue = observable, + class ObservableObservable = observable, + class Merge = typename rxu::defer_type::type, + class Value = rxu::value_type_t, + class Result = observable + > + 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, + is_coordination>, + class EmittedValue = rxu::value_type_t, + class SourceValue = observable, + class ObservableObservable = observable, + class Merge = typename rxu::defer_type>::type, + class Value = rxu::value_type_t, + class Result = observable + > + 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(cn))); + } + + template + static operators::detail::merge_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "merge_delay_error takes (optional Coordination, optional Value0, optional ValueN...)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-multicast.hpp b/3party/rxcpp/operators/rx-multicast.hpp new file mode 100644 index 0000000..193a11a --- /dev/null +++ b/3party/rxcpp/operators/rx-multicast.hpp @@ -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 +struct multicast_invalid_arguments {}; + +template +struct multicast_invalid : public rxo::operator_base> { + using type = observable, multicast_invalid>; +}; +template +using multicast_invalid_t = typename multicast_invalid::type; + +template +struct multicast : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t subject_type; + + struct multicast_state : public std::enable_shared_from_this + { + 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 connection; + }; + + std::shared_ptr state; + + multicast(source_type o, subject_type sub) + : state(std::make_shared(std::move(o), std::move(sub))) + { + } + template + void on_subscribe(Subscriber&& o) const { + state->subject_value.get_observable().subscribe(std::forward(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 +auto multicast(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Multicast = rxo::detail::multicast, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = connectable_observable> + static Result member(Observable&& o, Subject&& sub) { + return Result(Multicast(std::forward(o), std::forward(sub))); + } + + template + static operators::detail::multicast_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "multicast takes (Subject)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-observe_on.hpp b/3party/rxcpp/operators/rx-observe_on.hpp new file mode 100644 index 0000000..b50b773 --- /dev/null +++ b/3party/rxcpp/operators/rx-observe_on.hpp @@ -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 +struct observe_on_invalid_arguments {}; + +template +struct observe_on_invalid : public rxo::operator_base> { + using type = observable, observe_on_invalid>; +}; +template +using observe_on_invalid_t = typename observe_on_invalid::type; + +template +struct observe_on +{ + typedef rxu::decay_t source_value_type; + + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + + coordination_type coordination; + + observe_on(coordination_type cn) + : coordination(std::move(cn)) + { + } + + template + struct observe_on_observer + { + typedef observe_on_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer observer_type; + + typedef rxn::notification notification_type; + typedef typename notification_type::type base_notification_type; + typedef std::deque queue_type; + + struct mode + { + enum type { + Invalid = 0, + Processing, + Empty, + Disposed, + Errored + }; + }; + struct observe_on_state : std::enable_shared_from_this + { + 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& 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& 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 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 guard(lock); + self(); + if (lifetime.is_subscribed()) break; + } + } + RXCPP_CATCH(...) { + destination.on_error(rxu::current_exception()); + std::unique_lock 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 state; + + observe_on_observer(dest_type d, coordinator_type coor, composite_subscription cs) + : state(std::make_shared(std::move(d), std::move(coor), std::move(cs))) + { + } + + void on_next(source_value_type v) const { + std::unique_lock 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 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 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> 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 guard(keepAlive->lock); + keepAlive->ensure_processing(guard); + }); + + return make_subscriber(d, cs, make_observer(std::move(o))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(observe_on_observer::make(dest.as_dynamic(), coordination)) { + return observe_on_observer::make(dest.as_dynamic(), coordination); + } +}; + +} + +/*! @copydoc rx-observe_on.hpp +*/ +template +auto observe_on(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class ObserveOn = rxo::detail::observe_on>> + static auto member(Observable&& o, Coordination&& cn) + -> decltype(o.template lift(ObserveOn(std::forward(cn)))) { + return o.template lift(ObserveOn(std::forward(cn))); + } + + template + static operators::detail::observe_on_invalid_t 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 + auto in(Observable o) const + -> decltype(o.observe_on(coordination)) { + return o.observe_on(coordination); + } + template + auto out(Subscriber s) const + -> Subscriber { + return s; + } + template + auto act(F f) const + -> F { + return f; + } + }; + +public: + + explicit observe_on_one_worker(rxsc::scheduler sc) : factory(sc) {} + + typedef coordinator 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 diff --git a/3party/rxcpp/operators/rx-on_error_resume_next.hpp b/3party/rxcpp/operators/rx-on_error_resume_next.hpp new file mode 100644 index 0000000..bc9beba --- /dev/null +++ b/3party/rxcpp/operators/rx-on_error_resume_next.hpp @@ -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(rxu::error_ptr)` + + \param s the function of the form `observable(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 +struct on_error_resume_next_invalid_arguments {}; + +template +struct on_error_resume_next_invalid : public rxo::operator_base> { + using type = observable, on_error_resume_next_invalid>; +}; +template +using on_error_resume_next_invalid_t = typename on_error_resume_next_invalid::type; + + +template +struct on_error_resume_next +{ + typedef rxu::decay_t value_type; + typedef rxu::decay_t 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 + struct on_error_resume_next_observer + { + typedef on_error_resume_next_observer this_type; + typedef rxu::decay_t value_type; + typedef rxu::decay_t select_type; + typedef decltype((*(select_type*)nullptr)(rxu::error_ptr())) fallback_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, select_type s) { + auto cs = composite_subscription(); + return make_subscriber(cs, observer_type(this_type(std::move(d), cs, std::move(s)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(on_error_resume_next_observer::make(std::move(dest), selector)) { + return on_error_resume_next_observer::make(std::move(dest), selector); + } +}; + +} + +/*! @copydoc rx-on_error_resume_next.hpp +*/ +template +auto on_error_resume_next(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +/*! @copydoc rx-on_error_resume_next.hpp +*/ +template +auto switch_on_error(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class OnErrorResumeNext = rxo::detail::on_error_resume_next>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Selector&& p) + -> decltype(o.template lift(OnErrorResumeNext(std::forward(p)))) { + return o.template lift(OnErrorResumeNext(std::forward(p))); + } + + template + static operators::detail::on_error_resume_next_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "on_error_resume_next takes (Selector)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-pairwise.hpp b/3party/rxcpp/operators/rx-pairwise.hpp new file mode 100644 index 0000000..411cf27 --- /dev/null +++ b/3party/rxcpp/operators/rx-pairwise.hpp @@ -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 +struct pairwise_invalid_arguments {}; + +template +struct pairwise_invalid : public rxo::operator_base> { + using type = observable, pairwise_invalid>; +}; +template +using pairwise_invalid_t = typename pairwise_invalid::type; + +template +struct pairwise +{ + typedef rxu::decay_t source_value_type; + typedef std::tuple value_type; + + template + struct pairwise_observer + { + typedef pairwise_observer this_type; + typedef std::tuple value_type; + typedef rxu::decay_t dest_type; + typedef observer observer_type; + dest_type dest; + mutable rxu::detail::maybe 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 make(dest_type d) { + auto cs = d.get_subscription(); + return make_subscriber(std::move(cs), observer_type(this_type(std::move(d)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(pairwise_observer::make(std::move(dest))) { + return pairwise_observer::make(std::move(dest)); + } +}; + +} + +/*! @copydoc rx-pairwise.hpp +*/ +template +auto pairwise(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Pairwise = rxo::detail::pairwise, + class Value = rxu::value_type_t> + static auto member(Observable&& o) + -> decltype(o.template lift(Pairwise())) { + return o.template lift(Pairwise()); + } + + template + static operators::detail::pairwise_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "pairwise takes no arguments"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-publish.hpp b/3party/rxcpp/operators/rx-publish.hpp new file mode 100644 index 0000000..bc686fc --- /dev/null +++ b/3party/rxcpp/operators/rx-publish.hpp @@ -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 +struct publish_invalid_arguments {}; + +template +struct publish_invalid : public rxo::operator_base> { + using type = observable, publish_invalid>; +}; +template +using publish_invalid_t = typename publish_invalid::type; + +} + +/*! @copydoc rx-publish.hpp +*/ +template +auto publish(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(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 +auto publish_synchronized(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::subject, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o) { + return Result(Multicast(std::forward(o), Subject(composite_subscription()))); + } + + template>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::subject, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, composite_subscription cs) { + return Result(Multicast(std::forward(o), Subject(cs))); + } + + template>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::behavior, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, T first, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(first, cs))); + } + + template + static operators::detail::publish_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "publish takes (optional CompositeSubscription) or (T, optional CompositeSubscription)"); + } +}; + +template<> +struct member_overload +{ + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::synchronize>, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, Coordination&& cn, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(std::forward(cn), cs))); + } + + template + static operators::detail::publish_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "publish_synchronized takes (Coordination, optional CompositeSubscription)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-reduce.hpp b/3party/rxcpp/operators/rx-reduce.hpp new file mode 100644 index 0000000..e5dc819 --- /dev/null +++ b/3party/rxcpp/operators/rx-reduce.hpp @@ -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 +struct reduce_invalid_arguments {}; + +template +struct reduce_invalid : public rxo::operator_base> { + using type = observable, reduce_invalid>; +}; +template +using reduce_invalid_t = typename reduce_invalid::type; + +template +struct is_result_function_for { + + typedef rxu::decay_t result_selector_type; + typedef rxu::decay_t seed_type; + + struct tag_not_valid {}; + + template + static auto check(int) -> decltype((*(CRS*)nullptr)(*(CS*)nullptr)); + template + static tag_not_valid check(...); + + typedef rxu::decay_t(0))> type; + static const bool value = !std::is_same::value; +}; + +template +struct reduce_traits +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t accumulator_type; + typedef rxu::decay_t result_selector_type; + typedef rxu::decay_t seed_type; + + typedef T source_value_type; + + typedef typename is_result_function_for::type value_type; +}; + +template +struct reduce : public operator_base>> +{ + typedef reduce this_type; + typedef reduce_traits 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 + void on_subscribe(Subscriber o) const { + struct reduce_state_type + : public reduce_initial_type + , public std::enable_shared_from_this + { + 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(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 +struct initialize_seeder { + typedef T seed_type; + static seed_type seed() { + return seed_type{}; + } +}; + +template +struct average { + struct seed_type + { + seed_type() + : value() + , count(0) + { + } + rxu::maybe value; + int count; + rxu::detail::maybe stage; + }; + static seed_type seed() { + return seed_type{}; + } + template + seed_type operator()(seed_type a, U&& v) { + if (a.count != 0 && + (a.count == std::numeric_limits::max() || + ((v > 0) && (*(a.value) > (std::numeric_limits::max() - v))) || + ((v < 0) && (*(a.value) < (std::numeric_limits::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(*(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(v)); + a.count = 1; + } else if (a.value.empty()) { + a.value.reset(std::forward(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(*(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 +struct sum { + typedef rxu::maybe seed_type; + static seed_type seed() { + return seed_type(); + } + template + seed_type operator()(seed_type a, U&& v) const { + if (a.empty()) + a.reset(std::forward(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 +struct max { + typedef rxu::maybe seed_type; + static seed_type seed() { + return seed_type(); + } + template + seed_type operator()(seed_type a, U&& v) { + if (a.empty() || *a < v) + a.reset(std::forward(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 +struct min { + typedef rxu::maybe seed_type; + static seed_type seed() { + return seed_type(); + } + template + seed_type operator()(seed_type a, U&& v) { + if (a.empty() || v < *a) + a.reset(std::forward(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 +struct first { + using seed_type = rxu::maybe; + static seed_type seed() { + return seed_type(); + } + template + seed_type operator()(seed_type a, U&& v) { + a.reset(std::forward(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 +struct last { + using seed_type = rxu::maybe; + static seed_type seed() { + return seed_type(); + } + template + seed_type operator()(seed_type a, U&& v) { + a.reset(std::forward(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 +auto reduce(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +/*! @copydoc rx-reduce.hpp +*/ +template +auto accumulate(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(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 { + return operator_factory(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 { + return operator_factory(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> { + return operator_factory>(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 { + return operator_factory(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 { + return operator_factory(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 { + return operator_factory(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 { + return operator_factory(std::tuple<>{}); +} + +} + +template<> +struct member_overload +{ + + template, rxu::decay_t, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Seed&& s, Accumulator&& a, ResultSelector&& r) + { + return Result(Reduce(std::forward(o), std::forward(a), std::forward(r), std::forward(s))); + } + + template, + class Reduce = rxo::detail::reduce, rxu::decay_t, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Seed&& s, Accumulator&& a) + { + return Result(Reduce(std::forward(o), std::forward(a), rxu::detail::take_at<0>(), std::forward(s))); + } + + template + static operators::detail::reduce_invalid_t 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 +{ + template, + class Operation = operators::detail::first, + class Seed = decltype(Operation::seed()), + class Accumulator = Operation, + class ResultSelector = Operation, + class TakeOne = decltype(((rxu::decay_t*)nullptr)->take(1)), + class Reduce = rxo::detail::reduce, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class RValue = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o) + { + return Result(Reduce(o.take(1), Operation{}, Operation{}, Operation::seed())); + } + + template + static operators::detail::reduce_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "first does not support Observable::value_type"); + } +}; + +template<> +struct member_overload +{ + template, + class Operation = operators::detail::last, + class Seed = decltype(Operation::seed()), + class Accumulator = Operation, + class ResultSelector = Operation, + class Reduce = rxo::detail::reduce, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class RValue = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o) + { + return Result(Reduce(std::forward(o), Operation{}, Operation{}, Operation::seed())); + } + + template + static operators::detail::reduce_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "last does not support Observable::value_type"); + } +}; + +template<> +struct member_overload +{ + template, + class Operation = operators::detail::sum, + class Seed = decltype(Operation::seed()), + class Accumulator = Operation, + class ResultSelector = Operation, + class Reduce = rxo::detail::reduce, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class RValue = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o) + { + return Result(Reduce(std::forward(o), Operation{}, Operation{}, Operation::seed())); + } + + template + static operators::detail::reduce_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "sum does not support Observable::value_type"); + } +}; + +template<> +struct member_overload +{ + template, + class Operation = operators::detail::average, + class Seed = decltype(Operation::seed()), + class Accumulator = Operation, + class ResultSelector = Operation, + class Reduce = rxo::detail::reduce, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class RValue = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o) + { + return Result(Reduce(std::forward(o), Operation{}, Operation{}, Operation::seed())); + } + + template + static operators::detail::reduce_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "average does not support Observable::value_type"); + } +}; + +template<> +struct member_overload +{ + template, + class Operation = operators::detail::max, + class Seed = decltype(Operation::seed()), + class Accumulator = Operation, + class ResultSelector = Operation, + class Reduce = rxo::detail::reduce, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class RValue = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o) + { + return Result(Reduce(std::forward(o), Operation{}, Operation{}, Operation::seed())); + } + + template + static operators::detail::reduce_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "max does not support Observable::value_type"); + } +}; + +template<> +struct member_overload +{ + template, + class Operation = operators::detail::min, + class Seed = decltype(Operation::seed()), + class Accumulator = Operation, + class ResultSelector = Operation, + class Reduce = rxo::detail::reduce, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class RValue = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o) + { + return Result(Reduce(std::forward(o), Operation{}, Operation{}, Operation::seed())); + } + + template + static operators::detail::reduce_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "min does not support Observable::value_type"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-ref_count.hpp b/3party/rxcpp/operators/rx-ref_count.hpp new file mode 100644 index 0000000..1ce9a40 --- /dev/null +++ b/3party/rxcpp/operators/rx-ref_count.hpp @@ -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 +struct ref_count_invalid_arguments {}; + +template +struct ref_count_invalid : public rxo::operator_base> { + using type = observable, ref_count_invalid>; +}; +template +using ref_count_invalid_t = typename ref_count_invalid::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 +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 + void subscribe(Subscriber&& o) { + subscribable.subscribe(std::forward(o)); + } +}; + +// Note: explicit specializations have to be at namespace scope prior to C++17. +template +struct ref_count_state_base { + 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 + void subscribe(Subscriber&& o) { + connectable.subscribe(std::forward(o)); + } +}; + +template // note: type order flipped versus the operator. +struct ref_count : public operator_base +{ + typedef rxu::decay_t observable_type; + typedef rxu::decay_t connectable_type; + + // ref_count() == false + // ref_count(other) == true + using has_observable_t = rxu::negation>; + // 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, + public ref_count_state_base + { + template >> + explicit ref_count_state(connectable_type source) + : ref_count_state_base(std::move(source)) + , subscribers(0) + { + } + + template + ref_count_state(connectable_type other, + typename std::enable_if::type source) + : ref_count_state_base(std::move(other), + std::move(source)) + , subscribers(0) + { + } + + std::mutex lock; + long subscribers; + composite_subscription connection; + }; + std::shared_ptr state; + + // connectable_observable source = ...; + // source.ref_count(); + // + // calls connect on source after the subscribe on source. + template >> + explicit ref_count(connectable_type source) + : state(std::make_shared(std::move(source))) + { + } + + // connectable_observable other = ...; + // observable source = ...; + // source.ref_count(other); + // + // calls connect on 'other' after the subscribe on 'source'. + template + ref_count(connectable_type other, + typename std::enable_if::type source) + : state(std::make_shared(std::move(other), std::move(source))) + { + } + + template + void on_subscribe(Subscriber&& o) const { + std::unique_lock guard(state->lock); + auto needConnect = ++state->subscribers == 1; + auto keepAlive = state; + guard.unlock(); + o.add( + [keepAlive](){ + std::unique_lock guard_unsubscribe(keepAlive->lock); + if (--keepAlive->subscribers == 0) { + keepAlive->connection.unsubscribe(); + keepAlive->connection = composite_subscription(); + } + }); + keepAlive->subscribe(std::forward(o)); + if (needConnect) { + keepAlive->connectable.connect(keepAlive->connection); + } + } +}; + +} + +/*! @copydoc rx-ref_count.hpp +*/ +template +auto ref_count(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class RefCount = rxo::detail::ref_count>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(ConnectableObservable&& o) { + return Result(RefCount(std::forward(o))); + } + + template, + is_connectable_observable>, + class SourceValue = rxu::value_type_t, + class RefCount = rxo::detail::ref_count, + rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o, ConnectableObservable&& other) { + return Result(RefCount(std::forward(other), + std::forward(o))); + } + + template + static operators::detail::ref_count_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "ref_count takes (optional ConnectableObservable)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-repeat.hpp b/3party/rxcpp/operators/rx-repeat.hpp new file mode 100644 index 0000000..3b9ac89 --- /dev/null +++ b/3party/rxcpp/operators/rx-repeat.hpp @@ -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 +struct repeat_invalid_arguments {}; + +template +struct repeat_invalid : public rxo::operator_base> { + using type = observable, repeat_invalid>; +}; +template +using repeat_invalid_t = typename repeat_invalid::type; + +// Contain repeat variations in a namespace +namespace repeat { + struct event_handlers { + template + static inline void on_error(State& state, rxu::error_ptr& e) { + state->out.on_error(e); + } + + template + 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 + using finite = ::rxcpp::operators::detail::retry_repeat_common::finite + ; + + // Infinite repeat case + template + using infinite = ::rxcpp::operators::detail::retry_repeat_common::infinite + ; + +} +} // detail + +/*! @copydoc rx-repeat.hpp +*/ +template +auto repeat(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload { + template>, + class SourceValue = rxu::value_type_t, + class Repeat = rxo::detail::repeat::infinite>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o) { + return Result(Repeat(std::forward(o))); + } + + template>, + class SourceValue = rxu::value_type_t, + class Repeat = rxo::detail::repeat::finite, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Count&& c) { + return Result(Repeat(std::forward(o), std::forward(c))); + } + + template + static operators::detail::repeat_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "repeat takes (optional Count)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-replay.hpp b/3party/rxcpp/operators/rx-replay.hpp new file mode 100644 index 0000000..b2db8d8 --- /dev/null +++ b/3party/rxcpp/operators/rx-replay.hpp @@ -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 +struct replay_invalid_arguments {}; + +template +struct replay_invalid : public rxo::operator_base> { + using type = observable, replay_invalid>; +}; +template +using replay_invalid_t = typename replay_invalid::type; + +} + +/*! @copydoc rx-replay.hpp +*/ +template +auto replay(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + + template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o) { + return Result(Multicast(std::forward(o), Subject(identity_current_thread(), composite_subscription()))); + } + + template>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, composite_subscription cs) { + return Result(Multicast(std::forward(o), Subject(identity_current_thread(), cs))); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay>, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, Coordination&& cn, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(std::forward(cn), cs))); + } + + template, + std::is_integral>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, Count count, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(count, identity_current_thread(), cs))); + } + + template, + std::is_integral, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay>, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, Count count, Coordination&& cn, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(count, std::forward(cn), cs))); + } + + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable, + IsDuration>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, Duration&& d, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(std::forward(d), identity_current_thread(), cs))); + } + + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable, + IsDuration, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay>, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, Duration&& d, Coordination&& cn, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(std::forward(d), std::forward(cn), cs))); + } + + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable, + std::is_integral, + IsDuration>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, Count count, Duration&& d, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(count, std::forward(d), identity_current_thread(), cs))); + } + + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable, + std::is_integral, + IsDuration, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Subject = rxsub::replay>, + class Multicast = rxo::detail::multicast, Subject>, + class Result = connectable_observable + > + static Result member(Observable&& o, Count count, Duration&& d, Coordination&& cn, composite_subscription cs = composite_subscription()) { + return Result(Multicast(std::forward(o), Subject(count, std::forward(d), std::forward(cn), cs))); + } + + template + static operators::detail::replay_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "replay takes (optional Count, optional Duration, optional Coordination, optional CompositeSubscription)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-retry-repeat-common.hpp b/3party/rxcpp/operators/rx-retry-repeat-common.hpp new file mode 100644 index 0000000..30a71fe --- /dev/null +++ b/3party/rxcpp/operators/rx-retry-repeat-common.hpp @@ -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 + struct state_type : public std::enable_shared_from_this>, + 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 + struct finite : public operator_base { + typedef rxu::decay_t source_type; + typedef rxu::decay_t 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 + void on_subscribe(const Subscriber& s) const { + typedef state_type state_t; + // take a copy of the values for each subscription + auto state = std::make_shared(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 + struct infinite : public operator_base { + typedef rxu::decay_t 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 + void on_subscribe(const Subscriber& s) const { + typedef state_type state_t; + // take a copy of the values for each subscription + auto state = std::make_shared(initial_, s); + // start the first iteration + state->do_subscribe(); + } + + private: + values initial_; + }; + + + } + } + } +} diff --git a/3party/rxcpp/operators/rx-retry.hpp b/3party/rxcpp/operators/rx-retry.hpp new file mode 100644 index 0000000..63e5c27 --- /dev/null +++ b/3party/rxcpp/operators/rx-retry.hpp @@ -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 +struct retry_invalid_arguments {}; + +template +struct retry_invalid : public rxo::operator_base> { + using type = observable, retry_invalid>; +}; +template +using retry_invalid_t = typename retry_invalid::type; + +// Contain retry variations in a namespace +namespace retry { + struct event_handlers { + template + 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 + static inline void on_completed(State& state) { + state->out.on_completed(); + } + }; + + // Finite repeat case (explicitely limited with the number of times) + template + using finite = ::rxcpp::operators::detail::retry_repeat_common::finite + ; + + // Infinite repeat case + template + using infinite = ::rxcpp::operators::detail::retry_repeat_common::infinite + ; + +} +} // detail + +/*! @copydoc rx-retry.hpp +*/ +template +auto retry(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Retry = rxo::detail::retry::infinite>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o) { + return Result(Retry(std::forward(o))); + } + + template>, + class SourceValue = rxu::value_type_t, + class Retry = rxo::detail::retry::finite, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o, Count&& c) { + return Result(Retry(std::forward(o), std::forward(c))); + } + + template + static operators::detail::retry_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "retry takes (optional Count)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-sample_time.hpp b/3party/rxcpp/operators/rx-sample_time.hpp new file mode 100644 index 0000000..f50cbe4 --- /dev/null +++ b/3party/rxcpp/operators/rx-sample_time.hpp @@ -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 +struct sample_with_time_invalid_arguments {}; + +template +struct sample_with_time_invalid : public rxo::operator_base> { + using type = observable, sample_with_time_invalid>; +}; +template +using sample_with_time_invalid_t = typename sample_with_time_invalid::type; + +template +struct sample_with_time +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxu::decay_t 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 + struct sample_with_time_observer + { + typedef sample_with_time_observer this_type; + typedef T value_type; + typedef rxu::decay_t dest_type; + typedef observer 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; + }; + std::shared_ptr 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(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> make(dest_type d, sample_with_time_value v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return make_subscriber(cs, this_type(cs, std::move(d), std::move(v), std::move(coordinator))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(sample_with_time_observer::make(std::move(dest), initial)) { + return sample_with_time_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-sample_time.hpp +*/ +template +auto sample_with_time(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class SampleWithTime = rxo::detail::sample_with_time, identity_one_worker>> + static auto member(Observable&& o, Duration&& d) + -> decltype(o.template lift(SampleWithTime(std::forward(d), identity_current_thread()))) { + return o.template lift(SampleWithTime(std::forward(d), identity_current_thread())); + } + + template, + is_coordination, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class SampleWithTime = rxo::detail::sample_with_time, rxu::decay_t>> + static auto member(Observable&& o, Coordination&& cn, Duration&& d) + -> decltype(o.template lift(SampleWithTime(std::forward(d), std::forward(cn)))) { + return o.template lift(SampleWithTime(std::forward(d), std::forward(cn))); + } + + template, + is_coordination, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class SampleWithTime = rxo::detail::sample_with_time, rxu::decay_t>> + static auto member(Observable&& o, Duration&& d, Coordination&& cn) + -> decltype(o.template lift(SampleWithTime(std::forward(d), std::forward(cn)))) { + return o.template lift(SampleWithTime(std::forward(d), std::forward(cn))); + } + + template + static operators::detail::sample_with_time_invalid_t 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 diff --git a/3party/rxcpp/operators/rx-scan.hpp b/3party/rxcpp/operators/rx-scan.hpp new file mode 100644 index 0000000..73bcd87 --- /dev/null +++ b/3party/rxcpp/operators/rx-scan.hpp @@ -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 +struct scan_invalid_arguments {}; + +template +struct scan_invalid : public rxo::operator_base> { + using type = observable, scan_invalid>; +}; +template +using scan_invalid_t = typename scan_invalid::type; + +template +struct scan : public operator_base> +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t accumulator_type; + typedef rxu::decay_t 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 + void on_subscribe(Subscriber o) const { + struct scan_state_type + : public scan_initial_type + , public std::enable_shared_from_this + { + 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(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 +auto scan(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + is_accumulate_function_for, rxu::decay_t, rxu::decay_t>>, + class SourceValue = rxu::value_type_t, + class Scan = rxo::detail::scan, rxu::decay_t, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Seed s, Accumulator&& a) { + return Result(Scan(std::forward(o), std::forward(a), s)); + } + + template + static operators::detail::scan_invalid_t 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 diff --git a/3party/rxcpp/operators/rx-sequence_equal.hpp b/3party/rxcpp/operators/rx-sequence_equal.hpp new file mode 100644 index 0000000..3350e44 --- /dev/null +++ b/3party/rxcpp/operators/rx-sequence_equal.hpp @@ -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 +struct sequence_equal_invalid_arguments {}; + +template +struct sequence_equal_invalid : public rxo::operator_base> { + using type = observable, sequence_equal_invalid>; +}; +template +using sequence_equal_invalid_t = typename sequence_equal_invalid::type; + +template +struct sequence_equal : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t other_source_type; + typedef typename other_source_type::value_type other_source_value_type; + typedef rxu::decay_t predicate_type; + typedef rxu::decay_t 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 + void on_subscribe(Subscriber s) const { + + typedef Subscriber output_type; + + struct state_type + : public std::enable_shared_from_this + , 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_values; + mutable std::list other_values; + mutable bool source_completed; + mutable bool other_completed; + }; + + auto coordinator = initial.coordination.create_coordinator(); + auto state = std::make_shared(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( + 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 +auto sequence_equal(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + is_observable>, + class SourceValue = rxu::value_type_t, + class SequenceEqual = rxo::detail::sequence_equal, rxu::decay_t, rxu::equal_to<>, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, OtherObservable&& t) { + return Result(SequenceEqual(std::forward(o), std::forward(t), rxu::equal_to<>(), identity_current_thread())); + } + + template, + class Enabled = rxu::enable_if_all_true_type_t< + is_observable, + is_observable, + rxu::negation>, + class SourceValue = rxu::value_type_t, + class SequenceEqual = rxo::detail::sequence_equal, rxu::decay_t, rxu::decay_t, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, OtherObservable&& t, BinaryPredicate&& pred) { + return Result(SequenceEqual(std::forward(o), std::forward(t), std::forward(pred), identity_current_thread())); + } + + template, + is_observable, + is_coordination>, + class SourceValue = rxu::value_type_t, + class SequenceEqual = rxo::detail::sequence_equal, rxu::decay_t, rxu::equal_to<>, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, OtherObservable&& t, Coordination&& cn) { + return Result(SequenceEqual(std::forward(o), std::forward(t), rxu::equal_to<>(), std::forward(cn))); + } + + template, + is_observable, + is_coordination>, + class SourceValue = rxu::value_type_t, + class SequenceEqual = rxo::detail::sequence_equal, rxu::decay_t, rxu::decay_t, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, OtherObservable&& t, BinaryPredicate&& pred, Coordination&& cn) { + return Result(SequenceEqual(std::forward(o), std::forward(t), std::forward(pred), std::forward(cn))); + } + + template + static operators::detail::sequence_equal_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "sequence_equal takes (OtherObservable, optional BinaryPredicate, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-skip.hpp b/3party/rxcpp/operators/rx-skip.hpp new file mode 100644 index 0000000..b77c4da --- /dev/null +++ b/3party/rxcpp/operators/rx-skip.hpp @@ -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 +struct skip_invalid_arguments {}; + +template +struct skip_invalid : public rxo::operator_base> { + using type = observable, skip_invalid>; +}; + +template +using skip_invalid_t = typename skip_invalid::type; + +template +struct skip : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t 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 + void on_subscribe(const Subscriber& s) const { + + typedef Subscriber output_type; + struct state_type + : public std::enable_shared_from_this + , 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(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 +auto skip(AN&&... an) +-> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Skip = rxo::detail::skip, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Count&& c) { + return Result(Skip(std::forward(o), std::forward(c))); + } + + template + static operators::detail::skip_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "skip takes (optional Count)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-skip_last.hpp b/3party/rxcpp/operators/rx-skip_last.hpp new file mode 100644 index 0000000..9594715 --- /dev/null +++ b/3party/rxcpp/operators/rx-skip_last.hpp @@ -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 +struct skip_last_invalid_arguments {}; + +template +struct skip_last_invalid : public rxo::operator_base> { + using type = observable, skip_last_invalid>; +}; +template +using skip_last_invalid_t = typename skip_last_invalid::type; + +template +struct skip_last : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t count_type; + + typedef std::queue 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(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 + void on_subscribe(const Subscriber& s) const { + + typedef Subscriber output_type; + struct state_type + : public std::enable_shared_from_this + , 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(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 +auto skip_last(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class SkipLast = rxo::detail::skip_last, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Count&& c) { + return Result(SkipLast(std::forward(o), std::forward(c))); + } + + template + static operators::detail::skip_last_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "skip_last takes (Count)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-skip_until.hpp b/3party/rxcpp/operators/rx-skip_until.hpp new file mode 100644 index 0000000..4df6671 --- /dev/null +++ b/3party/rxcpp/operators/rx-skip_until.hpp @@ -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 +struct skip_until_invalid_arguments {}; + +template +struct skip_until_invalid : public rxo::operator_base> { + using type = observable, skip_until_invalid>; +}; +template +using skip_until_invalid_t = typename skip_until_invalid::type; + +template +struct skip_until : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t trigger_source_type; + typedef rxu::decay_t 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 + void on_subscribe(Subscriber s) const { + + typedef Subscriber output_type; + struct state_type + : public std::enable_shared_from_this + , 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(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( + // 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 +auto skip_until(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class Timer = typename rxu::defer_type::type, + class TimerValue = rxu::value_type_t, + class TriggerObservable = observable, + class SkipUntil = rxo::detail::skip_until, TriggerObservable, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, TimePoint&& when) { + auto cn = identity_current_thread(); + return Result(SkipUntil(std::forward(o), rxs::timer(std::forward(when), cn), cn)); + } + + template, + is_coordination, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class Timer = typename rxu::defer_type>::type, + class TimerValue = rxu::value_type_t, + class TriggerObservable = observable, + class SkipUntil = rxo::detail::skip_until, TriggerObservable, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, TimePoint&& when, Coordination cn) { + return Result(SkipUntil(std::forward(o), rxs::timer(std::forward(when), cn), cn)); + } + + template>, + class SourceValue = rxu::value_type_t, + class SkipUntil = rxo::detail::skip_until, rxu::decay_t, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, TriggerObservable&& t) { + return Result(SkipUntil(std::forward(o), std::forward(t), identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class SkipUntil = rxo::detail::skip_until, rxu::decay_t, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, TriggerObservable&& t, Coordination&& cn) { + return Result(SkipUntil(std::forward(o), std::forward(t), std::forward(cn))); + } + + template + static operators::detail::skip_until_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "skip_until takes (TriggerObservable, optional Coordination) or (TimePoint, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-skip_while.hpp b/3party/rxcpp/operators/rx-skip_while.hpp new file mode 100644 index 0000000..643d867 --- /dev/null +++ b/3party/rxcpp/operators/rx-skip_while.hpp @@ -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 +struct skip_while_invalid_arguments {}; + +template +struct skip_while_invalid : public rxo::operator_base> { + using type = observable, skip_while_invalid>; +}; +template +using skip_while_invalid_t = typename skip_while_invalid::type; + +template +struct skip_while +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t test_type; + test_type test; + + + skip_while(test_type t) + : test(std::move(t)) + { + } + + template + struct skip_while_observer + { + typedef skip_while_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, test_type t) { + return make_subscriber(d, this_type(d, std::move(t))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(skip_while_observer::make(std::move(dest), test)) { + return skip_while_observer::make(std::move(dest), test); + } +}; + +} + +/*! @copydoc rx-skip_while.hpp +*/ +template +auto skip_while(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); + } + +} + +template<> +struct member_overload +{ + template, + class TakeWhile = rxo::detail::skip_while>> + static auto member(Observable&& o, Predicate&& p) + -> decltype(o.template lift(TakeWhile(std::forward(p)))) { + return o.template lift(TakeWhile(std::forward(p))); + } + + template + static operators::detail::skip_while_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "skip_while takes (Predicate)"); + } +}; + + +} + +#endif diff --git a/3party/rxcpp/operators/rx-start_with.hpp b/3party/rxcpp/operators/rx-start_with.hpp new file mode 100644 index 0000000..4203631 --- /dev/null +++ b/3party/rxcpp/operators/rx-start_with.hpp @@ -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::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 +struct start_with_invalid_arguments {}; + +template +struct start_with_invalid : public rxo::operator_base> { + using type = observable, start_with_invalid>; +}; +template +using start_with_invalid_t = typename start_with_invalid::type; + +} + +/*! @copydoc rx-start_with.hpp +*/ +template +auto start_with(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class From = decltype(rxs::from(rxu::decay_t(std::declval()), rxu::decay_t(std::declval())...)) + > + static auto member(Observable&& o, Value0&& v0, ValueN&&... vn) + -> decltype(member_overload::member(std::declval(), std::forward(o))) { + auto first = rxs::from(rxu::decay_t(v0), rxu::decay_t(vn)...); + return member_overload::member(first, std::forward(o)); + } + + template + static operators::detail::start_with_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "start_with takes (Value0, optional ValueN...)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-subscribe.hpp b/3party/rxcpp/operators/rx-subscribe.hpp new file mode 100644 index 0000000..c3e3917 --- /dev/null +++ b/3party/rxcpp/operators/rx-subscribe.hpp @@ -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 subscribe_factory; + +template +class subscribe_factory> +{ + subscriber scrbr; +public: + subscribe_factory(subscriber s) + : scrbr(std::move(s)) + {} + template + auto operator()(Observable&& source) + -> decltype(std::forward(source).subscribe(std::move(scrbr))) { + return std::forward(source).subscribe(std::move(scrbr)); + } +}; + +} + +/*! @copydoc rx-subscribe.hpp +*/ +template +auto subscribe(ArgN&&... an) + -> detail::subscribe_factory(std::forward(an)...))> { + return detail::subscribe_factory(std::forward(an)...))> + (make_subscriber(std::forward(an)...)); +} + +namespace detail { + +class dynamic_factory +{ +public: + template + auto operator()(Observable&& source) + -> observable>> { + return observable>>(std::forward(source)); + } +}; + +} + +/*! Return a new observable that performs type-forgetting conversion of this observable. + + \return The source observable converted to observable. + + \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 + auto operator()(Observable&& source) + -> decltype(std::forward(source).as_blocking()) { + return std::forward(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 diff --git a/3party/rxcpp/operators/rx-subscribe_on.hpp b/3party/rxcpp/operators/rx-subscribe_on.hpp new file mode 100644 index 0000000..f026b59 --- /dev/null +++ b/3party/rxcpp/operators/rx-subscribe_on.hpp @@ -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 +struct subscribe_on_invalid_arguments {}; + +template +struct subscribe_on_invalid : public rxo::operator_base> { + using type = observable, subscribe_on_invalid>; +}; +template +using subscribe_on_invalid_t = typename subscribe_on_invalid::type; + +template +struct subscribe_on : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t 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 + void on_subscribe(Subscriber s) const { + + typedef Subscriber output_type; + struct subscribe_on_state_type + : public std::enable_shared_from_this + , 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(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 +auto subscribe_on(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class SubscribeOn = rxo::detail::subscribe_on, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Coordination&& cn) { + return Result(SubscribeOn(std::forward(o), std::forward(cn))); + } + + template + static operators::detail::subscribe_on_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "subscribe_on takes (Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-switch_if_empty.hpp b/3party/rxcpp/operators/rx-switch_if_empty.hpp new file mode 100644 index 0000000..8d3e57e --- /dev/null +++ b/3party/rxcpp/operators/rx-switch_if_empty.hpp @@ -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 +struct switch_if_empty_invalid_arguments {}; + +template +struct switch_if_empty_invalid : public rxo::operator_base> { + using type = observable, switch_if_empty_invalid>; +}; +template +using switch_if_empty_invalid_t = typename switch_if_empty_invalid::type; + +template +struct switch_if_empty +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t backup_source_type; + + backup_source_type backup; + + switch_if_empty(backup_source_type b) + : backup(std::move(b)) + { + } + + template + struct switch_if_empty_observer + { + typedef switch_if_empty_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, backup_source_type b) { + auto cs = composite_subscription(); + return make_subscriber(cs, observer_type(this_type(std::move(d), cs, std::move(b)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(switch_if_empty_observer::make(std::move(dest), std::move(backup))) { + return switch_if_empty_observer::make(std::move(dest), std::move(backup)); + } +}; + +} + +/*! @copydoc rx-switch_if_empty.hpp +*/ +template +auto switch_if_empty(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(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 +auto default_if_empty(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class SwitchIfEmpty = rxo::detail::switch_if_empty>> + static auto member(Observable&& o, BackupSource&& b) + -> decltype(o.template lift(SwitchIfEmpty(std::forward(b)))) { + return o.template lift(SwitchIfEmpty(std::forward(b))); + } + + template + static operators::detail::switch_if_empty_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "switch_if_empty takes (BackupSource)"); + } +}; + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class BackupSource = decltype(rxs::from(std::declval())), + class DefaultIfEmpty = rxo::detail::switch_if_empty> + static auto member(Observable&& o, Value v) + -> decltype(o.template lift(DefaultIfEmpty(rxs::from(std::move(v))))) { + return o.template lift(DefaultIfEmpty(rxs::from(std::move(v)))); + } + + template + static operators::detail::switch_if_empty_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "default_if_empty takes (Value)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-switch_on_next.hpp b/3party/rxcpp/operators/rx-switch_on_next.hpp new file mode 100644 index 0000000..4572121 --- /dev/null +++ b/3party/rxcpp/operators/rx-switch_on_next.hpp @@ -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 +struct switch_on_next_invalid_arguments {}; + +template +struct switch_on_next_invalid : public rxo::operator_base> { + using type = observable, switch_on_next_invalid>; +}; +template +using switch_on_next_invalid_t = typename switch_on_next_invalid::type; + +template +struct switch_on_next + : public operator_base>> +{ + //static_assert(is_observable::value, "switch_on_next requires an observable"); + //static_assert(is_observable::value, "switch_on_next requires an observable that contains observables"); + + typedef switch_on_next this_type; + + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t 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_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 + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct switch_state_type + : public std::enable_shared_from_this + , 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; + // 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(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( + 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( + 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 +auto switch_on_next(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class SwitchOnNext = rxo::detail::switch_on_next, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o) { + return Result(SwitchOnNext(std::forward(o), identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class SwitchOnNext = rxo::detail::switch_on_next, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable + > + static Result member(Observable&& o, Coordination&& cn) { + return Result(SwitchOnNext(std::forward(o), std::forward(cn))); + } + + template + static operators::detail::switch_on_next_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "switch_on_next takes (optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-take.hpp b/3party/rxcpp/operators/rx-take.hpp new file mode 100644 index 0000000..1e4da4d --- /dev/null +++ b/3party/rxcpp/operators/rx-take.hpp @@ -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 +struct take_invalid_arguments {}; + +template +struct take_invalid : public rxo::operator_base> { + using type = observable, take_invalid>; +}; +template +using take_invalid_t = typename take_invalid::type; + +template +struct take : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t 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 + void on_subscribe(const Subscriber& s) const { + + typedef Subscriber output_type; + struct state_type + : public std::enable_shared_from_this + , 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(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 +auto take(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Take = rxo::detail::take, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Count&& c) { + return Result(Take(std::forward(o), std::forward(c))); + } + + template + static operators::detail::take_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "take takes (optional Count)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-take_last.hpp b/3party/rxcpp/operators/rx-take_last.hpp new file mode 100644 index 0000000..12e28b6 --- /dev/null +++ b/3party/rxcpp/operators/rx-take_last.hpp @@ -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 +struct take_last_invalid_arguments {}; + +template +struct take_last_invalid : public rxo::operator_base> { + using type = observable, take_last_invalid>; +}; +template +using take_last_invalid_t = typename take_last_invalid::type; + +template +struct take_last : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t count_type; + + typedef std::queue 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(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 + void on_subscribe(const Subscriber& s) const { + + typedef Subscriber output_type; + struct state_type + : public std::enable_shared_from_this + , 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(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 +auto take_last(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class TakeLast = rxo::detail::take_last, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Count&& c) { + return Result(TakeLast(std::forward(o), std::forward(c))); + } + + template + static operators::detail::take_last_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "take_last takes (Count)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-take_until.hpp b/3party/rxcpp/operators/rx-take_until.hpp new file mode 100644 index 0000000..3fd9b71 --- /dev/null +++ b/3party/rxcpp/operators/rx-take_until.hpp @@ -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 +struct take_until_invalid_arguments {}; + +template +struct take_until_invalid : public rxo::operator_base> { + using type = observable, take_until_invalid>; +}; +template +using take_until_invalid_t = typename take_until_invalid::type; + +template +struct take_until : public operator_base +{ + typedef rxu::decay_t source_type; + typedef rxu::decay_t trigger_source_type; + typedef rxu::decay_t 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 + void on_subscribe(Subscriber s) const { + + typedef Subscriber output_type; + struct take_until_state_type + : public std::enable_shared_from_this + , 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(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( + // 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( + // 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 +auto take_until(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class Timer = typename rxu::defer_type::type, + class TimerValue = rxu::value_type_t, + class TriggerObservable = observable, + class TakeUntil = rxo::detail::take_until, TriggerObservable, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, TimePoint&& when) { + auto cn = identity_current_thread(); + return Result(TakeUntil(std::forward(o), rxs::timer(std::forward(when), cn), cn)); + } + + template, + is_coordination, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class Timer = typename rxu::defer_type>::type, + class TimerValue = rxu::value_type_t, + class TriggerObservable = observable, + class TakeUntil = rxo::detail::take_until, TriggerObservable, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, TimePoint&& when, Coordination cn) { + return Result(TakeUntil(std::forward(o), rxs::timer(std::forward(when), cn), cn)); + } + + template>, + class SourceValue = rxu::value_type_t, + class TakeUntil = rxo::detail::take_until, rxu::decay_t, identity_one_worker>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, TriggerObservable&& t) { + return Result(TakeUntil(std::forward(o), std::forward(t), identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class TakeUntil = rxo::detail::take_until, rxu::decay_t, rxu::decay_t>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, TriggerObservable&& t, Coordination&& cn) { + return Result(TakeUntil(std::forward(o), std::forward(t), std::forward(cn))); + } + + template + static operators::detail::take_until_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "take_until takes (TriggerObservable, optional Coordination) or (TimePoint, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-take_while.hpp b/3party/rxcpp/operators/rx-take_while.hpp new file mode 100644 index 0000000..85630e7 --- /dev/null +++ b/3party/rxcpp/operators/rx-take_while.hpp @@ -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 +struct take_while_invalid_arguments {}; + +template +struct take_while_invalid : public rxo::operator_base> { + using type = observable, take_while_invalid>; +}; +template +using take_while_invalid_t = typename take_while_invalid::type; + +template +struct take_while +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t test_type; + test_type test; + + + take_while(test_type t) + : test(std::move(t)) + { + } + + template + struct take_while_observer + { + typedef take_while_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, test_type t) { + return make_subscriber(d, this_type(d, std::move(t))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(take_while_observer::make(std::move(dest), test)) { + return take_while_observer::make(std::move(dest), test); + } +}; + +} + +/*! @copydoc rx-take_while.hpp +*/ +template +auto take_while(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); + } + +} + +template<> +struct member_overload +{ + template, + class TakeWhile = rxo::detail::take_while>> + static auto member(Observable&& o, Predicate&& p) + -> decltype(o.template lift(TakeWhile(std::forward(p)))) { + return o.template lift(TakeWhile(std::forward(p))); + } + + template + static operators::detail::take_while_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "take_while takes (Predicate)"); + } +}; + + +} + +#endif diff --git a/3party/rxcpp/operators/rx-tap.hpp b/3party/rxcpp/operators/rx-tap.hpp new file mode 100644 index 0000000..550163a --- /dev/null +++ b/3party/rxcpp/operators/rx-tap.hpp @@ -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 +struct tap_invalid_arguments {}; + +template +struct tap_invalid : public rxo::operator_base> { + using type = observable, tap_invalid>; +}; +template +using tap_invalid_t = typename tap_invalid::type; + +template +struct tap_observer_factory; + +template +struct tap_observer_factory> +{ + using source_value_type = rxu::decay_t; + using out_type = decltype(make_observer(*((ArgN*)nullptr)...)); + auto operator()(ArgN&&... an) -> out_type const { + return make_observer(std::forward(an)...); + } +}; + +template> +struct tap +{ + using source_value_type = rxu::decay_t; + using args_type = rxu::decay_t; + 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 + struct tap_observer + { + using this_type = tap_observer; + using value_type = source_value_type; + using dest_type = rxu::decay_t; + using factory_type = Factory; + using out_type = typename factory_type::out_type; + using observer_type = observer; + 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> make(dest_type d, out_type o) { + return make_subscriber(d, this_type(d, std::move(o))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(tap_observer::make(std::move(dest), out)) { + return tap_observer::make(std::move(dest), out); + } +}; + +} + +/*! @copydoc rx-tap.hpp +*/ +template +auto tap(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Tap = rxo::detail::tap...>>> + static auto member(Observable&& o, MakeObserverArgN&&... an) + -> decltype(o.template lift(Tap(std::make_tuple(std::forward(an)...)))) { + return o.template lift(Tap(std::make_tuple(std::forward(an)...))); + } + + template + static operators::detail::tap_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "tap takes (MakeObserverArgN...)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-time_interval.hpp b/3party/rxcpp/operators/rx-time_interval.hpp new file mode 100644 index 0000000..1a4c9a5 --- /dev/null +++ b/3party/rxcpp/operators/rx-time_interval.hpp @@ -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 +struct time_interval_invalid_arguments {}; + +template +struct time_interval_invalid : public rxo::operator_base> { + using type = observable, time_interval_invalid>; +}; +template +using time_interval_invalid_t = typename time_interval_invalid::type; + +template +struct time_interval +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t 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 + struct time_interval_observer + { + typedef time_interval_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, time_interval_values v) { + return make_subscriber(d, this_type(d, v.coordination)); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(time_interval_observer::make(std::move(dest), initial)) { + return time_interval_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-time_interval.hpp +*/ +template +auto time_interval(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class TimeInterval = rxo::detail::time_interval, + class Value = typename rxsc::scheduler::clock_type::time_point::duration> + static auto member(Observable&& o) + -> decltype(o.template lift(TimeInterval(identity_current_thread()))) { + return o.template lift(TimeInterval(identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class TimeInterval = rxo::detail::time_interval>, + class Value = typename rxsc::scheduler::clock_type::time_point::duration> + static auto member(Observable&& o, Coordination&& cn) + -> decltype(o.template lift(TimeInterval(std::forward(cn)))) { + return o.template lift(TimeInterval(std::forward(cn))); + } + + template + static operators::detail::time_interval_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "time_interval takes (optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-timeout.hpp b/3party/rxcpp/operators/rx-timeout.hpp new file mode 100644 index 0000000..d100fa4 --- /dev/null +++ b/3party/rxcpp/operators/rx-timeout.hpp @@ -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 +struct timeout_invalid_arguments {}; + +template +struct timeout_invalid : public rxo::operator_base> { + using type = observable, timeout_invalid>; +}; +template +using timeout_invalid_t = typename timeout_invalid::type; + +template +struct timeout +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxu::decay_t 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 + struct timeout_observer + { + typedef timeout_observer this_type; + typedef rxu::decay_t value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 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(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 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(); + } + + return std::function(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 make(dest_type d, timeout_values v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return make_subscriber(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(timeout_observer::make(std::move(dest), initial)) { + return timeout_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-timeout.hpp +*/ +template +auto timeout(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class Timeout = rxo::detail::timeout, identity_one_worker>> + static auto member(Observable&& o, Duration&& d) + -> decltype(o.template lift(Timeout(std::forward(d), identity_current_thread()))) { + return o.template lift(Timeout(std::forward(d), identity_current_thread())); + } + + template, + is_coordination, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class Timeout = rxo::detail::timeout, rxu::decay_t>> + static auto member(Observable&& o, Coordination&& cn, Duration&& d) + -> decltype(o.template lift(Timeout(std::forward(d), std::forward(cn)))) { + return o.template lift(Timeout(std::forward(d), std::forward(cn))); + } + + template, + is_coordination, + rxu::is_duration>, + class SourceValue = rxu::value_type_t, + class Timeout = rxo::detail::timeout, rxu::decay_t>> + static auto member(Observable&& o, Duration&& d, Coordination&& cn) + -> decltype(o.template lift(Timeout(std::forward(d), std::forward(cn)))) { + return o.template lift(Timeout(std::forward(d), std::forward(cn))); + } + + template + static operators::detail::timeout_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "timeout takes (optional Coordination, required Duration) or (required Duration, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-timestamp.hpp b/3party/rxcpp/operators/rx-timestamp.hpp new file mode 100644 index 0000000..923cf5d --- /dev/null +++ b/3party/rxcpp/operators/rx-timestamp.hpp @@ -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 +struct timestamp_invalid_arguments {}; + +template +struct timestamp_invalid : public rxo::operator_base> { + using type = observable, timestamp_invalid>; +}; +template +using timestamp_invalid_t = typename timestamp_invalid::type; + +template +struct timestamp +{ + typedef rxu::decay_t source_value_type; + typedef rxu::decay_t 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 + struct timestamp_observer + { + typedef timestamp_observer this_type; + typedef source_value_type value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 make(dest_type d, timestamp_values v) { + return make_subscriber(d, this_type(d, v.coordination)); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(timestamp_observer::make(std::move(dest), initial)) { + return timestamp_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-timestamp.hpp +*/ +template +auto timestamp(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Timestamp = rxo::detail::timestamp, + class Clock = typename rxsc::scheduler::clock_type::time_point, + class Value = std::pair> + static auto member(Observable&& o) + -> decltype(o.template lift(Timestamp(identity_current_thread()))) { + return o.template lift(Timestamp(identity_current_thread())); + } + + template, + is_coordination>, + class SourceValue = rxu::value_type_t, + class Timestamp = rxo::detail::timestamp>, + class Clock = typename rxsc::scheduler::clock_type::time_point, + class Value = std::pair> + static auto member(Observable&& o, Coordination&& cn) + -> decltype(o.template lift(Timestamp(std::forward(cn)))) { + return o.template lift(Timestamp(std::forward(cn))); + } + + template + static operators::detail::timestamp_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "timestamp takes (optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-window.hpp b/3party/rxcpp/operators/rx-window.hpp new file mode 100644 index 0000000..e033a84 --- /dev/null +++ b/3party/rxcpp/operators/rx-window.hpp @@ -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 +struct window_invalid_arguments {}; + +template +struct window_invalid : public rxo::operator_base> { + using type = observable, window_invalid>; +}; +template +using window_invalid_t = typename window_invalid::type; + +template +struct window +{ + typedef rxu::decay_t source_value_type; + typedef observable 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 + struct window_observer : public window_values + { + typedef window_observer this_type; + typedef rxu::decay_t value_type; + typedef rxu::decay_t dest_type; + typedef observer observer_type; + dest_type dest; + mutable int cursor; + mutable std::deque> subj; + + window_observer(dest_type d, window_values v) + : window_values(v) + , dest(std::move(d)) + , cursor(0) + { + subj.push_back(rxcpp::subjects::subject()); + 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()); + 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 make(dest_type d, window_values v) { + auto cs = d.get_subscription(); + return make_subscriber(std::move(cs), observer_type(this_type(std::move(d), std::move(v)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(window_observer::make(std::move(dest), initial)) { + return window_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-window.hpp +*/ +template +auto window(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class SourceValue = rxu::value_type_t, + class Window = rxo::detail::window, + class Value = rxu::value_type_t> + static auto member(Observable&& o, int count, int skip) + -> decltype(o.template lift(Window(count, skip))) { + return o.template lift(Window(count, skip)); + } + + template>, + class SourceValue = rxu::value_type_t, + class Window = rxo::detail::window, + class Value = rxu::value_type_t> + static auto member(Observable&& o, int count) + -> decltype(o.template lift(Window(count, count))) { + return o.template lift(Window(count, count)); + } + + template + static operators::detail::window_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "window takes (Count, optional Skip)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-window_time.hpp b/3party/rxcpp/operators/rx-window_time.hpp new file mode 100644 index 0000000..57f7572 --- /dev/null +++ b/3party/rxcpp/operators/rx-window_time.hpp @@ -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 +struct window_with_time_invalid_arguments {}; + +template +struct window_with_time_invalid : public rxo::operator_base> { + using type = observable, window_with_time_invalid>; +}; +template +using window_with_time_invalid_t = typename window_with_time_invalid::type; + +template +struct window_with_time +{ + typedef rxu::decay_t source_value_type; + typedef observable value_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxu::decay_t 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 + struct window_with_time_observer + { + typedef window_with_time_observer this_type; + typedef rxu::decay_t value_type; + typedef rxu::decay_t dest_type; + typedef observer 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> subj; + rxsc::scheduler::clock_type::time_point expected; + }; + std::shared_ptr 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(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()); + 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 make(dest_type d, window_with_time_values v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return make_subscriber(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(window_with_time_observer::make(std::move(dest), initial)) { + return window_with_time_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-window_time.hpp +*/ +template +auto window_with_time(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class WindowWithTime = rxo::detail::window_with_time, identity_one_worker>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration period) + -> decltype(o.template lift(WindowWithTime(period, period, identity_current_thread()))) { + return o.template lift(WindowWithTime(period, period, identity_current_thread())); + } + + template, + std::is_convertible, + is_coordination>, + class SourceValue = rxu::value_type_t, + class WindowWithTime = rxo::detail::window_with_time, rxu::decay_t>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration period, Coordination&& cn) + -> decltype(o.template lift(WindowWithTime(period, period, std::forward(cn)))) { + return o.template lift(WindowWithTime(period, period, std::forward(cn))); + } + + template, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class WindowWithTime = rxo::detail::window_with_time, identity_one_worker>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration&& period, Duration&& skip) + -> decltype(o.template lift(WindowWithTime(std::forward(period), std::forward(skip), identity_current_thread()))) { + return o.template lift(WindowWithTime(std::forward(period), std::forward(skip), identity_current_thread())); + } + + template, + std::is_convertible, + is_coordination>, + class SourceValue = rxu::value_type_t, + class WindowWithTime = rxo::detail::window_with_time, rxu::decay_t>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration&& period, Duration&& skip, Coordination&& cn) + -> decltype(o.template lift(WindowWithTime(std::forward(period), std::forward(skip), std::forward(cn)))) { + return o.template lift(WindowWithTime(std::forward(period), std::forward(skip), std::forward(cn))); + } + + template + static operators::detail::window_with_time_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "window_with_time takes (Duration, optional Duration, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-window_time_count.hpp b/3party/rxcpp/operators/rx-window_time_count.hpp new file mode 100644 index 0000000..9375737 --- /dev/null +++ b/3party/rxcpp/operators/rx-window_time_count.hpp @@ -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 +struct window_with_time_or_count_invalid_arguments {}; + +template +struct window_with_time_or_count_invalid : public rxo::operator_base> { + using type = observable, window_with_time_or_count_invalid>; +}; +template +using window_with_time_or_count_invalid_t = typename window_with_time_or_count_invalid::type; + +template +struct window_with_time_or_count +{ + typedef rxu::decay_t source_value_type; + typedef observable value_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxu::decay_t 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 + struct window_with_time_or_count_observer + { + typedef window_with_time_or_count_observer this_type; + typedef rxu::decay_t value_type; + typedef rxu::decay_t dest_type; + typedef observer 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 subj; + }; + typedef std::shared_ptr 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(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 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(); + 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(); + } + + return std::function(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 make(dest_type d, window_with_time_or_count_values v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return make_subscriber(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(window_with_time_or_count_observer::make(std::move(dest), initial)) { + return window_with_time_or_count_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-window_time_count.hpp +*/ +template +auto window_with_time_or_count(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + std::is_convertible>, + class SourceValue = rxu::value_type_t, + class WindowTimeCount = rxo::detail::window_with_time_or_count, identity_one_worker>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration&& period, int count) + -> decltype(o.template lift(WindowTimeCount(std::forward(period), count, identity_current_thread()))) { + return o.template lift(WindowTimeCount(std::forward(period), count, identity_current_thread())); + } + + template, + std::is_convertible, + is_coordination>, + class SourceValue = rxu::value_type_t, + class WindowTimeCount = rxo::detail::window_with_time_or_count, rxu::decay_t>, + class Value = rxu::value_type_t> + static auto member(Observable&& o, Duration&& period, int count, Coordination&& cn) + -> decltype(o.template lift(WindowTimeCount(std::forward(period), count, std::forward(cn)))) { + return o.template lift(WindowTimeCount(std::forward(period), count, std::forward(cn))); + } + + template + static operators::detail::window_with_time_or_count_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "window_with_time_or_count takes (Duration, Count, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-window_toggle.hpp b/3party/rxcpp/operators/rx-window_toggle.hpp new file mode 100644 index 0000000..b9f119a --- /dev/null +++ b/3party/rxcpp/operators/rx-window_toggle.hpp @@ -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 + \tparam ClosingSelector a function of type observable(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 +struct window_toggle_invalid_arguments {}; + +template +struct window_toggle_invalid : public rxo::operator_base> { + using type = observable, window_toggle_invalid>; +}; +template +using window_toggle_invalid_t = typename window_toggle_invalid::type; + +template +struct window_toggle +{ + typedef window_toggle this_type; + + using source_value_type = rxu::decay_t; + using coordination_type = rxu::decay_t; + using coordinator_type = typename coordination_type::coordinator_type; + using openings_type = rxu::decay_t; + using openings_value_type = typename openings_type::value_type; + using closing_selector_type = rxu::decay_t; + using closings_type = rxu::result_of_t; + 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 + struct window_toggle_observer + { + typedef window_toggle_observer this_type; + typedef rxu::decay_t value_type; + typedef rxu::decay_t dest_type; + typedef observer 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> subj; + }; + std::shared_ptr 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(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( + 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()); + 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(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( + 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 make(dest_type d, window_toggle_values v) { + auto cs = composite_subscription(); + auto coordinator = v.coordination.create_coordinator(d.get_subscription()); + + return make_subscriber(cs, observer_type(this_type(cs, std::move(d), std::move(v), std::move(coordinator)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(window_toggle_observer::make(std::move(dest), initial)) { + return window_toggle_observer::make(std::move(dest), initial); + } +}; + +} + +/*! @copydoc rx-window_toggle.hpp +*/ +template +auto window_toggle(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template, + class OpeningsType = rxu::decay_t, + class OpeningsValueType = typename OpeningsType::value_type, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables>>, + class SourceValue = rxu::value_type_t, + class WindowToggle = rxo::detail::window_toggle, rxu::decay_t, identity_one_worker>, + class Value = observable> + static auto member(Observable&& o, Openings&& openings, ClosingSelector&& closingSelector) + -> decltype(o.template lift(WindowToggle(std::forward(openings), std::forward(closingSelector), identity_immediate()))) { + return o.template lift(WindowToggle(std::forward(openings), std::forward(closingSelector), identity_immediate())); + } + + template, + class OpeningsType = rxu::decay_t, + class OpeningsValueType = typename OpeningsType::value_type, + class Enabled = rxu::enable_if_all_true_type_t< + all_observables>, + is_coordination>, + class SourceValue = rxu::value_type_t, + class WindowToggle = rxo::detail::window_toggle, rxu::decay_t, rxu::decay_t>, + class Value = observable> + static auto member(Observable&& o, Openings&& openings, ClosingSelector&& closingSelector, Coordination&& cn) + -> decltype(o.template lift(WindowToggle(std::forward(openings), std::forward(closingSelector), std::forward(cn)))) { + return o.template lift(WindowToggle(std::forward(openings), std::forward(closingSelector), std::forward(cn))); + } + + template + static operators::detail::window_toggle_invalid_t member(AN...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "window_toggle takes (Openings, ClosingSelector, optional Coordination)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-with_latest_from.hpp b/3party/rxcpp/operators/rx-with_latest_from.hpp new file mode 100644 index 0000000..616e5d8 --- /dev/null +++ b/3party/rxcpp/operators/rx-with_latest_from.hpp @@ -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 +struct with_latest_from_invalid_arguments {}; + +template +struct with_latest_from_invalid : public rxo::operator_base> { + using type = observable, with_latest_from_invalid>; +}; +template +using with_latest_from_invalid_t = typename with_latest_from_invalid::type; + +template +struct is_with_latest_from_selector_check { + typedef rxu::decay_t selector_type; + + struct tag_not_valid; + template + static auto check(int) -> decltype((*(CS*)nullptr)((*(typename CON::value_type*)nullptr)...)); + template + static tag_not_valid check(...); + + using type = decltype(check...>(0)); + + static const bool value = !std::is_same::value; +}; + +template +struct invalid_with_latest_from_selector { + static const bool value = false; +}; + +template +struct is_with_latest_from_selector : public std::conditional< + is_with_latest_from_selector_check::value, + is_with_latest_from_selector_check, + invalid_with_latest_from_selector>::type { +}; + +template +using result_with_latest_from_selector_t = typename is_with_latest_from_selector::type; + +template +struct with_latest_from_traits { + + typedef std::tuple tuple_source_type; + typedef std::tuple...> tuple_source_value_type; + + typedef rxu::decay_t selector_type; + typedef rxu::decay_t coordination_type; + + typedef typename is_with_latest_from_selector::type value_type; +}; + +template +struct with_latest_from : public operator_base>> +{ + typedef with_latest_from this_type; + + typedef with_latest_from_traits traits; + + typedef typename traits::tuple_source_type tuple_source_type; + typedef typename traits::tuple_source_value_type tuple_source_value_type; + + typedef typename traits::selector_type selector_type; + + typedef typename traits::coordination_type coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + + struct values + { + values(tuple_source_type o, selector_type s, coordination_type sf) + : source(std::move(o)) + , selector(std::move(s)) + , coordination(std::move(sf)) + { + } + tuple_source_type source; + selector_type selector; + coordination_type coordination; + }; + values initial; + + with_latest_from(coordination_type sf, selector_type s, tuple_source_type ts) + : initial(std::move(ts), std::move(s), std::move(sf)) + { + } + + template + void subscribe_one(std::shared_ptr state) const { + + typedef typename std::tuple_element::type::value_type source_value_type; + + composite_subscription innercs; + + // when the out observer is unsubscribed all the + // inner subscriptions are unsubscribed as well + state->out.add(innercs); + + auto source = on_exception( + [&](){return state->coordinator.in(std::get(state->source));}, + state->out); + if (source.empty()) { + return; + } + + // this subscribe does not share the observer subscription + // so that when it is unsubscribed the observer can be called + // until the inner subscriptions have finished + auto sink = make_subscriber( + state->out, + innercs, + // on_next + [state](source_value_type st) { + auto& value = std::get(state->latest); + + if (value.empty()) { + ++state->valuesSet; + } + + value.reset(st); + + if (state->valuesSet == sizeof... (ObservableN) && Index == 0) { + auto values = rxu::surely(state->latest); + auto selectedResult = rxu::apply(values, state->selector); + state->out.on_next(selectedResult); + } + }, + // on_error + [state](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 + void subscribe_all(std::shared_ptr state, rxu::values) const { + bool subscribed[] = {(subscribe_one<(sizeof...(IndexN)) - 1 - IndexN>(state), true)...}; + subscribed[0] = (*subscribed); // silence warning + } + + template + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct with_latest_from_state_type + : public std::enable_shared_from_this + , public values + { + with_latest_from_state_type(values i, coordinator_type coor, output_type oarg) + : values(std::move(i)) + , pendingCompletions(sizeof... (ObservableN)) + , valuesSet(0) + , coordinator(std::move(coor)) + , out(std::move(oarg)) + { + } + + // on_completed on the output must wait until all the + // subscriptions have received on_completed + mutable int pendingCompletions; + mutable int valuesSet; + mutable tuple_source_value_type latest; + coordinator_type coordinator; + output_type out; + }; + + auto coordinator = initial.coordination.create_coordinator(scbr.get_subscription()); + + // take a copy of the values for each subscription + auto state = std::make_shared(initial, std::move(coordinator), std::move(scbr)); + + subscribe_all(state, typename rxu::values_from::type()); + } +}; + +} + +/*! @copydoc rx-with_latest_from.hpp +*/ +template +auto with_latest_from(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class with_latest_from = rxo::detail::with_latest_from, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, ObservableN&&... on) + { + return Result(with_latest_from(identity_current_thread(), rxu::pack(), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + all_observables>, + class ResolvedSelector = rxu::decay_t, + class with_latest_from = rxo::detail::with_latest_from, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Selector&& s, ObservableN&&... on) + { + return Result(with_latest_from(identity_current_thread(), std::forward(s), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + all_observables>, + class with_latest_from = rxo::detail::with_latest_from, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Coordination&& cn, ObservableN&&... on) + { + return Result(with_latest_from(std::forward(cn), rxu::pack(), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + operators::detail::is_with_latest_from_selector, + all_observables>, + class ResolvedSelector = rxu::decay_t, + class with_latest_from = rxo::detail::with_latest_from, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Coordination&& cn, Selector&& s, ObservableN&&... on) + { + return Result(with_latest_from(std::forward(cn), std::forward(s), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template + static operators::detail::with_latest_from_invalid_t member(const AN&...) { + std::terminate(); + return {}; + static_assert(sizeof...(AN) == 10000, "with_latest_from takes (optional Coordination, optional Selector, required Observable, optional Observable...), Selector takes (Observable::value_type...)"); + } +}; + +} + +#endif diff --git a/3party/rxcpp/operators/rx-zip.hpp b/3party/rxcpp/operators/rx-zip.hpp new file mode 100644 index 0000000..b8169fd --- /dev/null +++ b/3party/rxcpp/operators/rx-zip.hpp @@ -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 +struct zip_source_state +{ + using value_type = rxu::value_type_t; + zip_source_state() + : completed(false) + { + } + std::list values; + bool completed; +}; + +struct values_not_empty { + template + bool operator()(zip_source_state& source) const { + return !source.values.empty(); + } +}; + +struct source_completed_values_empty { + template + bool operator()(zip_source_state& source) const { + return source.completed && source.values.empty(); + } +}; + +struct extract_value_front { + template> + Value operator()(zip_source_state& source) const { + auto val = std::move(source.values.front()); + source.values.pop_front(); + return val; + } +}; + +template +struct zip_invalid_arguments {}; + +template +struct zip_invalid : public rxo::operator_base> { + using type = observable, zip_invalid>; +}; +template +using zip_invalid_t = typename zip_invalid::type; + +template +struct is_zip_selector_check { + typedef rxu::decay_t selector_type; + + struct tag_not_valid; + template + static auto check(int) -> decltype((*(CS*)nullptr)((*(typename CON::value_type*)nullptr)...)); + template + static tag_not_valid check(...); + + using type = decltype(check...>(0)); + + static const bool value = !std::is_same::value; +}; + +template +struct invalid_zip_selector { + static const bool value = false; +}; + +template +struct is_zip_selector : public std::conditional< + is_zip_selector_check::value, + is_zip_selector_check, + invalid_zip_selector>::type { +}; + +template +using result_zip_selector_t = typename is_zip_selector::type; + +template +struct zip_traits { + typedef std::tuple...> tuple_source_type; + typedef std::tuple...> tuple_source_values_type; + + typedef rxu::decay_t selector_type; + typedef rxu::decay_t coordination_type; + + typedef typename is_zip_selector::type value_type; +}; + +template +struct zip : public operator_base>> +{ + typedef zip this_type; + + typedef zip_traits 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 + void subscribe_one(std::shared_ptr state) const { + + typedef typename std::tuple_element::type::value_type source_value_type; + + composite_subscription innercs; + + // when the out observer is unsubscribed all the + // inner subscriptions are unsubscribed as well + state->out.add(innercs); + + auto source = on_exception( + [&](){return state->coordinator.in(std::get(state->source));}, + state->out); + if (source.empty()) { + return; + } + + // this subscribe does not share the observer subscription + // so that when it is unsubscribed the observer can be called + // until the inner subscriptions have finished + auto sink = make_subscriber( + state->out, + innercs, + // on_next + [state](source_value_type st) { + auto& values = std::get(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(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 + void subscribe_all(std::shared_ptr state, rxu::values) const { + bool subscribed[] = {(subscribe_one(state), true)...}; + subscribed[0] = (*subscribed); // silence warning + } + + template + void on_subscribe(Subscriber scbr) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct zip_state_type + : public std::enable_shared_from_this + , 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(initial, std::move(coordinator), std::move(scbr)); + + subscribe_all(state, typename rxu::values_from::type()); + } +}; + +} + +/*! @copydoc rx-zip.hpp +*/ +template +auto zip(AN&&... an) + -> operator_factory { + return operator_factory(std::make_tuple(std::forward(an)...)); +} + +} + +template<> +struct member_overload +{ + template>, + class Zip = rxo::detail::zip, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, ObservableN&&... on) + { + return Result(Zip(identity_current_thread(), rxu::pack(), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + all_observables>, + class ResolvedSelector = rxu::decay_t, + class Zip = rxo::detail::zip, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Selector&& s, ObservableN&&... on) + { + return Result(Zip(identity_current_thread(), std::forward(s), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + all_observables>, + class Zip = rxo::detail::zip, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Coordination&& cn, ObservableN&&... on) + { + return Result(Zip(std::forward(cn), rxu::pack(), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template, + operators::detail::is_zip_selector, + all_observables>, + class ResolvedSelector = rxu::decay_t, + class Zip = rxo::detail::zip, rxu::decay_t...>, + class Value = rxu::value_type_t, + class Result = observable> + static Result member(Observable&& o, Coordination&& cn, Selector&& s, ObservableN&&... on) + { + return Result(Zip(std::forward(cn), std::forward(s), std::make_tuple(std::forward(o), std::forward(on)...))); + } + + template + static operators::detail::zip_invalid_t 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 diff --git a/3party/rxcpp/rx-composite_exception.hpp b/3party/rxcpp/rx-composite_exception.hpp new file mode 100644 index 0000000..cddd03e --- /dev/null +++ b/3party/rxcpp/rx-composite_exception.hpp @@ -0,0 +1,34 @@ +// 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_COMPOSITE_EXCEPTION_HPP) +#define RXCPP_SOURCES_RX_COMPOSITE_EXCEPTION_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +struct composite_exception : std::exception { + + typedef std::vector exception_values; + + virtual const char *what() const RXCPP_NOEXCEPT override { + return "rxcpp composite exception"; + } + + virtual bool empty() const { + return exceptions.empty(); + } + + virtual composite_exception add(rxu::error_ptr exception_ptr) { + exceptions.push_back(exception_ptr); + return *this; + } + + exception_values exceptions; +}; + +} + +#endif diff --git a/3party/rxcpp/rx-connectable_observable.hpp b/3party/rxcpp/rx-connectable_observable.hpp new file mode 100644 index 0000000..7038e24 --- /dev/null +++ b/3party/rxcpp/rx-connectable_observable.hpp @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_CONNECTABLE_OBSERVABLE_HPP) +#define RXCPP_RX_CONNECTABLE_OBSERVABLE_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace detail { + +template +struct has_on_connect +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CT*)nullptr).on_connect(composite_subscription())); + template + static not_void check(...); + + typedef decltype(check(0)) detail_result; + static const bool value = std::is_same::value; +}; + +} + +template +class dynamic_connectable_observable + : public dynamic_observable +{ + struct state_type + : public std::enable_shared_from_this + { + typedef std::function onconnect_type; + + onconnect_type on_connect; + }; + std::shared_ptr state; + + template + void construct(const dynamic_observable& o, tag_dynamic_observable&&) { + state = o.state; + } + + template + void construct(dynamic_observable&& o, tag_dynamic_observable&&) { + state = std::move(o.state); + } + + template + void construct(SO&& source, rxs::tag_source&&) { + auto so = std::make_shared>(std::forward(source)); + state->on_connect = [so](composite_subscription cs) mutable { + so->on_connect(std::move(cs)); + }; + } + +public: + + typedef tag_dynamic_observable dynamic_observable_tag; + + dynamic_connectable_observable() + { + } + + template + explicit dynamic_connectable_observable(SOF sof) + : dynamic_observable(sof) + , state(std::make_shared()) + { + construct(std::move(sof), + typename std::conditional::value, tag_dynamic_observable, rxs::tag_source>::type()); + } + + template + dynamic_connectable_observable(SF&& sf, CF&& cf) + : dynamic_observable(std::forward(sf)) + , state(std::make_shared()) + { + state->on_connect = std::forward(cf); + } + + using dynamic_observable::on_subscribe; + + void on_connect(composite_subscription cs) const { + state->on_connect(std::move(cs)); + } +}; + +template +connectable_observable make_dynamic_connectable_observable(Source&& s) { + return connectable_observable(dynamic_connectable_observable(std::forward(s))); +} + + +/*! + \brief a source of values that is shared across all subscribers and does not start until connectable_observable::connect() is called. + + \ingroup group-observable + +*/ +template +class connectable_observable + : public observable +{ + typedef connectable_observable this_type; + typedef observable base_type; + typedef rxu::decay_t source_operator_type; + + static_assert(detail::has_on_connect::value, "inner must have on_connect method void(composite_subscription)"); + +public: + typedef tag_connectable_observable observable_tag; + + connectable_observable() + { + } + + explicit connectable_observable(const SourceOperator& o) + : base_type(o) + { + } + explicit connectable_observable(SourceOperator&& o) + : base_type(std::move(o)) + { + } + + // implicit conversion between observables of the same value_type + template + connectable_observable(const connectable_observable& o) + : base_type(o) + {} + // implicit conversion between observables of the same value_type + template + connectable_observable(connectable_observable&& o) + : base_type(std::move(o)) + {} + + /// + /// takes any function that will take this observable and produce a result value. + /// this is intended to allow externally defined operators, that use subscribe, + /// to be connected into the expression. + /// + template + auto op(OperatorFactory&& of) const + -> decltype(of(*(const this_type*)nullptr)) { + return of(*this); + static_assert(is_operator_factory_for::value, "Function passed for op() must have the signature Result(SourceObservable)"); + } + + /// + /// performs type-forgetting conversion to a new composite_observable + /// + connectable_observable as_dynamic() { + return *this; + } + + composite_subscription connect(composite_subscription cs = composite_subscription()) { + base_type::source_operator.on_connect(cs); + return cs; + } + + /*! @copydoc rx-ref_count.hpp + */ + template + auto ref_count(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(ref_count_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(ref_count_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-connect_forever.hpp + */ + template + auto connect_forever(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(connect_forever_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(connect_forever_tag{}, *this, std::forward(an)...); + } +}; + + +} + +// +// support range() >> filter() >> subscribe() syntax +// '>>' is spelled 'stream' +// +template +auto operator >> (const rxcpp::connectable_observable& source, OperatorFactory&& of) + -> decltype(source.op(std::forward(of))) { + return source.op(std::forward(of)); +} + +// +// support range() | filter() | subscribe() syntax +// '|' is spelled 'pipe' +// +template +auto operator | (const rxcpp::connectable_observable& source, OperatorFactory&& of) + -> decltype(source.op(std::forward(of))) { + return source.op(std::forward(of)); +} + +#endif diff --git a/3party/rxcpp/rx-coordination.hpp b/3party/rxcpp/rx-coordination.hpp new file mode 100644 index 0000000..34e1223 --- /dev/null +++ b/3party/rxcpp/rx-coordination.hpp @@ -0,0 +1,316 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_COORDINATION_HPP) +#define RXCPP_RX_COORDINATION_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +struct tag_coordinator {}; +struct coordinator_base {typedef tag_coordinator coordinator_tag;}; + +template +struct is_coordinator : public std::false_type {}; + +template +struct is_coordinator::type> + : public std::is_convertible {}; + +struct tag_coordination {}; +struct coordination_base {typedef tag_coordination coordination_tag;}; + +namespace detail { + +template +struct is_coordination : public std::false_type {}; + +template +struct is_coordination::type> + : public std::is_convertible {}; + +} + +template> +struct is_coordination : detail::is_coordination +{ +}; + +template> +using coordination_tag_t = typename DecayedCoordination::coordination_tag; + +template +class coordinator : public coordinator_base +{ +public: + typedef Input input_type; + +private: + struct not_supported {typedef not_supported type;}; + + template + struct get_observable + { + typedef decltype((*(input_type*)nullptr).in((*(Observable*)nullptr))) type; + }; + + template + struct get_subscriber + { + typedef decltype((*(input_type*)nullptr).out((*(Subscriber*)nullptr))) type; + }; + + template + struct get_action_function + { + typedef decltype((*(input_type*)nullptr).act((*(F*)nullptr))) type; + }; + +public: + input_type input; + + template + struct get + { + typedef typename std::conditional< + rxsc::detail::is_action_function::value, get_action_function, typename std::conditional< + is_observable::value, get_observable, typename std::conditional< + is_subscriber::value, get_subscriber, not_supported>::type>::type>::type::type type; + }; + + coordinator(Input i) : input(i) {} + + rxsc::worker get_worker() const { + return input.get_worker(); + } + rxsc::scheduler get_scheduler() const { + return input.get_scheduler(); + } + + template + auto in(Observable o) const + -> typename get_observable::type { + return input.in(std::move(o)); + static_assert(is_observable::value, "can only synchronize observables"); + } + + template + auto out(Subscriber s) const + -> typename get_subscriber::type { + return input.out(std::move(s)); + static_assert(is_subscriber::value, "can only synchronize subscribers"); + } + + template + auto act(F f) const + -> typename get_action_function::type { + return input.act(std::move(f)); + static_assert(rxsc::detail::is_action_function::value, "can only synchronize action functions"); + } +}; + +class identity_one_worker : public coordination_base +{ + rxsc::scheduler factory; + + class input_type + { + rxsc::worker controller; + rxsc::scheduler factory; + public: + explicit input_type(rxsc::worker w) + : controller(w) + , factory(rxsc::make_same_worker(w)) + { + } + 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 + auto in(Observable o) const + -> Observable { + return o; + } + template + auto out(Subscriber s) const + -> Subscriber { + return s; + } + template + auto act(F f) const + -> F { + return f; + } + }; + +public: + + explicit identity_one_worker(rxsc::scheduler sc) : factory(sc) {} + + typedef coordinator 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 identity_one_worker identity_immediate() { + static identity_one_worker r(rxsc::make_immediate()); + return r; +} + +inline identity_one_worker identity_current_thread() { + static identity_one_worker r(rxsc::make_current_thread()); + return r; +} + +inline identity_one_worker identity_same_worker(rxsc::worker w) { + return identity_one_worker(rxsc::make_same_worker(w)); +} + +class serialize_one_worker : public coordination_base +{ + rxsc::scheduler factory; + + template + struct serialize_action + { + F dest; + std::shared_ptr lock; + serialize_action(F d, std::shared_ptr m) + : dest(std::move(d)) + , lock(std::move(m)) + { + if (!lock) { + std::terminate(); + } + } + auto operator()(const rxsc::schedulable& scbl) const + -> decltype(dest(scbl)) { + std::unique_lock guard(*lock); + return dest(scbl); + } + }; + + template + struct serialize_observer + { + typedef serialize_observer this_type; + typedef rxu::decay_t dest_type; + typedef typename dest_type::value_type value_type; + typedef observer observer_type; + dest_type dest; + std::shared_ptr lock; + + serialize_observer(dest_type d, std::shared_ptr m) + : dest(std::move(d)) + , lock(std::move(m)) + { + if (!lock) { + std::terminate(); + } + } + void on_next(value_type v) const { + std::unique_lock guard(*lock); + dest.on_next(v); + } + void on_error(rxu::error_ptr e) const { + std::unique_lock guard(*lock); + dest.on_error(e); + } + void on_completed() const { + std::unique_lock guard(*lock); + dest.on_completed(); + } + + template + static subscriber make(const Subscriber& s, std::shared_ptr m) { + return make_subscriber(s, observer_type(this_type(s.get_observer(), std::move(m)))); + } + }; + + class input_type + { + rxsc::worker controller; + rxsc::scheduler factory; + std::shared_ptr lock; + public: + explicit input_type(rxsc::worker w, std::shared_ptr m) + : controller(w) + , factory(rxsc::make_same_worker(w)) + , lock(std::move(m)) + { + } + 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 + auto in(Observable o) const + -> Observable { + return o; + } + template + auto out(const Subscriber& s) const + -> decltype(serialize_observer::make(s, lock)) { + return serialize_observer::make(s, lock); + } + template + auto act(F f) const + -> serialize_action { + return serialize_action(std::move(f), lock); + } + }; + +public: + + explicit serialize_one_worker(rxsc::scheduler sc) : factory(sc) {} + + typedef coordinator 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)); + std::shared_ptr lock = std::make_shared(); + return coordinator_type(input_type(std::move(w), std::move(lock))); + } +}; + +inline serialize_one_worker serialize_event_loop() { + static serialize_one_worker r(rxsc::make_event_loop()); + return r; +} + +inline serialize_one_worker serialize_new_thread() { + static serialize_one_worker r(rxsc::make_new_thread()); + return r; +} + +inline serialize_one_worker serialize_same_worker(rxsc::worker w) { + return serialize_one_worker(rxsc::make_same_worker(w)); +} + +} + +#endif diff --git a/3party/rxcpp/rx-coroutine.hpp b/3party/rxcpp/rx-coroutine.hpp new file mode 100644 index 0000000..317f867 --- /dev/null +++ b/3party/rxcpp/rx-coroutine.hpp @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +/*! \file rx-coroutine.hpp + + \brief The proposal to add couroutines to the standard adds `co_await`, `for co_await`, `co_yield` and `co_return`. This file adds `begin(observable<>)` & `end(observable<>)` which enables `for co_await` to work with the `observable<>` type. + + for co_await (auto c : interval(seconds(1), observe_on_event_loop()) | take(3)) { + printf("%d\n", c); + } + +*/ + +#if !defined(RXCPP_RX_COROUTINE_HPP) +#define RXCPP_RX_COROUTINE_HPP + +#include "rx-includes.hpp" + +#ifdef _RESUMABLE_FUNCTIONS_SUPPORTED + +#include + +#include + +namespace rxcpp { +namespace coroutine { + +using namespace std; +using namespace std::chrono; +using namespace std::experimental; + +template +struct co_observable_iterator; + +template +struct co_observable_iterator_state : std::enable_shared_from_this> +{ + using value_type = typename Source::value_type; + + ~co_observable_iterator_state() { + lifetime.unsubscribe(); + } + explicit co_observable_iterator_state(const Source& o) : o(o) {} + + coroutine_handle<> caller{}; + composite_subscription lifetime{}; + const value_type* value{nullptr}; + exception_ptr error{nullptr}; + Source o; +}; + +template +struct co_observable_inc_awaiter +{ + bool await_ready() { + return false; + } + + bool await_suspend(coroutine_handle<> handle) { + if (!state->lifetime.is_subscribed()) {return false;} + state->caller = handle; + return true; + } + + co_observable_iterator await_resume(); + + shared_ptr> state; +}; + +template +struct co_observable_iterator : public iterator +{ + using value_type = typename Source::value_type; + + co_observable_iterator() {} + + explicit co_observable_iterator(const Source& o) : state(make_shared>(o)) {} + explicit co_observable_iterator(const shared_ptr>& o) : state(o) {} + + co_observable_iterator(co_observable_iterator&&)=default; + co_observable_iterator& operator=(co_observable_iterator&&)=default; + + co_observable_inc_awaiter operator++() + { + return co_observable_inc_awaiter{state}; + } + + co_observable_iterator& operator++(int) = delete; + // not implementing postincrement + + bool operator==(co_observable_iterator const &rhs) const + { + return !!state && !rhs.state && !state->lifetime.is_subscribed(); + } + + bool operator!=(co_observable_iterator const &rhs) const + { + return !(*this == rhs); + } + + value_type const &operator*() const + { + return *(state->value); + } + + value_type const *operator->() const + { + return std::addressof(operator*()); + } + + shared_ptr> state; +}; + +template +co_observable_iterator co_observable_inc_awaiter::await_resume() { + if (!!state->error) {rethrow_exception(state->error);} + return co_observable_iterator{state}; +} + +template +struct co_observable_iterator_awaiter +{ + using iterator=co_observable_iterator; + using value_type=typename iterator::value_type; + + explicit co_observable_iterator_awaiter(const Source& o) : it(o) { + } + + bool await_ready() { + return false; + } + + void await_suspend(coroutine_handle<> handle) { + weak_ptr> wst=it.state; + it.state->caller = handle; + it.state->o | + rxo::finally([wst](){ + auto st = wst.lock(); + if (st && !!st->caller) { + auto caller = st->caller; + st->caller = nullptr; + caller(); + } + }) | + rxo::subscribe( + it.state->lifetime, + // next + [wst](const value_type& v){ + auto st = wst.lock(); + if (!st || !st->caller) {terminate();} + st->value = addressof(v); + auto caller = st->caller; + st->caller = nullptr; + caller(); + }, + // error + [wst](exception_ptr e){ + auto st = wst.lock(); + if (!st || !st->caller) {terminate();} + st->error = e; + auto caller = st->caller; + st->caller = nullptr; + caller(); + }); + } + + iterator await_resume() { + if (!!it.state->error) {rethrow_exception(it.state->error);} + return std::move(it); + } + + iterator it; +}; + +} +} + +namespace std +{ + +template +auto begin(const rxcpp::observable& o) + -> rxcpp::coroutine::co_observable_iterator_awaiter> { + return rxcpp::coroutine::co_observable_iterator_awaiter>{o}; +} + +template +auto end(const rxcpp::observable&) + -> rxcpp::coroutine::co_observable_iterator> { + return rxcpp::coroutine::co_observable_iterator>{}; +} + +} + +#endif + +#endif diff --git a/3party/rxcpp/rx-grouped_observable.hpp b/3party/rxcpp/rx-grouped_observable.hpp new file mode 100644 index 0000000..031bc53 --- /dev/null +++ b/3party/rxcpp/rx-grouped_observable.hpp @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_GROUPED_OBSERVABLE_HPP) +#define RXCPP_RX_GROUPED_OBSERVABLE_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace detail { + +template +struct has_on_get_key_for +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CS*)nullptr).on_get_key()); + template + static not_void check(...); + + typedef decltype(check(0)) detail_result; + static const bool value = std::is_same>::value; +}; + +} + +template +class dynamic_grouped_observable + : public dynamic_observable +{ +public: + typedef rxu::decay_t key_type; + typedef tag_dynamic_grouped_observable dynamic_observable_tag; + +private: + struct state_type + : public std::enable_shared_from_this + { + typedef std::function ongetkey_type; + + ongetkey_type on_get_key; + }; + std::shared_ptr state; + + template + friend bool operator==(const dynamic_grouped_observable&, const dynamic_grouped_observable&); + + template + void construct(const dynamic_grouped_observable& o, const tag_dynamic_grouped_observable&) { + state = o.state; + } + + template + void construct(dynamic_grouped_observable&& o, const tag_dynamic_grouped_observable&) { + state = std::move(o.state); + } + + template + void construct(SO&& source, const rxs::tag_source&) { + auto so = std::make_shared>(std::forward(source)); + state->on_get_key = [so]() mutable { + return so->on_get_key(); + }; + } + +public: + + dynamic_grouped_observable() + { + } + + template + explicit dynamic_grouped_observable(SOF sof) + : dynamic_observable(sof) + , state(std::make_shared()) + { + construct(std::move(sof), + typename std::conditional::value, tag_dynamic_grouped_observable, rxs::tag_source>::type()); + } + + template + dynamic_grouped_observable(SF&& sf, CF&& cf) + : dynamic_observable(std::forward(sf)) + , state(std::make_shared()) + { + state->on_connect = std::forward(cf); + } + + using dynamic_observable::on_subscribe; + + key_type on_get_key() const { + return state->on_get_key(); + } +}; + +template +inline bool operator==(const dynamic_grouped_observable& lhs, const dynamic_grouped_observable& rhs) { + return lhs.state == rhs.state; +} +template +inline bool operator!=(const dynamic_grouped_observable& lhs, const dynamic_grouped_observable& rhs) { + return !(lhs == rhs); +} + +template +grouped_observable make_dynamic_grouped_observable(Source&& s) { + return grouped_observable(dynamic_grouped_observable(std::forward(s))); +} + + + +/*! + \brief a source of observables which each emit values from one category specified by the key selector. + + \ingroup group-observable + +*/ +template +class grouped_observable + : public observable +{ + typedef grouped_observable this_type; + typedef observable base_type; + typedef rxu::decay_t source_operator_type; + + static_assert(detail::has_on_get_key_for::value, "inner must have on_get_key method key_type()"); + +public: + typedef rxu::decay_t key_type; + typedef tag_grouped_observable observable_tag; + + grouped_observable() + { + } + + explicit grouped_observable(const SourceOperator& o) + : base_type(o) + { + } + explicit grouped_observable(SourceOperator&& o) + : base_type(std::move(o)) + { + } + + // implicit conversion between observables of the same value_type + template + grouped_observable(const grouped_observable& o) + : base_type(o) + {} + // implicit conversion between observables of the same value_type + template + grouped_observable(grouped_observable&& o) + : base_type(std::move(o)) + {} + + /// + /// performs type-forgetting conversion to a new grouped_observable + /// + grouped_observable as_dynamic() const { + return *this; + } + + key_type get_key() const { + return base_type::source_operator.on_get_key(); + } +}; + + +} + +// +// support range() >> filter() >> subscribe() syntax +// '>>' is spelled 'stream' +// +template +auto operator >> (const rxcpp::grouped_observable& source, OperatorFactory&& of) + -> decltype(source.op(std::forward(of))) { + return source.op(std::forward(of)); +} + +// +// support range() | filter() | subscribe() syntax +// '|' is spelled 'pipe' +// +template +auto operator | (const rxcpp::grouped_observable& source, OperatorFactory&& of) + -> decltype(source.op(std::forward(of))) { + return source.op(std::forward(of)); +} + +#endif diff --git a/3party/rxcpp/rx-includes.hpp b/3party/rxcpp/rx-includes.hpp new file mode 100644 index 0000000..55b1e0b --- /dev/null +++ b/3party/rxcpp/rx-includes.hpp @@ -0,0 +1,278 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_INCLUDES_HPP) +#define RXCPP_RX_INCLUDES_HPP + +#include "rx-trace.hpp" + +// some configuration macros +#if defined(_MSC_VER) + +#if _MSC_VER > 1600 +#pragma warning(disable: 4348) // false positives on : redefinition of default parameter : parameter 2 +#define RXCPP_USE_RVALUEREF 1 +#endif + +#if _MSC_VER >= 1800 +#define RXCPP_USE_VARIADIC_TEMPLATES 1 +#endif + +#if _CPPRTTI +#define RXCPP_USE_RTTI 1 +#endif + +#if _HAS_EXCEPTIONS +#define RXCPP_USE_EXCEPTIONS 1 +#endif + +#define RXCPP_NORETURN __declspec(noreturn) + +#elif defined(__clang__) + +#if __has_feature(cxx_rvalue_references) +#define RXCPP_USE_RVALUEREF 1 +#endif +#if __has_feature(cxx_rtti) +#define RXCPP_USE_RTTI 1 +#endif +#if __has_feature(cxx_variadic_templates) +#define RXCPP_USE_VARIADIC_TEMPLATES 1 +#endif +#if __has_feature(cxx_exceptions) +#define RXCPP_USE_EXCEPTIONS 1 +#endif + +#if __has_feature(cxx_attributes) +#define RXCPP_NORETURN [[noreturn]] +#else +#define RXCPP_NORETURN __attribute__ ((noreturn)) +#endif + +#elif defined(__GNUG__) + +#define GCC_VERSION (__GNUC__ * 10000 + \ + __GNUC_MINOR__ * 100 + \ + __GNUC_PATCHLEVEL__) + +#if GCC_VERSION >= 40801 +#define RXCPP_USE_RVALUEREF 1 +#endif + +#if GCC_VERSION >= 40400 +#define RXCPP_USE_VARIADIC_TEMPLATES 1 +#endif + +#if defined(__GXX_RTTI) +#define RXCPP_USE_RTTI 1 +#endif + +#if defined(__EXCEPTIONS) +#define RXCPP_USE_EXCEPTIONS 1 +#endif + +#define RXCPP_NORETURN __attribute__ ((noreturn)) + +#endif + +// +// control std::hash<> of enum +// force with RXCPP_FORCE_HASH_ENUM & RXCPP_FORCE_HASH_ENUM_UNDERLYING +// in time use ifdef to detect library support for std::hash<> of enum +// +#define RXCPP_HASH_ENUM 0 +#define RXCPP_HASH_ENUM_UNDERLYING 1 + +#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) +#define RXCPP_USE_WINRT 0 +#else +#define RXCPP_USE_WINRT 1 +#endif + +#if defined(__APPLE__) && defined(__MACH__) +#include +#if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1) +#define RXCPP_ON_IOS +#endif +#endif + +#if defined(__ANDROID__) +#define RXCPP_ON_ANDROID +#endif + +#if defined(RXCPP_FORCE_USE_VARIADIC_TEMPLATES) +#undef RXCPP_USE_VARIADIC_TEMPLATES +#define RXCPP_USE_VARIADIC_TEMPLATES RXCPP_FORCE_USE_VARIADIC_TEMPLATES +#endif + +#if defined(RXCPP_FORCE_USE_RVALUEREF) +#undef RXCPP_USE_RVALUEREF +#define RXCPP_USE_RVALUEREF RXCPP_FORCE_USE_RVALUEREF +#endif + +#if defined(RXCPP_FORCE_USE_RTTI) +#undef RXCPP_USE_RTTI +#define RXCPP_USE_RTTI RXCPP_FORCE_USE_RTTI +#endif + +#if defined(RXCPP_FORCE_USE_EXCEPTIONS) +#undef RXCPP_USE_EXCEPTIONS +#define RXCPP_USE_EXCEPTIONS RXCPP_FORCE_USE_EXCEPTIONS +#endif + +#if defined(RXCPP_FORCE_USE_WINRT) +#undef RXCPP_USE_WINRT +#define RXCPP_USE_WINRT RXCPP_FORCE_USE_WINRT +#endif + +#if defined(RXCPP_FORCE_HASH_ENUM) +#undef RXCPP_HASH_ENUM +#define RXCPP_HASH_ENUM RXCPP_FORCE_HASH_ENUM +#endif + +#if defined(RXCPP_FORCE_HASH_ENUM_UNDERLYING) +#undef RXCPP_HASH_ENUM_UNDERLYING +#define RXCPP_HASH_ENUM_UNDERLYING RXCPP_FORCE_HASH_ENUM_UNDERLYING +#endif + +#if defined(RXCPP_FORCE_ON_IOS) +#undef RXCPP_ON_IOS +#define RXCPP_ON_IOS RXCPP_FORCE_ON_IOS +#endif + +#if defined(RXCPP_FORCE_ON_ANDROID) +#undef RXCPP_ON_ANDROID +#define RXCPP_ON_ANDROID RXCPP_FORCE_ON_ANDROID +#endif + +#if defined(_MSC_VER) && !RXCPP_USE_VARIADIC_TEMPLATES +// resolve args needs enough to store all the possible resolved args +#define _VARIADIC_MAX 10 +#endif + +#if defined(_MSC_VER) && (_MSC_VER <= 1800) +#define RXCPP_NOEXCEPT +#else +#define RXCPP_NOEXCEPT noexcept +#endif + +#pragma push_macro("min") +#pragma push_macro("max") +#undef min +#undef max + +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(RXCPP_ON_IOS) || defined(RXCPP_ON_ANDROID) +#include +#endif + +#include "rx-util.hpp" +#include "rx-predef.hpp" +#include "rx-subscription.hpp" +#include "rx-observer.hpp" +#include "rx-scheduler.hpp" +#include "rx-subscriber.hpp" +#include "rx-notification.hpp" +#include "rx-coordination.hpp" +#include "rx-sources.hpp" +#include "rx-subjects.hpp" +#include "rx-operators.hpp" +#include "rx-observable.hpp" +#include "rx-connectable_observable.hpp" +#include "rx-grouped_observable.hpp" + +#if !defined(RXCPP_LITE) +#include "operators/rx-all.hpp" +#include "operators/rx-amb.hpp" +#include "operators/rx-any.hpp" +#include "operators/rx-buffer_count.hpp" +#include "operators/rx-buffer_time.hpp" +#include "operators/rx-buffer_time_count.hpp" +#include "operators/rx-combine_latest.hpp" +#include "operators/rx-concat.hpp" +#include "operators/rx-concat_map.hpp" +#include "operators/rx-connect_forever.hpp" +#include "operators/rx-debounce.hpp" +#include "operators/rx-delay.hpp" +#include "operators/rx-distinct.hpp" +#include "operators/rx-distinct_until_changed.hpp" +#include "operators/rx-element_at.hpp" +#include "operators/rx-filter.hpp" +#include "operators/rx-finally.hpp" +#include "operators/rx-flat_map.hpp" +#include "operators/rx-group_by.hpp" +#include "operators/rx-ignore_elements.hpp" +#include "operators/rx-map.hpp" +#include "operators/rx-merge.hpp" +#include "operators/rx-merge_delay_error.hpp" +#include "operators/rx-observe_on.hpp" +#include "operators/rx-on_error_resume_next.hpp" +#include "operators/rx-pairwise.hpp" +#include "operators/rx-reduce.hpp" +#include "operators/rx-repeat.hpp" +#include "operators/rx-replay.hpp" +#include "operators/rx-retry.hpp" +#include "operators/rx-sample_time.hpp" +#include "operators/rx-scan.hpp" +#include "operators/rx-sequence_equal.hpp" +#include "operators/rx-skip.hpp" +#include "operators/rx-skip_while.hpp" +#include "operators/rx-skip_last.hpp" +#include "operators/rx-skip_until.hpp" +#include "operators/rx-start_with.hpp" +#include "operators/rx-subscribe_on.hpp" +#include "operators/rx-switch_if_empty.hpp" +#include "operators/rx-switch_on_next.hpp" +#include "operators/rx-take.hpp" +#include "operators/rx-take_last.hpp" +#include "operators/rx-take_until.hpp" +#include "operators/rx-take_while.hpp" +#include "operators/rx-tap.hpp" +#include "operators/rx-time_interval.hpp" +#include "operators/rx-timeout.hpp" +#include "operators/rx-timestamp.hpp" +#include "operators/rx-window.hpp" +#include "operators/rx-window_time.hpp" +#include "operators/rx-window_time_count.hpp" +#include "operators/rx-window_toggle.hpp" +#include "operators/rx-with_latest_from.hpp" +#include "operators/rx-zip.hpp" +#endif + +#pragma pop_macro("min") +#pragma pop_macro("max") + +#endif diff --git a/3party/rxcpp/rx-lite.hpp b/3party/rxcpp/rx-lite.hpp new file mode 100644 index 0000000..a0a9d92 --- /dev/null +++ b/3party/rxcpp/rx-lite.hpp @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_HPP) +#define RXCPP_RX_HPP + +#define RXCPP_LITE +#include "rx-includes.hpp" + +#endif diff --git a/3party/rxcpp/rx-notification.hpp b/3party/rxcpp/rx-notification.hpp new file mode 100644 index 0000000..20e0c69 --- /dev/null +++ b/3party/rxcpp/rx-notification.hpp @@ -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 + +#if !defined(RXCPP_RX_NOTIFICATION_HPP) +#define RXCPP_RX_NOTIFICATION_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace notifications { + +class subscription +{ + long s; + long u; + +public: + explicit inline subscription(long s) + : s(s), u(std::numeric_limits::max()) { + } + inline subscription(long s, long u) + : s(s), u(u) { + } + inline long subscribe() const { + return s; + } + inline long unsubscribe() const { + return u; + } +}; + +inline bool operator == (subscription lhs, subscription rhs) { + return lhs.subscribe() == rhs.subscribe() && lhs.unsubscribe() == rhs.unsubscribe(); +} + +inline std::ostream& operator<< (std::ostream& out, const subscription& s) { + out << s.subscribe() << "-" << s.unsubscribe(); + return out; +} + +namespace detail { + +template +struct notification_base + : public std::enable_shared_from_this> +{ + typedef subscriber observer_type; + typedef std::shared_ptr> type; + + virtual ~notification_base() {} + + virtual void out(std::ostream& out) const =0; + virtual bool equals(const type& other) const = 0; + virtual void accept(const observer_type& o) const =0; +}; + +template +std::ostream& operator<< (std::ostream& out, const std::vector& v); + +template +auto to_stream(std::ostream& os, const T& t, int, int) + -> decltype(os << t) { + return os << t; +} + +#if RXCPP_USE_RTTI +template +std::ostream& to_stream(std::ostream& os, const T&, int, ...) { + return os << "< " << typeid(T).name() << " does not support ostream>"; +} +#endif + +template +std::ostream& to_stream(std::ostream& os, const T&, ...) { + return os << ""; +} + +template +inline std::ostream& ostreamvector (std::ostream& os, const std::vector& v) { + os << "["; + bool doemit = false; + for(auto& i : v) { + if (doemit) { + os << ", "; + } else { + doemit = true; + } + to_stream(os, i, 0, 0); + } + os << "]"; + return os; +} + +template +inline std::ostream& operator<< (std::ostream& os, const std::vector& v) { + return ostreamvector(os, v); +} + +template +auto equals(const T& lhs, const T& rhs, int) + -> decltype(bool(lhs == rhs)) { + return lhs == rhs; +} + +template +bool equals(const T&, const T&, ...) { + rxu::throw_exception(std::runtime_error("value does not support equality tests")); + return false; +} + +} + +template +struct notification +{ + typedef typename detail::notification_base::type type; + typedef typename detail::notification_base::observer_type observer_type; + +private: + typedef detail::notification_base base; + + struct on_next_notification : public base { + on_next_notification(T value) : value(std::move(value)) { + } + on_next_notification(const on_next_notification& o) : value(o.value) {} + on_next_notification(const on_next_notification&& o) : value(std::move(o.value)) {} + on_next_notification& operator=(on_next_notification o) { value = std::move(o.value); return *this; } + virtual void out(std::ostream& os) const { + os << "on_next( "; + detail::to_stream(os, value, 0, 0); + os << ")"; + } + virtual bool equals(const typename base::type& other) const { + bool result = false; + other->accept(make_subscriber(make_observer_dynamic([this, &result](T v) { + result = detail::equals(this->value, v, 0); + }))); + return result; + } + virtual void accept(const typename base::observer_type& o) const { + o.on_next(value); + } + const T value; + }; + + struct on_error_notification : public base { + on_error_notification(rxu::error_ptr ep) : ep(ep) { + } + on_error_notification(const on_error_notification& o) : ep(o.ep) {} + on_error_notification(const on_error_notification&& o) : ep(std::move(o.ep)) {} + on_error_notification& operator=(on_error_notification o) { ep = std::move(o.ep); return *this; } + virtual void out(std::ostream& os) const { + os << "on_error("; + os << rxu::what(ep); + os << ")"; + } + virtual bool equals(const typename base::type& other) const { + bool result = false; + // not trying to compare exceptions + other->accept(make_subscriber(make_observer_dynamic([](T){}, [&result](rxu::error_ptr){ + result = true; + }))); + return result; + } + virtual void accept(const typename base::observer_type& o) const { + o.on_error(ep); + } + const rxu::error_ptr ep; + }; + + struct on_completed_notification : public base { + on_completed_notification() { + } + virtual void out(std::ostream& os) const { + os << "on_completed()"; + } + virtual bool equals(const typename base::type& other) const { + bool result = false; + other->accept(make_subscriber(make_observer_dynamic([](T){}, [&result](){ + result = true; + }))); + return result; + } + virtual void accept(const typename base::observer_type& o) const { + o.on_completed(); + } + }; + + struct exception_tag {}; + + template + static + type make_on_error(exception_tag&&, Exception&& e) { + rxu::error_ptr ep = rxu::make_error_ptr(std::forward(e)); + return std::make_shared(ep); + } + + struct exception_ptr_tag {}; + + static + type make_on_error(exception_ptr_tag&&, rxu::error_ptr ep) { + return std::make_shared(ep); + } + +public: + template + static type on_next(U value) { + return std::make_shared(std::move(value)); + } + + static type on_completed() { + return std::make_shared(); + } + + template + static type on_error(Exception&& e) { + return make_on_error(typename std::conditional< + std::is_same, rxu::error_ptr>::value, + exception_ptr_tag, exception_tag>::type(), + std::forward(e)); + } +}; + +template +bool operator == (const std::shared_ptr>& lhs, const std::shared_ptr>& rhs) { + if (!lhs && !rhs) {return true;} + if (!lhs || !rhs) {return false;} + return lhs->equals(rhs); +} + +template +std::ostream& operator<< (std::ostream& os, const std::shared_ptr>& n) { + n->out(os); + return os; +} + + +template +class recorded +{ + long t; + T v; +public: + recorded(long t, T v) + : t(t), v(v) { + } + long time() const { + return t; + } + const T& value() const { + return v; + } +}; + +template +bool operator == (recorded lhs, recorded rhs) { + return lhs.time() == rhs.time() && lhs.value() == rhs.value(); +} + +template +std::ostream& operator<< (std::ostream& out, const recorded& r) { + out << "@" << r.time() << "-" << r.value(); + return out; +} + +} +namespace rxn=notifications; + +inline std::ostream& operator<< (std::ostream& out, const std::vector& vs) { + return rxcpp::notifications::detail::ostreamvector(out, vs); +} +template +inline std::ostream& operator<< (std::ostream& out, const std::vector>& vr) { + return rxcpp::notifications::detail::ostreamvector(out, vr); +} + +} + +#endif diff --git a/3party/rxcpp/rx-observable-fwd.hpp b/3party/rxcpp/rx-observable-fwd.hpp new file mode 100644 index 0000000..83b4da8 --- /dev/null +++ b/3party/rxcpp/rx-observable-fwd.hpp @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_OBSERVABLE_FWD_HPP) +#define RXCPP_RX_OBSERVABLE_FWD_HPP + +#include + +namespace rxcpp { + +template +class dynamic_observable; + +template< + class T = void, + class SourceObservable = typename std::conditional::value, + void, dynamic_observable>::type> +class observable; + +template +observable make_observable_dynamic(Source&&); + +} + +#endif diff --git a/3party/rxcpp/rx-observable.hpp b/3party/rxcpp/rx-observable.hpp new file mode 100644 index 0000000..232dac1 --- /dev/null +++ b/3party/rxcpp/rx-observable.hpp @@ -0,0 +1,1807 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_OBSERVABLE_HPP) +#define RXCPP_RX_OBSERVABLE_HPP + +#include "rx-includes.hpp" + +#ifdef __GNUG__ +#define EXPLICIT_THIS this-> +#else +#define EXPLICIT_THIS +#endif + +namespace rxcpp { + +namespace detail { + +template +struct has_on_subscribe_for +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CT*)nullptr).on_subscribe(*(CS*)nullptr)); + template + static not_void check(...); + + typedef decltype(check, T>(0)) detail_result; + static const bool value = std::is_same::value; +}; + +} + +template +class dynamic_observable + : public rxs::source_base +{ + struct state_type + : public std::enable_shared_from_this + { + typedef std::function)> onsubscribe_type; + + onsubscribe_type on_subscribe; + }; + std::shared_ptr state; + + template + friend bool operator==(const dynamic_observable&, const dynamic_observable&); + + template + void construct(SO&& source, rxs::tag_source&&) { + rxu::decay_t so = std::forward(source); + state->on_subscribe = [so](subscriber o) mutable { + so.on_subscribe(std::move(o)); + }; + } + + struct tag_function {}; + template + void construct(F&& f, tag_function&&) { + state->on_subscribe = std::forward(f); + } + +public: + + typedef tag_dynamic_observable dynamic_observable_tag; + + dynamic_observable() + { + } + + template + explicit dynamic_observable(SOF&& sof, typename std::enable_if::value, void**>::type = 0) + : state(std::make_shared()) + { + construct(std::forward(sof), + typename std::conditional::value || rxo::is_operator::value, rxs::tag_source, tag_function>::type()); + } + + void on_subscribe(subscriber o) const { + state->on_subscribe(std::move(o)); + } + + template + typename std::enable_if::value, void>::type + on_subscribe(Subscriber o) const { + state->on_subscribe(o.as_dynamic()); + } +}; + +template +inline bool operator==(const dynamic_observable& lhs, const dynamic_observable& rhs) { + return lhs.state == rhs.state; +} +template +inline bool operator!=(const dynamic_observable& lhs, const dynamic_observable& rhs) { + return !(lhs == rhs); +} + +template +observable make_observable_dynamic(Source&& s) { + return observable(dynamic_observable(std::forward(s))); +} + +namespace detail { +template +struct resolve_observable; + +template +struct resolve_observable +{ + typedef typename SO::type type; + typedef typename type::value_type value_type; + static const bool value = true; + typedef observable observable_type; + template + static observable_type make(const Default&, AN&&... an) { + return observable_type(type(std::forward(an)...)); + } +}; +template +struct resolve_observable +{ + static const bool value = false; + typedef Default observable_type; + template + static observable_type make(const observable_type& that, const AN&...) { + return that; + } +}; +template +struct resolve_observable +{ + typedef typename SO::type type; + typedef typename type::value_type value_type; + static const bool value = true; + typedef observable observable_type; + template + static observable_type make(AN&&... an) { + return observable_type(type(std::forward(an)...)); + } +}; +template +struct resolve_observable +{ + static const bool value = false; + typedef void observable_type; + template + static observable_type make(const AN&...) { + } +}; + +} + +template class SO, class... AN> +struct defer_observable + : public detail::resolve_observable> +{ +}; + +/*! + \brief a source of values whose methods block until all values have been emitted. subscribe or use one of the operator methods that reduce the values emitted to a single value. + + \ingroup group-observable + +*/ +template +class blocking_observable +{ + template + static auto blocking_subscribe(const Obsvbl& source, bool do_rethrow, ArgN&&... an) + -> void { + std::mutex lock; + std::condition_variable wake; + bool disposed = false; + rxu::error_ptr error; + + auto dest = make_subscriber(std::forward(an)...); + + // keep any error to rethrow at the end. + auto scbr = make_subscriber( + dest, + [&](T t){dest.on_next(t);}, + [&](rxu::error_ptr e){ + if (do_rethrow) { + error = e; + } else { + dest.on_error(e); + } + }, + [&](){dest.on_completed();} + ); + + auto cs = scbr.get_subscription(); + cs.add( + [&](){ + std::unique_lock guard(lock); + disposed = true; + wake.notify_one(); + }); + + source.subscribe(std::move(scbr)); + + std::unique_lock guard(lock); + wake.wait(guard, + [&](){ + return disposed; + }); + + if (error) {rxu::rethrow_exception(error);} + } + +public: + typedef rxu::decay_t observable_type; + observable_type source; + ~blocking_observable() + { + } + blocking_observable(observable_type s) : source(std::move(s)) {} + + /// + /// `subscribe` will cause this observable to emit values to the provided subscriber. + /// + /// \return void + /// + /// \param an... - the arguments are passed to make_subscriber(). + /// + /// callers must provide enough arguments to make a subscriber. + /// overrides are supported. thus + /// `subscribe(thesubscriber, composite_subscription())` + /// will take `thesubscriber.get_observer()` and the provided + /// subscription and subscribe to the new subscriber. + /// the `on_next`, `on_error`, `on_completed` methods can be supplied instead of an observer + /// if a subscription or subscriber is not provided then a new subscription will be created. + /// + template + auto subscribe(ArgN&&... an) const + -> void { + return blocking_subscribe(source, false, std::forward(an)...); + } + + /// + /// `subscribe_with_rethrow` will cause this observable to emit values to the provided subscriber. + /// + /// \note If the source observable calls on_error, the raised exception is rethrown by this method. + /// + /// \note If the source observable calls on_error, the `on_error` method on the subscriber will not be called. + /// + /// \return void + /// + /// \param an... - the arguments are passed to make_subscriber(). + /// + /// callers must provide enough arguments to make a subscriber. + /// overrides are supported. thus + /// `subscribe(thesubscriber, composite_subscription())` + /// will take `thesubscriber.get_observer()` and the provided + /// subscription and subscribe to the new subscriber. + /// the `on_next`, `on_error`, `on_completed` methods can be supplied instead of an observer + /// if a subscription or subscriber is not provided then a new subscription will be created. + /// + template + auto subscribe_with_rethrow(ArgN&&... an) const + -> void { + return blocking_subscribe(source, true, std::forward(an)...); + } + + /*! Return the first item emitted by this blocking_observable, or throw an std::runtime_error exception if it emits no items. + + \return The first item emitted by this blocking_observable. + + \note If the source observable calls on_error, the raised exception is rethrown by this method. + + \sample + When the source observable emits at least one item: + \snippet blocking_observable.cpp blocking first sample + \snippet output.txt blocking first sample + + When the source observable is empty: + \snippet blocking_observable.cpp blocking first empty sample + \snippet output.txt blocking first empty sample + */ + template + auto first(AN**...) -> delayed_type_t const { + rxu::maybe result; + composite_subscription cs; + subscribe_with_rethrow( + cs, + [&](T v){result.reset(v); cs.unsubscribe();}); + if (result.empty()) + rxu::throw_exception(rxcpp::empty_error("first() requires a stream with at least one value")); + return result.get(); + static_assert(sizeof...(AN) == 0, "first() was passed too many arguments."); + } + + /*! Return the last item emitted by this blocking_observable, or throw an std::runtime_error exception if it emits no items. + + \return The last item emitted by this blocking_observable. + + \note If the source observable calls on_error, the raised exception is rethrown by this method. + + \sample + When the source observable emits at least one item: + \snippet blocking_observable.cpp blocking last sample + \snippet output.txt blocking last sample + + When the source observable is empty: + \snippet blocking_observable.cpp blocking last empty sample + \snippet output.txt blocking last empty sample + */ + template + auto last(AN**...) -> delayed_type_t const { + rxu::maybe result; + subscribe_with_rethrow( + [&](T v){result.reset(v);}); + if (result.empty()) + rxu::throw_exception(rxcpp::empty_error("last() requires a stream with at least one value")); + return result.get(); + static_assert(sizeof...(AN) == 0, "last() was passed too many arguments."); + } + + /*! Return the total number of items emitted by this blocking_observable. + + \return The total number of items emitted by this blocking_observable. + + \sample + \snippet blocking_observable.cpp blocking count sample + \snippet output.txt blocking count sample + + When the source observable calls on_error: + \snippet blocking_observable.cpp blocking count error sample + \snippet output.txt blocking count error sample + */ + int count() const { + int result = 0; + source.count().as_blocking().subscribe_with_rethrow( + [&](int v){result = v;}); + return result; + } + + /*! Return the sum of all items emitted by this blocking_observable, or throw an std::runtime_error exception if it emits no items. + + \return The sum of all items emitted by this blocking_observable. + + \sample + When the source observable emits at least one item: + \snippet blocking_observable.cpp blocking sum sample + \snippet output.txt blocking sum sample + + When the source observable is empty: + \snippet blocking_observable.cpp blocking sum empty sample + \snippet output.txt blocking sum empty sample + + When the source observable calls on_error: + \snippet blocking_observable.cpp blocking sum error sample + \snippet output.txt blocking sum error sample + */ + T sum() const { + return source.sum().as_blocking().last(); + } + + /*! Return the average value of all items emitted by this blocking_observable, or throw an std::runtime_error exception if it emits no items. + + \return The average value of all items emitted by this blocking_observable. + + \sample + When the source observable emits at least one item: + \snippet blocking_observable.cpp blocking average sample + \snippet output.txt blocking average sample + + When the source observable is empty: + \snippet blocking_observable.cpp blocking average empty sample + \snippet output.txt blocking average empty sample + + When the source observable calls on_error: + \snippet blocking_observable.cpp blocking average error sample + \snippet output.txt blocking average error sample + */ + double average() const { + return source.average().as_blocking().last(); + } + + /*! Return the max of all items emitted by this blocking_observable, or throw an std::runtime_error exception if it emits no items. + + \return The max of all items emitted by this blocking_observable. + + \sample + When the source observable emits at least one item: + \snippet blocking_observable.cpp blocking max sample + \snippet output.txt blocking max sample + + When the source observable is empty: + \snippet blocking_observable.cpp blocking max empty sample + \snippet output.txt blocking max empty sample + + When the source observable calls on_error: + \snippet blocking_observable.cpp blocking max error sample + \snippet output.txt blocking max error sample +*/ + T max() const { + return source.max().as_blocking().last(); + } + + /*! Return the min of all items emitted by this blocking_observable, or throw an std::runtime_error exception if it emits no items. + + \return The min of all items emitted by this blocking_observable. + + \sample + When the source observable emits at least one item: + \snippet blocking_observable.cpp blocking min sample + \snippet output.txt blocking min sample + + When the source observable is empty: + \snippet blocking_observable.cpp blocking min empty sample + \snippet output.txt blocking min empty sample + + When the source observable calls on_error: + \snippet blocking_observable.cpp blocking min error sample + \snippet output.txt blocking min error sample +*/ + T min() const { + return source.min().as_blocking().last(); + } +}; + +namespace detail { + +template +struct safe_subscriber +{ + safe_subscriber(SourceOperator& so, Subscriber& o) : so(std::addressof(so)), o(std::addressof(o)) {} + + void subscribe() { + RXCPP_TRY { + so->on_subscribe(*o); + } RXCPP_CATCH(...) { + if (!o->is_subscribed()) { + rxu::rethrow_current_exception(); + } + o->on_error(rxu::make_error_ptr(rxu::current_exception())); + o->unsubscribe(); + } + } + + void operator()(const rxsc::schedulable&) { + subscribe(); + } + + SourceOperator* so; + Subscriber* o; +}; + +} + +template<> +class observable; + +/*! + \defgroup group-observable Observables + + \brief These are the set of observable classes in rxcpp. + + \class rxcpp::observable + + \ingroup group-observable group-core + + \brief a source of values. subscribe or use one of the operator methods that return a new observable, which uses this observable as a source. + + \par Some code + This sample will observable::subscribe() to values from a observable::range(). + + \sample + \snippet range.cpp range sample + \snippet output.txt range sample + +*/ +template +class observable + : public observable_base +{ + static_assert(std::is_same::value, "SourceOperator::value_type must be the same as T in observable"); + + typedef observable this_type; + +public: + typedef rxu::decay_t source_operator_type; + mutable source_operator_type source_operator; + +private: + + template + friend class observable; + + template + friend bool operator==(const observable&, const observable&); + + template + auto detail_subscribe(Subscriber o) const + -> composite_subscription { + + typedef rxu::decay_t subscriber_type; + + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + static_assert(std::is_same::value && std::is_convertible::value, "the value types in the sequence must match or be convertible"); + static_assert(detail::has_on_subscribe_for::value, "inner must have on_subscribe method that accepts this subscriber "); + + trace_activity().subscribe_enter(*this, o); + + if (!o.is_subscribed()) { + trace_activity().subscribe_return(*this); + return o.get_subscription(); + } + + detail::safe_subscriber subscriber(source_operator, o); + + // make sure to let current_thread take ownership of the thread as early as possible. + if (rxsc::current_thread::is_schedule_required()) { + const auto& sc = rxsc::make_current_thread(); + sc.create_worker(o.get_subscription()).schedule(subscriber); + } else { + // current_thread already owns this thread. + subscriber.subscribe(); + } + + trace_activity().subscribe_return(*this); + return o.get_subscription(); + } + +public: + typedef T value_type; + + static_assert(rxo::is_operator::value || rxs::is_source::value, "observable must wrap an operator or source"); + + ~observable() + { + } + + observable() + { + } + + explicit observable(const source_operator_type& o) + : source_operator(o) + { + } + explicit observable(source_operator_type&& o) + : source_operator(std::move(o)) + { + } + + /// implicit conversion between observables of the same value_type + template + observable(const observable& o) + : source_operator(o.source_operator) + {} + /// implicit conversion between observables of the same value_type + template + observable(observable&& o) + : source_operator(std::move(o.source_operator)) + {} + +#if 0 + template + void on_subscribe(observer o) const { + source_operator.on_subscribe(o); + } +#endif + + /*! @copydoc rxcpp::operators::as_dynamic + */ + template + observable as_dynamic(AN**...) const { + return *this; + static_assert(sizeof...(AN) == 0, "as_dynamic() was passed too many arguments."); + } + + /*! @copydoc rx-ref_count.hpp + */ + template + auto ref_count(AN... an) const // ref_count(ConnectableObservable&&) + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(ref_count_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(ref_count_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rxcpp::operators::as_blocking + */ + template + blocking_observable as_blocking(AN**...) const { + return blocking_observable(*this); + static_assert(sizeof...(AN) == 0, "as_blocking() was passed too many arguments."); + } + + /// \cond SHOW_SERVICE_MEMBERS + + /// + /// takes any function that will take this observable and produce a result value. + /// this is intended to allow externally defined operators, that use subscribe, + /// to be connected into the expression. + /// + template + auto op(OperatorFactory&& of) const + -> decltype(of(*(const this_type*)nullptr)) { + return of(*this); + static_assert(is_operator_factory_for::value, "Function passed for op() must have the signature Result(SourceObservable)"); + } + + /*! @copydoc rx-lift.hpp + */ + template + auto lift(Operator&& op) const + -> observable>, rxo::detail::lift_operator> { + return observable>, rxo::detail::lift_operator>( + rxo::detail::lift_operator(source_operator, std::forward(op))); + static_assert(detail::is_lift_function_for, Operator>::value, "Function passed for lift() must have the signature subscriber<...>(subscriber)"); + } + + /// + /// 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. + /// + template + auto lift_if(Operator&& op) const + -> typename std::enable_if, Operator>::value, + observable>, rxo::detail::lift_operator>>::type { + return observable>, rxo::detail::lift_operator>( + rxo::detail::lift_operator(source_operator, std::forward(op))); + } + /// + /// 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. + /// + template + auto lift_if(Operator&&) const + -> typename std::enable_if, Operator>::value, + decltype(rxs::from())>::type { + return rxs::from(); + } + /// \endcond + + /*! @copydoc rx-subscribe.hpp + */ + template + auto subscribe(ArgN&&... an) const + -> composite_subscription { + return detail_subscribe(make_subscriber(std::forward(an)...)); + } + + /*! @copydoc rx-all.hpp + */ + template + auto all(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(all_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(all_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rxcpp::operators::is_empty + */ + template + auto is_empty(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(is_empty_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(is_empty_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-any.hpp + */ + template + auto any(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(any_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(any_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rxcpp::operators::exists + */ + template + auto exists(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(exists_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(exists_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rxcpp::operators::contains + */ + template + auto contains(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(contains_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(contains_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-filter.hpp + */ + template + auto filter(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(filter_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(filter_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-switch_if_empty.hpp + */ + template + auto switch_if_empty(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(switch_if_empty_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(switch_if_empty_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rxcpp::operators::default_if_empty + */ + template + auto default_if_empty(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(default_if_empty_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(default_if_empty_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-sequence_equal.hpp + */ + template + auto sequence_equal(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(sequence_equal_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(sequence_equal_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-tap.hpp + */ + template + auto tap(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(tap_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(tap_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-time_interval.hpp + */ + template + auto time_interval(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(time_interval_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(time_interval_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-timeout.hpp + */ + template + auto timeout(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(timeout_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(timeout_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-timestamp.hpp + */ + template + auto timestamp(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(timestamp_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(timestamp_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-finally.hpp + */ + template + auto finally(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(finally_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(finally_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-on_error_resume_next.hpp + */ + template + auto on_error_resume_next(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(on_error_resume_next_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(on_error_resume_next_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-on_error_resume_next.hpp + */ + template + auto switch_on_error(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(on_error_resume_next_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(on_error_resume_next_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-map.hpp + */ + template + auto map(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(map_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(map_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-map.hpp + */ + template + auto transform(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(map_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(map_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-debounce.hpp + */ + template + auto debounce(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(debounce_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(debounce_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-delay.hpp + */ + template + auto delay(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(delay_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(delay_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-distinct.hpp + */ + template + auto distinct(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(distinct_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(distinct_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-distinct_until_changed.hpp + */ + template + auto distinct_until_changed(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(distinct_until_changed_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(distinct_until_changed_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-element_at.hpp + */ + template + auto element_at(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(element_at_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(element_at_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-window.hpp + */ + template + auto window(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(window_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(window_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-window_time.hpp + */ + template + auto window_with_time(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(window_with_time_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(window_with_time_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-window_time_count.hpp + */ + template + auto window_with_time_or_count(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(window_with_time_or_count_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(window_with_time_or_count_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-window_toggle.hpp + */ + template + auto window_toggle(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(window_toggle_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(window_toggle_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-buffer_count.hpp + */ + template + auto buffer(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(buffer_count_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(buffer_count_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-buffer_time.hpp + */ + template + auto buffer_with_time(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(buffer_with_time_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(buffer_with_time_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-buffer_time_count.hpp + */ + template + auto buffer_with_time_or_count(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(buffer_with_time_or_count_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(buffer_with_time_or_count_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-switch_on_next.hpp + */ + template + auto switch_on_next(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(switch_on_next_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(switch_on_next_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-merge.hpp + */ + template + auto merge(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(merge_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(merge_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-merge_delay_error.hpp + */ + template + auto merge_delay_error(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(merge_delay_error_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(merge_delay_error_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-amb.hpp + */ + template + auto amb(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(amb_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(amb_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-flat_map.hpp + */ + template + auto flat_map(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(flat_map_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(flat_map_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-flat_map.hpp + */ + template + auto merge_transform(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(flat_map_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(flat_map_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-concat.hpp + */ + template + auto concat(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(concat_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(concat_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-concat_map.hpp + */ + template + auto concat_map(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(concat_map_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(concat_map_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-concat_map.hpp + */ + template + auto concat_transform(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(concat_map_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(concat_map_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-with_latest_from.hpp + */ + template + auto with_latest_from(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(with_latest_from_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(with_latest_from_tag{}, *this, std::forward(an)...); + } + + + /*! @copydoc rx-combine_latest.hpp + */ + template + auto combine_latest(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(combine_latest_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(combine_latest_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-zip.hpp + */ + template + auto zip(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(zip_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(zip_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-group_by.hpp + */ + template + inline auto group_by(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(group_by_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(group_by_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-ignore_elements.hpp + */ + template + auto ignore_elements(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(ignore_elements_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(ignore_elements_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-muticast.hpp + */ + template + auto multicast(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(multicast_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(multicast_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-publish.hpp + */ + template + auto publish(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(publish_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(publish_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rxcpp::operators::publish_synchronized + */ + template + auto publish_synchronized(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(publish_synchronized_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(publish_synchronized_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-replay.hpp + */ + template + auto replay(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(replay_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(replay_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-subscribe_on.hpp + */ + template + auto subscribe_on(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(subscribe_on_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(subscribe_on_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-observe_on.hpp + */ + template + auto observe_on(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(observe_on_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(observe_on_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-reduce.hpp + */ + template + auto reduce(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(reduce_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(reduce_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-reduce.hpp + */ + template + auto accumulate(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(reduce_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(reduce_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rxcpp::operators::first + */ + template + auto first(AN**...) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(delayed_type::value(), *(this_type*)nullptr)) + /// \endcond + { + return observable_member(delayed_type::value(), *this); + static_assert(sizeof...(AN) == 0, "first() was passed too many arguments."); + } + + /*! @copydoc rxcpp::operators::last + */ + template + auto last(AN**...) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(delayed_type::value(), *(this_type*)nullptr)) + /// \endcond + { + return observable_member(delayed_type::value(), *this); + static_assert(sizeof...(AN) == 0, "last() was passed too many arguments."); + } + + /*! @copydoc rxcpp::operators::count + */ + template + auto count(AN**...) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(delayed_type::value(), *(this_type*)nullptr, 0, rxu::count(), identity_for())) + /// \endcond + { + return observable_member(delayed_type::value(), *this, 0, rxu::count(), identity_for()); + static_assert(sizeof...(AN) == 0, "count() was passed too many arguments."); + } + + /*! @copydoc rxcpp::operators::sum + */ + template + auto sum(AN**...) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(delayed_type::value(), *(this_type*)nullptr)) + /// \endcond + { + return observable_member(delayed_type::value(), *this); + static_assert(sizeof...(AN) == 0, "sum() was passed too many arguments."); + } + + /*! @copydoc rxcpp::operators::average + */ + template + auto average(AN**...) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(delayed_type::value(), *(this_type*)nullptr)) + /// \endcond + { + return observable_member(delayed_type::value(), *this); + static_assert(sizeof...(AN) == 0, "average() was passed too many arguments."); + } + + /*! @copydoc rxcpp::operators::max + */ + template + auto max(AN**...) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(delayed_type::value(), *(this_type*)nullptr)) + /// \endcond + { + return observable_member(delayed_type::value(), *this); + static_assert(sizeof...(AN) == 0, "max() was passed too many arguments."); + } + + /*! @copydoc rxcpp::operators::min + */ + template + auto min(AN**...) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(delayed_type::value(), *(this_type*)nullptr)) + /// \endcond + { + return observable_member(delayed_type::value(), *this); + static_assert(sizeof...(AN) == 0, "min() was passed too many arguments."); + } + + /*! @copydoc rx-scan.hpp + */ + template + auto scan(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(scan_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(scan_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-sample_time.hpp + */ + template + auto sample_with_time(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(sample_with_time_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(sample_with_time_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-skip.hpp + */ + template + auto skip(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(skip_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(skip_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-skip.hpp + */ + template + auto skip_while(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(skip_while_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(skip_while_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-skip_last.hpp + */ + template + auto skip_last(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(skip_last_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(skip_last_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-skip_until.hpp + */ + template + auto skip_until(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(skip_until_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(skip_until_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-take.hpp + */ + template + auto take(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(take_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(take_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-take_last.hpp + */ + template + auto take_last(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(take_last_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(take_last_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-take_until.hpp + */ + template + auto take_until(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(take_until_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(take_until_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-take_while.hpp + */ + template + auto take_while(AN&&... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(take_while_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(take_while_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-repeat.hpp + */ + template + auto repeat(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(repeat_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(repeat_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-retry.hpp + */ + template + auto retry(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(retry_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(retry_tag{}, *(this_type*)this, std::forward(an)...); + } + + /*! @copydoc rx-start_with.hpp + */ + template + auto start_with(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(start_with_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(start_with_tag{}, *this, std::forward(an)...); + } + + /*! @copydoc rx-pairwise.hpp + */ + template + auto pairwise(AN... an) const + /// \cond SHOW_SERVICE_MEMBERS + -> decltype(observable_member(pairwise_tag{}, *(this_type*)nullptr, std::forward(an)...)) + /// \endcond + { + return observable_member(pairwise_tag{}, *this, std::forward(an)...); + } +}; + +template +inline bool operator==(const observable& lhs, const observable& rhs) { + return lhs.source_operator == rhs.source_operator; +} +template +inline bool operator!=(const observable& lhs, const observable& rhs) { + return !(lhs == rhs); +} + +/*! + \defgroup group-core Basics + + \brief These are the core classes that combine to represent a set of values emitted over time that can be cancelled. + + \class rxcpp::observable + + \brief typed as ```rxcpp::observable<>```, this is a collection of factory methods that return an observable. + + \ingroup group-core + + \par Create a new type of observable + + \sample + \snippet create.cpp Create sample + \snippet output.txt Create sample + + \par Create an observable that emits a range of values + + \sample + \snippet range.cpp range sample + \snippet output.txt range sample + + \par Create an observable that emits nothing / generates an error / immediately completes + + \sample + \snippet never.cpp never sample + \snippet output.txt never sample + \snippet error.cpp error sample + \snippet output.txt error sample + \snippet empty.cpp empty sample + \snippet output.txt empty sample + + \par Create an observable that generates new observable for each subscriber + + \sample + \snippet defer.cpp defer sample + \snippet output.txt defer sample + + \par Create an observable that emits items every specified interval of time + + \sample + \snippet interval.cpp interval sample + \snippet output.txt interval sample + + \par Create an observable that emits items in the specified interval of time + + \sample + \snippet timer.cpp duration timer sample + \snippet output.txt duration timer sample + + \par Create an observable that emits all items from a collection + + \sample + \snippet iterate.cpp iterate sample + \snippet output.txt iterate sample + + \par Create an observable that emits a set of specified items + + \sample + \snippet from.cpp from sample + \snippet output.txt from sample + + \par Create an observable that emits a single item + + \sample + \snippet just.cpp just sample + \snippet output.txt just sample + + \par Create an observable that emits a set of items and then subscribes to another observable + + \sample + \snippet start_with.cpp full start_with sample + \snippet output.txt full start_with sample + + \par Create an observable that generates a new observable based on a generated resource for each subscriber + + \sample + \snippet scope.cpp scope sample + \snippet output.txt scope sample + +*/ +template<> +class observable +{ + ~observable(); +public: + /*! @copydoc rx-create.hpp + */ + template + static auto create(OnSubscribe os) + -> decltype(rxs::create(std::move(os))) { + return rxs::create(std::move(os)); + } + + /*! @copydoc rx-range.hpp + */ + template + static auto range(T first = 0, T last = std::numeric_limits::max(), std::ptrdiff_t step = 1) + -> decltype(rxs::range(first, last, step, identity_current_thread())) { + return rxs::range(first, last, step, identity_current_thread()); + } + /*! @copydoc rx-range.hpp + */ + template + static auto range(T first, T last, std::ptrdiff_t step, Coordination cn) + -> decltype(rxs::range(first, last, step, std::move(cn))) { + return rxs::range(first, last, step, std::move(cn)); + } + /*! @copydoc rx-range.hpp + */ + template + static auto range(T first, T last, Coordination cn) + -> decltype(rxs::range(first, last, std::move(cn))) { + return rxs::range(first, last, std::move(cn)); + } + /*! @copydoc rx-range.hpp + */ + template + static auto range(T first, Coordination cn) + -> decltype(rxs::range(first, std::move(cn))) { + return rxs::range(first, std::move(cn)); + } + + /*! @copydoc rx-never.hpp + */ + template + static auto never() + -> decltype(rxs::never()) { + return rxs::never(); + } + + /*! @copydoc rx-defer.hpp + */ + template + static auto defer(ObservableFactory of) + -> decltype(rxs::defer(std::move(of))) { + return rxs::defer(std::move(of)); + } + + /*! @copydoc rx-interval.hpp + */ + template + static auto interval(rxsc::scheduler::clock_type::duration period, AN**...) + -> decltype(rxs::interval(period)) { + return rxs::interval(period); + static_assert(sizeof...(AN) == 0, "interval(period) was passed too many arguments."); + } + /*! @copydoc rx-interval.hpp + */ + template + static auto interval(rxsc::scheduler::clock_type::duration period, Coordination cn) + -> decltype(rxs::interval(period, std::move(cn))) { + return rxs::interval(period, std::move(cn)); + } + /*! @copydoc rx-interval.hpp + */ + template + static auto interval(rxsc::scheduler::clock_type::time_point initial, rxsc::scheduler::clock_type::duration period, AN**...) + -> decltype(rxs::interval(initial, period)) { + return rxs::interval(initial, period); + static_assert(sizeof...(AN) == 0, "interval(initial, period) was passed too many arguments."); + } + /*! @copydoc rx-interval.hpp + */ + template + static auto interval(rxsc::scheduler::clock_type::time_point initial, rxsc::scheduler::clock_type::duration period, Coordination cn) + -> decltype(rxs::interval(initial, period, std::move(cn))) { + return rxs::interval(initial, period, std::move(cn)); + } + + /*! @copydoc rx-timer.hpp + */ + template + static auto timer(rxsc::scheduler::clock_type::time_point at, AN**...) + -> decltype(rxs::timer(at)) { + return rxs::timer(at); + static_assert(sizeof...(AN) == 0, "timer(at) was passed too many arguments."); + } + /*! @copydoc rx-timer.hpp + */ + template + static auto timer(rxsc::scheduler::clock_type::duration after, AN**...) + -> decltype(rxs::timer(after)) { + return rxs::timer(after); + static_assert(sizeof...(AN) == 0, "timer(after) was passed too many arguments."); + } + /*! @copydoc rx-timer.hpp + */ + template + static auto timer(rxsc::scheduler::clock_type::time_point when, Coordination cn) + -> decltype(rxs::timer(when, std::move(cn))) { + return rxs::timer(when, std::move(cn)); + } + /*! @copydoc rx-timer.hpp + */ + template + static auto timer(rxsc::scheduler::clock_type::duration when, Coordination cn) + -> decltype(rxs::timer(when, std::move(cn))) { + return rxs::timer(when, std::move(cn)); + } + + /*! @copydoc rx-iterate.hpp + */ + template + static auto iterate(Collection c) + -> decltype(rxs::iterate(std::move(c), identity_current_thread())) { + return rxs::iterate(std::move(c), identity_current_thread()); + } + /*! @copydoc rx-iterate.hpp + */ + template + static auto iterate(Collection c, Coordination cn) + -> decltype(rxs::iterate(std::move(c), std::move(cn))) { + return rxs::iterate(std::move(c), std::move(cn)); + } + + /*! @copydoc rxcpp::sources::from() + */ + template + static auto from() + -> decltype( rxs::from()) { + return rxs::from(); + } + /*! @copydoc rxcpp::sources::from(Coordination cn) + */ + template + static auto from(Coordination cn) + -> typename std::enable_if::value, + decltype( rxs::from(std::move(cn)))>::type { + return rxs::from(std::move(cn)); + } + /*! @copydoc rxcpp::sources::from(Value0 v0, ValueN... vn) + */ + template + static auto from(Value0 v0, ValueN... vn) + -> typename std::enable_if::value, + decltype( rxs::from(v0, vn...))>::type { + return rxs::from(v0, vn...); + } + /*! @copydoc rxcpp::sources::from(Coordination cn, Value0 v0, ValueN... vn) + */ + template + static auto from(Coordination cn, Value0 v0, ValueN... vn) + -> typename std::enable_if::value, + decltype( rxs::from(std::move(cn), v0, vn...))>::type { + return rxs::from(std::move(cn), v0, vn...); + } + + /*! @copydoc rxcpp::sources::just(Value0 v0) + */ + template + static auto just(T v) + -> decltype(rxs::just(std::move(v))) { + return rxs::just(std::move(v)); + } + /*! @copydoc rxcpp::sources::just(Value0 v0, Coordination cn) + */ + template + static auto just(T v, Coordination cn) + -> decltype(rxs::just(std::move(v), std::move(cn))) { + return rxs::just(std::move(v), std::move(cn)); + } + + /*! @copydoc rxcpp::sources::start_with(Observable o, Value0 v0, ValueN... vn) + */ + template + static auto start_with(Observable o, Value0 v0, ValueN... vn) + -> decltype(rxs::start_with(std::move(o), std::move(v0), std::move(vn)...)) { + return rxs::start_with(std::move(o), std::move(v0), std::move(vn)...); + } + + /*! @copydoc rx-empty.hpp + */ + template + static auto empty() + -> decltype(from()) { + return from(); + } + /*! @copydoc rx-empty.hpp + */ + template + static auto empty(Coordination cn) + -> decltype(from(std::move(cn))) { + return from(std::move(cn)); + } + + /*! @copydoc rx-error.hpp + */ + template + static auto error(Exception&& e) + -> decltype(rxs::error(std::forward(e))) { + return rxs::error(std::forward(e)); + } + /*! @copydoc rx-error.hpp + */ + template + static auto error(Exception&& e, Coordination cn) + -> decltype(rxs::error(std::forward(e), std::move(cn))) { + return rxs::error(std::forward(e), std::move(cn)); + } + + /*! @copydoc rx-scope.hpp + */ + template + static auto scope(ResourceFactory rf, ObservableFactory of) + -> decltype(rxs::scope(std::move(rf), std::move(of))) { + return rxs::scope(std::move(rf), std::move(of)); + } +}; + +} + +// +// support range() >> filter() >> subscribe() syntax +// '>>' is spelled 'stream' +// +template +auto operator >> (const rxcpp::observable& source, OperatorFactory&& of) + -> decltype(source.op(std::forward(of))) { + return source.op(std::forward(of)); +} + +// +// support range() | filter() | subscribe() syntax +// '|' is spelled 'pipe' +// +template +auto operator | (const rxcpp::observable& source, OperatorFactory&& of) + -> decltype(source.op(std::forward(of))) { + return source.op(std::forward(of)); +} + +#endif diff --git a/3party/rxcpp/rx-observer.hpp b/3party/rxcpp/rx-observer.hpp new file mode 100644 index 0000000..92321f1 --- /dev/null +++ b/3party/rxcpp/rx-observer.hpp @@ -0,0 +1,665 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_OBSERVER_HPP) +#define RXCPP_RX_OBSERVER_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + + +template +struct observer_base +{ + typedef T value_type; + typedef tag_observer observer_tag; +}; + +namespace detail { +template +struct OnNextEmpty +{ + void operator()(const T&) const {} +}; +struct OnErrorEmpty +{ + void operator()(rxu::error_ptr) const { + // error implicitly ignored, abort + std::terminate(); + } +}; +struct OnErrorIgnore +{ + void operator()(rxu::error_ptr) const { + } +}; +struct OnCompletedEmpty +{ + void operator()() const {} +}; + +template +struct OnNextForward +{ + using state_t = rxu::decay_t; + using onnext_t = rxu::decay_t; + OnNextForward() : onnext() {} + explicit OnNextForward(onnext_t on) : onnext(std::move(on)) {} + onnext_t onnext; + void operator()(state_t& s, T& t) const { + onnext(s, t); + } + void operator()(state_t& s, T&& t) const { + onnext(s, t); + } +}; +template +struct OnNextForward +{ + using state_t = rxu::decay_t; + OnNextForward() {} + void operator()(state_t& s, T& t) const { + s.on_next(t); + } + void operator()(state_t& s, T&& t) const { + s.on_next(t); + } +}; + +template +struct OnErrorForward +{ + using state_t = rxu::decay_t; + using onerror_t = rxu::decay_t; + OnErrorForward() : onerror() {} + explicit OnErrorForward(onerror_t oe) : onerror(std::move(oe)) {} + onerror_t onerror; + void operator()(state_t& s, rxu::error_ptr ep) const { + onerror(s, ep); + } +}; +template +struct OnErrorForward +{ + using state_t = rxu::decay_t; + OnErrorForward() {} + void operator()(state_t& s, rxu::error_ptr ep) const { + s.on_error(ep); + } +}; + +template +struct OnCompletedForward +{ + using state_t = rxu::decay_t; + using oncompleted_t = rxu::decay_t; + OnCompletedForward() : oncompleted() {} + explicit OnCompletedForward(oncompleted_t oc) : oncompleted(std::move(oc)) {} + oncompleted_t oncompleted; + void operator()(state_t& s) const { + oncompleted(s); + } +}; +template +struct OnCompletedForward +{ + OnCompletedForward() {} + void operator()(State& s) const { + s.on_completed(); + } +}; + +template +struct is_on_next_of +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CF*)nullptr)(*(CT*)nullptr)); + template + static not_void check(...); + + typedef decltype(check>(0)) detail_result; + static const bool value = std::is_same::value; +}; + +template +struct is_on_error +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CF*)nullptr)(*(rxu::error_ptr*)nullptr)); + template + static not_void check(...); + + static const bool value = std::is_same>(0)), void>::value; +}; + +template +struct is_on_error_for +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CF*)nullptr)(*(State*)nullptr, *(rxu::error_ptr*)nullptr)); + template + static not_void check(...); + + static const bool value = std::is_same>(0)), void>::value; +}; + +template +struct is_on_completed +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CF*)nullptr)()); + template + static not_void check(...); + + static const bool value = std::is_same>(0)), void>::value; +}; + +} + + +/*! + \brief consumes values from an observable using `State` that may implement on_next, on_error and on_completed with optional overrides of each function. + + \tparam T - the type of value in the stream + \tparam State - the type of the stored state + \tparam OnNext - the type of a function that matches `void(State&, T)`. Called 0 or more times. If `void` State::on_next will be called. + \tparam OnError - the type of a function that matches `void(State&, rxu::error_ptr)`. Called 0 or 1 times, no further calls will be made. If `void` State::on_error will be called. + \tparam OnCompleted - the type of a function that matches `void(State&)`. Called 0 or 1 times, no further calls will be made. If `void` State::on_completed will be called. + + \ingroup group-core + +*/ +template +class observer : public observer_base +{ +public: + using this_type = observer; + using state_t = rxu::decay_t; + using on_next_t = typename std::conditional< + !std::is_same::value, + rxu::decay_t, + detail::OnNextForward>::type; + using on_error_t = typename std::conditional< + !std::is_same::value, + rxu::decay_t, + detail::OnErrorForward>::type; + using on_completed_t = typename std::conditional< + !std::is_same::value, + rxu::decay_t, + detail::OnCompletedForward>::type; + +private: + mutable state_t state; + on_next_t onnext; + on_error_t onerror; + on_completed_t oncompleted; + +public: + + explicit observer(state_t s, on_next_t n = on_next_t(), on_error_t e = on_error_t(), on_completed_t c = on_completed_t()) + : state(std::move(s)) + , onnext(std::move(n)) + , onerror(std::move(e)) + , oncompleted(std::move(c)) + { + } + explicit observer(state_t s, on_next_t n, on_completed_t c) + : state(std::move(s)) + , onnext(std::move(n)) + , onerror(on_error_t()) + , oncompleted(std::move(c)) + { + } + observer(const this_type& o) + : state(o.state) + , onnext(o.onnext) + , onerror(o.onerror) + , oncompleted(o.oncompleted) + { + } + observer(this_type&& o) + : state(std::move(o.state)) + , onnext(std::move(o.onnext)) + , onerror(std::move(o.onerror)) + , oncompleted(std::move(o.oncompleted)) + { + } + this_type& operator=(this_type o) { + state = std::move(o.state); + onnext = std::move(o.onnext); + onerror = std::move(o.onerror); + oncompleted = std::move(o.oncompleted); + return *this; + } + + void on_next(T& t) const { + onnext(state, t); + } + void on_next(T&& t) const { + onnext(state, std::move(t)); + } + void on_error(rxu::error_ptr e) const { + onerror(state, e); + } + void on_completed() const { + oncompleted(state); + } + observer as_dynamic() const { + return observer(*this); + } +}; + +/*! + \brief consumes values from an observable using default empty method implementations with optional overrides of each function. + + \tparam T - the type of value in the stream + \tparam OnNext - the type of a function that matches `void(T)`. Called 0 or more times. If `void` OnNextEmpty is used. + \tparam OnError - the type of a function that matches `void(rxu::error_ptr)`. Called 0 or 1 times, no further calls will be made. If `void` OnErrorEmpty is used. + \tparam OnCompleted - the type of a function that matches `void()`. Called 0 or 1 times, no further calls will be made. If `void` OnCompletedEmpty is used. + + \ingroup group-core + +*/ +template +class observer : public observer_base +{ +public: + using this_type = observer; + using on_next_t = typename std::conditional< + !std::is_same::value, + rxu::decay_t, + detail::OnNextEmpty>::type; + using on_error_t = typename std::conditional< + !std::is_same::value, + rxu::decay_t, + detail::OnErrorEmpty>::type; + using on_completed_t = typename std::conditional< + !std::is_same::value, + rxu::decay_t, + detail::OnCompletedEmpty>::type; + +private: + on_next_t onnext; + on_error_t onerror; + on_completed_t oncompleted; + +public: + static_assert(detail::is_on_next_of::value, "Function supplied for on_next must be a function with the signature void(T);"); + static_assert(detail::is_on_error::value, "Function supplied for on_error must be a function with the signature void(rxu::error_ptr);"); + static_assert(detail::is_on_completed::value, "Function supplied for on_completed must be a function with the signature void();"); + + observer() + : onnext(on_next_t()) + , onerror(on_error_t()) + , oncompleted(on_completed_t()) + { + } + + explicit observer(on_next_t n, on_error_t e = on_error_t(), on_completed_t c = on_completed_t()) + : onnext(std::move(n)) + , onerror(std::move(e)) + , oncompleted(std::move(c)) + { + } + observer(const this_type& o) + : onnext(o.onnext) + , onerror(o.onerror) + , oncompleted(o.oncompleted) + { + } + observer(this_type&& o) + : onnext(std::move(o.onnext)) + , onerror(std::move(o.onerror)) + , oncompleted(std::move(o.oncompleted)) + { + } + this_type& operator=(this_type o) { + onnext = std::move(o.onnext); + onerror = std::move(o.onerror); + oncompleted = std::move(o.oncompleted); + return *this; + } + + void on_next(T& t) const { + onnext(t); + } + void on_next(T&& t) const { + onnext(std::move(t)); + } + void on_error(rxu::error_ptr e) const { + onerror(e); + } + void on_completed() const { + oncompleted(); + } + observer as_dynamic() const { + return observer(*this); + } +}; + +namespace detail +{ + +template +struct virtual_observer : public std::enable_shared_from_this> +{ + virtual ~virtual_observer() {} + virtual void on_next(T&) const {}; + virtual void on_next(T&&) const {}; + virtual void on_error(rxu::error_ptr) const {}; + virtual void on_completed() const {}; +}; + +template +struct specific_observer : public virtual_observer +{ + explicit specific_observer(Observer o) + : destination(std::move(o)) + { + } + + Observer destination; + virtual void on_next(T& t) const { + destination.on_next(t); + } + virtual void on_next(T&& t) const { + destination.on_next(std::move(t)); + } + virtual void on_error(rxu::error_ptr e) const { + destination.on_error(e); + } + virtual void on_completed() const { + destination.on_completed(); + } +}; + +} + +/*! + \brief consumes values from an observable using type-forgetting (shared allocated state with virtual methods) + + \tparam T - the type of value in the stream + + \ingroup group-core + +*/ +template +class observer : public observer_base +{ +public: + typedef tag_dynamic_observer dynamic_observer_tag; + +private: + using this_type = observer; + using base_type = observer_base; + using virtual_observer = detail::virtual_observer; + + std::shared_ptr destination; + + template + static auto make_destination(Observer o) + -> std::shared_ptr { + return std::make_shared>(std::move(o)); + } + +public: + observer() + { + } + observer(const this_type& o) + : destination(o.destination) + { + } + observer(this_type&& o) + : destination(std::move(o.destination)) + { + } + + template + explicit observer(Observer o) + : destination(make_destination(std::move(o))) + { + } + + this_type& operator=(this_type o) { + destination = std::move(o.destination); + return *this; + } + + // perfect forwarding delays the copy of the value. + template + void on_next(V&& v) const { + if (destination) { + destination->on_next(std::forward(v)); + } + } + void on_error(rxu::error_ptr e) const { + if (destination) { + destination->on_error(e); + } + } + void on_completed() const { + if (destination) { + destination->on_completed(); + } + } + + observer as_dynamic() const { + return *this; + } +}; + +template +auto make_observer() + -> observer, DefaultOnError> { + return observer, DefaultOnError>(); +} + +template +auto make_observer(observer o) + -> observer { + return observer(std::move(o)); +} +template +auto make_observer(Observer ob) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !detail::is_on_error::value && + is_observer::value, + Observer>::type { + return std::move(ob); +} +template +auto make_observer(Observer ob) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !detail::is_on_error::value && + !is_observer::value, + observer>::type { + return observer(std::move(ob)); +} +template +auto make_observer(OnNext on) + -> typename std::enable_if< + detail::is_on_next_of::value, + observer>::type { + return observer( + std::move(on)); +} +template +auto make_observer(OnError oe) + -> typename std::enable_if< + !detail::is_on_next_of::value && + detail::is_on_error::value, + observer, OnError>>::type { + return observer, OnError>( + detail::OnNextEmpty(), std::move(oe)); +} +template +auto make_observer(OnNext on, OnError oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + observer>::type { + return observer( + std::move(on), std::move(oe)); +} +template +auto make_observer(OnNext on, OnCompleted oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + observer>::type { + return observer( + std::move(on), DefaultOnError(), std::move(oc)); +} +template +auto make_observer(OnNext on, OnError oe, OnCompleted oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + observer>::type { + return observer( + std::move(on), std::move(oe), std::move(oc)); +} + + +template +auto make_observer(State os, OnNext on) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !detail::is_on_error::value, + observer>::type { + return observer( + std::move(os), std::move(on)); +} +template +auto make_observer(State os, OnError oe) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !detail::is_on_error::value && + detail::is_on_error_for::value, + observer, OnError>>::type { + return observer, OnError>( + std::move(os), detail::OnNextEmpty(), std::move(oe)); +} +template +auto make_observer(State os, OnNext on, OnError oe) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !detail::is_on_error::value && + detail::is_on_error_for::value, + observer>::type { + return observer( + std::move(os), std::move(on), std::move(oe)); +} +template +auto make_observer(State os, OnNext on, OnCompleted oc) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !detail::is_on_error::value, + observer>::type { + return observer( + std::move(os), std::move(on), std::move(oc)); +} +template +auto make_observer(State os, OnNext on, OnError oe, OnCompleted oc) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !detail::is_on_error::value && + detail::is_on_error_for::value, + observer>::type { + return observer( + std::move(os), std::move(on), std::move(oe), std::move(oc)); +} + +template +auto make_observer_dynamic(Observer o) + -> typename std::enable_if< + !detail::is_on_next_of::value, + observer>::type { + return observer(std::move(o)); +} +template +auto make_observer_dynamic(OnNext&& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + observer>::type { + return observer( + make_observer(std::forward(on))); +} +template +auto make_observer_dynamic(OnNext&& on, OnError&& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + observer>::type { + return observer( + make_observer(std::forward(on), std::forward(oe))); +} +template +auto make_observer_dynamic(OnNext&& on, OnCompleted&& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + observer>::type { + return observer( + make_observer(std::forward(on), std::forward(oc))); +} +template +auto make_observer_dynamic(OnNext&& on, OnError&& oe, OnCompleted&& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + observer>::type { + return observer( + make_observer(std::forward(on), std::forward(oe), std::forward(oc))); +} + +namespace detail { + +template +struct maybe_from_result +{ + typedef decltype((*(F*)nullptr)()) decl_result_type; + typedef rxu::decay_t result_type; + typedef rxu::maybe type; +}; + +} + +template +auto on_exception(const F& f, const OnError& c) + -> typename std::enable_if::value, typename detail::maybe_from_result::type>::type { + typename detail::maybe_from_result::type r; + RXCPP_TRY { + r.reset(f()); + } RXCPP_CATCH(...) { + c(rxu::current_exception()); + } + return r; +} + +template +auto on_exception(const F& f, const Subscriber& s) + -> typename std::enable_if::value, typename detail::maybe_from_result::type>::type { + typename detail::maybe_from_result::type r; + RXCPP_TRY { + r.reset(f()); + } RXCPP_CATCH(...) { + s.on_error(rxu::current_exception()); + } + return r; +} + +} + +#endif diff --git a/3party/rxcpp/rx-operators.hpp b/3party/rxcpp/rx-operators.hpp new file mode 100644 index 0000000..aa7d7f4 --- /dev/null +++ b/3party/rxcpp/rx-operators.hpp @@ -0,0 +1,534 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_OPERATORS_HPP) +#define RXCPP_RX_OPERATORS_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace operators { + +struct tag_operator {}; +template +struct operator_base +{ + typedef T value_type; + typedef tag_operator operator_tag; +}; + +namespace detail { + +template +struct is_operator : std::false_type +{ +}; + +template +struct is_operator> + : std::is_convertible +{ +}; + +} + +template> +struct is_operator : detail::is_operator +{ +}; + + +} +namespace rxo=operators; + +template +struct member_overload +{ + template + static auto member(AN&&...) -> + typename Tag::template include_header { + return typename Tag::template include_header(); + } +}; + +template +struct delayed_type{using value_type = T; static T value(AN**...) {return T{};}}; + +template +using delayed_type_t = rxu::value_type_t>; + +template>> +auto observable_member(Tag, AN&&... an) -> + decltype(Overload::member(std::forward(an)...)) { + return Overload::member(std::forward(an)...); +} + +template +class operator_factory +{ + using this_type = operator_factory; + using tag_type = rxu::decay_t; + using tuple_type = std::tuple...>; + + tuple_type an; + +public: + operator_factory(tuple_type an) + : an(std::move(an)) + { + } + + template + auto operator()(tag_type t, ZN&&... zn) const + -> decltype(observable_member(t, std::forward(zn)...)) { + return observable_member(t, std::forward(zn)...); + } + + template + auto operator()(Observable source) const + -> decltype(rxu::apply(std::tuple_cat(std::make_tuple(tag_type{}, source), (*(tuple_type*)nullptr)), (*(this_type*)nullptr))) { + return rxu::apply(std::tuple_cat(std::make_tuple(tag_type{}, source), an), *this); + } +}; + +} + +#include "operators/rx-lift.hpp" +#include "operators/rx-subscribe.hpp" + +namespace rxcpp { + +struct amb_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct all_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct is_empty_tag : all_tag {}; + +struct any_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct exists_tag : any_tag {}; +struct contains_tag : any_tag {}; + +struct buffer_count_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct buffer_with_time_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct buffer_with_time_or_count_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct combine_latest_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct concat_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct concat_map_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct connect_forever_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct debounce_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct delay_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct distinct_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct distinct_until_changed_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct element_at_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct filter_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct finally_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct flat_map_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct group_by_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct ignore_elements_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct map_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct merge_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; +struct merge_delay_error_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct multicast_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct observe_on_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct on_error_resume_next_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +class empty_error: public std::runtime_error +{ + public: + explicit empty_error(const std::string& msg): + std::runtime_error(msg) + {} +}; +struct reduce_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; +struct first_tag : reduce_tag {}; +struct last_tag : reduce_tag {}; +struct sum_tag : reduce_tag {}; +struct average_tag : reduce_tag {}; +struct min_tag : reduce_tag {}; +struct max_tag : reduce_tag {}; + +struct ref_count_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct pairwise_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct publish_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; +struct publish_synchronized_tag : publish_tag {}; + +struct repeat_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct replay_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct retry_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct sample_with_time_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct scan_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct sequence_equal_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct skip_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct skip_while_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct skip_last_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct skip_until_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct start_with_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct subscribe_on_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct switch_if_empty_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; +struct default_if_empty_tag : switch_if_empty_tag {}; + +struct switch_on_next_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct take_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct take_last_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct take_while_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct take_until_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct tap_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct timeout_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct time_interval_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct timestamp_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct window_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct window_with_time_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct window_with_time_or_count_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct window_toggle_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct with_latest_from_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +struct zip_tag { + template + struct include_header{ + static_assert(Included::value, "missing include: please #include "); + }; +}; + +} + +#include "operators/rx-multicast.hpp" +#include "operators/rx-publish.hpp" +#include "operators/rx-ref_count.hpp" + +#endif diff --git a/3party/rxcpp/rx-predef.hpp b/3party/rxcpp/rx-predef.hpp new file mode 100644 index 0000000..908a380 --- /dev/null +++ b/3party/rxcpp/rx-predef.hpp @@ -0,0 +1,317 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_PREDEF_HPP) +#define RXCPP_RX_PREDEF_HPP + +#include "rx-includes.hpp" +#include "rx-observable-fwd.hpp" + +namespace rxcpp { + +// +// create a typedef for rxcpp_trace_type to override the default +// +inline auto trace_activity() -> decltype(rxcpp_trace_activity(trace_tag()))& { + static decltype(rxcpp_trace_activity(trace_tag())) trace; + return trace; +} + + +struct tag_action {}; +template +struct is_action : public std::false_type {}; + +template +struct is_action::type> + : public std::is_convertible {}; + + +struct tag_worker {}; +template +class is_worker +{ + struct not_void {}; + template + static typename C::worker_tag* check(int); + template + static not_void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_worker*>::value; +}; + +struct tag_scheduler {}; +template +class is_scheduler +{ + struct not_void {}; + template + static typename C::scheduler_tag* check(int); + template + static not_void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_scheduler*>::value; +}; + +struct tag_schedulable {}; +template +class is_schedulable +{ + struct not_void {}; + template + static typename C::schedulable_tag* check(int); + template + static not_void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_schedulable*>::value; +}; + +namespace detail +{ + +struct stateless_observer_tag {}; + +} + +// state with optional overrides +template +class observer; + +// no state with optional overrides +template +class observer; + +// virtual functions forward to dynamically allocated shared observer instance. +template +class observer; + +struct tag_observer {}; +template +class is_observer +{ + template + static typename C::observer_tag* check(int); + template + static void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_observer*>::value; +}; + +struct tag_dynamic_observer {}; +template +class is_dynamic_observer +{ + struct not_void {}; + template + static typename C::dynamic_observer_tag* check(int); + template + static not_void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_dynamic_observer*>::value; +}; + +struct tag_subscriber {}; +template +class is_subscriber +{ + struct not_void {}; + template + static typename C::subscriber_tag* check(int); + template + static not_void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_subscriber*>::value; +}; + +struct tag_dynamic_observable {}; +template +class is_dynamic_observable +{ + struct not_void {}; + template + static typename C::dynamic_observable_tag* check(int); + template + static not_void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_dynamic_observable*>::value; +}; + +template class SO, class... AN> +struct defer_observable; + +struct tag_observable {}; +template +struct observable_base { + typedef tag_observable observable_tag; + typedef T value_type; +}; + +namespace detail { + +template +struct is_observable : std::false_type +{ +}; + +template +struct is_observable> + : std::is_convertible +{ +}; + +} + +template> +struct is_observable : detail::is_observable +{ +}; + +template> +using observable_tag_t = typename DecayedObservable::observable_tag; + +// extra indirection for vs2013 support +template +struct expand_observable_tags { struct type; }; +template +struct expand_observable_tags, rxu::types_checked_t> +{ + using type = rxu::types; +}; +template +using observable_tags_t = typename expand_observable_tags>::type; + +template +using all_observables = rxu::all_true_type...>; + +struct tag_dynamic_connectable_observable : public tag_dynamic_observable {}; + +template +class is_dynamic_connectable_observable +{ + struct not_void {}; + template + static typename C::dynamic_observable_tag* check(int); + template + static not_void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_dynamic_connectable_observable*>::value; +}; + +template +class dynamic_connectable_observable; + +template::value, + void, dynamic_connectable_observable>::type> +class connectable_observable; + +struct tag_connectable_observable : public tag_observable {}; +template +class is_connectable_observable +{ + template + static typename C::observable_tag check(int); + template + static void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_connectable_observable>::value; +}; + +struct tag_dynamic_grouped_observable : public tag_dynamic_observable {}; + +template +class is_dynamic_grouped_observable +{ + struct not_void {}; + template + static typename C::dynamic_observable_tag* check(int); + template + static not_void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_dynamic_grouped_observable*>::value; +}; + +template +class dynamic_grouped_observable; + +template::value, + void, dynamic_grouped_observable>::type> +class grouped_observable; + +template +grouped_observable make_dynamic_grouped_observable(Source&& s); + +struct tag_grouped_observable : public tag_observable {}; +template +class is_grouped_observable +{ + template + static typename C::observable_tag check(int); + template + static void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_grouped_observable>::value; +}; + +template +struct is_operator_factory_for { + using function_type = rxu::decay_t; + using source_type = rxu::decay_t; + +// check methods instead of void_t for vs2013 support + + struct tag_not_valid; + template + static auto check(int) -> decltype((*(CS*)nullptr)((*(CO*)nullptr))); + template + static tag_not_valid check(...); + + using type = decltype(check(0)); + + static const bool value = !std::is_same::value && is_observable::value; +}; + +// +// this type is the default used by operators that subscribe to +// multiple sources. It assumes that the sources are already synchronized +// +struct identity_observable +{ + template + auto operator()(Observable o) + -> Observable { + return std::move(o); + static_assert(is_observable::value, "only support observables"); + } +}; + +template +struct identity_for +{ + T operator()(T t) { + return std::move(t); + } +}; + +template +struct is_accumulate_function_for { + + typedef rxu::decay_t accumulator_type; + typedef rxu::decay_t seed_type; + typedef T source_value_type; + + struct tag_not_valid {}; + template + static auto check(int) -> decltype((*(CRS*)nullptr)(*(CS*)nullptr, *(CV*)nullptr)); + template + static tag_not_valid check(...); + + typedef decltype(check(0)) type; + static const bool value = std::is_same::value; +}; + +} + +#endif diff --git a/3party/rxcpp/rx-scheduler.hpp b/3party/rxcpp/rx-scheduler.hpp new file mode 100644 index 0000000..b6fd831 --- /dev/null +++ b/3party/rxcpp/rx-scheduler.hpp @@ -0,0 +1,961 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SCHEDULER_HPP) +#define RXCPP_RX_SCHEDULER_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +class worker_interface; +class scheduler_interface; + +namespace detail { + +class action_type; +typedef std::shared_ptr action_ptr; + +typedef std::shared_ptr worker_interface_ptr; +typedef std::shared_ptr const_worker_interface_ptr; + +typedef std::weak_ptr worker_interface_weak_ptr; +typedef std::weak_ptr const_worker_interface_weak_ptr; + +typedef std::shared_ptr scheduler_interface_ptr; +typedef std::shared_ptr const_scheduler_interface_ptr; + +inline action_ptr shared_empty() { + static action_ptr shared_empty = std::make_shared(); + return shared_empty; +} + +} + +// It is essential to keep virtual function calls out of an inner loop. +// To make tail-recursion work efficiently the recursion objects create +// a space on the stack inside the virtual function call in the actor that +// allows the callback and the scheduler to share stack space that records +// the request and the allowance without any virtual calls in the loop. + +/// recursed is set on a schedulable by the action to allow the called +/// function to request to be rescheduled. +class recursed +{ + bool& isrequested; + recursed operator=(const recursed&); +public: + explicit recursed(bool& r) + : isrequested(r) + { + } + /// request to be rescheduled + inline void operator()() const { + isrequested = true; + } +}; + +/// recurse is passed to the action by the scheduler. +/// the action uses recurse to coordinate the scheduler and the function. +class recurse +{ + std::atomic& isallowed; + mutable bool isrequested; + recursed requestor; + recurse operator=(const recurse&); +public: + explicit recurse(std::atomic& a) + : isallowed(a) + , isrequested(true) + , requestor(isrequested) + { + } + /// does the scheduler allow tail-recursion now? + inline bool is_allowed() const { + return isallowed; + } + /// did the function request to be recursed? + inline bool is_requested() const { + return isrequested; + } + /// reset the function request. call before each call to the function. + inline void reset() const { + isrequested = false; + } + /// get the recursed to set into the schedulable for the function to use to request recursion + inline const recursed& get_recursed() const { + return requestor; + } +}; + +/// recursion is used by the scheduler to signal to each action whether tail recursion is allowed. +class recursion +{ + mutable std::atomic isallowed; + recurse recursor; + recursion operator=(const recursion&); +public: + recursion() + : isallowed(true) + , recursor(isallowed) + { + } + explicit recursion(bool b) + : isallowed(b) + , recursor(isallowed) + { + } + /// set whether tail-recursion is allowed + inline void reset(bool b = true) const { + isallowed = b; + } + /// get the recurse to pass into each action being called + inline const recurse& get_recurse() const { + return recursor; + } +}; + + +struct action_base +{ + typedef tag_action action_tag; +}; + +class schedulable; + +/// action provides type-forgetting for a potentially recursive set of calls to a function that takes a schedulable +class action : public action_base +{ + typedef action this_type; + detail::action_ptr inner; +public: + action() + { + } + explicit action(detail::action_ptr i) + : inner(std::move(i)) + { + } + + /// return the empty action + inline static action empty() { + return action(detail::shared_empty()); + } + + /// call the function + inline void operator()(const schedulable& s, const recurse& r) const; +}; + +struct scheduler_base +{ + typedef std::chrono::steady_clock clock_type; + typedef tag_scheduler scheduler_tag; +}; + +struct worker_base : public subscription_base +{ + typedef tag_worker worker_tag; +}; + +class worker_interface + : public std::enable_shared_from_this +{ + typedef worker_interface this_type; + +public: + typedef scheduler_base::clock_type clock_type; + + virtual ~worker_interface() {} + + virtual clock_type::time_point now() const = 0; + + virtual void schedule(const schedulable& scbl) const = 0; + virtual void schedule(clock_type::time_point when, const schedulable& scbl) const = 0; +}; + +namespace detail { + +template +struct is_action_function +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CF*)nullptr)(*(schedulable*)nullptr)); + template + static not_void check(...); + + static const bool value = std::is_same>(0)), void>::value; +}; + +} + +class weak_worker; + +/// a worker ensures that all scheduled actions on the same instance are executed in-order with no overlap +/// a worker ensures that all scheduled actions are unsubscribed when it is unsubscribed +/// some inner implementations will impose additional constraints on the execution of items. +class worker : public worker_base +{ + typedef worker this_type; + detail::worker_interface_ptr inner; + composite_subscription lifetime; + friend bool operator==(const worker&, const worker&); + friend class weak_worker; +public: + typedef scheduler_base::clock_type clock_type; + typedef composite_subscription::weak_subscription weak_subscription; + + worker() + { + } + worker(composite_subscription cs, detail::const_worker_interface_ptr i) + : inner(std::const_pointer_cast(i)) + , lifetime(std::move(cs)) + { + } + worker(composite_subscription cs, worker o) + : inner(o.inner) + , lifetime(std::move(cs)) + { + } + + inline const composite_subscription& get_subscription() const { + return lifetime; + } + inline composite_subscription& get_subscription() { + return lifetime; + } + + // composite_subscription + // + inline bool is_subscribed() const { + return lifetime.is_subscribed(); + } + inline weak_subscription add(subscription s) const { + return lifetime.add(std::move(s)); + } + inline void remove(weak_subscription w) const { + return lifetime.remove(std::move(w)); + } + inline void clear() const { + return lifetime.clear(); + } + inline void unsubscribe() const { + return lifetime.unsubscribe(); + } + + // worker_interface + // + /// return the current time for this worker + inline clock_type::time_point now() const { + return inner->now(); + } + + /// insert the supplied schedulable to be run as soon as possible + inline void schedule(const schedulable& scbl) const { + // force rebinding scbl to this worker + schedule_rebind(scbl); + } + + /// insert the supplied schedulable to be run at the time specified + inline void schedule(clock_type::time_point when, const schedulable& scbl) const { + // force rebinding scbl to this worker + schedule_rebind(when, scbl); + } + + // helpers + // + + /// insert the supplied schedulable to be run at now() + the delay specified + inline void schedule(clock_type::duration when, const schedulable& scbl) const { + // force rebinding scbl to this worker + schedule_rebind(now() + when, scbl); + } + + /// insert the supplied schedulable to be run at the initial time specified and then again at initial + (N * period) + /// this will continue until the worker or schedulable is unsubscribed. + inline void schedule_periodically(clock_type::time_point initial, clock_type::duration period, const schedulable& scbl) const { + // force rebinding scbl to this worker + schedule_periodically_rebind(initial, period, scbl); + } + + /// insert the supplied schedulable to be run at now() + the initial delay specified and then again at now() + initial + (N * period) + /// this will continue until the worker or schedulable is unsubscribed. + inline void schedule_periodically(clock_type::duration initial, clock_type::duration period, const schedulable& scbl) const { + // force rebinding scbl to this worker + schedule_periodically_rebind(now() + initial, period, scbl); + } + + /// use the supplied arguments to make a schedulable and then insert it to be run + template + auto schedule(Arg0&& a0, ArgN&&... an) const + -> typename std::enable_if< + (detail::is_action_function::value || + is_subscription::value) && + !is_schedulable::value>::type; + template + /// use the supplied arguments to make a schedulable and then insert it to be run + void schedule_rebind(const schedulable& scbl, ArgN&&... an) const; + + /// use the supplied arguments to make a schedulable and then insert it to be run + template + auto schedule(clock_type::time_point when, Arg0&& a0, ArgN&&... an) const + -> typename std::enable_if< + (detail::is_action_function::value || + is_subscription::value) && + !is_schedulable::value>::type; + /// use the supplied arguments to make a schedulable and then insert it to be run + template + void schedule_rebind(clock_type::time_point when, const schedulable& scbl, ArgN&&... an) const; + + /// use the supplied arguments to make a schedulable and then insert it to be run + template + auto schedule_periodically(clock_type::time_point initial, clock_type::duration period, Arg0&& a0, ArgN&&... an) const + -> typename std::enable_if< + (detail::is_action_function::value || + is_subscription::value) && + !is_schedulable::value>::type; + /// use the supplied arguments to make a schedulable and then insert it to be run + template + void schedule_periodically_rebind(clock_type::time_point initial, clock_type::duration period, const schedulable& scbl, ArgN&&... an) const; +}; + +inline bool operator==(const worker& lhs, const worker& rhs) { + return lhs.inner == rhs.inner && lhs.lifetime == rhs.lifetime; +} +inline bool operator!=(const worker& lhs, const worker& rhs) { + return !(lhs == rhs); +} + +class weak_worker +{ + detail::worker_interface_weak_ptr inner; + composite_subscription lifetime; + +public: + weak_worker() + { + } + explicit weak_worker(worker& owner) + : inner(owner.inner) + , lifetime(owner.lifetime) + { + } + + worker lock() const { + return worker(lifetime, inner.lock()); + } +}; + +class scheduler_interface + : public std::enable_shared_from_this +{ + typedef scheduler_interface this_type; + +public: + typedef scheduler_base::clock_type clock_type; + + virtual ~scheduler_interface() {} + + virtual clock_type::time_point now() const = 0; + + virtual worker create_worker(composite_subscription cs) const = 0; +}; + + +struct schedulable_base : + // public subscription_base, <- already in worker base + public worker_base, + public action_base +{ + typedef tag_schedulable schedulable_tag; +}; + +/*! + \brief allows functions to be called at specified times and possibly in other contexts. + + \ingroup group-core + +*/ +class scheduler : public scheduler_base +{ + typedef scheduler this_type; + detail::scheduler_interface_ptr inner; + friend bool operator==(const scheduler&, const scheduler&); +public: + typedef scheduler_base::clock_type clock_type; + + scheduler() + { + } + explicit scheduler(detail::scheduler_interface_ptr i) + : inner(std::move(i)) + { + } + explicit scheduler(detail::const_scheduler_interface_ptr i) + : inner(std::const_pointer_cast(i)) + { + } + + /// return the current time for this scheduler + inline clock_type::time_point now() const { + return inner->now(); + } + /// create a worker with a lifetime. + /// when the worker is unsubscribed all scheduled items will be unsubscribed. + /// items scheduled to a worker will be run one at a time. + /// scheduling order is preserved: when more than one item is scheduled for + /// time T then at time T they will be run in the order that they were scheduled. + inline worker create_worker(composite_subscription cs = composite_subscription()) const { + return inner->create_worker(cs); + } +}; + +template +inline scheduler make_scheduler(ArgN&&... an) { + return scheduler(std::static_pointer_cast(std::make_shared(std::forward(an)...))); +} + +inline scheduler make_scheduler(std::shared_ptr si) { + return scheduler(si); +} + +class schedulable : public schedulable_base +{ + typedef schedulable this_type; + + composite_subscription lifetime; + weak_worker controller; + action activity; + bool scoped; + composite_subscription::weak_subscription action_scope; + + struct detacher + { + ~detacher() + { + if (that) { + that->unsubscribe(); + } + } + detacher(const this_type* that) + : that(that) + { + } + const this_type* that; + }; + + class recursed_scope_type + { + mutable const recursed* requestor; + + class exit_recursed_scope_type + { + const recursed_scope_type* that; + public: + ~exit_recursed_scope_type() + { + if (that != nullptr) { + that->requestor = nullptr; + } + } + exit_recursed_scope_type(const recursed_scope_type* that) + : that(that) + { + } + exit_recursed_scope_type(exit_recursed_scope_type && other) RXCPP_NOEXCEPT + : that(other.that) + { + other.that = nullptr; + } + }; + public: + recursed_scope_type() + : requestor(nullptr) + { + } + recursed_scope_type(const recursed_scope_type&) + : requestor(nullptr) + { + // does not aquire recursion scope + } + recursed_scope_type& operator=(const recursed_scope_type& ) + { + // no change in recursion scope + return *this; + } + exit_recursed_scope_type reset(const recurse& r) const { + requestor = std::addressof(r.get_recursed()); + return exit_recursed_scope_type(this); + } + bool is_recursed() const { + return !!requestor; + } + void operator()() const { + (*requestor)(); + } + }; + recursed_scope_type recursed_scope; + +public: + typedef composite_subscription::weak_subscription weak_subscription; + typedef scheduler_base::clock_type clock_type; + + ~schedulable() + { + if (scoped) { + controller.lock().remove(action_scope); + } + } + schedulable() + : scoped(false) + { + } + + /// action and worker share lifetime + schedulable(worker q, action a) + : lifetime(q.get_subscription()) + , controller(q) + , activity(std::move(a)) + , scoped(false) + { + } + /// action and worker have independent lifetimes + schedulable(composite_subscription cs, worker q, action a) + : lifetime(std::move(cs)) + , controller(q) + , activity(std::move(a)) + , scoped(true) + , action_scope(controller.lock().add(lifetime)) + { + } + /// inherit lifetimes + schedulable(schedulable scbl, worker q, action a) + : lifetime(scbl.get_subscription()) + , controller(q) + , activity(std::move(a)) + , scoped(scbl.scoped) + , action_scope(scbl.scoped ? controller.lock().add(lifetime) : weak_subscription()) + { + } + + inline const composite_subscription& get_subscription() const { + return lifetime; + } + inline composite_subscription& get_subscription() { + return lifetime; + } + inline const worker get_worker() const { + return controller.lock(); + } + inline worker get_worker() { + return controller.lock(); + } + inline const action& get_action() const { + return activity; + } + inline action& get_action() { + return activity; + } + + inline static schedulable empty(worker sc) { + return schedulable(composite_subscription::empty(), sc, action::empty()); + } + + inline auto set_recursed(const recurse& r) const + -> decltype(recursed_scope.reset(r)) { + return recursed_scope.reset(r); + } + + // recursed + // + bool is_recursed() const { + return recursed_scope.is_recursed(); + } + /// requests tail-recursion of the same action + /// this will exit the process if called when + /// is_recursed() is false. + /// Note: to improve perf it is not required + /// to call is_recursed() before calling this + /// operator. Context is sufficient. The schedulable + /// passed to the action by the scheduler will return + /// true from is_recursed() + inline void operator()() const { + recursed_scope(); + } + + // composite_subscription + // + inline bool is_subscribed() const { + return lifetime.is_subscribed(); + } + inline weak_subscription add(subscription s) const { + return lifetime.add(std::move(s)); + } + template + auto add(F f) const + -> typename std::enable_if::value, weak_subscription>::type { + return lifetime.add(make_subscription(std::move(f))); + } + inline void remove(weak_subscription w) const { + return lifetime.remove(std::move(w)); + } + inline void clear() const { + return lifetime.clear(); + } + inline void unsubscribe() const { + return lifetime.unsubscribe(); + } + + // scheduler + // + inline clock_type::time_point now() const { + return controller.lock().now(); + } + /// put this on the queue of the stored scheduler to run asap + inline void schedule() const { + if (is_subscribed()) { + get_worker().schedule(*this); + } + } + /// put this on the queue of the stored scheduler to run at the specified time + inline void schedule(clock_type::time_point when) const { + if (is_subscribed()) { + get_worker().schedule(when, *this); + } + } + /// put this on the queue of the stored scheduler to run after a delay from now + inline void schedule(clock_type::duration when) const { + if (is_subscribed()) { + get_worker().schedule(when, *this); + } + } + + // action + // + /// invokes the action + inline void operator()(const recurse& r) const { + if (!is_subscribed()) { + return; + } + detacher protect(this); + activity(*this, r); + protect.that = nullptr; + } +}; + +struct current_thread; + +namespace detail { + +class action_type + : public std::enable_shared_from_this +{ + typedef action_type this_type; + +public: + typedef std::function function_type; + +private: + function_type f; + +public: + action_type() + { + } + + action_type(function_type f) + : f(std::move(f)) + { + } + + inline void operator()(const schedulable& s, const recurse& r) { + if (!f) { + std::terminate(); + } + f(s, r); + } +}; + +class action_tailrecurser + : public std::enable_shared_from_this +{ + typedef action_type this_type; + +public: + typedef std::function function_type; + +private: + function_type f; + +public: + action_tailrecurser() + { + } + + action_tailrecurser(function_type f) + : f(std::move(f)) + { + } + + inline void operator()(const schedulable& s, const recurse& r) { + if (!f) { + std::terminate(); + } + trace_activity().action_enter(s); + auto scope = s.set_recursed(r); + while (s.is_subscribed()) { + r.reset(); + f(s); + if (!r.is_allowed() || !r.is_requested()) { + if (r.is_requested()) { + s.schedule(); + } + break; + } + trace_activity().action_recurse(s); + } + trace_activity().action_return(s); + } +}; +} + +inline void action::operator()(const schedulable& s, const recurse& r) const { + (*inner)(s, r); +} + +inline action make_action_empty() { + return action::empty(); +} + +template +inline action make_action(F&& f) { + static_assert(detail::is_action_function::value, "action function must be void(schedulable)"); + auto fn = std::forward(f); + return action(std::make_shared(detail::action_tailrecurser(fn))); +} + +// copy +inline auto make_schedulable( + const schedulable& scbl) + -> schedulable { + return schedulable(scbl); +} +// move +inline auto make_schedulable( + schedulable&& scbl) + -> schedulable { + return schedulable(std::move(scbl)); +} + +inline schedulable make_schedulable(worker sc, action a) { + return schedulable(sc, a); +} +inline schedulable make_schedulable(worker sc, composite_subscription cs, action a) { + return schedulable(cs, sc, a); +} + +template +auto make_schedulable(worker sc, F&& f) + -> typename std::enable_if::value, schedulable>::type { + return schedulable(sc, make_action(std::forward(f))); +} +template +auto make_schedulable(worker sc, composite_subscription cs, F&& f) + -> typename std::enable_if::value, schedulable>::type { + return schedulable(cs, sc, make_action(std::forward(f))); +} +template +auto make_schedulable(schedulable scbl, composite_subscription cs, F&& f) + -> typename std::enable_if::value, schedulable>::type { + return schedulable(cs, scbl.get_worker(), make_action(std::forward(f))); +} +template +auto make_schedulable(schedulable scbl, worker sc, F&& f) + -> typename std::enable_if::value, schedulable>::type { + return schedulable(scbl, sc, make_action(std::forward(f))); +} +template +auto make_schedulable(schedulable scbl, F&& f) + -> typename std::enable_if::value, schedulable>::type { + return schedulable(scbl, scbl.get_worker(), make_action(std::forward(f))); +} + +inline auto make_schedulable(schedulable scbl, composite_subscription cs) + -> schedulable { + return schedulable(cs, scbl.get_worker(), scbl.get_action()); +} +inline auto make_schedulable(schedulable scbl, worker sc, composite_subscription cs) + -> schedulable { + return schedulable(cs, sc, scbl.get_action()); +} +inline auto make_schedulable(schedulable scbl, worker sc) + -> schedulable { + return schedulable(scbl, sc, scbl.get_action()); +} + +template +auto worker::schedule(Arg0&& a0, ArgN&&... an) const + -> typename std::enable_if< + (detail::is_action_function::value || + is_subscription::value) && + !is_schedulable::value>::type { + auto scbl = make_schedulable(*this, std::forward(a0), std::forward(an)...); + trace_activity().schedule_enter(*inner.get(), scbl); + inner->schedule(std::move(scbl)); + trace_activity().schedule_return(*inner.get()); +} +template +void worker::schedule_rebind(const schedulable& scbl, ArgN&&... an) const { + auto rescbl = make_schedulable(scbl, *this, std::forward(an)...); + trace_activity().schedule_enter(*inner.get(), rescbl); + inner->schedule(std::move(rescbl)); + trace_activity().schedule_return(*inner.get()); +} + +template +auto worker::schedule(clock_type::time_point when, Arg0&& a0, ArgN&&... an) const + -> typename std::enable_if< + (detail::is_action_function::value || + is_subscription::value) && + !is_schedulable::value>::type { + auto scbl = make_schedulable(*this, std::forward(a0), std::forward(an)...); + trace_activity().schedule_when_enter(*inner.get(), when, scbl); + inner->schedule(when, std::move(scbl)); + trace_activity().schedule_when_return(*inner.get()); +} +template +void worker::schedule_rebind(clock_type::time_point when, const schedulable& scbl, ArgN&&... an) const { + auto rescbl = make_schedulable(scbl, *this, std::forward(an)...); + trace_activity().schedule_when_enter(*inner.get(), when, rescbl); + inner->schedule(when, std::move(rescbl)); + trace_activity().schedule_when_return(*inner.get()); +} + +template +auto worker::schedule_periodically(clock_type::time_point initial, clock_type::duration period, Arg0&& a0, ArgN&&... an) const + -> typename std::enable_if< + (detail::is_action_function::value || + is_subscription::value) && + !is_schedulable::value>::type { + schedule_periodically_rebind(initial, period, make_schedulable(*this, std::forward(a0), std::forward(an)...)); +} +template +void worker::schedule_periodically_rebind(clock_type::time_point initial, clock_type::duration period, const schedulable& scbl, ArgN&&... an) const { + auto keepAlive = *this; + auto target = std::make_shared(initial); + auto activity = make_schedulable(scbl, keepAlive, std::forward(an)...); + auto periodic = make_schedulable( + activity, + [keepAlive, target, period, activity](schedulable self) { + // any recursion requests will be pushed to the scheduler queue + recursion r(false); + // call action + activity(r.get_recurse()); + + // schedule next occurance (if the action took longer than 'period' target will be in the past) + *target += period; + self.schedule(*target); + }); + trace_activity().schedule_when_enter(*inner.get(), *target, periodic); + inner->schedule(*target, periodic); + trace_activity().schedule_when_return(*inner.get()); +} + +namespace detail { + +template +struct time_schedulable +{ + typedef TimePoint time_point_type; + + time_schedulable(TimePoint when, schedulable a) + : when(when) + , what(std::move(a)) + { + } + TimePoint when; + schedulable what; +}; + + +// Sorts time_schedulable items in priority order sorted +// on value of time_schedulable.when. Items with equal +// values for when are sorted in fifo order. +template +class schedulable_queue { +public: + typedef time_schedulable item_type; + typedef std::pair elem_type; + typedef std::vector container_type; + typedef const item_type& const_reference; + +private: + struct compare_elem + { + bool operator()(const elem_type& lhs, const elem_type& rhs) const { + if (lhs.first.when == rhs.first.when) { + return lhs.second > rhs.second; + } + else { + return lhs.first.when > rhs.first.when; + } + } + }; + + typedef std::priority_queue< + elem_type, + container_type, + compare_elem + > queue_type; + + queue_type q; + + int64_t ordinal; +public: + + schedulable_queue() + : ordinal(0) + { + } + + const_reference top() const { + return q.top().first; + } + + void pop() { + q.pop(); + } + + bool empty() const { + return q.empty(); + } + + void push(const item_type& value) { + q.push(elem_type(value, ordinal++)); + } + + void push(item_type&& value) { + q.push(elem_type(std::move(value), ordinal++)); + } +}; + +} + +} +namespace rxsc=schedulers; + +} + +#include "schedulers/rx-currentthread.hpp" +#include "schedulers/rx-runloop.hpp" +#include "schedulers/rx-newthread.hpp" +#include "schedulers/rx-eventloop.hpp" +#include "schedulers/rx-immediate.hpp" +#include "schedulers/rx-virtualtime.hpp" +#include "schedulers/rx-sameworker.hpp" + +#endif diff --git a/3party/rxcpp/rx-sources.hpp b/3party/rxcpp/rx-sources.hpp new file mode 100644 index 0000000..51ed07d --- /dev/null +++ b/3party/rxcpp/rx-sources.hpp @@ -0,0 +1,49 @@ + +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SOURCES_HPP) +#define RXCPP_RX_SOURCES_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace sources { + +struct tag_source {}; +template +struct source_base +{ + typedef T value_type; + typedef tag_source source_tag; +}; +template +class is_source +{ + template + static typename C::source_tag* check(int); + template + static void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_source*>::value; +}; + +} +namespace rxs=sources; + +} + +#include "sources/rx-create.hpp" +#include "sources/rx-range.hpp" +#include "sources/rx-iterate.hpp" +#include "sources/rx-interval.hpp" +#include "sources/rx-empty.hpp" +#include "sources/rx-defer.hpp" +#include "sources/rx-never.hpp" +#include "sources/rx-error.hpp" +#include "sources/rx-scope.hpp" +#include "sources/rx-timer.hpp" + +#endif diff --git a/3party/rxcpp/rx-subjects.hpp b/3party/rxcpp/rx-subjects.hpp new file mode 100644 index 0000000..bcbe3c8 --- /dev/null +++ b/3party/rxcpp/rx-subjects.hpp @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SCHEDULER_SUBJECTS_HPP) +#define RXCPP_RX_SCHEDULER_SUBJECTS_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace subjects { + +} +namespace rxsub=subjects; + +} + +#include "subjects/rx-subject.hpp" +#include "subjects/rx-behavior.hpp" +#include "subjects/rx-replaysubject.hpp" +#include "subjects/rx-synchronize.hpp" + +#endif diff --git a/3party/rxcpp/rx-subscriber.hpp b/3party/rxcpp/rx-subscriber.hpp new file mode 100644 index 0000000..3d6c515 --- /dev/null +++ b/3party/rxcpp/rx-subscriber.hpp @@ -0,0 +1,835 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SUBSCRIBER_HPP) +#define RXCPP_RX_SUBSCRIBER_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +template +struct subscriber_base : public observer_base, public subscription_base +{ + typedef tag_subscriber subscriber_tag; +}; + +/*! + \brief binds an observer that consumes values with a composite_subscription that controls lifetime. + + \ingroup group-core + +*/ +template> +class subscriber : public subscriber_base +{ + static_assert(!is_subscriber::value, "not allowed to nest subscribers"); + static_assert(is_observer::value, "subscriber must contain an observer"); + typedef subscriber this_type; + typedef rxu::decay_t observer_type; + + composite_subscription lifetime; + observer_type destination; + trace_id id; + + struct nextdetacher + { + ~nextdetacher() + { + trace_activity().on_next_return(*that); + if (do_unsubscribe) { + that->unsubscribe(); + } + } + nextdetacher(const this_type* that) + : that(that) + , do_unsubscribe(true) + { + } + template + void operator()(U u) { + trace_activity().on_next_enter(*that, u); + RXCPP_TRY { + that->destination.on_next(std::move(u)); + do_unsubscribe = false; + } RXCPP_CATCH(...) { + auto ex = rxu::current_exception(); + trace_activity().on_error_enter(*that, ex); + that->destination.on_error(std::move(ex)); + trace_activity().on_error_return(*that); + } + } + const this_type* that; + volatile bool do_unsubscribe; + }; + + struct errordetacher + { + ~errordetacher() + { + trace_activity().on_error_return(*that); + that->unsubscribe(); + } + errordetacher(const this_type* that) + : that(that) + { + } + inline void operator()(rxu::error_ptr ex) { + trace_activity().on_error_enter(*that, ex); + that->destination.on_error(std::move(ex)); + } + const this_type* that; + }; + + struct completeddetacher + { + ~completeddetacher() + { + trace_activity().on_completed_return(*that); + that->unsubscribe(); + } + completeddetacher(const this_type* that) + : that(that) + { + } + inline void operator()() { + trace_activity().on_completed_enter(*that); + that->destination.on_completed(); + } + const this_type* that; + }; + + subscriber(); +public: + typedef typename composite_subscription::weak_subscription weak_subscription; + + subscriber(const this_type& o) + : lifetime(o.lifetime) + , destination(o.destination) + , id(o.id) + { + } + subscriber(this_type&& o) + : lifetime(std::move(o.lifetime)) + , destination(std::move(o.destination)) + , id(std::move(o.id)) + { + } + + template + friend class subscriber; + + template + subscriber( + const subscriber& o, + typename std::enable_if< + !std::is_same>::value && + std::is_same>::value, void**>::type = nullptr) + : lifetime(o.lifetime) + , destination(o.destination.as_dynamic()) + , id(o.id) + { + } + + template + subscriber(trace_id id, composite_subscription cs, U&& o) + : lifetime(std::move(cs)) + , destination(std::forward(o)) + , id(std::move(id)) + { + static_assert(!is_subscriber::value, "cannot nest subscribers"); + static_assert(is_observer::value, "must pass observer to subscriber"); + trace_activity().create_subscriber(*this); + } + + this_type& operator=(this_type o) { + lifetime = std::move(o.lifetime); + destination = std::move(o.destination); + id = std::move(o.id); + return *this; + } + + const observer_type& get_observer() const { + return destination; + } + observer_type& get_observer() { + return destination; + } + const composite_subscription& get_subscription() const { + return lifetime; + } + composite_subscription& get_subscription() { + return lifetime; + } + trace_id get_id() const { + return id; + } + + subscriber as_dynamic() const { + return subscriber(id, lifetime, destination.as_dynamic()); + } + + // observer + // + template + void on_next(V&& v) const { + if (!is_subscribed()) { + return; + } + nextdetacher protect(this); + protect(std::forward(v)); + } + void on_error(rxu::error_ptr e) const { + if (!is_subscribed()) { + return; + } + errordetacher protect(this); + protect(std::move(e)); + } + void on_completed() const { + if (!is_subscribed()) { + return; + } + completeddetacher protect(this); + protect(); + } + + // composite_subscription + // + bool is_subscribed() const { + return lifetime.is_subscribed(); + } + weak_subscription add(subscription s) const { + return lifetime.add(std::move(s)); + } + template + auto add(F f) const + -> typename std::enable_if::value, weak_subscription>::type { + return lifetime.add(make_subscription(std::move(f))); + } + void remove(weak_subscription w) const { + return lifetime.remove(std::move(w)); + } + void clear() const { + return lifetime.clear(); + } + void unsubscribe() const { + return lifetime.unsubscribe(); + } + +}; + +template +auto make_subscriber( + subscriber o) + -> subscriber { + return subscriber(std::move(o)); +} + +// observer +// + +template +auto make_subscriber() + -> typename std::enable_if< + detail::is_on_next_of>::value, + subscriber>>>::type { + return subscriber>>(trace_id::make_next_id_subscriber(), composite_subscription(), + observer>(detail::OnNextEmpty())); +} + +template +auto make_subscriber( + const observer& o) + -> subscriber> { + return subscriber>(trace_id::make_next_id_subscriber(), composite_subscription(), o); +} +template +auto make_subscriber(const Observer& o) + -> typename std::enable_if< + is_observer::value && + !is_subscriber::value, + subscriber>::type { + return subscriber(trace_id::make_next_id_subscriber(), composite_subscription(), o); +} +template +auto make_subscriber(const Observer& o) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !is_subscriber::value && + !is_subscription::value && + !is_observer::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), composite_subscription(), o); +} +template +auto make_subscriber(const OnNext& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), composite_subscription(), + observer(on)); +} +template +auto make_subscriber(const OnNext& on, const OnError& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), composite_subscription(), + observer(on, oe)); +} +template +auto make_subscriber(const OnNext& on, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), composite_subscription(), + observer(on, detail::OnErrorEmpty(), oc)); +} +template +auto make_subscriber(const OnNext& on, const OnError& oe, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), composite_subscription(), + observer(on, oe, oc)); +} + +// explicit lifetime +// + +template +auto make_subscriber(const composite_subscription& cs) + -> subscriber>> { + return subscriber>>(trace_id::make_next_id_subscriber(), cs, + observer>(detail::OnNextEmpty())); +} + +template +auto make_subscriber(const composite_subscription& cs, + const observer& o) + -> subscriber> { + return subscriber>(trace_id::make_next_id_subscriber(), cs, o); +} +template +auto make_subscriber(const composite_subscription& cs, + const subscriber& s) + -> subscriber { + return subscriber(trace_id::make_next_id_subscriber(), cs, s.get_observer()); +} +template +auto make_subscriber(const composite_subscription& cs, const Observer& o) + -> typename std::enable_if< + !is_subscriber::value && + is_observer::value, + subscriber>::type { + return subscriber(trace_id::make_next_id_subscriber(), cs, o); +} +template +auto make_subscriber(const composite_subscription& cs, const Observer& o) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !is_subscriber::value && + !is_subscription::value && + !is_observer::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), cs, make_observer(o)); +} +template +auto make_subscriber(const composite_subscription& cs, const OnNext& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), cs, + observer(on)); +} +template +auto make_subscriber(const composite_subscription& cs, const OnNext& on, const OnError& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), cs, + observer(on, oe)); +} +template +auto make_subscriber(const composite_subscription& cs, const OnNext& on, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), cs, + observer(on, detail::OnErrorEmpty(), oc)); +} +template +auto make_subscriber(const composite_subscription& cs, const OnNext& on, const OnError& oe, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + subscriber>>::type { + return subscriber>(trace_id::make_next_id_subscriber(), cs, + observer(on, oe, oc)); +} + +// explicit id +// + +template +auto make_subscriber(trace_id id) + -> subscriber>> { + return subscriber>>(std::move(id), composite_subscription(), + observer>(detail::OnNextEmpty())); +} + +template +auto make_subscriber(trace_id id, const composite_subscription& cs) + -> subscriber>> { + return subscriber>>(std::move(id), cs, + observer>(detail::OnNextEmpty())); +} + +template +auto make_subscriber(trace_id id, + const observer& o) + -> subscriber> { + return subscriber>(std::move(id), composite_subscription(), o); +} +template +auto make_subscriber(trace_id id, const composite_subscription& cs, + const observer& o) + -> subscriber> { + return subscriber>(std::move(id), cs, o); +} +template +auto make_subscriber(trace_id id, const Observer& o) + -> typename std::enable_if< + is_observer::value, + subscriber>::type { + return subscriber(std::move(id), composite_subscription(), o); +} +template +auto make_subscriber(trace_id id, const composite_subscription& cs, const Observer& o) + -> typename std::enable_if< + is_observer::value, + subscriber>::type { + return subscriber(std::move(id), cs, o); +} +template +auto make_subscriber(trace_id id, const Observer& o) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !is_subscriber::value && + !is_subscription::value && + !is_observer::value, + subscriber>>::type { + return subscriber>(std::move(id), composite_subscription(), o); +} +template +auto make_subscriber(trace_id id, const composite_subscription& cs, const Observer& o) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !is_subscriber::value && + !is_subscription::value && + !is_observer::value, + subscriber>>::type { + return subscriber>(std::move(id), cs, o); +} +template +auto make_subscriber(trace_id id, const OnNext& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + subscriber>>::type { + return subscriber>(std::move(id), composite_subscription(), + observer(on)); +} +template +auto make_subscriber(trace_id id, const composite_subscription& cs, const OnNext& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + subscriber>>::type { + return subscriber>(std::move(id), cs, + observer(on)); +} +template +auto make_subscriber(trace_id id, const OnNext& on, const OnError& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + subscriber>>::type { + return subscriber>(std::move(id), composite_subscription(), + observer(on, oe)); +} +template +auto make_subscriber(trace_id id, const composite_subscription& cs, const OnNext& on, const OnError& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + subscriber>>::type { + return subscriber>(std::move(id), cs, + observer(on, oe)); +} +template +auto make_subscriber(trace_id id, const OnNext& on, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + subscriber>>::type { + return subscriber>(std::move(id), composite_subscription(), + observer(on, detail::OnErrorEmpty(), oc)); +} +template +auto make_subscriber(trace_id id, const composite_subscription& cs, const OnNext& on, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + subscriber>>::type { + return subscriber>(std::move(id), cs, + observer(on, detail::OnErrorEmpty(), oc)); +} +template +auto make_subscriber(trace_id id, const OnNext& on, const OnError& oe, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + subscriber>>::type { + return subscriber>(std::move(id), composite_subscription(), + observer(on, oe, oc)); +} +template +auto make_subscriber(trace_id id, const composite_subscription& cs, const OnNext& on, const OnError& oe, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + subscriber>>::type { + return subscriber>(std::move(id), cs, + observer(on, oe, oc)); +} + +// chain defaults from subscriber +// + +template +auto make_subscriber(const subscriber& scbr, + const observer& o) + -> subscriber> { + auto r = subscriber>(trace_id::make_next_id_subscriber(), scbr.get_subscription(), o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, + const observer& o) + -> subscriber> { + auto r = subscriber>(std::move(id), scbr.get_subscription(), o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const Observer& o) + -> typename std::enable_if< + is_observer::value, + subscriber>::type { + auto r = subscriber(std::move(id), scbr.get_subscription(), o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const Observer& o) + -> typename std::enable_if< + !is_subscription::value && + is_observer::value, + subscriber>::type { + auto r = subscriber(trace_id::make_next_id_subscriber(), scbr.get_subscription(), o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const Observer& o) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !is_subscriber::value && + !is_subscription::value && + !is_observer::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), scbr.get_subscription(), make_observer(o)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const Observer& o) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !is_subscriber::value && + !is_subscription::value && + !is_observer::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), scbr.get_subscription(), o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const OnNext& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), scbr.get_subscription(), + observer(on)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const OnNext& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), scbr.get_subscription(), + observer(on)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const OnNext& on, const OnError& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), scbr.get_subscription(), + observer(on, oe)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const OnNext& on, const OnError& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), scbr.get_subscription(), + observer(on, oe)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const OnNext& on, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), scbr.get_subscription(), + observer(on, detail::OnErrorEmpty(), oc)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const OnNext& on, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), scbr.get_subscription(), + observer(on, detail::OnErrorEmpty(), oc)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const OnNext& on, const OnError& oe, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), scbr.get_subscription(), + observer(on, oe, oc)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const OnNext& on, const OnError& oe, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), scbr.get_subscription(), + observer(on, oe, oc)); + trace_activity().connect(r, scbr); + return r; +} + +template +auto make_subscriber(const subscriber& , const composite_subscription& cs, + const observer& o) + -> subscriber> { + return subscriber>(trace_id::make_next_id_subscriber(), cs, o); +} +template +auto make_subscriber(const subscriber&, trace_id id, const composite_subscription& cs, + const observer& o) + -> subscriber> { + return subscriber>(std::move(id), cs, o); +} +template +auto make_subscriber(const subscriber& scbr, const composite_subscription& cs, const Observer& o) + -> typename std::enable_if< + is_observer::value, + subscriber>::type { + auto r = subscriber(trace_id::make_next_id_subscriber(), cs, o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const composite_subscription& cs, const Observer& o) + -> typename std::enable_if< + is_observer::value, + subscriber>::type { + auto r = subscriber(std::move(id), cs, o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const composite_subscription& cs, const Observer& o) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !is_subscriber::value && + !is_subscription::value && + !is_observer::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), cs, o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const composite_subscription& cs, const Observer& o) + -> typename std::enable_if< + !detail::is_on_next_of::value && + !is_subscriber::value && + !is_subscription::value && + !is_observer::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), cs, o); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const composite_subscription& cs, const OnNext& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), cs, + observer(on)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const composite_subscription& cs, const OnNext& on) + -> typename std::enable_if< + detail::is_on_next_of::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), cs, + observer(on)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const composite_subscription& cs, const OnNext& on, const OnError& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), cs, + observer(on, oe)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const composite_subscription& cs, const OnNext& on, const OnError& oe) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), cs, + observer(on, oe)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const composite_subscription& cs, const OnNext& on, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), cs, + observer(on, detail::OnErrorEmpty(), oc)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const composite_subscription& cs, const OnNext& on, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_completed::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), cs, + observer(on, detail::OnErrorEmpty(), oc)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, const composite_subscription& cs, const OnNext& on, const OnError& oe, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + subscriber>>::type { + auto r = subscriber>(trace_id::make_next_id_subscriber(), cs, + observer(on, oe, oc)); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const composite_subscription& cs, const OnNext& on, const OnError& oe, const OnCompleted& oc) + -> typename std::enable_if< + detail::is_on_next_of::value && + detail::is_on_error::value && + detail::is_on_completed::value, + subscriber>>::type { + auto r = subscriber>(std::move(id), cs, + observer(on, oe, oc)); + trace_activity().connect(r, scbr); + return r; +} + +template +auto make_subscriber(const subscriber& scbr, const composite_subscription& cs) + -> subscriber { + auto r = subscriber(scbr.get_id(), cs, scbr.get_observer()); + trace_activity().connect(r, scbr); + return r; +} +template +auto make_subscriber(const subscriber& scbr, trace_id id, const composite_subscription& cs) + -> subscriber { + auto r = subscriber(std::move(id), cs, scbr.get_observer()); + trace_activity().connect(r, scbr); + return r; +} + +template +auto make_subscriber(const subscriber& scbr, trace_id id) + -> subscriber { + auto r = subscriber(std::move(id), scbr.get_subscription(), scbr.get_observer()); + trace_activity().connect(r, scbr); + return r; +} + +} + +#endif diff --git a/3party/rxcpp/rx-subscription.hpp b/3party/rxcpp/rx-subscription.hpp new file mode 100644 index 0000000..2d6bcb6 --- /dev/null +++ b/3party/rxcpp/rx-subscription.hpp @@ -0,0 +1,613 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SUBSCRIPTION_HPP) +#define RXCPP_RX_SUBSCRIPTION_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace detail { + +template +struct is_unsubscribe_function +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CF*)nullptr)()); + template + static not_void check(...); + + static const bool value = std::is_same>(0)), void>::value; +}; + +} + +struct tag_subscription {}; +struct subscription_base {typedef tag_subscription subscription_tag;}; +template +class is_subscription +{ + template + static typename C::subscription_tag* check(int); + template + static void check(...); +public: + static const bool value = std::is_convertible>(0)), tag_subscription*>::value; +}; + +template +class static_subscription +{ + typedef rxu::decay_t unsubscribe_call_type; + unsubscribe_call_type unsubscribe_call; + static_subscription() + { + } +public: + static_subscription(const static_subscription& o) + : unsubscribe_call(o.unsubscribe_call) + { + } + static_subscription(static_subscription&& o) + : unsubscribe_call(std::move(o.unsubscribe_call)) + { + } + static_subscription(unsubscribe_call_type s) + : unsubscribe_call(std::move(s)) + { + } + void unsubscribe() const { + unsubscribe_call(); + } +}; + +class subscription : public subscription_base +{ + class base_subscription_state : public std::enable_shared_from_this + { + base_subscription_state(); + public: + + explicit base_subscription_state(bool initial) + : issubscribed(initial) + { + } + virtual ~base_subscription_state() {} + virtual void unsubscribe() { + } + std::atomic issubscribed; + }; +public: + typedef std::weak_ptr weak_state_type; + +private: + template + struct subscription_state : public base_subscription_state + { + typedef rxu::decay_t inner_t; + subscription_state(inner_t i) + : base_subscription_state(true) + , inner(std::move(i)) + { + } + virtual void unsubscribe() { + if (issubscribed.exchange(false)) { + trace_activity().unsubscribe_enter(*this); + inner.unsubscribe(); + trace_activity().unsubscribe_return(*this); + } + } + inner_t inner; + }; + +protected: + std::shared_ptr state; + + friend bool operator<(const subscription&, const subscription&); + friend bool operator==(const subscription&, const subscription&); + +private: + subscription(weak_state_type w) + : state(w.lock()) + { + if (!state) { + std::terminate(); + } + } + + explicit subscription(std::shared_ptr s) + : state(std::move(s)) + { + if (!state) { + std::terminate(); + } + } +public: + + subscription() + : state(std::make_shared(false)) + { + if (!state) { + std::terminate(); + } + } + template + explicit subscription(U u, typename std::enable_if::value, void**>::type = nullptr) + : state(std::make_shared>(std::move(u))) + { + if (!state) { + std::terminate(); + } + } + template + explicit subscription(U u, typename std::enable_if::value && is_subscription::value, void**>::type = nullptr) + // intentionally slice + : state(std::move((*static_cast(&u)).state)) + { + if (!state) { + std::terminate(); + } + } + subscription(const subscription& o) + : state(o.state) + { + if (!state) { + std::terminate(); + } + } + subscription(subscription&& o) + : state(std::move(o.state)) + { + if (!state) { + std::terminate(); + } + } + subscription& operator=(subscription o) { + state = std::move(o.state); + return *this; + } + bool is_subscribed() const { + if (!state) { + std::terminate(); + } + return state->issubscribed; + } + void unsubscribe() const { + if (!state) { + std::terminate(); + } + auto keepAlive = state; + state->unsubscribe(); + } + + weak_state_type get_weak() { + return state; + } + + // Atomically promote weak subscription to strong. + // Calls std::terminate if w has already expired. + static subscription lock(weak_state_type w) { + return subscription(w); + } + + // Atomically try to promote weak subscription to strong. + // Returns an empty maybe<> if w has already expired. + static rxu::maybe maybe_lock(weak_state_type w) { + auto strong_subscription = w.lock(); + if (!strong_subscription) { + return rxu::detail::maybe{}; + } else { + return rxu::detail::maybe{subscription{std::move(strong_subscription)}}; + } + } +}; + +inline bool operator<(const subscription& lhs, const subscription& rhs) { + return lhs.state < rhs.state; +} +inline bool operator==(const subscription& lhs, const subscription& rhs) { + return lhs.state == rhs.state; +} +inline bool operator!=(const subscription& lhs, const subscription& rhs) { + return !(lhs == rhs); +} + + +inline auto make_subscription() + -> subscription { + return subscription(); +} +template +auto make_subscription(I&& i) + -> typename std::enable_if::value && !detail::is_unsubscribe_function::value, + subscription>::type { + return subscription(std::forward(i)); +} +template +auto make_subscription(Unsubscribe&& u) + -> typename std::enable_if::value, + subscription>::type { + return subscription(static_subscription(std::forward(u))); +} + +class composite_subscription; + +namespace detail { + +struct tag_composite_subscription_empty {}; + +class composite_subscription_inner +{ +private: + typedef subscription::weak_state_type weak_subscription; + struct composite_subscription_state : public std::enable_shared_from_this + { + // invariant: cannot access this data without the lock held. + std::set subscriptions; + // double checked locking: + // issubscribed must be loaded again after each lock acquisition. + // invariant: + // never call subscription::unsubscribe with lock held. + std::mutex lock; + // invariant: transitions from 'true' to 'false' exactly once, at any time. + std::atomic issubscribed; + + ~composite_subscription_state() + { + std::unique_lock guard(lock); + subscriptions.clear(); + } + + composite_subscription_state() + : issubscribed(true) + { + } + composite_subscription_state(tag_composite_subscription_empty) + : issubscribed(false) + { + } + + // Atomically add 's' to the set of subscriptions. + // + // If unsubscribe() has already occurred, this immediately + // calls s.unsubscribe(). + // + // cs.unsubscribe() [must] happens-before s.unsubscribe() + // + // Due to the un-atomic nature of calling 's.unsubscribe()', + // it is possible to observe the unintuitive + // add(s)=>s.unsubscribe() prior + // to any of the unsubscribe()=>sN.unsubscribe(). + inline weak_subscription add(subscription s) { + if (!issubscribed) { // load.acq [seq_cst] + s.unsubscribe(); + } else if (s.is_subscribed()) { + std::unique_lock guard(lock); + if (!issubscribed) { // load.acq [seq_cst] + // unsubscribe was called concurrently. + guard.unlock(); + // invariant: do not call unsubscribe with lock held. + s.unsubscribe(); + } else { + subscriptions.insert(s); + } + } + return s.get_weak(); + } + + // Atomically remove 'w' from the set of subscriptions. + // + // This does nothing if 'w' was already previously removed, + // or refers to an expired value. + inline void remove(weak_subscription w) { + if (issubscribed) { // load.acq [seq_cst] + rxu::maybe maybe_subscription = subscription::maybe_lock(w); + + if (maybe_subscription.empty()) { + // Do nothing if the subscription has already expired. + return; + } + + std::unique_lock guard(lock); + // invariant: subscriptions must be accessed under the lock. + + if (issubscribed) { // load.acq [seq_cst] + subscription& s = maybe_subscription.get(); + subscriptions.erase(std::move(s)); + } // else unsubscribe() was called concurrently; this becomes a no-op. + } + } + + // Atomically clear all subscriptions that were observably added + // (and not subsequently observably removed). + // + // Un-atomically call unsubscribe on those subscriptions. + // + // forall subscriptions in {add(s1),add(s2),...} + // - {remove(s3), remove(s4), ...}: + // cs.unsubscribe() || cs.clear() happens before s.unsubscribe() + // + // cs.unsubscribe() observed-before cs.clear ==> do nothing. + inline void clear() { + if (issubscribed) { // load.acq [seq_cst] + std::unique_lock guard(lock); + + if (!issubscribed) { // load.acq [seq_cst] + // unsubscribe was called concurrently. + return; + } + + std::set v(std::move(subscriptions)); + // invariant: do not call unsubscribe with lock held. + guard.unlock(); + std::for_each(v.begin(), v.end(), + [](const subscription& s) { + s.unsubscribe(); }); + } + } + + // Atomically clear all subscriptions that were observably added + // (and not subsequently observably removed). + // + // Un-atomically call unsubscribe on those subscriptions. + // + // Switches to an 'unsubscribed' state, all subsequent + // adds are immediately unsubscribed. + // + // cs.unsubscribe() [must] happens-before + // cs.add(s) ==> s.unsubscribe() + // + // forall subscriptions in {add(s1),add(s2),...} + // - {remove(s3), remove(s4), ...}: + // cs.unsubscribe() || cs.clear() happens before s.unsubscribe() + inline void unsubscribe() { + if (issubscribed.exchange(false)) { // cas.acq_rel [seq_cst] + std::unique_lock guard(lock); + + // is_subscribed can only transition to 'false' once, + // does not need an extra atomic access here. + + std::set v(std::move(subscriptions)); + // invariant: do not call unsubscribe with lock held. + guard.unlock(); + std::for_each(v.begin(), v.end(), + [](const subscription& s) { + s.unsubscribe(); }); + } + } + }; + +public: + typedef std::shared_ptr shared_state_type; + +protected: + mutable shared_state_type state; + +public: + composite_subscription_inner() + : state(std::make_shared()) + { + } + composite_subscription_inner(tag_composite_subscription_empty et) + : state(std::make_shared(et)) + { + } + + composite_subscription_inner(const composite_subscription_inner& o) + : state(o.state) + { + if (!state) { + std::terminate(); + } + } + composite_subscription_inner(composite_subscription_inner&& o) + : state(std::move(o.state)) + { + if (!state) { + std::terminate(); + } + } + + composite_subscription_inner& operator=(composite_subscription_inner o) + { + state = std::move(o.state); + if (!state) { + std::terminate(); + } + return *this; + } + + inline weak_subscription add(subscription s) const { + if (!state) { + std::terminate(); + } + return state->add(std::move(s)); + } + inline void remove(weak_subscription w) const { + if (!state) { + std::terminate(); + } + state->remove(std::move(w)); + } + inline void clear() const { + if (!state) { + std::terminate(); + } + state->clear(); + } + inline void unsubscribe() { + if (!state) { + std::terminate(); + } + state->unsubscribe(); + } +}; + +inline composite_subscription shared_empty(); + +} + +/*! + \brief controls lifetime for scheduler::schedule and observable::subscribe. + + \ingroup group-core + +*/ +class composite_subscription + : protected detail::composite_subscription_inner + , public subscription +{ + typedef detail::composite_subscription_inner inner_type; +public: + typedef subscription::weak_state_type weak_subscription; + + composite_subscription(detail::tag_composite_subscription_empty et) + : inner_type(et) + , subscription() // use empty base + { + } + +public: + + composite_subscription() + : inner_type() + , subscription(*static_cast(this)) + { + } + + composite_subscription(const composite_subscription& o) + : inner_type(o) + , subscription(static_cast(o)) + { + } + composite_subscription(composite_subscription&& o) + : inner_type(std::move(o)) + , subscription(std::move(static_cast(o))) + { + } + + composite_subscription& operator=(composite_subscription o) + { + inner_type::operator=(std::move(o)); + subscription::operator=(std::move(*static_cast(&o))); + return *this; + } + + static inline composite_subscription empty() { + return detail::shared_empty(); + } + + using subscription::is_subscribed; + using subscription::unsubscribe; + + using inner_type::clear; + + inline weak_subscription add(subscription s) const { + if (s == static_cast(*this)) { + // do not nest the same subscription + std::terminate(); + //return s.get_weak(); + } + auto that = this->subscription::state.get(); + trace_activity().subscription_add_enter(*that, s); + auto w = inner_type::add(std::move(s)); + trace_activity().subscription_add_return(*that); + return w; + } + + template + auto add(F f) const + -> typename std::enable_if::value, weak_subscription>::type { + return add(make_subscription(std::move(f))); + } + + inline void remove(weak_subscription w) const { + auto that = this->subscription::state.get(); + trace_activity().subscription_remove_enter(*that, w); + inner_type::remove(w); + trace_activity().subscription_remove_return(*that); + } +}; + +inline bool operator<(const composite_subscription& lhs, const composite_subscription& rhs) { + return static_cast(lhs) < static_cast(rhs); +} +inline bool operator==(const composite_subscription& lhs, const composite_subscription& rhs) { + return static_cast(lhs) == static_cast(rhs); +} +inline bool operator!=(const composite_subscription& lhs, const composite_subscription& rhs) { + return !(lhs == rhs); +} + +namespace detail { + +inline composite_subscription shared_empty() { + static composite_subscription shared_empty = composite_subscription(tag_composite_subscription_empty()); + return shared_empty; +} + +} + +template +class resource : public subscription_base +{ +public: + typedef typename composite_subscription::weak_subscription weak_subscription; + + resource() + : lifetime(composite_subscription()) + , value(std::make_shared>()) + { + } + + explicit resource(T t, composite_subscription cs = composite_subscription()) + : lifetime(std::move(cs)) + , value(std::make_shared>(rxu::detail::maybe(std::move(t)))) + { + auto localValue = value; + lifetime.add( + [localValue](){ + localValue->reset(); + } + ); + } + + T& get() { + return value.get()->get(); + } + composite_subscription& get_subscription() { + return lifetime; + } + + bool is_subscribed() const { + return lifetime.is_subscribed(); + } + weak_subscription add(subscription s) const { + return lifetime.add(std::move(s)); + } + template + auto add(F f) const + -> typename std::enable_if::value, weak_subscription>::type { + return lifetime.add(make_subscription(std::move(f))); + } + void remove(weak_subscription w) const { + return lifetime.remove(std::move(w)); + } + void clear() const { + return lifetime.clear(); + } + void unsubscribe() const { + return lifetime.unsubscribe(); + } + +protected: + composite_subscription lifetime; + std::shared_ptr> value; +}; + +} + +#endif diff --git a/3party/rxcpp/rx-test.hpp b/3party/rxcpp/rx-test.hpp new file mode 100644 index 0000000..37737f7 --- /dev/null +++ b/3party/rxcpp/rx-test.hpp @@ -0,0 +1,137 @@ +#pragma once + +#if !defined(RXCPP_RX_TEST_HPP) +#define RXCPP_RX_TEST_HPP + +#include "rx-includes.hpp" + +namespace rxcpp { + +namespace test { + +namespace detail { + +template +struct test_subject_base + : public std::enable_shared_from_this> +{ + typedef rxn::recorded::type> recorded_type; + typedef std::shared_ptr> type; + + virtual ~test_subject_base() {} + virtual void on_subscribe(subscriber) const =0; + virtual std::vector messages() const =0; + virtual std::vector subscriptions() const =0; +}; + +template +struct test_source + : public rxs::source_base +{ + explicit test_source(typename test_subject_base::type ts) + : ts(std::move(ts)) + { + if (!this->ts) std::terminate(); + } + typename test_subject_base::type ts; + void on_subscribe(subscriber o) const { + ts->on_subscribe(std::move(o)); + } + template + typename std::enable_if>::value, void>::type + on_subscribe(Subscriber o) const { + + static_assert(is_subscriber::value, "on_subscribe must be passed a subscriber."); + + ts->on_subscribe(o.as_dynamic()); + } +}; + +} + +template +class testable_observer + : public observer +{ + typedef observer observer_base; + typedef typename detail::test_subject_base::type test_subject; + test_subject ts; + +public: + typedef typename detail::test_subject_base::recorded_type recorded_type; + + testable_observer(test_subject ts, observer_base ob) + : observer_base(std::move(ob)) + , ts(std::move(ts)) + { + } + + std::vector messages() const { + return ts->messages(); + } +}; + +//struct tag_test_observable : public tag_observable {}; + +/*! + \brief a source of values that records the time of each subscription/unsubscription and all the values and the time they were emitted. + + \ingroup group-observable + +*/ +template +class testable_observable + : public observable> +{ + typedef observable> observable_base; + typedef typename detail::test_subject_base::type test_subject; + test_subject ts; + + //typedef tag_test_observable observable_tag; + +public: + typedef typename detail::test_subject_base::recorded_type recorded_type; + + explicit testable_observable(test_subject ts) + : observable_base(detail::test_source(ts)) + , ts(ts) + { + } + + std::vector subscriptions() const { + return ts->subscriptions(); + } + + std::vector messages() const { + return ts->messages(); + } +}; + +} +namespace rxt=test; + +} + +// +// support range() >> filter() >> subscribe() syntax +// '>>' is spelled 'stream' +// +template +auto operator >> (const rxcpp::test::testable_observable& source, OperatorFactory&& of) + -> decltype(source.op(std::forward(of))) { + return source.op(std::forward(of)); +} + +// +// support range() | filter() | subscribe() syntax +// '|' is spelled 'pipe' +// +template +auto operator | (const rxcpp::test::testable_observable& source, OperatorFactory&& of) + -> decltype(source.op(std::forward(of))) { + return source.op(std::forward(of)); +} + +#include "schedulers/rx-test.hpp" + +#endif diff --git a/3party/rxcpp/rx-trace.hpp b/3party/rxcpp/rx-trace.hpp new file mode 100644 index 0000000..bf0abaf --- /dev/null +++ b/3party/rxcpp/rx-trace.hpp @@ -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 + +#if !defined(RXCPP_RX_TRACE_HPP) +#define RXCPP_RX_TRACE_HPP + +#include +#include +#include + +namespace rxcpp { + +struct trace_id +{ + static inline trace_id make_next_id_subscriber() { + static std::atomic id(0xB0000000); + return trace_id{++id}; + } + unsigned long id; +}; + +inline bool operator==(const trace_id& lhs, const trace_id& rhs) { + return lhs.id == rhs.id; +} +inline bool operator!=(const trace_id& lhs, const trace_id& rhs) { + return !(lhs==rhs); +} + +inline bool operator<(const trace_id& lhs, const trace_id& rhs) { + if ((lhs.id & 0xF0000000) != (rhs.id & 0xF0000000)) std::terminate(); + return lhs.id < rhs.id; +} +inline bool operator>(const trace_id& lhs, const trace_id& rhs) { + return rhs + inline void schedule_enter(const Worker&, const Schedulable&) {} + template + inline void schedule_return(const Worker&) {} + template + inline void schedule_when_enter(const Worker&, const When&, const Schedulable&) {} + template + inline void schedule_when_return(const Worker&) {} + + template + inline void action_enter(const Schedulable&) {} + template + inline void action_return(const Schedulable&) {} + template + inline void action_recurse(const Schedulable&) {} + + template + inline void subscribe_enter(const Observable& , const Subscriber& ) {} + template + inline void subscribe_return(const Observable& ) {} + + template + inline void connect(const SubscriberFrom&, const SubscriberTo&) {} + + template + inline void lift_enter(const OperatorSource&, const OperatorChain&, const Subscriber&, const SubscriberLifted&) {} + template + inline void lift_return(const OperatorSource&, const OperatorChain&) {} + + template + inline void unsubscribe_enter(const SubscriptionState&) {} + template + inline void unsubscribe_return(const SubscriptionState&) {} + + template + inline void subscription_add_enter(const SubscriptionState&, const Subscription&) {} + template + inline void subscription_add_return(const SubscriptionState&) {} + + template + inline void subscription_remove_enter(const SubscriptionState&, const WeakSubscription&) {} + template + inline void subscription_remove_return(const SubscriptionState&) {} + + template + inline void create_subscriber(const Subscriber&) {} + + template + inline void on_next_enter(const Subscriber&, const T&) {} + template + inline void on_next_return(const Subscriber&) {} + + template + inline void on_error_enter(const Subscriber&, const ErrorPtr&) {} + template + inline void on_error_return(const Subscriber&) {} + + template + inline void on_completed_enter(const Subscriber&) {} + template + inline void on_completed_return(const Subscriber&) {} +}; + +struct trace_tag {}; + +} + +inline auto rxcpp_trace_activity(...) -> rxcpp::trace_noop; + + +#endif diff --git a/3party/rxcpp/rx-util.hpp b/3party/rxcpp/rx-util.hpp new file mode 100644 index 0000000..d76fa76 --- /dev/null +++ b/3party/rxcpp/rx-util.hpp @@ -0,0 +1,1042 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_UTIL_HPP) +#define RXCPP_RX_UTIL_HPP + +#include "rx-includes.hpp" + +#if !defined(RXCPP_ON_IOS) && !defined(RXCPP_ON_ANDROID) && !defined(RXCPP_THREAD_LOCAL) +#if defined(_MSC_VER) +#define RXCPP_THREAD_LOCAL __declspec(thread) +#else +#define RXCPP_THREAD_LOCAL __thread +#endif +#endif + +#if !defined(RXCPP_DELETE) +#if defined(_MSC_VER) +#define RXCPP_DELETE __pragma(warning(disable: 4822)) =delete +#else +#define RXCPP_DELETE =delete +#endif +#endif + +#define RXCPP_CONCAT(Prefix, Suffix) Prefix ## Suffix +#define RXCPP_CONCAT_EVALUATE(Prefix, Suffix) RXCPP_CONCAT(Prefix, Suffix) + +#define RXCPP_MAKE_IDENTIFIER(Prefix) RXCPP_CONCAT_EVALUATE(Prefix, __LINE__) + +// Provide replacements for try/catch keywords, using which is a compilation error +// when exceptions are disabled with -fno-exceptions. +#if RXCPP_USE_EXCEPTIONS +#define RXCPP_TRY try +#define RXCPP_CATCH(...) catch(__VA_ARGS__) +// See also rxu::throw_exception for 'throw' keyword replacement. +#else +#define RXCPP_TRY if ((true)) +#define RXCPP_CATCH(...) if ((false)) +// See also rxu::throw_exception, which will std::terminate without exceptions. +#endif + +namespace rxcpp { + +namespace util { + +template using value_type_t = typename std::decay::type::value_type; +template using decay_t = typename std::decay::type; +#ifdef __cpp_lib_is_invocable +template struct result_of; + +template +struct result_of +{ + using type = std::invoke_result_t; +}; +#else +template using result_of = std::result_of; +#endif +template using result_of_t = typename result_of::type; + +template +std::vector to_vector(const T (&arr) [size]) { + return std::vector(std::begin(arr), std::end(arr)); +} + +template +std::vector to_vector(std::initializer_list il) { + return std::vector(il); +} + +template +typename std::enable_if::value && std::is_pod::value, std::vector>::type to_vector(T0 t0, TN... tn) { + return to_vector({t0, tn...}); +} + +// lifted from https://github.com/ericniebler/range-v3/blob/630fc70baa07cbfd222f329e44a3122ab64ce364/include/range/v3/range_fwd.hpp +// removed constexpr & noexcept to support older VC compilers +template +/*constexpr*/ T const &as_const(T & t) /*noexcept*/ +{ + return t; +} +template +void as_const(T const &&) = delete; + +template +struct values {}; + +template +struct values_from; + +template +struct values_from +{ + typedef values type; +}; + +template +struct values_from +{ + typedef typename values_from::type type; +}; + +template +struct all_true; + +template +struct all_true +{ + static const bool value = B; +}; +template +struct all_true +{ + static const bool value = B && all_true::value; +}; + +template +using enable_if_all_true_t = typename std::enable_if::value>::type; + +template +struct all_true_type; + +template +struct all_true_type +{ + static const bool value = B::value; +}; +template +struct all_true_type +{ + static const bool value = B::value && all_true_type::value; +}; + +template +using enable_if_all_true_type_t = typename std::enable_if::value>::type; + +struct all_values_true { + template + bool operator()(ValueN... vn) const; + + template + bool operator()(Value0 v0) const { + return v0; + } + + template + bool operator()(Value0 v0, ValueN... vn) const { + return v0 && all_values_true()(vn...); + } +}; + +struct any_value_true { + template + bool operator()(ValueN... vn) const; + + template + bool operator()(Value0 v0) const { + return v0; + } + + template + bool operator()(Value0 v0, ValueN... vn) const { + return v0 || any_value_true()(vn...); + } +}; + +template +struct types {}; + +// +// based on Walter Brown's void_t proposal +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf +// + +struct types_checked {}; + +namespace detail { +template struct types_checked_from {typedef types_checked type;}; +} + +template +struct types_checked_from {typedef typename detail::types_checked_from::type type;}; + +template +using types_checked_t = typename types_checked_from::type; + + +template +struct expand_value_types { struct type; }; +template +struct expand_value_types, types_checked_t::type::value_type...>> +{ + using type = types::type::value_type...>; +}; +template +using value_types_t = typename expand_value_types>::type; + + +template +struct value_type_from : public std::false_type {typedef types_checked type;}; + +template +struct value_type_from>::type> + : public std::true_type {typedef value_type_t type;}; + +namespace detail { +template +auto apply(std::tuple p, values, F&& f) + -> decltype(f(std::forward(std::get(p))...)) { + return f(std::forward(std::get(p))...); +} + +template +auto apply_to_each(std::tuple& p, values, F_inner& f_inner, F_outer& f_outer) + -> decltype(f_outer(std::move(f_inner(std::get(p)))...)) { + return f_outer(std::move(f_inner(std::get(p)))...); +} + +template +auto apply_to_each(std::tuple& p, values, const F_inner& f_inner, const F_outer& f_outer) + -> decltype(f_outer(std::move(f_inner(std::get(p)))...)) { + return f_outer(std::move(f_inner(std::get(p)))...); +} + +} +template +auto apply(std::tuple p, F&& f) + -> decltype(detail::apply(std::move(p), typename values_from::type(), std::forward(f))) { + return detail::apply(std::move(p), typename values_from::type(), std::forward(f)); +} + +template +auto apply_to_each(std::tuple& p, F_inner& f_inner, F_outer& f_outer) + -> decltype(detail::apply_to_each(p, typename values_from::type(), f_inner, f_outer)) { + return detail::apply_to_each(p, typename values_from::type(), f_inner, f_outer); +} + +template +auto apply_to_each(std::tuple& p, const F_inner& f_inner, const F_outer& f_outer) + -> decltype(detail::apply_to_each(p, typename values_from::type(), f_inner, f_outer)) { + return detail::apply_to_each(p, typename values_from::type(), f_inner, f_outer); +} + +namespace detail { + +template +struct apply_to +{ + F to; + + explicit apply_to(F f) + : to(std::move(f)) + { + } + + template + auto operator()(std::tuple p) + -> decltype(rxcpp::util::apply(std::move(p), to)) { + return rxcpp::util::apply(std::move(p), to); + } + template + auto operator()(std::tuple p) const + -> decltype(rxcpp::util::apply(std::move(p), to)) { + return rxcpp::util::apply(std::move(p), to); + } +}; + +} + +template +auto apply_to(F f) + -> detail::apply_to { + return detail::apply_to(std::move(f)); +} + +namespace detail { + +struct pack +{ + template + auto operator()(ParamN... pn) + -> decltype(std::make_tuple(std::move(pn)...)) { + return std::make_tuple(std::move(pn)...); + } + template + auto operator()(ParamN... pn) const + -> decltype(std::make_tuple(std::move(pn)...)) { + return std::make_tuple(std::move(pn)...); + } +}; + +} + +inline auto pack() + -> detail::pack { + return detail::pack(); +} + +namespace detail { + +template +struct take_at +{ + template + auto operator()(ParamN... pn) + -> typename std::tuple_element...>>::type { + return std::get(std::make_tuple(std::move(pn)...)); + } + template + auto operator()(ParamN... pn) const + -> typename std::tuple_element...>>::type { + return std::get(std::make_tuple(std::move(pn)...)); + } +}; + +} + +template +inline auto take_at() + -> detail::take_at { + return detail::take_at(); +} + +template +struct resolve_type; + +template class Deferred, class... AN> +struct defer_trait +{ + template + struct tag_valid {static const bool valid = true; static const bool value = R;}; + struct tag_not_valid {static const bool valid = false; static const bool value = false;}; + typedef Deferred::type...> resolved_type; + template + static auto check(int) -> tag_valid; + template + static tag_not_valid check(...); + + typedef decltype(check(0)) tag_type; + static const bool valid = tag_type::valid; + static const bool value = tag_type::value; + static const bool not_value = valid && !value; +}; + +template class Deferred, class... AN> +struct defer_type +{ + template + struct tag_valid {typedef R type; static const bool value = true;}; + struct tag_not_valid {typedef void type; static const bool value = false;}; + typedef Deferred::type...> resolved_type; + template + static auto check(int) -> tag_valid; + template + static tag_not_valid check(...); + + typedef decltype(check(0)) tag_type; + typedef typename tag_type::type type; + static const bool value = tag_type::value; +}; + +template class Deferred, class... AN> +struct defer_value_type +{ + template + struct tag_valid {typedef R type; static const bool value = true;}; + struct tag_not_valid {typedef void type; static const bool value = false;}; + typedef Deferred::type...> resolved_type; + template + static auto check(int) -> tag_valid>; + template + static tag_not_valid check(...); + + typedef decltype(check(0)) tag_type; + typedef typename tag_type::type type; + static const bool value = tag_type::value; +}; + +template class Deferred, class... AN> +struct defer_seed_type +{ + template + struct tag_valid {typedef R type; static const bool value = true;}; + struct tag_not_valid {typedef void type; static const bool value = false;}; + typedef Deferred::type...> resolved_type; + template + static auto check(int) -> tag_valid; + template + static tag_not_valid check(...); + + typedef decltype(check(0)) tag_type; + typedef typename tag_type::type type; + static const bool value = tag_type::value; +}; + +template +struct resolve_type +{ + typedef D type; +}; +template class Deferred, class... AN> +struct resolve_type> +{ + typedef typename defer_type::type type; +}; +template class Deferred, class... AN> +struct resolve_type> +{ + typedef typename defer_value_type::type type; +}; +template class Deferred, class... AN> +struct resolve_type> +{ + typedef typename defer_seed_type::type type; +}; + +struct plus +{ + template + auto operator()(LHS&& lhs, RHS&& rhs) const + -> decltype(std::forward(lhs) + std::forward(rhs)) + { return std::forward(lhs) + std::forward(rhs); } +}; + +struct count +{ + template + int operator()(int cnt, T&&) const + { return cnt + 1; } +}; + +struct less +{ + template + auto operator()(LHS&& lhs, RHS&& rhs) const + -> decltype(std::forward(lhs) < std::forward(rhs)) + { return std::forward(lhs) < std::forward(rhs); } +}; + +template +struct ret +{ + template + auto operator()(LHS&& ) const + -> decltype(T()) + { return T(); } +}; + +template +struct equal_to +{ + bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } +}; + +template<> +struct equal_to +{ + template + auto operator()(LHS&& lhs, RHS&& rhs) const + -> decltype(std::forward(lhs) == std::forward(rhs)) + { return std::forward(lhs) == std::forward(rhs); } +}; + +namespace detail { +template +struct print_function +{ + OStream& os; + Delimit delimit; + print_function(OStream& os, Delimit d) : os(os), delimit(std::move(d)) {} + + template + void operator()(const TN&... tn) const { + bool inserts[] = {(os << tn, true)...}; + inserts[0] = *reinterpret_cast(inserts); // silence warning + delimit(); + } + + template + void operator()(const std::tuple& tpl) const { + rxcpp::util::apply(tpl, *this); + } +}; + +template +struct endline +{ + OStream& os; + endline(OStream& os) : os(os) {} + void operator()() const { + os << std::endl; + } +private: + endline& operator=(const endline&) RXCPP_DELETE; +}; + +template +struct insert_value +{ + OStream& os; + ValueType value; + insert_value(OStream& os, ValueType v) : os(os), value(std::move(v)) {} + void operator()() const { + os << value; + } +private: + insert_value& operator=(const insert_value&) RXCPP_DELETE; +}; + +template +struct insert_function +{ + OStream& os; + Function call; + insert_function(OStream& os, Function f) : os(os), call(std::move(f)) {} + void operator()() const { + call(os); + } +private: + insert_function& operator=(const insert_function&) RXCPP_DELETE; +}; + +template +auto print_followed_with(OStream& os, Delimit d) + -> detail::print_function { + return detail::print_function(os, std::move(d)); +} + +} + +template +auto endline(OStream& os) + -> detail::endline { + return detail::endline(os); +} + +template +auto println(OStream& os) + -> decltype(detail::print_followed_with(os, endline(os))) { + return detail::print_followed_with(os, endline(os)); +} +template +auto print_followed_with(OStream& os, Delimit d) + -> decltype(detail::print_followed_with(os, detail::insert_function(os, std::move(d)))) { + return detail::print_followed_with(os, detail::insert_function(os, std::move(d))); +} +template +auto print_followed_by(OStream& os, DelimitValue dv) + -> decltype(detail::print_followed_with(os, detail::insert_value(os, std::move(dv)))) { + return detail::print_followed_with(os, detail::insert_value(os, std::move(dv))); +} + +inline std::string what(std::exception_ptr ep) { +#if RXCPP_USE_EXCEPTIONS + try {std::rethrow_exception(ep);} + catch (const std::exception& ex) { + return ex.what(); + } catch (...) { + return std::string(""); + } +#endif + (void)ep; + return std::string(""); +} + +namespace detail { + +template +class maybe +{ + bool is_set; + typename std::aligned_storage::value>::type + storage; +public: + maybe() + : is_set(false) + { + } + + maybe(T value) + : is_set(false) + { + new (reinterpret_cast(&storage)) T(value); + is_set = true; + } + + maybe(const maybe& other) + : is_set(false) + { + if (other.is_set) { + new (reinterpret_cast(&storage)) T(other.get()); + is_set = true; + } + } + maybe(maybe&& other) + : is_set(false) + { + if (other.is_set) { + new (reinterpret_cast(&storage)) T(std::move(other.get())); + is_set = true; + other.reset(); + } + } + + ~maybe() + { + reset(); + } + + typedef T value_type; + typedef T* iterator; + typedef const T* const_iterator; + + bool empty() const { + return !is_set; + } + + std::size_t size() const { + return is_set ? 1 : 0; + } + + iterator begin() { + return reinterpret_cast(&storage); + } + const_iterator begin() const { + return reinterpret_cast(&storage); + } + + iterator end() { + return reinterpret_cast(&storage) + size(); + } + const_iterator end() const { + return reinterpret_cast(&storage) + size(); + } + + T* operator->() { + if (!is_set) std::terminate(); + return reinterpret_cast(&storage); + } + const T* operator->() const { + if (!is_set) std::terminate(); + return reinterpret_cast(&storage); + } + + T& operator*() { + if (!is_set) std::terminate(); + return *reinterpret_cast(&storage); + } + const T& operator*() const { + if (!is_set) std::terminate(); + return *reinterpret_cast(&storage); + } + + T& get() { + if (!is_set) std::terminate(); + return *reinterpret_cast(&storage); + } + const T& get() const { + if (!is_set) std::terminate(); + return *reinterpret_cast(&storage); + } + + void reset() + { + if (is_set) { + is_set = false; + reinterpret_cast(&storage)->~T(); + //std::fill_n(reinterpret_cast(&storage), sizeof(T), 0); + } + } + + template + void reset(U&& value) { + reset(); + new (reinterpret_cast(&storage)) T(std::forward(value)); + is_set = true; + } + + maybe& operator=(const T& other) { + reset(other); + return *this; + } + maybe& operator=(const maybe& other) { + if (!other.empty()) { + reset(other.get()); + } else { + reset(); + } + return *this; + } +}; + +} +using detail::maybe; + +namespace detail { + struct surely + { + template + auto operator()(T... t) + -> decltype(std::make_tuple(t.get()...)) { + return std::make_tuple(t.get()...); + } + template + auto operator()(T... t) const + -> decltype(std::make_tuple(t.get()...)) { + return std::make_tuple(t.get()...); + } + }; +} + +template +inline auto surely(const std::tuple& tpl) + -> decltype(apply(tpl, detail::surely())) { + return apply(tpl, detail::surely()); +} + +namespace detail { + +template +class unwinder +{ +public: + ~unwinder() + { + if (!!function) + { + RXCPP_TRY { + (*function)(); + } RXCPP_CATCH(...) { + std::terminate(); + } + } + } + + explicit unwinder(Function* functionArg) + : function(functionArg) + { + } + + void dismiss() + { + function = nullptr; + } + +private: + unwinder(); + unwinder(const unwinder&); + unwinder& operator=(const unwinder&); + + Function* function; +}; + +} + +#if !defined(RXCPP_THREAD_LOCAL) +template +class thread_local_storage +{ +private: + pthread_key_t key; + +public: + thread_local_storage() + { + pthread_key_create(&key, NULL); + } + + ~thread_local_storage() + { + pthread_key_delete(key); + } + + thread_local_storage& operator =(T* p) + { + pthread_setspecific(key, p); + return *this; + } + + bool operator !() + { + return pthread_getspecific(key) == NULL; + } + + T* operator ->() + { + return static_cast(pthread_getspecific(key)); + } + + T* get() + { + return static_cast(pthread_getspecific(key)); + } +}; +#endif + +template +struct is_string : std::false_type { +}; + +template +struct is_string::type> + : std::is_base_of< + std::basic_string< + typename T::value_type, + typename T::traits_type, + typename T::allocator_type>, T> { +}; + +namespace detail { + + template + struct is_duration : std::false_type {}; + + template + struct is_duration> + : std::is_convertible*> {}; + +} + +template > +struct is_duration : detail::is_duration {}; + + +// C++17 negation +namespace detail { + template + struct not_value : std::conditional::type { + }; +} + +template +struct negation : detail::not_value {}; + +} + +#if !RXCPP_USE_EXCEPTIONS +namespace util { + +namespace detail { + +struct error_base { + virtual const char* what() = 0; + virtual ~error_base() {} +}; + +// Use the "Type Erasure" idiom to wrap an std::exception-like +// value into an error pointer. +// +// Supported types: +// exception, bad_exception, bad_alloc. +template +struct error_specific : public error_base { + error_specific(const E& e) : data(e) {} + error_specific(E&& e) : data(std::move(e)) {} + + virtual ~error_specific() {} + + virtual const char* what() { + return data.what(); + } + + E data; +}; + +} + +} +#endif + +namespace util { + +#if RXCPP_USE_EXCEPTIONS +using error_ptr = std::exception_ptr; +#else +// Note: std::exception_ptr cannot be used directly when exceptions are disabled. +// Any attempt to 'throw' or to call into any of the std functions accepting +// an std::exception_ptr will either fail to compile or result in an abort at runtime. +using error_ptr = std::shared_ptr; + +inline std::string what(error_ptr ep) { + return std::string(ep->what()); +} +#endif + +// TODO: Do we really need an identity make? +// (It was causing some compilation errors deep inside templates). +inline error_ptr make_error_ptr(error_ptr e) { + return e; +} + +// Replace std::make_exception_ptr (which would immediately terminate +// when exceptions are disabled). +template +error_ptr make_error_ptr(E&& e) { +#if RXCPP_USE_EXCEPTIONS + return std::make_exception_ptr(std::forward(e)); +#else + using e_type = rxcpp::util::decay_t; + using pointed_to_type = rxcpp::util::detail::error_specific; + auto sp = std::make_shared(std::forward(e)); + return std::static_pointer_cast(sp); +#endif +} + +// Replace std::rethrow_exception to be compatible with our error_ptr typedef. +RXCPP_NORETURN inline void rethrow_exception(error_ptr e) { +#if RXCPP_USE_EXCEPTIONS + std::rethrow_exception(e); +#else + // error_ptr != std::exception_ptr so we can't use std::rethrow_exception + // + // However even if we could, calling std::rethrow_exception just terminates if exceptions are disabled. + // + // Therefore this function should only be called when we are completely giving up and have no idea + // how to handle the error. + (void)e; + std::terminate(); +#endif +} + +// A replacement for the "throw" keyword which is illegal when +// exceptions are disabled with -fno-exceptions. +template +RXCPP_NORETURN inline void throw_exception(E&& e) { +#if RXCPP_USE_EXCEPTIONS + throw std::forward(e); +#else + // "throw" keyword is unsupported when exceptions are disabled. + // Immediately terminate instead. + (void)e; + std::terminate(); +#endif +} + +// TODO: Do we really need this? rxu::rethrow_exception(rxu::current_exception()) +// would have the same semantics in either case. +RXCPP_NORETURN inline void rethrow_current_exception() { +#if RXCPP_USE_EXCEPTIONS + std::rethrow_exception(std::current_exception()); +#else + std::terminate(); +#endif +} + +// If called during exception handling, return the currently caught exception. +// Otherwise return null. +inline error_ptr current_exception() { +#if RXCPP_USE_EXCEPTIONS + return std::current_exception(); +#else + // When exceptions are disabled, we can never be inside of a catch block. + // Return null similar to std::current_exception returning null outside of catch. + return nullptr; +#endif +} + +} +namespace rxu=util; + + +// +// due to an noisy static_assert issue in more than one std lib impl, +// rxcpp maintains a whitelist filter for the types that are allowed +// to be hashed. this allows is_hashable to work. +// +// NOTE: this should eventually be removed! +// +template +struct filtered_hash; + +#if RXCPP_HASH_ENUM +template +struct filtered_hash::value>::type> : std::hash { +}; +#elif RXCPP_HASH_ENUM_UNDERLYING +template +struct filtered_hash::value>::type> : std::hash::type> { +}; +#endif + +template +struct filtered_hash::value>::type> : std::hash { +}; +template +struct filtered_hash::value>::type> : std::hash { +}; +template +struct filtered_hash::value>::type> : std::hash { +}; +template +struct filtered_hash>::value>::type> { + using argument_type = T; + using result_type = std::size_t; + + result_type operator()(argument_type const & dur) const + { + return std::hash{}(dur.count()); + } +}; +template +struct filtered_hash>::value>::type> { + using argument_type = T; + using result_type = std::size_t; + + result_type operator()(argument_type const & tp) const + { + return std::hash{}(tp.time_since_epoch().count()); + } +}; + +template +struct is_hashable + : std::false_type {}; + +template +struct is_hashable::result_type, + typename filtered_hash::argument_type, + typename rxu::result_of(T)>::type>::type> + : std::true_type {}; + +} + +#define RXCPP_UNWIND(Name, Function) \ + RXCPP_UNWIND_EXPLICIT(uwfunc_ ## Name, Name, Function) + +#define RXCPP_UNWIND_AUTO(Function) \ + RXCPP_UNWIND_EXPLICIT(RXCPP_MAKE_IDENTIFIER(uwfunc_), RXCPP_MAKE_IDENTIFIER(unwind_), Function) + +#define RXCPP_UNWIND_EXPLICIT(FunctionName, UnwinderName, Function) \ + auto FunctionName = (Function); \ + rxcpp::util::detail::unwinder UnwinderName(std::addressof(FunctionName)) + +#endif diff --git a/3party/rxcpp/schedulers/rx-currentthread.hpp b/3party/rxcpp/schedulers/rx-currentthread.hpp new file mode 100644 index 0000000..fafa3c1 --- /dev/null +++ b/3party/rxcpp/schedulers/rx-currentthread.hpp @@ -0,0 +1,272 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SCHEDULER_CURRENT_THREAD_HPP) +#define RXCPP_RX_SCHEDULER_CURRENT_THREAD_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +namespace detail { + +struct action_queue +{ + typedef action_queue this_type; + + typedef scheduler_base::clock_type clock; + typedef time_schedulable item_type; + +private: + typedef schedulable_queue queue_item_time; + +public: + struct current_thread_queue_type { + std::shared_ptr w; + recursion r; + queue_item_time q; + }; + +private: +#if defined(RXCPP_THREAD_LOCAL) + static current_thread_queue_type*& current_thread_queue() { + static RXCPP_THREAD_LOCAL current_thread_queue_type* q; + return q; + } +#else + static rxu::thread_local_storage& current_thread_queue() { + static rxu::thread_local_storage q; + return q; + } +#endif + +public: + + static bool owned() { + return !!current_thread_queue(); + } + static const std::shared_ptr& get_worker_interface() { + return current_thread_queue()->w; + } + static recursion& get_recursion() { + return current_thread_queue()->r; + } + static bool empty() { + if (!current_thread_queue()) { + std::terminate(); + } + return current_thread_queue()->q.empty(); + } + static queue_item_time::const_reference top() { + if (!current_thread_queue()) { + std::terminate(); + } + return current_thread_queue()->q.top(); + } + static void pop() { + auto& state = current_thread_queue(); + if (!state) { + std::terminate(); + } + state->q.pop(); + if (state->q.empty()) { + // allow recursion + state->r.reset(true); + } + } + static void push(item_type item) { + auto& state = current_thread_queue(); + if (!state) { + std::terminate(); + } + if (!item.what.is_subscribed()) { + return; + } + state->q.push(std::move(item)); + // disallow recursion + state->r.reset(false); + } + static std::shared_ptr ensure(std::shared_ptr w) { + if (!!current_thread_queue()) { + std::terminate(); + } + // create and publish new queue + current_thread_queue() = new current_thread_queue_type(); + current_thread_queue()->w = w; + return w; + } + static std::unique_ptr create(std::shared_ptr w) { + std::unique_ptr result(new current_thread_queue_type()); + result->w = std::move(w); + return result; + } + static void set(current_thread_queue_type* q) { + if (!!current_thread_queue()) { + std::terminate(); + } + // publish new queue + current_thread_queue() = q; + } + static void destroy(current_thread_queue_type* q) { + delete q; + } + static void destroy() { + if (!current_thread_queue()) { + std::terminate(); + } +#if defined(RXCPP_THREAD_LOCAL) + destroy(current_thread_queue()); +#else + destroy(current_thread_queue().get()); +#endif + current_thread_queue() = nullptr; + } +}; + +} + +struct current_thread : public scheduler_interface +{ +private: + typedef current_thread this_type; + current_thread(const this_type&); + + typedef detail::action_queue queue_type; + + struct derecurser : public worker_interface + { + private: + typedef current_thread this_type; + derecurser(const this_type&); + public: + derecurser() + { + } + virtual ~derecurser() + { + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual void schedule(const schedulable& scbl) const { + queue_type::push(queue_type::item_type(now(), scbl)); + } + + virtual void schedule(clock_type::time_point when, const schedulable& scbl) const { + queue_type::push(queue_type::item_type(when, scbl)); + } + }; + + struct current_worker : public worker_interface + { + private: + typedef current_thread this_type; + current_worker(const this_type&); + public: + current_worker() + { + } + virtual ~current_worker() + { + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual void schedule(const schedulable& scbl) const { + schedule(now(), scbl); + } + + virtual void schedule(clock_type::time_point when, const schedulable& scbl) const { + if (!scbl.is_subscribed()) { + return; + } + + { + // check ownership + if (queue_type::owned()) { + // already has an owner - delegate + queue_type::get_worker_interface()->schedule(when, scbl); + return; + } + + // take ownership + queue_type::ensure(std::make_shared()); + } + // release ownership + RXCPP_UNWIND_AUTO([]{ + queue_type::destroy(); + }); + + const auto& recursor = queue_type::get_recursion().get_recurse(); + std::this_thread::sleep_until(when); + if (scbl.is_subscribed()) { + scbl(recursor); + } + if (queue_type::empty()) { + return; + } + + // loop until queue is empty + for ( + auto next = queue_type::top().when; + (std::this_thread::sleep_until(next), true); + next = queue_type::top().when + ) { + auto what = queue_type::top().what; + + queue_type::pop(); + + if (what.is_subscribed()) { + what(recursor); + } + + if (queue_type::empty()) { + break; + } + } + } + }; + + std::shared_ptr wi; + +public: + current_thread() + : wi(std::make_shared()) + { + } + virtual ~current_thread() + { + } + + static bool is_schedule_required() { return !queue_type::owned(); } + + inline bool is_tail_recursion_allowed() const { + return queue_type::empty(); + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual worker create_worker(composite_subscription cs) const { + return worker(std::move(cs), wi); + } +}; + +inline const scheduler& make_current_thread() { + static scheduler instance = make_scheduler(); + return instance; +} + +} + +} + +#endif diff --git a/3party/rxcpp/schedulers/rx-eventloop.hpp b/3party/rxcpp/schedulers/rx-eventloop.hpp new file mode 100644 index 0000000..4f83767 --- /dev/null +++ b/3party/rxcpp/schedulers/rx-eventloop.hpp @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SCHEDULER_EVENT_LOOP_HPP) +#define RXCPP_RX_SCHEDULER_EVENT_LOOP_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +struct event_loop : public scheduler_interface +{ +private: + typedef event_loop this_type; + event_loop(const this_type&); + + struct loop_worker : public worker_interface + { + private: + typedef loop_worker this_type; + loop_worker(const this_type&); + + typedef detail::schedulable_queue< + typename clock_type::time_point> queue_item_time; + + typedef queue_item_time::item_type item_type; + + composite_subscription lifetime; + worker controller; + std::shared_ptr alive; + + public: + virtual ~loop_worker() + { + } + loop_worker(composite_subscription cs, worker w, std::shared_ptr alive) + : lifetime(cs) + , controller(w) + , alive(alive) + { + auto token = controller.add(cs); + cs.add([token, w](){ + w.remove(token); + }); + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual void schedule(const schedulable& scbl) const { + controller.schedule(lifetime, scbl.get_action()); + } + + virtual void schedule(clock_type::time_point when, const schedulable& scbl) const { + controller.schedule(when, lifetime, scbl.get_action()); + } + }; + + mutable thread_factory factory; + scheduler newthread; + mutable std::atomic count; + composite_subscription loops_lifetime; + std::vector loops; + +public: + event_loop() + : factory([](std::function start){ + return std::thread(std::move(start)); + }) + , newthread(make_new_thread()) + , count(0) + { + auto remaining = std::max(std::thread::hardware_concurrency(), unsigned(4)); + while (remaining--) { + loops.push_back(newthread.create_worker(loops_lifetime)); + } + } + explicit event_loop(thread_factory tf) + : factory(tf) + , newthread(make_new_thread(tf)) + , count(0) + { + auto remaining = std::max(std::thread::hardware_concurrency(), unsigned(4)); + while (remaining--) { + loops.push_back(newthread.create_worker(loops_lifetime)); + } + } + virtual ~event_loop() + { + loops_lifetime.unsubscribe(); + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual worker create_worker(composite_subscription cs) const { + return worker(cs, std::make_shared(cs, loops[++count % loops.size()], this->shared_from_this())); + } +}; + +inline scheduler make_event_loop() { + static scheduler instance = make_scheduler(); + return instance; +} +inline scheduler make_event_loop(thread_factory tf) { + return make_scheduler(tf); +} + +} + +} + +#endif diff --git a/3party/rxcpp/schedulers/rx-immediate.hpp b/3party/rxcpp/schedulers/rx-immediate.hpp new file mode 100644 index 0000000..0d2b695 --- /dev/null +++ b/3party/rxcpp/schedulers/rx-immediate.hpp @@ -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 + +#if !defined(RXCPP_RX_SCHEDULER_IMMEDIATE_HPP) +#define RXCPP_RX_SCHEDULER_IMMEDIATE_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +struct immediate : public scheduler_interface +{ +private: + typedef immediate this_type; + immediate(const this_type&); + + struct immediate_worker : public worker_interface + { + private: + typedef immediate_worker this_type; + immediate_worker(const this_type&); + public: + virtual ~immediate_worker() + { + } + immediate_worker() + { + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual void schedule(const schedulable& scbl) const { + if (scbl.is_subscribed()) { + // allow recursion + recursion r(true); + scbl(r.get_recurse()); + } + } + + virtual void schedule(clock_type::time_point when, const schedulable& scbl) const { + std::this_thread::sleep_until(when); + if (scbl.is_subscribed()) { + // allow recursion + recursion r(true); + scbl(r.get_recurse()); + } + } + }; + + std::shared_ptr wi; + +public: + immediate() + : wi(std::make_shared()) + { + } + virtual ~immediate() + { + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual worker create_worker(composite_subscription cs) const { + return worker(std::move(cs), wi); + } +}; + +inline const scheduler& make_immediate() { + static scheduler instance = make_scheduler(); + return instance; +} + +} + +} + +#endif diff --git a/3party/rxcpp/schedulers/rx-newthread.hpp b/3party/rxcpp/schedulers/rx-newthread.hpp new file mode 100644 index 0000000..1500b25 --- /dev/null +++ b/3party/rxcpp/schedulers/rx-newthread.hpp @@ -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 + +#if !defined(RXCPP_RX_SCHEDULER_NEW_THREAD_HPP) +#define RXCPP_RX_SCHEDULER_NEW_THREAD_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +typedef std::function)> thread_factory; + +struct new_thread : public scheduler_interface +{ +private: + typedef new_thread this_type; + new_thread(const this_type&); + + struct new_worker : public worker_interface + { + private: + typedef new_worker this_type; + + typedef detail::action_queue queue_type; + + new_worker(const this_type&); + + struct new_worker_state : public std::enable_shared_from_this + { + typedef detail::schedulable_queue< + typename clock_type::time_point> queue_item_time; + + typedef queue_item_time::item_type item_type; + + virtual ~new_worker_state() + { + } + + explicit new_worker_state(composite_subscription cs) + : lifetime(cs) + { + } + + composite_subscription lifetime; + mutable std::mutex lock; + mutable std::condition_variable wake; + mutable queue_item_time q; + std::thread worker; + recursion r; + }; + + std::shared_ptr state; + + public: + virtual ~new_worker() + { + } + + explicit new_worker(std::shared_ptr ws) + : state(ws) + { + } + + new_worker(composite_subscription cs, thread_factory& tf) + : state(std::make_shared(cs)) + { + auto keepAlive = state; + + state->lifetime.add([keepAlive](){ + std::unique_lock guard(keepAlive->lock); + auto expired = std::move(keepAlive->q); + keepAlive->q = new_worker_state::queue_item_time{}; + if (!keepAlive->q.empty()) std::terminate(); + keepAlive->wake.notify_one(); + + if (keepAlive->worker.joinable() && keepAlive->worker.get_id() != std::this_thread::get_id()) { + guard.unlock(); + keepAlive->worker.join(); + } + else { + keepAlive->worker.detach(); + } + }); + + state->worker = tf([keepAlive](){ + + // take ownership + queue_type::ensure(std::make_shared(keepAlive)); + // release ownership + RXCPP_UNWIND_AUTO([]{ + queue_type::destroy(); + }); + + for(;;) { + std::unique_lock guard(keepAlive->lock); + if (keepAlive->q.empty()) { + keepAlive->wake.wait(guard, [keepAlive](){ + return !keepAlive->lifetime.is_subscribed() || !keepAlive->q.empty(); + }); + } + if (!keepAlive->lifetime.is_subscribed()) { + break; + } + auto& peek = keepAlive->q.top(); + if (!peek.what.is_subscribed()) { + keepAlive->q.pop(); + continue; + } + auto when = peek.when; + if (clock_type::now() < when) { + keepAlive->wake.wait_until(guard, when); + continue; + } + auto what = peek.what; + keepAlive->q.pop(); + keepAlive->r.reset(keepAlive->q.empty()); + guard.unlock(); + what(keepAlive->r.get_recurse()); + } + }); + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual void schedule(const schedulable& scbl) const { + schedule(now(), scbl); + } + + virtual void schedule(clock_type::time_point when, const schedulable& scbl) const { + if (scbl.is_subscribed()) { + std::unique_lock guard(state->lock); + state->q.push(new_worker_state::item_type(when, scbl)); + state->r.reset(false); + } + state->wake.notify_one(); + } + }; + + mutable thread_factory factory; + +public: + new_thread() + : factory([](std::function start){ + return std::thread(std::move(start)); + }) + { + } + explicit new_thread(thread_factory tf) + : factory(tf) + { + } + virtual ~new_thread() + { + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual worker create_worker(composite_subscription cs) const { + return worker(cs, std::make_shared(cs, factory)); + } +}; + +inline scheduler make_new_thread() { + static scheduler instance = make_scheduler(); + return instance; +} +inline scheduler make_new_thread(thread_factory tf) { + return make_scheduler(tf); +} + +} + +} + +#endif diff --git a/3party/rxcpp/schedulers/rx-runloop.hpp b/3party/rxcpp/schedulers/rx-runloop.hpp new file mode 100644 index 0000000..2e99a5c --- /dev/null +++ b/3party/rxcpp/schedulers/rx-runloop.hpp @@ -0,0 +1,214 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SCHEDULER_RUN_LOOP_HPP) +#define RXCPP_RX_SCHEDULER_RUN_LOOP_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +namespace detail { + +struct run_loop_state : public std::enable_shared_from_this +{ + typedef scheduler::clock_type clock_type; + + typedef detail::schedulable_queue< + clock_type::time_point> queue_item_time; + + typedef queue_item_time::item_type item_type; + typedef queue_item_time::const_reference const_reference_item_type; + + virtual ~run_loop_state() + { + } + + run_loop_state() + { + } + + composite_subscription lifetime; + mutable std::mutex lock; + mutable queue_item_time q; + recursion r; + std::function notify_earlier_wakeup; +}; + +} + + +struct run_loop_scheduler : public scheduler_interface +{ +private: + typedef run_loop_scheduler this_type; + run_loop_scheduler(const this_type&); + + struct run_loop_worker : public worker_interface + { + private: + typedef run_loop_worker this_type; + + run_loop_worker(const this_type&); + + public: + std::weak_ptr state; + + virtual ~run_loop_worker() + { + } + + explicit run_loop_worker(std::weak_ptr ws) + : state(ws) + { + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual void schedule(const schedulable& scbl) const { + schedule(now(), scbl); + } + + virtual void schedule(clock_type::time_point when, const schedulable& scbl) const { + if (scbl.is_subscribed()) { + auto st = state.lock(); + std::unique_lock guard(st->lock); + const bool need_earlier_wakeup_notification = st->notify_earlier_wakeup && + (st->q.empty() || when < st->q.top().when); + st->q.push(detail::run_loop_state::item_type(when, scbl)); + st->r.reset(false); + if (need_earlier_wakeup_notification) st->notify_earlier_wakeup(when); + guard.unlock(); // So we can't get attempt to recursively lock the state + } + } + }; + + std::weak_ptr state; + +public: + explicit run_loop_scheduler(std::weak_ptr ws) + : state(ws) + { + } + virtual ~run_loop_scheduler() + { + } + + virtual clock_type::time_point now() const { + return clock_type::now(); + } + + virtual worker create_worker(composite_subscription cs) const { + auto lifetime = state.lock()->lifetime; + auto token = lifetime.add(cs); + cs.add([=](){lifetime.remove(token);}); + return worker(cs, create_worker_interface()); + } + + std::shared_ptr create_worker_interface() const { + return std::make_shared(state); + } +}; + +class run_loop +{ +private: + typedef run_loop this_type; + // don't allow this instance to copy/move since it owns current_thread queue + // for the thread it is constructed on. + run_loop(const this_type&); + run_loop(this_type&&); + + typedef detail::action_queue queue_type; + + typedef detail::run_loop_state::item_type item_type; + typedef detail::run_loop_state::const_reference_item_type const_reference_item_type; + + std::shared_ptr state; + std::shared_ptr sc; + +public: + typedef scheduler::clock_type clock_type; + run_loop() + : state(std::make_shared()) + , sc(std::make_shared(state)) + { + // take ownership so that the current_thread scheduler + // uses the same queue on this thread + queue_type::ensure(sc->create_worker_interface()); + } + ~run_loop() + { + state->lifetime.unsubscribe(); + + std::unique_lock guard(state->lock); + + // release ownership + queue_type::destroy(); + + auto expired = std::move(state->q); + if (!state->q.empty()) std::terminate(); + } + + clock_type::time_point now() const { + return clock_type::now(); + } + + composite_subscription get_subscription() const { + return state->lifetime; + } + + bool empty() const { + std::unique_lock guard(state->lock); + return state->q.empty(); + } + + const_reference_item_type peek() const { + std::unique_lock guard(state->lock); + return state->q.top(); + } + + void dispatch() const { + std::unique_lock guard(state->lock); + if (state->q.empty()) { + return; + } + auto& peek = state->q.top(); + if (!peek.what.is_subscribed()) { + state->q.pop(); + return; + } + if (clock_type::now() < peek.when) { + return; + } + auto what = peek.what; + state->q.pop(); + state->r.reset(state->q.empty()); + guard.unlock(); + what(state->r.get_recurse()); + } + + scheduler get_scheduler() const { + return make_scheduler(sc); + } + + void set_notify_earlier_wakeup(std::function const& f) { + std::unique_lock guard(state->lock); + state->notify_earlier_wakeup = f; + } +}; + +inline scheduler make_run_loop(const run_loop& r) { + return r.get_scheduler(); +} + +} + +} + +#endif diff --git a/3party/rxcpp/schedulers/rx-sameworker.hpp b/3party/rxcpp/schedulers/rx-sameworker.hpp new file mode 100644 index 0000000..4bfbd0b --- /dev/null +++ b/3party/rxcpp/schedulers/rx-sameworker.hpp @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SCHEDULER_SAME_WORKER_HPP) +#define RXCPP_RX_SCHEDULER_SAME_WORKER_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +struct same_worker : public scheduler_interface +{ +private: + typedef same_worker this_type; + same_worker(const this_type&); + + rxsc::worker controller; + +public: + explicit same_worker(rxsc::worker w) + : controller(std::move(w)) + { + } + virtual ~same_worker() + { + } + + virtual clock_type::time_point now() const { + return controller.now(); + } + + virtual worker create_worker(composite_subscription cs) const { + // use different lifetime + auto inner_lifetime = controller.get_subscription(); + auto token = inner_lifetime.add(cs); + cs.add([inner_lifetime, token](){inner_lifetime.remove(token);}); + return worker(cs, controller); + } +}; + +inline scheduler make_same_worker(rxsc::worker w) { + return make_scheduler(std::move(w)); +} + +} + +} + +#endif diff --git a/3party/rxcpp/schedulers/rx-test.hpp b/3party/rxcpp/schedulers/rx-test.hpp new file mode 100644 index 0000000..aaa073d --- /dev/null +++ b/3party/rxcpp/schedulers/rx-test.hpp @@ -0,0 +1,617 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SCHEDULER_TEST_HPP) +#define RXCPP_RX_SCHEDULER_TEST_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +namespace detail { + +class test_type : public scheduler_interface +{ +public: + + typedef scheduler_interface::clock_type clock_type; + + struct test_type_state : public virtual_time + { + typedef virtual_time base; + + using base::schedule_absolute; + using base::schedule_relative; + + clock_type::time_point now() const { + return to_time_point(clock_now); + } + + virtual void schedule_absolute(long when, const schedulable& a) const + { + if (when <= base::clock_now) + when = base::clock_now + 1; + + return base::schedule_absolute(when, a); + } + + virtual long add(long absolute, long relative) const + { + return absolute + relative; + } + + virtual clock_type::time_point to_time_point(long absolute) const + { + return clock_type::time_point(std::chrono::milliseconds(absolute)); + } + + virtual long to_relative(clock_type::duration d) const + { + return static_cast(std::chrono::duration_cast(d).count()); + } + }; + +private: + mutable std::shared_ptr state; + +public: + struct test_type_worker : public worker_interface + { + mutable std::shared_ptr state; + + typedef test_type_state::absolute absolute; + typedef test_type_state::relative relative; + + test_type_worker(std::shared_ptr st) + : state(std::move(st)) + { + } + + virtual clock_type::time_point now() const { + return state->now(); + } + + virtual void schedule(const schedulable& scbl) const { + state->schedule_absolute(state->clock(), scbl); + } + + virtual void schedule(clock_type::time_point when, const schedulable& scbl) const { + state->schedule_relative(state->to_relative(when - now()), scbl); + } + + void schedule_absolute(absolute when, const schedulable& scbl) const { + state->schedule_absolute(when, scbl); + } + + void schedule_relative(relative when, const schedulable& scbl) const { + state->schedule_relative(when, scbl); + } + + bool is_enabled() const {return state->is_enabled();} + absolute clock() const {return state->clock();} + + void start() const + { + state->start(); + } + + void stop() const + { + state->stop(); + } + + void advance_to(absolute time) const + { + state->advance_to(time); + } + + void advance_by(relative time) const + { + state->advance_by(time); + } + + void sleep(relative time) const + { + state->sleep(time); + } + + template + subscriber> make_subscriber() const; + }; + +public: + test_type() + : state(std::make_shared()) + { + } + + virtual clock_type::time_point now() const { + return state->now(); + } + + virtual worker create_worker(composite_subscription cs) const { + return worker(cs, std::make_shared(state)); + } + + bool is_enabled() const {return state->is_enabled();} + long clock() { + return state->clock(); + } + + clock_type::time_point to_time_point(long absolute) const { + return state->to_time_point(absolute); + } + + std::shared_ptr create_test_type_worker_interface() const { + return std::make_shared(state); + } + + template + rxt::testable_observable make_hot_observable(std::vector>>> messages) const; + + template + rxt::testable_observable make_cold_observable(std::vector>>> messages) const; +}; + +template +class mock_observer + : public rxt::detail::test_subject_base +{ + typedef typename rxn::notification notification_type; + typedef rxn::recorded recorded_type; + +public: + explicit mock_observer(std::shared_ptr sc) + : sc(sc) + { + } + + std::shared_ptr sc; + std::vector m; + + virtual void on_subscribe(subscriber) const { + std::terminate(); + } + virtual std::vector subscriptions() const { + std::terminate(); + } + + virtual std::vector messages() const { + return m; + } +}; + +template +subscriber> test_type::test_type_worker::make_subscriber() const +{ + typedef typename rxn::notification notification_type; + typedef rxn::recorded recorded_type; + + auto ts = std::make_shared>(state); + + return rxcpp::make_subscriber(rxt::testable_observer(ts, make_observer_dynamic( + // on_next + [ts](T value) + { + ts->m.push_back( + recorded_type(ts->sc->clock(), notification_type::on_next(value))); + }, + // on_error + [ts](rxu::error_ptr e) + { + ts->m.push_back( + recorded_type(ts->sc->clock(), notification_type::on_error(e))); + }, + // on_completed + [ts]() + { + ts->m.push_back( + recorded_type(ts->sc->clock(), notification_type::on_completed())); + }))); +} + +template +class cold_observable + : public rxt::detail::test_subject_base +{ + typedef cold_observable this_type; + std::shared_ptr sc; + typedef rxn::recorded::type> recorded_type; + mutable std::vector mv; + mutable std::vector sv; + mutable worker controller; + +public: + + cold_observable(std::shared_ptr sc, worker w, std::vector mv) + : sc(sc) + , mv(std::move(mv)) + , controller(w) + { + } + + template + cold_observable(std::shared_ptr sc, worker w, Iterator begin, Iterator end) + : sc(sc) + , mv(begin, end) + , controller(w) + { + } + + virtual void on_subscribe(subscriber o) const { + sv.push_back(rxn::subscription(sc->clock())); + auto index = sv.size() - 1; + + for (auto& message : mv) { + auto n = message.value(); + sc->schedule_relative(message.time(), make_schedulable( + controller, + [n, o](const schedulable&) { + if (o.is_subscribed()) { + n->accept(o); + } + })); + } + + auto sharedThis = std::static_pointer_cast(this->shared_from_this()); + o.add([sharedThis, index]() { + sharedThis->sv[index] = rxn::subscription(sharedThis->sv[index].subscribe(), sharedThis->sc->clock()); + }); + } + + virtual std::vector subscriptions() const { + return sv; + } + + virtual std::vector messages() const { + return mv; + } +}; + +template +rxt::testable_observable test_type::make_cold_observable(std::vector>>> messages) const +{ + auto co = std::make_shared>(state, create_worker(composite_subscription()), std::move(messages)); + return rxt::testable_observable(co); +} + +template +class hot_observable + : public rxt::detail::test_subject_base +{ + typedef hot_observable this_type; + std::shared_ptr sc; + typedef rxn::recorded::type> recorded_type; + typedef subscriber observer_type; + mutable std::vector mv; + mutable std::vector sv; + mutable std::list observers; + mutable worker controller; + +public: + + hot_observable(std::shared_ptr sc, worker w, std::vector mv) + : sc(sc) + , mv(mv) + , controller(w) + { + for (auto& message : mv) { + auto n = message.value(); + sc->schedule_absolute(message.time(), make_schedulable( + controller, + [this, n](const schedulable&) { + auto local = this->observers; + for (auto& o : local) { + if (o.is_subscribed()) { + n->accept(o); + } + } + })); + } + } + + virtual ~hot_observable() {} + + virtual void on_subscribe(observer_type o) const { + auto olocation = observers.insert(observers.end(), o); + + sv.push_back(rxn::subscription(sc->clock())); + auto index = sv.size() - 1; + + auto sharedThis = std::static_pointer_cast(this->shared_from_this()); + o.add([sharedThis, index, olocation]() { + sharedThis->sv[index] = rxn::subscription(sharedThis->sv[index].subscribe(), sharedThis->sc->clock()); + sharedThis->observers.erase(olocation); + }); + } + + virtual std::vector subscriptions() const { + return sv; + } + + virtual std::vector messages() const { + return mv; + } +}; + +template +rxt::testable_observable test_type::make_hot_observable(std::vector>>> messages) const +{ + auto worker = create_worker(composite_subscription()); + auto shared = std::make_shared>(state, worker, std::move(messages)); + return rxt::testable_observable(shared); +} + +template +struct is_create_source_function +{ + struct not_void {}; + template + static auto check(int) -> decltype((*(CF*)nullptr)()); + template + static not_void check(...); + + static const bool value = is_observable>(0))>::value; +}; + +} + +class test : public scheduler +{ + std::shared_ptr tester; +public: + + explicit test(std::shared_ptr t) + : scheduler(std::static_pointer_cast(t)) + , tester(t) + { + } + + typedef detail::test_type::clock_type clock_type; + + static const long created_time = 100; + static const long subscribed_time = 200; + static const long unsubscribed_time = 1000; + + template + struct messages + { + typedef typename rxn::notification notification_type; + typedef rxn::recorded recorded_type; + typedef rxn::subscription subscription_type; + + messages() {} + + template + static recorded_type next(long ticks, U value) { + return recorded_type(ticks, notification_type::on_next(std::move(value))); + } + + static recorded_type completed(long ticks) { + return recorded_type(ticks, notification_type::on_completed()); + } + + template + static recorded_type error(long ticks, Exception&& e) { + return recorded_type(ticks, notification_type::on_error(std::forward(e))); + } + + static rxn::subscription subscribe(long subscribe, long unsubscribe) { + return rxn::subscription(subscribe, unsubscribe); + } + }; + + class test_worker : public worker + { + std::shared_ptr tester; + public: + + ~test_worker() { + } + + explicit test_worker(composite_subscription cs, std::shared_ptr t) + : worker(cs, std::static_pointer_cast(t)) + , tester(t) + { + } + + bool is_enabled() const {return tester->is_enabled();} + long clock() const {return tester->clock();} + + void schedule_absolute(long when, const schedulable& a) const { + tester->schedule_absolute(when, a); + } + + void schedule_relative(long when, const schedulable& a) const { + tester->schedule_relative(when, a); + } + + template + auto schedule_absolute(long when, Arg0&& a0, ArgN&&... an) const + -> typename std::enable_if< + (detail::is_action_function::value || + is_subscription::value) && + !is_schedulable::value>::type { + tester->schedule_absolute(when, make_schedulable(*this, std::forward(a0), std::forward(an)...)); + } + + template + auto schedule_relative(long when, Arg0&& a0, ArgN&&... an) const + -> typename std::enable_if< + (detail::is_action_function::value || + is_subscription::value) && + !is_schedulable::value>::type { + tester->schedule_relative(when, make_schedulable(*this, std::forward(a0), std::forward(an)...)); + } + + void advance_to(long time) const + { + tester->advance_to(time); + } + + void advance_by(long time) const + { + tester->advance_by(time); + } + + void sleep(long time) const + { + tester->sleep(time); + } + + template + auto start(F createSource, long created, long subscribed, long unsubscribed) const + -> subscriber> + { + struct state_type + : public std::enable_shared_from_this + { + typedef decltype(createSource()) source_type; + + std::unique_ptr source; + subscriber> o; + + explicit state_type(subscriber> o) + : source() + , o(o) + { + } + }; + auto state = std::make_shared(this->make_subscriber()); + + schedule_absolute(created, [createSource, state](const schedulable&) { + state->source.reset(new typename state_type::source_type(createSource())); + }); + schedule_absolute(subscribed, [state](const schedulable&) { + state->source->subscribe(state->o); + }); + schedule_absolute(unsubscribed, [state](const schedulable&) { + state->o.unsubscribe(); + }); + + tester->start(); + + return state->o; + } + + template + auto start(F&& createSource, long unsubscribed) const + -> subscriber> + { + return start(std::forward(createSource), created_time, subscribed_time, unsubscribed); + } + + template + auto start(F&& createSource) const + -> subscriber> + { + return start(std::forward(createSource), created_time, subscribed_time, unsubscribed_time); + } + + template + struct start_traits + { + typedef decltype((*(F*)nullptr)()) source_type; + typedef typename source_type::value_type value_type; + typedef subscriber> subscriber_type; + }; + + template + auto start(F createSource, long created, long subscribed, long unsubscribed) const + -> typename std::enable_if::value, start_traits>::type::subscriber_type + { + return start>>(std::move(createSource), created, subscribed, unsubscribed); + } + + template + auto start(F createSource, long unsubscribed) const + -> typename std::enable_if::value, start_traits>::type::subscriber_type + { + return start>>(std::move(createSource), created_time, subscribed_time, unsubscribed); + } + + template + auto start(F createSource) const + -> typename std::enable_if::value, start_traits>::type::subscriber_type + { + return start>>(std::move(createSource), created_time, subscribed_time, unsubscribed_time); + } + + void start() const { + tester->start(); + } + + template + subscriber> make_subscriber() const { + return tester->make_subscriber(); + } + }; + + clock_type::time_point now() const { + return tester->now(); + } + + test_worker create_worker(composite_subscription cs = composite_subscription()) const { + return test_worker(cs, tester->create_test_type_worker_interface()); + } + + bool is_enabled() const {return tester->is_enabled();} + long clock() const {return tester->clock();} + + clock_type::time_point to_time_point(long absolute) const { + return tester->to_time_point(absolute); + } + + template + rxt::testable_observable make_hot_observable(std::vector>>> messages) const{ + return tester->make_hot_observable(std::move(messages)); + } + + template + auto make_hot_observable(const T (&arr) [size]) const + -> decltype(tester->make_hot_observable(std::vector())) { + return tester->make_hot_observable(rxu::to_vector(arr)); + } + + template + auto make_hot_observable(std::initializer_list il) const + -> decltype(tester->make_hot_observable(std::vector())) { + return tester->make_hot_observable(std::vector(il)); + } + + template + rxt::testable_observable make_cold_observable(std::vector>>> messages) const { + return tester->make_cold_observable(std::move(messages)); + } + + template + auto make_cold_observable(const T (&arr) [size]) const + -> decltype(tester->make_cold_observable(std::vector())) { + return tester->make_cold_observable(rxu::to_vector(arr)); + } + + template + auto make_cold_observable(std::initializer_list il) const + -> decltype(tester->make_cold_observable(std::vector())) { + return tester->make_cold_observable(std::vector(il)); + } +}; + + +inline test make_test() { + return test(std::make_shared()); +} + +} + +inline identity_one_worker identity_test() { + static identity_one_worker r(rxsc::make_test()); + return r; +} + +} + +#endif diff --git a/3party/rxcpp/schedulers/rx-virtualtime.hpp b/3party/rxcpp/schedulers/rx-virtualtime.hpp new file mode 100644 index 0000000..c36ddf3 --- /dev/null +++ b/3party/rxcpp/schedulers/rx-virtualtime.hpp @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SCHEDULER_VIRTUAL_TIME_HPP) +#define RXCPP_RX_SCHEDULER_VIRTUAL_TIME_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace schedulers { + +namespace detail { + +template +struct virtual_time_base : std::enable_shared_from_this> +{ +private: + typedef virtual_time_base this_type; + virtual_time_base(const virtual_time_base&); + + mutable bool isenabled; + +public: + typedef Absolute absolute; + typedef Relative relative; + + virtual ~virtual_time_base() + { + } + +protected: + virtual_time_base() + : isenabled(false) + , clock_now(0) + { + } + explicit virtual_time_base(absolute initialClock) + : isenabled(false) + , clock_now(initialClock) + { + } + + mutable absolute clock_now; + + typedef time_schedulable item_type; + + virtual absolute add(absolute, relative) const =0; + + virtual typename scheduler_base::clock_type::time_point to_time_point(absolute) const =0; + virtual relative to_relative(typename scheduler_base::clock_type::duration) const =0; + + virtual item_type top() const =0; + virtual void pop() const =0; + virtual bool empty() const =0; + +public: + + virtual void schedule_absolute(absolute, const schedulable&) const =0; + + virtual void schedule_relative(relative when, const schedulable& a) const { + auto at = add(clock_now, when); + return schedule_absolute(at, a); + } + + bool is_enabled() const {return isenabled;} + absolute clock() const {return clock_now;} + + void start() const + { + if (!isenabled) { + isenabled = true; + rxsc::recursion r; + r.reset(false); + while (!empty() && isenabled) { + auto next = top(); + pop(); + if (next.what.is_subscribed()) { + if (next.when > clock_now) { + clock_now = next.when; + } + next.what(r.get_recurse()); + } + } + isenabled = false; + } + } + + void stop() const + { + isenabled = false; + } + + void advance_to(absolute time) const + { + if (time < clock_now) { + std::terminate(); + } + + if (time == clock_now) { + return; + } + + if (!isenabled) { + isenabled = true; + rxsc::recursion r; + while (!empty() && isenabled) { + auto next = top(); + if (next.when <= time) { + pop(); + if (!next.what.is_subscribed()) { + continue; + } + if (next.when > clock_now) { + clock_now = next.when; + } + next.what(r.get_recurse()); + } + else { + break; + } + } + isenabled = false; + clock_now = time; + } + else { + std::terminate(); + } + } + + void advance_by(relative time) const + { + auto dt = add(clock_now, time); + + if (dt < clock_now) { + std::terminate(); + } + + if (dt == clock_now) { + return; + } + + if (!isenabled) { + advance_to(dt); + } + else { + std::terminate(); + } + } + + void sleep(relative time) const + { + auto dt = add(clock_now, time); + + if (dt < clock_now) { + std::terminate(); + } + + clock_now = dt; + } + +}; + +} + +template +struct virtual_time : public detail::virtual_time_base +{ + typedef detail::virtual_time_base base; + + typedef typename base::item_type item_type; + + typedef detail::schedulable_queue< + typename item_type::time_point_type> queue_item_time; + + mutable queue_item_time q; + +public: + virtual ~virtual_time() + { + } + +protected: + virtual_time() + { + } + explicit virtual_time(typename base::absolute initialClock) + : base(initialClock) + { + } + + virtual item_type top() const { + return q.top(); + } + virtual void pop() const { + q.pop(); + } + virtual bool empty() const { + return q.empty(); + } + + using base::schedule_absolute; + using base::schedule_relative; + + virtual void schedule_absolute(typename base::absolute when, const schedulable& a) const + { + // use a separate subscription here so that a's subscription is not affected + auto run = make_schedulable( + a.get_worker(), + composite_subscription(), + [a](const schedulable& scbl) { + rxsc::recursion r; + r.reset(false); + if (scbl.is_subscribed()) { + scbl.unsubscribe(); // unsubscribe() run, not a; + a(r.get_recurse()); + } + }); + q.push(item_type(when, run)); + } +}; + + + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-create.hpp b/3party/rxcpp/sources/rx-create.hpp new file mode 100644 index 0000000..95b9533 --- /dev/null +++ b/3party/rxcpp/sources/rx-create.hpp @@ -0,0 +1,92 @@ +// 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_CREATE_HPP) +#define RXCPP_SOURCES_RX_CREATE_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-create.hpp + + \brief Returns an observable that executes the specified function when a subscriber subscribes to it. + + \tparam T the type of the items that this observable emits + \tparam OnSubscribe the type of OnSubscribe handler function + + \param os OnSubscribe event handler + + \return Observable that executes the specified function when a Subscriber subscribes to it. + + \sample + \snippet create.cpp Create sample + \snippet output.txt Create sample + + \warning + It is good practice to check the observer's is_subscribed state from within the function you pass to create + so that your observable can stop emitting items or doing expensive calculations when there is no longer an interested observer. + + \badcode + \snippet create.cpp Create bad code + \snippet output.txt Create bad code + + \goodcode + \snippet create.cpp Create good code + \snippet output.txt Create good code + + \warning + It is good practice to use operators like observable::take to control lifetime rather than use the subscription explicitly. + + \goodcode + \snippet create.cpp Create great code + \snippet output.txt Create great code +*/ + +namespace rxcpp { + +namespace sources { + +namespace detail { + +template +struct create : public source_base +{ + typedef create this_type; + + typedef rxu::decay_t on_subscribe_type; + + on_subscribe_type on_subscribe_function; + + create(on_subscribe_type os) + : on_subscribe_function(std::move(os)) + { + } + + template + void on_subscribe(Subscriber o) const { + + on_exception( + [&](){ + this->on_subscribe_function(o); + return true; + }, + o); + } +}; + +} + +/*! @copydoc rx-create.hpp + */ +template +auto create(OnSubscribe os) + -> observable> { + return observable>( + detail::create(std::move(os))); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-defer.hpp b/3party/rxcpp/sources/rx-defer.hpp new file mode 100644 index 0000000..306b4d0 --- /dev/null +++ b/3party/rxcpp/sources/rx-defer.hpp @@ -0,0 +1,83 @@ +// 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_DEFER_HPP) +#define RXCPP_SOURCES_RX_DEFER_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-defer.hpp + + \brief Returns an observable that calls the specified observable factory to create an observable for each new observer that subscribes. + + \tparam ObservableFactory the type of the observable factory + + \param of the observable factory function to invoke for each observer that subscribes to the resulting observable + + \return observable whose observers' subscriptions trigger an invocation of the given observable factory function + + \sample + \snippet defer.cpp defer sample + \snippet output.txt defer sample +*/ + +namespace rxcpp { + +namespace sources { + +namespace detail { + +template +struct defer_traits +{ + typedef rxu::decay_t observable_factory_type; + typedef decltype((*(observable_factory_type*)nullptr)()) collection_type; + typedef typename collection_type::value_type value_type; +}; + +template +struct defer : public source_base>> +{ + typedef defer this_type; + typedef defer_traits traits; + + typedef typename traits::observable_factory_type observable_factory_type; + typedef typename traits::collection_type collection_type; + + observable_factory_type observable_factory; + + defer(observable_factory_type of) + : observable_factory(std::move(of)) + { + } + template + void on_subscribe(Subscriber o) const { + + auto selectedCollection = on_exception( + [this](){return this->observable_factory();}, + o); + if (selectedCollection.empty()) { + return; + } + + selectedCollection->subscribe(o); + } +}; + +} + +/*! @copydoc rx-defer.hpp + */ +template +auto defer(ObservableFactory of) + -> observable>, detail::defer> { + return observable>, detail::defer>( + detail::defer(std::move(of))); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-empty.hpp b/3party/rxcpp/sources/rx-empty.hpp new file mode 100644 index 0000000..2e11a16 --- /dev/null +++ b/3party/rxcpp/sources/rx-empty.hpp @@ -0,0 +1,53 @@ +// 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_EMPTY_HPP) +#define RXCPP_SOURCES_RX_EMPTY_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-empty.hpp + + \brief Returns an observable that sends no items to observer and immediately completes, on the specified scheduler. + + \tparam T the type of (not) emitted items + \tparam Coordination the type of the scheduler (optional) + + \param cn the scheduler to use for scheduling the items (optional) + + \return Observable that sends no items to observer and immediately completes. + + \sample + \snippet empty.cpp empty sample + \snippet output.txt empty sample + + \sample + \snippet empty.cpp threaded empty sample + \snippet output.txt threaded empty sample +*/ + +namespace rxcpp { + +namespace sources { + +/*! @copydoc rx-empty.hpp + */ +template +auto empty() + -> decltype(from()) { + return from(); +} +/*! @copydoc rx-empty.hpp + */ +template +auto empty(Coordination cn) + -> decltype(from(std::move(cn))) { + return from(std::move(cn)); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-error.hpp b/3party/rxcpp/sources/rx-error.hpp new file mode 100644 index 0000000..f245d02 --- /dev/null +++ b/3party/rxcpp/sources/rx-error.hpp @@ -0,0 +1,133 @@ +// 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_ERROR_HPP) +#define RXCPP_SOURCES_RX_ERROR_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-error.hpp + + \brief Returns an observable that sends no items to observer and immediately generates an error, on the specified scheduler. + + \tparam T the type of (not) emitted items + \tparam Exception the type of the error + \tparam Coordination the type of the scheduler (optional) + + \param e the error to be passed to observers + \param cn the scheduler to use for scheduling the items (optional) + + \return Observable that sends no items to observer and immediately generates an error. + + \sample + \snippet error.cpp error sample + \snippet output.txt error sample + + \sample + \snippet error.cpp threaded error sample + \snippet output.txt threaded error sample +*/ + +namespace rxcpp { + +namespace sources { + +namespace detail { + +template +struct error : public source_base +{ + typedef error this_type; + + typedef rxu::decay_t coordination_type; + + typedef typename coordination_type::coordinator_type coordinator_type; + + struct error_initial_type + { + error_initial_type(rxu::error_ptr e, coordination_type cn) + : exception(e) + , coordination(std::move(cn)) + { + } + rxu::error_ptr exception; + coordination_type coordination; + }; + error_initial_type initial; + + error(rxu::error_ptr e, coordination_type cn) + : initial(e, std::move(cn)) + { + } + + template + void on_subscribe(Subscriber o) const { + + // creates a worker whose lifetime is the same as this subscription + auto coordinator = initial.coordination.create_coordinator(o.get_subscription()); + auto controller = coordinator.get_worker(); + auto exception = initial.exception; + + auto producer = [=](const rxsc::schedulable&){ + auto& dest = o; + if (!dest.is_subscribed()) { + // terminate loop + return; + } + + dest.on_error(exception); + // o is unsubscribed + }; + auto selectedProducer = on_exception( + [&](){return coordinator.act(producer);}, + o); + if (selectedProducer.empty()) { + return; + } + controller.schedule(selectedProducer.get()); + } +}; + +struct throw_ptr_tag{}; +struct throw_instance_tag{}; + +template +auto make_error(throw_ptr_tag&&, rxu::error_ptr exception, Coordination cn) + -> observable> { + return observable>(error(std::move(exception), std::move(cn))); +} + +template +auto make_error(throw_instance_tag&&, E e, Coordination cn) + -> observable> { + rxu::error_ptr ep = rxu::make_error_ptr(e); + return observable>(error(std::move(ep), std::move(cn))); +} + +} + +} + +namespace sources { + +/*! @copydoc rx-error.hpp + */ +template +auto error(E e) + -> decltype(detail::make_error(typename std::conditional>::value, detail::throw_ptr_tag, detail::throw_instance_tag>::type(), std::move(e), identity_immediate())) { + return detail::make_error(typename std::conditional>::value, detail::throw_ptr_tag, detail::throw_instance_tag>::type(), std::move(e), identity_immediate()); +} +/*! @copydoc rx-error.hpp + */ +template +auto error(E e, Coordination cn) + -> decltype(detail::make_error(typename std::conditional>::value, detail::throw_ptr_tag, detail::throw_instance_tag>::type(), std::move(e), std::move(cn))) { + return detail::make_error(typename std::conditional>::value, detail::throw_ptr_tag, detail::throw_instance_tag>::type(), std::move(e), std::move(cn)); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-interval.hpp b/3party/rxcpp/sources/rx-interval.hpp new file mode 100644 index 0000000..7d3d47d --- /dev/null +++ b/3party/rxcpp/sources/rx-interval.hpp @@ -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 + +#if !defined(RXCPP_SOURCES_RX_INTERVAL_HPP) +#define RXCPP_SOURCES_RX_INTERVAL_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-interval.hpp + + \brief Returns an observable that emits a sequential integer every specified time interval, on the specified scheduler. + + \tparam Coordination the type of the scheduler (optional) + + \param period period between emitted values + \param cn the scheduler to use for scheduling the items (optional) + + \return Observable that sends a sequential integer each time interval + + \sample + \snippet interval.cpp interval sample + \snippet output.txt interval sample + + \sample + \snippet interval.cpp immediate interval sample + \snippet output.txt immediate interval sample + + \sample + \snippet interval.cpp threaded interval sample + \snippet output.txt threaded interval sample + + \sample + \snippet interval.cpp threaded immediate interval sample + \snippet output.txt threaded immediate interval sample +*/ + +namespace rxcpp { + +namespace sources { + +namespace detail { + +template +struct interval : public source_base +{ + typedef interval this_type; + + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + + struct interval_initial_type + { + interval_initial_type(rxsc::scheduler::clock_type::time_point i, rxsc::scheduler::clock_type::duration p, coordination_type cn) + : initial(i) + , period(p) + , coordination(std::move(cn)) + { + } + rxsc::scheduler::clock_type::time_point initial; + rxsc::scheduler::clock_type::duration period; + coordination_type coordination; + }; + interval_initial_type initial; + + interval(rxsc::scheduler::clock_type::time_point i, rxsc::scheduler::clock_type::duration p, coordination_type cn) + : initial(i, p, std::move(cn)) + { + } + template + void on_subscribe(Subscriber o) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + // creates a worker whose lifetime is the same as this subscription + auto coordinator = initial.coordination.create_coordinator(o.get_subscription()); + + auto controller = coordinator.get_worker(); + + auto counter = std::make_shared(0); + + auto producer = [o, counter](const rxsc::schedulable&) { + // send next value + o.on_next(++(*counter)); + }; + + auto selectedProducer = on_exception( + [&](){return coordinator.act(producer);}, + o); + if (selectedProducer.empty()) { + return; + } + + controller.schedule_periodically(initial.initial, initial.period, selectedProducer.get()); + } +}; + +template +struct defer_interval : public defer_observable< + rxu::all_true< + std::is_convertible::value, + is_coordination::value>, + void, + interval, Coordination> +{ +}; + +} + + +/*! @copydoc rx-interval.hpp + */ +template +auto interval(Duration period) + -> typename std::enable_if< + detail::defer_interval::value, + typename detail::defer_interval::observable_type>::type { + return detail::defer_interval::make(identity_current_thread().now(), period, identity_current_thread()); +} + +/*! @copydoc rx-interval.hpp + */ +template +auto interval(rxsc::scheduler::clock_type::duration period, Coordination cn) + -> typename std::enable_if< + detail::defer_interval::value, + typename detail::defer_interval::observable_type>::type { + return detail::defer_interval::make(cn.now(), period, std::move(cn)); +} + +/*! @copydoc rx-interval.hpp + */ +template +auto interval(rxsc::scheduler::clock_type::time_point when, Duration period) + -> typename std::enable_if< + detail::defer_interval::value, + typename detail::defer_interval::observable_type>::type { + return detail::defer_interval::make(when, period, identity_current_thread()); +} + +/*! @copydoc rx-interval.hpp + */ +template +auto interval(rxsc::scheduler::clock_type::time_point when, rxsc::scheduler::clock_type::duration period, Coordination cn) + -> typename std::enable_if< + detail::defer_interval::value, + typename detail::defer_interval::observable_type>::type { + return detail::defer_interval::make(when, period, std::move(cn)); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-iterate.hpp b/3party/rxcpp/sources/rx-iterate.hpp new file mode 100644 index 0000000..07f50bf --- /dev/null +++ b/3party/rxcpp/sources/rx-iterate.hpp @@ -0,0 +1,325 @@ +// 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 +struct is_iterable +{ + typedef rxu::decay_t collection_type; + + struct not_void {}; + template + static auto check(int) -> decltype(std::begin(*(CC*)nullptr)); + template + static not_void check(...); + + static const bool value = !std::is_same(0)), not_void>::value; +}; + +template +struct iterate_traits +{ + typedef rxu::decay_t collection_type; + typedef rxu::decay_t iterator_type; + typedef rxu::value_type_t> value_type; +}; + +template +struct iterate : public source_base>> +{ + typedef iterate this_type; + typedef iterate_traits traits; + + typedef rxu::decay_t 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 + void on_subscribe(Subscriber o) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + typedef typename coordinator_type::template get::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 +auto iterate(Collection c) + -> observable>, detail::iterate> { + return observable>, detail::iterate>( + detail::iterate(std::move(c), identity_immediate())); +} +/*! @copydoc rx-iterate.hpp + */ +template +auto iterate(Collection c, Coordination cn) + -> observable>, detail::iterate> { + return observable>, detail::iterate>( + detail::iterate(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#from(Value0,ValueN...) operator. + + \note This is a degenerate case of ```from(Value0 v0, ValueN... vn)``` operator. +*/ +template +auto from() + -> decltype(iterate(std::initializer_list(), identity_immediate())) { + return iterate(std::initializer_list(), 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 +auto from(Coordination cn) + -> typename std::enable_if::value, + decltype( iterate(std::initializer_list(), std::move(cn)))>::type { + return iterate(std::initializer_list(), 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::iterate instead. +*/ +template +auto from(Value0 v0, ValueN... vn) + -> typename std::enable_if::value, + decltype(iterate(*(std::array*)nullptr, identity_immediate()))>::type { + std::array 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::iterate instead. +*/ +template +auto from(Coordination cn, Value0 v0, ValueN... vn) + -> typename std::enable_if::value, + decltype(iterate(*(std::array*)nullptr, std::move(cn)))>::type { + std::array 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 +auto just(Value0 v0) + -> typename std::enable_if::value, + decltype(iterate(*(std::array*)nullptr, identity_immediate()))>::type { + std::array 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 +auto just(Value0 v0, Coordination cn) + -> typename std::enable_if::value, + decltype(iterate(*(std::array*)nullptr, std::move(cn)))>::type { + std::array 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::start_with method of the existing observable: + \snippet start_with.cpp short start_with sample + \snippet output.txt short start_with sample +*/ +template +auto start_with(Observable o, Value0 v0, ValueN... vn) + -> decltype(from(rxu::value_type_t(v0), rxu::value_type_t(vn)...).concat(o)) { + return from(rxu::value_type_t(v0), rxu::value_type_t(vn)...).concat(o); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-never.hpp b/3party/rxcpp/sources/rx-never.hpp new file mode 100644 index 0000000..ea34399 --- /dev/null +++ b/3party/rxcpp/sources/rx-never.hpp @@ -0,0 +1,51 @@ +// 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_NEVER_HPP) +#define RXCPP_SOURCES_RX_NEVER_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-never.hpp + + \brief Returns an observable that never sends any items or notifications to observer. + + \tparam T the type of (not) emitted items + + \return Observable that never sends any items or notifications to observer. + + \sample + \snippet never.cpp never sample + \snippet output.txt never sample +*/ + +namespace rxcpp { + +namespace sources { + +namespace detail { + +template +struct never : public source_base +{ + template + void on_subscribe(Subscriber) const { + } +}; + +} + +/*! @copydoc rx-never.hpp + */ +template +auto never() + -> observable> { + return observable>(detail::never()); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-range.hpp b/3party/rxcpp/sources/rx-range.hpp new file mode 100644 index 0000000..c56ee1f --- /dev/null +++ b/3party/rxcpp/sources/rx-range.hpp @@ -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 + +#if !defined(RXCPP_SOURCES_RX_RANGE_HPP) +#define RXCPP_SOURCES_RX_RANGE_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-range.hpp + + \brief Returns an observable that sends values in the range ```first```-```last``` by adding ```step``` to the previous value. The values are sent on the specified scheduler. + + \tparam T the type of the values that this observable emits + \tparam Coordination the type of the scheduler (optional) + + \param first first value to send (optional) + \param last last value to send (optional) + \param step value to add to the previous value to get the next value (optional) + \param cn the scheduler to run the generator loop on (optional) + + \return Observable that sends values in the range ```first```-```last``` by adding ```step``` to the previous value using the specified scheduler. + + \sample + \snippet range.cpp threaded range sample + \snippet output.txt threaded range sample + + An alternative way to specify the scheduler for emitted values is to use observable::subscribe_on operator + \snippet range.cpp subscribe_on range sample + \snippet output.txt subscribe_on range sample +*/ + +namespace rxcpp { + +namespace sources { + +namespace detail { + +template +struct range : public source_base +{ + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + + struct range_state_type + { + range_state_type(T f, T l, std::ptrdiff_t s, coordination_type cn) + : next(f) + , last(l) + , step(s) + , coordination(std::move(cn)) + { + } + mutable T next; + T last; + std::ptrdiff_t step; + coordination_type coordination; + }; + range_state_type initial; + range(T f, T l, std::ptrdiff_t s, coordination_type cn) + : initial(f, l, s, std::move(cn)) + { + } + template + void on_subscribe(Subscriber o) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + // creates a worker whose lifetime is the same as this subscription + auto coordinator = initial.coordination.create_coordinator(o.get_subscription()); + + auto controller = coordinator.get_worker(); + + auto state = initial; + + auto producer = [=](const rxsc::schedulable& self){ + auto& dest = o; + if (!dest.is_subscribed()) { + // terminate loop + return; + } + + // send next value + dest.on_next(state.next); + if (!dest.is_subscribed()) { + // terminate loop + return; + } + + if (std::max(state.last, state.next) - std::min(state.last, state.next) < std::abs(state.step)) { + if (state.last != state.next) { + dest.on_next(state.last); + } + dest.on_completed(); + // o is unsubscribed + return; + } + state.next = static_cast(state.step + state.next); + + // 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-create.hpp + */ +template +auto range(T first = 0, T last = std::numeric_limits::max(), std::ptrdiff_t step = 1) + -> observable> { + return observable>( + detail::range(first, last, step, identity_current_thread())); +} +/*! @copydoc rx-create.hpp + */ +template +auto range(T first, T last, std::ptrdiff_t step, Coordination cn) + -> observable> { + return observable>( + detail::range(first, last, step, std::move(cn))); +} +/*! @copydoc rx-create.hpp + */ +template +auto range(T first, T last, Coordination cn) + -> typename std::enable_if::value, + observable>>::type { + return observable>( + detail::range(first, last, 1, std::move(cn))); +} +/*! @copydoc rx-create.hpp + */ +template +auto range(T first, Coordination cn) + -> typename std::enable_if::value, + observable>>::type { + return observable>( + detail::range(first, std::numeric_limits::max(), 1, std::move(cn))); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-scope.hpp b/3party/rxcpp/sources/rx-scope.hpp new file mode 100644 index 0000000..d7151f2 --- /dev/null +++ b/3party/rxcpp/sources/rx-scope.hpp @@ -0,0 +1,124 @@ +// 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_SCOPE_HPP) +#define RXCPP_SOURCES_RX_SCOPE_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-scope.hpp + + \brief Returns an observable that makes an observable by the specified observable factory using the resource provided by the specified resource factory for each new observer that subscribes. + + \tparam ResourceFactory the type of the resource factory + \tparam ObservableFactory the type of the observable factory + + \param rf the resource factory function that resturn the rxcpp::resource that is used as a resource by the observable factory + \param of the observable factory function to invoke for each observer that subscribes to the resulting observable + + \return observable that makes an observable by the specified observable factory using the resource provided by the specified resource factory for each new observer that subscribes. + + \sample + \snippet scope.cpp scope sample + \snippet output.txt scope sample +*/ + +namespace rxcpp { + +namespace sources { + +namespace detail { + +template +struct scope_traits +{ + typedef rxu::decay_t resource_factory_type; + typedef rxu::decay_t observable_factory_type; + typedef decltype((*(resource_factory_type*)nullptr)()) resource_type; + typedef decltype((*(observable_factory_type*)nullptr)(resource_type())) collection_type; + typedef typename collection_type::value_type value_type; + + static_assert(is_subscription::value, "ResourceFactory must return a subscription"); +}; + +template +struct scope : public source_base>> +{ + typedef scope_traits traits; + typedef typename traits::resource_factory_type resource_factory_type; + typedef typename traits::observable_factory_type observable_factory_type; + typedef typename traits::resource_type resource_type; + typedef typename traits::value_type value_type; + + struct values + { + values(resource_factory_type rf, observable_factory_type of) + : resource_factory(std::move(rf)) + , observable_factory(std::move(of)) + { + } + resource_factory_type resource_factory; + observable_factory_type observable_factory; + }; + values initial; + + + scope(resource_factory_type rf, observable_factory_type of) + : initial(std::move(rf), std::move(of)) + { + } + + template + void on_subscribe(Subscriber o) const { + + struct state_type + : public std::enable_shared_from_this + , public values + { + state_type(values i, Subscriber o) + : values(i) + , out(std::move(o)) + { + } + Subscriber out; + rxu::detail::maybe resource; + }; + + auto state = std::make_shared(state_type(initial, std::move(o))); + + state->resource = on_exception( + [&](){return state->resource_factory(); }, + state->out); + if (state->resource.empty()) { + return; + } + state->out.add(state->resource->get_subscription()); + + auto selectedCollection = on_exception( + [state](){return state->observable_factory(state->resource.get()); }, + state->out); + if (selectedCollection.empty()) { + return; + } + + selectedCollection->subscribe(state->out); + } +}; + +} + +/*! @copydoc rx-scope.hpp + */ +template +auto scope(ResourceFactory rf, ObservableFactory of) + -> observable>, detail::scope> { + return observable>, detail::scope>( + detail::scope(std::move(rf), std::move(of))); +} + +} + +} + +#endif diff --git a/3party/rxcpp/sources/rx-timer.hpp b/3party/rxcpp/sources/rx-timer.hpp new file mode 100644 index 0000000..2b8e270 --- /dev/null +++ b/3party/rxcpp/sources/rx-timer.hpp @@ -0,0 +1,135 @@ +// 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_TIMER_HPP) +#define RXCPP_SOURCES_RX_TIMER_HPP + +#include "../rx-includes.hpp" + +/*! \file rx-timer.hpp + + \brief Returns an observable that emits an integer at the specified time point. + + \tparam Coordination the type of the scheduler (optional) + + \param when time point when the value is emitted + \param cn the scheduler to use for scheduling the items (optional) + + \return Observable that emits an integer at the specified time point + + \sample + \snippet timer.cpp timepoint timer sample + \snippet output.txt timepoint timer sample + + \sample + \snippet timer.cpp duration timer sample + \snippet output.txt duration timer sample + + \sample + \snippet timer.cpp threaded timepoint timer sample + \snippet output.txt threaded timepoint timer sample + + \sample + \snippet timer.cpp threaded duration timer sample + \snippet output.txt threaded duration timer sample +*/ + +namespace rxcpp { + +namespace sources { + +namespace detail { + +template +struct timer : public source_base +{ + typedef timer this_type; + + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + + struct timer_initial_type + { + timer_initial_type(rxsc::scheduler::clock_type::time_point t, coordination_type cn) + : when(t) + , coordination(std::move(cn)) + { + } + rxsc::scheduler::clock_type::time_point when; + coordination_type coordination; + }; + timer_initial_type initial; + + timer(rxsc::scheduler::clock_type::time_point t, coordination_type cn) + : initial(t, std::move(cn)) + { + } + timer(rxsc::scheduler::clock_type::duration p, coordination_type cn) + : initial(rxsc::scheduler::clock_type::time_point(), std::move(cn)) + { + initial.when = initial.coordination.now() + p; + } + template + void on_subscribe(Subscriber o) const { + static_assert(is_subscriber::value, "subscribe must be passed a subscriber"); + + // creates a worker whose lifetime is the same as this subscription + auto coordinator = initial.coordination.create_coordinator(o.get_subscription()); + auto controller = coordinator.get_worker(); + + auto producer = [o](const rxsc::schedulable&) { + // send the value and complete + o.on_next(1L); + o.on_completed(); + }; + + auto selectedProducer = on_exception( + [&](){return coordinator.act(producer);}, + o); + if (selectedProducer.empty()) { + return; + } + + controller.schedule(initial.when, selectedProducer.get()); + } +}; + +template +struct defer_timer : public defer_observable< + rxu::all_true< + std::is_convertible::value || + std::is_convertible::value, + is_coordination::value>, + void, + timer, Coordination> +{ +}; + +} + +/*! @copydoc rx-timer.hpp + */ +template +auto timer(TimePointOrDuration when) + -> typename std::enable_if< + detail::defer_timer::value, + typename detail::defer_timer::observable_type>::type { + return detail::defer_timer::make(when, identity_current_thread()); +} + +/*! @copydoc rx-timer.hpp + */ +template +auto timer(TimePointOrDuration when, Coordination cn) + -> typename std::enable_if< + detail::defer_timer::value, + typename detail::defer_timer::observable_type>::type { + return detail::defer_timer::make(when, std::move(cn)); +} + +} + +} + +#endif diff --git a/3party/rxcpp/subjects/rx-behavior.hpp b/3party/rxcpp/subjects/rx-behavior.hpp new file mode 100644 index 0000000..fd0cad3 --- /dev/null +++ b/3party/rxcpp/subjects/rx-behavior.hpp @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_BEHAVIOR_HPP) +#define RXCPP_RX_BEHAVIOR_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace subjects { + +namespace detail { + +template +class behavior_observer : public detail::multicast_observer +{ + typedef behavior_observer this_type; + typedef detail::multicast_observer base_type; + + class behavior_observer_state : public std::enable_shared_from_this + { + mutable std::mutex lock; + mutable T value; + + public: + behavior_observer_state(T first) + : value(first) + { + } + + void reset(T v) const { + std::unique_lock guard(lock); + value = std::move(v); + } + T get() const { + std::unique_lock guard(lock); + return value; + } + }; + + std::shared_ptr state; + +public: + behavior_observer(T f, composite_subscription l) + : base_type(l) + , state(std::make_shared(std::move(f))) + { + } + + subscriber get_subscriber() const { + return make_subscriber(this->get_id(), this->get_subscription(), observer>(*this)).as_dynamic(); + } + + T get_value() const { + return state->get(); + } + + template + void on_next(V v) const { + state->reset(v); + base_type::on_next(std::move(v)); + } +}; + +} + +template +class behavior +{ + detail::behavior_observer s; + +public: + explicit behavior(T f, composite_subscription cs = composite_subscription()) + : s(std::move(f), cs) + { + } + + bool has_observers() const { + return s.has_observers(); + } + + T get_value() const { + return s.get_value(); + } + + subscriber get_subscriber() const { + return s.get_subscriber(); + } + + observable get_observable() const { + auto keepAlive = s; + return make_observable_dynamic([keepAlive](subscriber o){ + if (keepAlive.get_subscription().is_subscribed()) { + o.on_next(keepAlive.get_value()); + } + keepAlive.add(keepAlive.get_subscriber(), std::move(o)); + }); + } +}; + +} + +} + +#endif diff --git a/3party/rxcpp/subjects/rx-replaysubject.hpp b/3party/rxcpp/subjects/rx-replaysubject.hpp new file mode 100644 index 0000000..0e9d732 --- /dev/null +++ b/3party/rxcpp/subjects/rx-replaysubject.hpp @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_REPLAYSUBJECT_HPP) +#define RXCPP_RX_REPLAYSUBJECT_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace subjects { + +namespace detail { + +template +struct replay_traits +{ + typedef rxu::maybe count_type; + typedef rxu::maybe period_type; + typedef rxsc::scheduler::clock_type::time_point time_point_type; + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; +}; + +template +class replay_observer : public detail::multicast_observer +{ + typedef replay_observer this_type; + typedef detail::multicast_observer base_type; + + typedef replay_traits traits; + typedef typename traits::count_type count_type; + typedef typename traits::period_type period_type; + typedef typename traits::time_point_type time_point_type; + typedef typename traits::coordination_type coordination_type; + typedef typename traits::coordinator_type coordinator_type; + + class replay_observer_state : public std::enable_shared_from_this + { + mutable std::mutex lock; + mutable std::list values; + mutable std::list time_points; + mutable count_type count; + mutable period_type period; + mutable composite_subscription replayLifetime; + public: + mutable coordination_type coordination; + mutable coordinator_type coordinator; + + private: + void remove_oldest() const { + values.pop_front(); + if (!period.empty()) { + time_points.pop_front(); + } + } + + public: + ~replay_observer_state(){ + replayLifetime.unsubscribe(); + } + explicit replay_observer_state(count_type _count, period_type _period, coordination_type _coordination, coordinator_type _coordinator, composite_subscription _replayLifetime) + : count(_count) + , period(_period) + , replayLifetime(_replayLifetime) + , coordination(std::move(_coordination)) + , coordinator(std::move(_coordinator)) + { + } + + void add(T v) const { + std::unique_lock guard(lock); + + if (!count.empty()) { + if (values.size() == count.get()) + remove_oldest(); + } + + if (!period.empty()) { + auto now = coordination.now(); + while (!time_points.empty() && (now - time_points.front() > period.get())) + remove_oldest(); + time_points.push_back(now); + } + + values.push_back(std::move(v)); + } + std::list get() const { + std::unique_lock guard(lock); + return values; + } + }; + + std::shared_ptr state; + +public: + replay_observer(count_type count, period_type period, coordination_type coordination, composite_subscription replayLifetime, composite_subscription subscriberLifetime) + : base_type(subscriberLifetime) + { + replayLifetime.add(subscriberLifetime); + auto coordinator = coordination.create_coordinator(replayLifetime); + state = std::make_shared(std::move(count), std::move(period), std::move(coordination), std::move(coordinator), std::move(replayLifetime)); + } + + subscriber get_subscriber() const { + return make_subscriber(this->get_id(), this->get_subscription(), observer>(*this)).as_dynamic(); + } + + std::list get_values() const { + return state->get(); + } + + coordinator_type& get_coordinator() const { + return state->coordinator; + } + + template + void on_next(V v) const { + state->add(v); + base_type::on_next(std::move(v)); + } +}; + +} + +template +class replay +{ + typedef detail::replay_traits traits; + typedef typename traits::count_type count_type; + typedef typename traits::period_type period_type; + typedef typename traits::time_point_type time_point_type; + + detail::replay_observer s; + +public: + explicit replay(Coordination cn, composite_subscription cs = composite_subscription()) + : s(count_type(), period_type(), cn, cs, composite_subscription{}) + { + } + + replay(std::size_t count, Coordination cn, composite_subscription cs = composite_subscription()) + : s(count_type(std::move(count)), period_type(), cn, cs, composite_subscription{}) + { + } + + replay(rxsc::scheduler::clock_type::duration period, Coordination cn, composite_subscription cs = composite_subscription()) + : s(count_type(), period_type(period), cn, cs, composite_subscription{}) + { + } + + replay(std::size_t count, rxsc::scheduler::clock_type::duration period, Coordination cn, composite_subscription cs = composite_subscription()) + : s(count_type(count), period_type(period), cn, cs, composite_subscription{}) + { + } + + bool has_observers() const { + return s.has_observers(); + } + + std::list get_values() const { + return s.get_values(); + } + + subscriber get_subscriber() const { + return s.get_subscriber(); + } + + observable get_observable() const { + auto keepAlive = s; + auto observable = make_observable_dynamic([keepAlive](subscriber o){ + for (auto&& value: keepAlive.get_values()) { + o.on_next(value); + } + keepAlive.add(keepAlive.get_subscriber(), std::move(o)); + }); + return s.get_coordinator().in(observable); + } +}; + +} + +} + +#endif diff --git a/3party/rxcpp/subjects/rx-subject.hpp b/3party/rxcpp/subjects/rx-subject.hpp new file mode 100644 index 0000000..4ce96b4 --- /dev/null +++ b/3party/rxcpp/subjects/rx-subject.hpp @@ -0,0 +1,277 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SUBJECT_HPP) +#define RXCPP_RX_SUBJECT_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace subjects { + +namespace detail { + +template +class multicast_observer +{ + typedef subscriber observer_type; + typedef std::vector list_type; + + struct mode + { + enum type { + Invalid = 0, + Casting, + Disposed, + Completed, + Errored + }; + }; + + struct state_type + : public std::enable_shared_from_this + { + explicit state_type(composite_subscription cs) + : current(mode::Casting) + , lifetime(cs) + { + } + std::mutex lock; + typename mode::type current; + rxu::error_ptr error; + composite_subscription lifetime; + }; + + struct completer_type + : public std::enable_shared_from_this + { + ~completer_type() + { + } + completer_type(std::shared_ptr s, const std::shared_ptr& old, observer_type o) + : state(s) + { + retain(old); + observers.push_back(o); + } + completer_type(std::shared_ptr s, const std::shared_ptr& old) + : state(s) + { + retain(old); + } + void retain(const std::shared_ptr& old) { + if (old) { + observers.reserve(old->observers.size() + 1); + std::copy_if( + old->observers.begin(), old->observers.end(), + std::inserter(observers, observers.end()), + [](const observer_type& o){ + return o.is_subscribed(); + }); + } + } + std::shared_ptr state; + list_type observers; + }; + + // this type prevents a circular ref between state and completer + struct binder_type + : public std::enable_shared_from_this + { + explicit binder_type(composite_subscription cs) + : state(std::make_shared(cs)) + , id(trace_id::make_next_id_subscriber()) + { + } + + std::shared_ptr state; + + trace_id id; + + // used to avoid taking lock in on_next + mutable std::weak_ptr current_completer; + + // must only be accessed under state->lock + mutable std::shared_ptr completer; + }; + + std::shared_ptr b; + +public: + typedef subscriber>> input_subscriber_type; + + explicit multicast_observer(composite_subscription cs) + : b(std::make_shared(cs)) + { + std::weak_ptr binder = b; + b->state->lifetime.add([binder](){ + auto b = binder.lock(); + if (b && b->state->current == mode::Casting){ + b->state->current = mode::Disposed; + b->current_completer.reset(); + b->completer.reset(); + } + }); + } + trace_id get_id() const { + return b->id; + } + composite_subscription get_subscription() const { + return b->state->lifetime; + } + input_subscriber_type get_subscriber() const { + return make_subscriber(get_id(), get_subscription(), observer>(*this)); + } + bool has_observers() const { + std::unique_lock guard(b->state->lock); + return b->completer && !b->completer->observers.empty(); + } + template + void add(const SubscriberFrom& sf, observer_type o) const { + trace_activity().connect(sf, o); + std::unique_lock guard(b->state->lock); + switch (b->state->current) { + case mode::Casting: + { + if (o.is_subscribed()) { + std::weak_ptr binder = b; + o.add([=](){ + auto b = binder.lock(); + if (b) { + std::unique_lock guard(b->state->lock); + b->completer = std::make_shared(b->state, b->completer); + } + }); + b->completer = std::make_shared(b->state, b->completer, o); + } + } + break; + case mode::Completed: + { + guard.unlock(); + o.on_completed(); + return; + } + break; + case mode::Errored: + { + auto e = b->state->error; + guard.unlock(); + o.on_error(e); + return; + } + break; + case mode::Disposed: + { + guard.unlock(); + o.unsubscribe(); + return; + } + break; + default: + std::terminate(); + } + } + template + void on_next(V v) const { + auto current_completer = b->current_completer.lock(); + if (!current_completer) { + std::unique_lock guard(b->state->lock); + b->current_completer = b->completer; + current_completer = b->current_completer.lock(); + } + if (!current_completer || current_completer->observers.empty()) { + return; + } + for (auto& o : current_completer->observers) { + if (o.is_subscribed()) { + o.on_next(v); + } + } + } + void on_error(rxu::error_ptr e) const { + std::unique_lock guard(b->state->lock); + if (b->state->current == mode::Casting) { + b->state->error = e; + b->state->current = mode::Errored; + auto s = b->state->lifetime; + auto c = std::move(b->completer); + b->current_completer.reset(); + guard.unlock(); + if (c) { + for (auto& o : c->observers) { + if (o.is_subscribed()) { + o.on_error(e); + } + } + } + s.unsubscribe(); + } + } + void on_completed() const { + std::unique_lock guard(b->state->lock); + if (b->state->current == mode::Casting) { + b->state->current = mode::Completed; + auto s = b->state->lifetime; + auto c = std::move(b->completer); + b->current_completer.reset(); + guard.unlock(); + if (c) { + for (auto& o : c->observers) { + if (o.is_subscribed()) { + o.on_completed(); + } + } + } + s.unsubscribe(); + } + } +}; + + +} + +template +class subject +{ + detail::multicast_observer s; + +public: + typedef subscriber>> subscriber_type; + typedef observable observable_type; + subject() + : s(composite_subscription()) + { + } + explicit subject(composite_subscription cs) + : s(cs) + { + } + + bool has_observers() const { + return s.has_observers(); + } + + composite_subscription get_subscription() const { + return s.get_subscription(); + } + + subscriber_type get_subscriber() const { + return s.get_subscriber(); + } + + observable get_observable() const { + auto keepAlive = s; + return make_observable_dynamic([=](subscriber o){ + keepAlive.add(keepAlive.get_subscriber(), std::move(o)); + }); + } +}; + +} + +} + +#endif diff --git a/3party/rxcpp/subjects/rx-synchronize.hpp b/3party/rxcpp/subjects/rx-synchronize.hpp new file mode 100644 index 0000000..88f33ca --- /dev/null +++ b/3party/rxcpp/subjects/rx-synchronize.hpp @@ -0,0 +1,263 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +#pragma once + +#if !defined(RXCPP_RX_SYNCHRONIZE_HPP) +#define RXCPP_RX_SYNCHRONIZE_HPP + +#include "../rx-includes.hpp" + +namespace rxcpp { + +namespace subjects { + +namespace detail { + +template +class synchronize_observer : public detail::multicast_observer +{ + typedef synchronize_observer this_type; + typedef detail::multicast_observer base_type; + + typedef rxu::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef typename coordinator_type::template get>::type output_type; + + struct synchronize_observer_state : public std::enable_shared_from_this + { + typedef rxn::notification notification_type; + typedef typename notification_type::type base_notification_type; + typedef std::deque queue_type; + + struct mode + { + enum type { + Invalid = 0, + Processing, + Empty, + Disposed + }; + }; + + mutable std::mutex lock; + mutable std::condition_variable wake; + mutable queue_type fill_queue; + composite_subscription lifetime; + mutable typename mode::type current; + coordinator_type coordinator; + output_type destination; + + void ensure_processing(std::unique_lock& guard) const { + if (!guard.owns_lock()) { + std::terminate(); + } + if (current == mode::Empty) { + current = mode::Processing; + auto keepAlive = this->shared_from_this(); + + auto drain_queue = [keepAlive, this](const rxsc::schedulable& self){ + RXCPP_TRY { + std::unique_lock guard(lock); + if (!destination.is_subscribed()) { + current = mode::Disposed; + fill_queue.clear(); + guard.unlock(); + lifetime.unsubscribe(); + return; + } + if (fill_queue.empty()) { + current = mode::Empty; + return; + } + auto notification = std::move(fill_queue.front()); + fill_queue.pop_front(); + guard.unlock(); + notification->accept(destination); + self(); + } RXCPP_CATCH(...) { + destination.on_error(rxu::current_exception()); + std::unique_lock guard(lock); + current = mode::Empty; + } + }; + + auto selectedDrain = on_exception( + [&](){return coordinator.act(drain_queue);}, + destination); + if (selectedDrain.empty()) { + return; + } + + auto processor = coordinator.get_worker(); + guard.unlock(); + processor.schedule(lifetime, selectedDrain.get()); + } + } + + synchronize_observer_state(coordinator_type coor, composite_subscription cs, output_type scbr) + : lifetime(std::move(cs)) + , current(mode::Empty) + , coordinator(std::move(coor)) + , destination(std::move(scbr)) + { + } + + template + void on_next(V v) const { + if (lifetime.is_subscribed()) { + std::unique_lock guard(lock); + fill_queue.push_back(notification_type::on_next(std::move(v))); + ensure_processing(guard); + } + wake.notify_one(); + } + void on_error(rxu::error_ptr e) const { + if (lifetime.is_subscribed()) { + std::unique_lock guard(lock); + fill_queue.push_back(notification_type::on_error(e)); + ensure_processing(guard); + } + wake.notify_one(); + } + void on_completed() const { + if (lifetime.is_subscribed()) { + std::unique_lock guard(lock); + fill_queue.push_back(notification_type::on_completed()); + ensure_processing(guard); + } + wake.notify_one(); + } + }; + + std::shared_ptr state; + +public: + synchronize_observer(coordination_type cn, composite_subscription dl, composite_subscription il) + : base_type(dl) + { + auto o = make_subscriber(dl, make_observer_dynamic( *static_cast(this) )); + + // creates a worker whose lifetime is the same as the destination subscription + auto coordinator = cn.create_coordinator(dl); + + state = std::make_shared(std::move(coordinator), std::move(il), std::move(o)); + } + + subscriber get_subscriber() const { + return make_subscriber(this->get_id(), state->lifetime, observer>(*this)).as_dynamic(); + } + + template + void on_next(V v) const { + state->on_next(std::move(v)); + } + void on_error(rxu::error_ptr e) const { + state->on_error(e); + } + void on_completed() const { + state->on_completed(); + } +}; + +} + +template +class synchronize +{ + detail::synchronize_observer s; + +public: + explicit synchronize(Coordination cn, composite_subscription cs = composite_subscription()) + : s(std::move(cn), std::move(cs), composite_subscription()) + { + } + + bool has_observers() const { + return s.has_observers(); + } + + subscriber get_subscriber() const { + return s.get_subscriber(); + } + + observable get_observable() const { + auto keepAlive = s; + return make_observable_dynamic([=](subscriber o){ + keepAlive.add(keepAlive.get_subscriber(), std::move(o)); + }); + } +}; + +} + +class synchronize_in_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 + auto in(Observable o) const + -> decltype(o.publish_synchronized(coordination).ref_count()) { + return o.publish_synchronized(coordination).ref_count(); + } + template + auto out(Subscriber s) const + -> Subscriber { + return s; + } + template + auto act(F f) const + -> F { + return f; + } + }; + +public: + + explicit synchronize_in_one_worker(rxsc::scheduler sc) : factory(sc) {} + + typedef coordinator 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 synchronize_in_one_worker synchronize_event_loop() { + static synchronize_in_one_worker r(rxsc::make_event_loop()); + return r; +} + +inline synchronize_in_one_worker synchronize_new_thread() { + static synchronize_in_one_worker r(rxsc::make_new_thread()); + return r; +} + +} + +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 18558ec..7f455b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ if (SLED_LOCATION_PATH) target_compile_definitions(sled PRIVATE __SLED_LOCATION_PATH="${SLED_LOCATION_PATH}") endif() # add_subdirectory(3party/eigen EXCLUDE_FROM_ALL) -target_include_directories(sled PUBLIC 3party/eigen 3party/inja) +target_include_directories(sled PUBLIC 3party/eigen 3party/inja 3party/rxcpp) target_sources( sled PRIVATE @@ -101,12 +101,12 @@ if(SLED_BUILD_TESTS) FetchContent_MakeAvailable(googletest) add_executable(sled_tests src/filesystem/path_test.cc - src/futures/future_test.cc src/strings/base64_test.cc src/cleanup_test.cc src/status_or_test.cc src/system/fiber/fiber_test.cc src/system/thread_pool_test.cc + src/rx_test.cc ) target_link_libraries(sled_tests PRIVATE sled GTest::gtest GTest::gtest_main) add_test(NAME sled_tests COMMAND sled_tests) diff --git a/include/sled/futures/future.h b/include/sled/futures/future.h deleted file mode 100644 index 05e6373..0000000 --- a/include/sled/futures/future.h +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once -#include -#ifndef SLED_FUTURES_FUTURE_H -#define SLED_FUTURES_FUTURE_H - -#include "sled/futures/state.h" -#include "sled/futures/try.h" -#include "sled/units/time_delta.h" -#include - -namespace sled { -template -class Promise; -template -class Future; - -namespace { -template -struct IsFuture : std::false_type { - typedef T Inner; -}; - -template -struct IsFuture> : std::true_type { - typedef T Inner; -}; - -template -struct InvokeResult { - using Type = decltype(std::declval()(std::declval()...)); -}; - -template -struct CallableWith { - template::Type> - static constexpr bool check(std::nullptr_t) - { - return true; - } - - template - static constexpr bool check(...) - { - return false; - } - - static constexpr bool value = check(check(nullptr)); -}; - -template -struct ArgResult { - using Function = F; - using Result = typename InvokeResult::Type; - - static constexpr bool IsTry() { return is_try; } -}; - -template -struct CallableResult { - typedef typename std::conditional< - CallableWith::value, - ArgResult, - typename std::conditional< - CallableWith::value, - ArgResult, - typename std::conditional::value, - ArgResult, - typename std::conditional &&>::value, - ArgResult &&>, - ArgResult &>>::type>::type>::type>::type - Arg; - typedef IsFuture ReturnsFuture; - typedef Future Return; -}; -}// namespace - -template -class Future { - using TimeoutCallback = std::function; - -public: - Future() = default; - - explicit Future(std::shared_ptr> state) : state_(std::move(state)) {} - - Future(const Future &) = delete; - - Future &operator=(const Future &) = delete; - Future(Future &&) = default; - Future &operator=(Future &&) = default; - - bool Valid() const { return state_ != nullptr; } - - template> - auto Then(F &&func) -> typename R::Return - { - typedef typename R::Arg Arguments; - return ThenImpl(std::forward(func), Arguments()); - } - - // for returns Future - template - typename std::enable_if::type - ThenImpl(F &&func, typename InvokeResult::Type) - { - static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument"); - typedef typename R::Return::Inner ValueType; - Promise promise; - - auto next_future = promise.GetFuture(); - { - auto weak_parent_state = std::weak_ptr>(state_); - next_future.SetOnTimeout([weak_parent_state = weak_parent_state](TimeoutCallback &&cb) { - auto parent_state = weak_parent_state.lock(); - if (!parent_state) { return; } - - // set timeout - { - MutexLock lock(&parent_state->mutex_); - if (parent_state->progress_ != State::kNone) { return; } - parent_state->progress_ = State::kTimeout; - } - }); - - next_future.SetCallback([weak_parent_state = weak_parent_state, - func = std::forward::type>(func), - promise = std::move(promise)](typename TryWrapper::Type &&t) { - auto parent_state = weak_parent_state.lock(); - if (parent_state) { - } else { - } - }); - } - - return std::move(next_future); - } - - Future &Via() { return *this; } - - Future &OnTimeout(sled::TimeDelta duration, TimeoutCallback &&cb) { return *this; } - -private: - void SetCallback(TryWrapper &&t); - void SetOnTimeout(std::function &&func); - - std::shared_ptr> state_; -}; -}// namespace sled -#endif// SLED_FUTURES_FUTURE_H diff --git a/include/sled/futures/promise.h b/include/sled/futures/promise.h deleted file mode 100644 index f0848c3..0000000 --- a/include/sled/futures/promise.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once -#ifndef SLED_FUTURES_PROMISE_H -#define SLED_FUTURES_PROMISE_H - -#include -#include - -namespace sled { -namespace { -enum class Progress { - kNone, - kTimeout, - kDone, - kRetrieved, -}; - -template -struct State { - Progress progress_ = Progress::kNone; -}; - -}// namespace - -template -class Future; -template -class Try; - -template -class Promise { -public: - static Promise MakeEmpty() noexcept; - Promise(); - ~Promise(); - Promise(Promise const &) = delete; - Promise &operator=(Promise const &) = delete; - Promise(Promise &&other) noexcept; - Promise &operator=(Promise &&other) noexcept; - - Future GetFuture(); - - template - typename std::enable_if::value>::type - SetValue(ValueType &&value) - {} -}; - -}// namespace sled -#endif// SLED_FUTURES_PROMISE_H diff --git a/include/sled/futures/state.h b/include/sled/futures/state.h deleted file mode 100644 index cf01250..0000000 --- a/include/sled/futures/state.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#ifndef SLED_FUTURES_STATE_H -#define SLED_FUTURES_STATE_H -#include "sled/futures/try.h" -#include "sled/synchronization/mutex.h" -#include - -namespace sled { -template -struct State { -public: - enum InnerState { - kNone, - kTimeout, - kDone, - }; - - static_assert(std::is_same::value || std::is_copy_constructible() || std::is_move_constructible(), - "Must be copyable or movable or void"); - - State() {} - - sled::Mutex &GetMutex() { return mutex_; } - - using ValueType = typename TryWrapper::Type; - sled::Mutex mutex_; - ValueType value_ GUARDED_BY(mutex_); - InnerState progress_ GUARDED_BY(mutex_) = kNone; - std::function)> on_timeout_; - std::function on_then_; -}; - -}// namespace sled -#endif// SLED_FUTURES_STATE_H diff --git a/include/sled/futures/try.h b/include/sled/futures/try.h deleted file mode 100644 index d0b9ee3..0000000 --- a/include/sled/futures/try.h +++ /dev/null @@ -1,267 +0,0 @@ -#pragma once -#ifndef SLED_FUTURES_TRY_H -#define SLED_FUTURES_TRY_H -#include -#include -#include - -namespace sled { - -template -class Try { - enum class State { - kNone, - kException, - kValue, - }; - -public: - inline Try() = default; - - Try(const T &t) : state_(State::kValue), value_(t) {} - - Try(T &&t) : state_(State::kValue), value_(std::move(t)) {} - - explicit Try(std::exception_ptr e) : state_(State::kException), exception_(std::move(e)) {} - - Try(Try &&other_try) noexcept : state_(other_try.state_) - { - if (state_ == State::Value) { - new (&value_) T(std::move(other_try.value_)); - } else { - new (&exception_) std::exception_ptr(std::move(other_try.exception_)); - } - } - - Try(const Try &other_try) : state_(other_try.state_) - { - if (state_ == State::kValue) { - new (&value_) T(other_try.value_); - } else { - new (&exception_) std::exception_ptr(other_try.exception_); - } - } - - Try &operator=(Try &&other_try) noexcept - { - if (this == &other_try) { return *this; } - this->~Try(); - state_ = other_try.state_; - if (state_ == State::kValue) { - new (&value_) T(std::move(other_try.value_)); - } else { - new (&exception_) std::exception_ptr(std::move(other_try.exception_)); - } - return *this; - } - - Try &operator=(const Try &other_try) - { - if (this == &other_try) { return *this; } - this->~Try(); - state_ = other_try.state_; - if (state_ == State::kValue) { - new (&value_) T(other_try.value_); - } else { - new (&exception_) std::exception_ptr(other_try.exception_); - } - return *this; - } - - ~Try() - { - if (state_ == State::kValue) { - value_.~T(); - } else if (state_ == State::kException) { - exception_.~exception_ptr(); - } - } - - bool HasValue() const noexcept { return state_ == State::kValue; } - - bool HasException() const noexcept { return state_ == State::kException; } - - const T &Value() const & - { - AssertHasValue(); - return value_; - } - - T &Value() & - { - AssertHasValue(); - return value_; - } - - T &&Value() && - { - AssertHasValue(); - return std::move(value_); - } - - const std::exception_ptr &Exception() const & - { - AssertHasException(); - return exception_; - } - - std::exception_ptr &Exception() & - { - AssertHasException(); - return exception_; - } - - std::exception_ptr &&Exception() && - { - AssertHasException(); - return std::move(exception_); - } - - const T &operator*() const & { return Value(); } - - T &operator*() & { return Value(); } - - T &&operator*() && { return Value(); } - - explicit operator bool() const { return HasValue(); } - -private: - void AssertHasValue() const - { - if (state_ == State::kNone) { - throw std::runtime_error("Try is empty"); - } else if (state_ == State::kException) { - std::rethrow_exception(exception_); - } - } - - void AssertHasException() const - { - if (!HasException()) { throw std::runtime_error("Not exceptioin"); } - } - - State state_ = State::kNone; - - union { - T value_; - std::exception_ptr exception_; - }; -}; - -template<> -class Try { - enum class State { kValue, kException }; - -public: - Try() : state_(State::kValue) {} - - Try(Try &&other_try) : state_(other_try.state_) - { - if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); } - } - - Try(const Try &other_try) : state_(other_try.state_) - { - if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); } - } - - explicit Try(std::exception_ptr e) : exception_(std::move(e)) {} - - ~Try() - { - if (state_ == State::kException) { exception_.~exception_ptr(); } - } - - Try &operator=(Try &&other_try) - { - if (this == &other_try) { return *this; } - this->~Try(); - state_ = other_try.state_; - if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); } - return *this; - } - - Try &operator=(const Try &other_try) - { - if (this == &other_try) { return *this; } - this->~Try(); - state_ = other_try.state_; - if (state_ == State::kException) { new (&exception_) std::exception_ptr(std::move(other_try.exception_)); } - return *this; - } - - bool HasValue() const noexcept { return state_ == State::kValue; } - - bool HasException() const noexcept { return state_ == State::kException; } - - const std::exception_ptr &Exception() const & - { - AssertHasException(); - return exception_; - } - - std::exception_ptr &Exception() & - { - AssertHasException(); - return exception_; - } - - std::exception_ptr &&Exception() && - { - AssertHasException(); - return std::move(exception_); - } - - explicit operator bool() const { return HasValue(); } - -private: - void AssertHasException() const - { - if (!HasException()) { throw std::runtime_error("Not exception"); } - } - - void AssertHasValue() const - { - if (!HasValue()) { throw std::runtime_error("Try is empty"); } - } - - State state_; - std::exception_ptr exception_; -}; - -template -struct TryWrapper { - using Type = Try; -}; - -template -struct TryWrapper> { - using Type = Try; -}; - -template::value> -typename std::enable_if::value, typename TryWrapper::Type>::type -WrapWithTry(F &&f, Args &&...args) -{ - try { - return typename TryWrapper::Type(std::forward(f)(std::forward(args)...)); - } catch (...) { - return typename TryWrapper::Type(std::current_exception()); - } -} - -template::value> -typename std::enable_if::value, typename TryWrapper::Type>::type -WrapWithTry(F &&f, Args &&...args) -{ - try { - - std::forward(f)(std::forward(args)...); - return Try(); - } catch (...) { - return Try(std::current_exception()); - } -} - -}// namespace sled -#endif// SLED_FUTURES_TRY_H diff --git a/include/sled/rx.h b/include/sled/rx.h new file mode 100644 index 0000000..752b9eb --- /dev/null +++ b/include/sled/rx.h @@ -0,0 +1,11 @@ +#pragma once +#ifndef SLED_RX_H +#define SLED_RX_H + +#include "rx-lite.hpp" + +namespace sled { +using namespace rxcpp; +} + +#endif// SLED_RX_H diff --git a/include/sled/sled.h b/include/sled/sled.h index 2c9a5e3..792e4e2 100644 --- a/include/sled/sled.h +++ b/include/sled/sled.h @@ -67,4 +67,5 @@ #include "sled/variant.h" #include "inja.hpp" +#include "rx.h" #endif// SLED_SLED_H diff --git a/src/futures/future_test.cc b/src/futures/future_test.cc deleted file mode 100644 index b9e20df..0000000 --- a/src/futures/future_test.cc +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include - -TEST(Future, Test1) -{ - sled::Promise promise; - sled::Future future = promise.GetFuture(); - promise.SetValue(42); -} diff --git a/src/rx_test.cc b/src/rx_test.cc new file mode 100644 index 0000000..bbf8295 --- /dev/null +++ b/src/rx_test.cc @@ -0,0 +1,10 @@ +#include "schedulers/rx-currentthread.hpp" +#include +#include + +TEST(RX, interval) +{ + auto sc = sled::schedulers::make_current_thread(); + sled::observable<>::interval(std::chrono::seconds(1), sled::synchronize_in_one_worker(sc)) + .subscribe([](long counter) {}); +}