Skip to content

Commit

Permalink
decouple exists and contains from observable (#276)
Browse files Browse the repository at this point in the history
* decouple exists and contains from observable

* add missing any_tag specialization
  • Loading branch information
gchudnov authored and Kirk Shoop committed Nov 25, 2016
1 parent 63b8d82 commit 4ab756b
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 68 deletions.
157 changes: 134 additions & 23 deletions Rx/v2/src/rxcpp/operators/rx-any.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

#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

Expand All @@ -13,10 +37,21 @@ namespace operators {

namespace detail {

template<class... AN>
struct any_invalid_arguments {};

template<class... AN>
struct any_invalid : public rxo::operator_base<any_invalid_arguments<AN...>> {
using type = observable<any_invalid_arguments<AN...>, any_invalid<AN...>>;
};
template<class... AN>
using any_invalid_t = typename any_invalid<AN...>::type;

template<class T, class Predicate>
struct any
{
typedef rxu::decay_t<T> source_value_type;
typedef bool value_type;
typedef rxu::decay_t<Predicate> test_type;
test_type test;

Expand Down Expand Up @@ -66,7 +101,7 @@ struct any
}
}

static subscriber<value_type, observer<value_type, this_type>> make(dest_type d, test_type t) {
static subscriber<value_type, observer_type> make(dest_type d, test_type t) {
return make_subscriber<value_type>(d, this_type(d, std::move(t)));
}
};
Expand All @@ -78,37 +113,113 @@ struct any
}
};

template <class Predicate>
class any_factory
{
typedef rxu::decay_t<Predicate> test_type;
}

test_type test;
public:
any_factory(test_type t) : test(t) { }
/*! @copydoc rx-any.hpp
*/
template<class... AN>
auto any(AN&&... an)
-> operator_factory<any_tag, AN...> {
return operator_factory<any_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}

template<class Observable>
auto operator()(Observable&& source)
-> decltype(source.template lift<rxu::value_type_t<rxu::decay_t<Observable>>>(any<rxu::value_type_t<rxu::decay_t<Observable>>, Predicate>(test))) {
return source.template lift<rxu::value_type_t<rxu::decay_t<Observable>>>(any<rxu::value_type_t<rxu::decay_t<Observable>>, Predicate>(test));
}
};
/*! \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.
template <class Predicate>
inline auto exists(Predicate test)
-> detail::any_factory<Predicate> {
return detail::any_factory<Predicate>(test);
\sample
\snippet exists.cpp exists sample
\snippet output.txt exists sample
*/
template<class... AN>
auto exists(AN&&... an)
-> operator_factory<exists_tag, AN...> {
return operator_factory<exists_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}

template <class T>
inline auto contains(T value)
-> detail::any_factory<std::function<bool(T)>> {
return detail::any_factory<std::function<bool(T)>>([value](T n) { return n == value; });
/*! \brief Returns an Observable that emits true if the source Observable emitted a specified item, otherwise false. Emits false if the source Observable terminates without emitting any item.
\tparam T the type of the item to search for.
\param value the item to search for.
\return An observable that emits true if the source Observable emitted a specified item, otherwise false.
\sample
\snippet contains.cpp contains sample
\snippet output.txt contains sample
*/
template<class... AN>
auto contains(AN&&... an)
-> operator_factory<contains_tag, AN...> {
return operator_factory<contains_tag, AN...>(std::make_tuple(std::forward<AN>(an)...));
}

}

template<>
struct member_overload<any_tag>
{
template<class Observable, class Predicate,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class Any = rxo::detail::any<SourceValue, rxu::decay_t<Predicate>>,
class Value = rxu::value_type_t<Any>>
static auto member(Observable&& o, Predicate&& p)
-> decltype(o.template lift<Value>(Any(std::forward<Predicate>(p)))) {
return o.template lift<Value>(Any(std::forward<Predicate>(p)));
}

template<class... AN>
static operators::detail::any_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "any takes (Predicate)");
}
};

template<>
struct member_overload<exists_tag>
: member_overload<any_tag>
{
using member_overload<any_tag>::member;

template<class... AN>
static operators::detail::any_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "exists takes (Predicate)");
}
};

template<>
struct member_overload<contains_tag>
{
template<class Observable, class T,
class SourceValue = rxu::value_type_t<Observable>,
class Enabled = rxu::enable_if_all_true_type_t<
is_observable<Observable>>,
class Predicate = std::function<bool(T)>,
class Any = rxo::detail::any<SourceValue, rxu::decay_t<Predicate>>,
class Value = rxu::value_type_t<Any>>
static auto member(Observable&& o, T&& value)
-> decltype(o.template lift<Value>(Any(nullptr))) {
return o.template lift<Value>(Any([value](T n) { return n == value; }));
}

template<class... AN>
static operators::detail::any_invalid_t<AN...> member(const AN&...) {
std::terminate();
return {};
static_assert(sizeof...(AN) == 10000, "contains takes (T)");
}
};

}

#endif
1 change: 1 addition & 0 deletions Rx/v2/src/rxcpp/rx-includes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@

#if !defined(RXCPP_LITE)
#include "operators/rx-all.hpp"
#include "operators/rx-any.hpp"
#include "operators/rx-combine_latest.hpp"
#include "operators/rx-debounce.hpp"
#include "operators/rx-delay.hpp"
Expand Down
55 changes: 15 additions & 40 deletions Rx/v2/src/rxcpp/rx-observable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,51 +742,26 @@ class observable
return observable_member(all_tag{}, *this, std::forward<AN>(an)...);
}

/*! 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 Observable that emits true if any item emitted by the source observable satisfies a specified condition, otherwise false.
\sample
\snippet exists.cpp exists sample
\snippet output.txt exists sample
*/
template<class Predicate>
auto exists(Predicate p) const
/// \cond SHOW_SERVICE_MEMBERS
-> decltype(EXPLICIT_THIS lift<bool>(rxo::detail::any<T, Predicate>(std::move(p))))
/// \endcond
/*! @copydoc rxcpp::operators::exists
*/
template<class... AN>
auto exists(AN&&... an) const
/// \cond SHOW_SERVICE_MEMBERS
-> decltype(observable_member(exists_tag{}, *(this_type*)nullptr, std::forward<AN>(an)...))
/// \endcond
{
return lift<bool>(rxo::detail::any<T, Predicate>(std::move(p)));
return observable_member(exists_tag{}, *this, std::forward<AN>(an)...);
}

// workaround for - rx-observable.hpp(799): error C2066: cast to function type is illegal
// 799 was: decltype(EXPLICIT_THIS lift<bool>(rxo::detail::any<T, std::function<bool(T)>>(std::function<bool(T)>{})))
typedef std::function<bool(T)> boolFromT;

/*! 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.
\param value the item to search for.
\return 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
*/
/*! @copydoc rxcpp::operators::contains
*/
template<class... AN>
auto contains(T value, AN**...) const
/// \cond SHOW_SERVICE_MEMBERS
-> decltype(EXPLICIT_THIS lift<bool>(rxo::detail::any<T, boolFromT>(nullptr)))
/// \endcond
auto contains(AN&&... an) const
/// \cond SHOW_SERVICE_MEMBERS
-> decltype(observable_member(contains_tag{}, *(this_type*)nullptr, std::forward<AN>(an)...))
/// \endcond
{
return lift<bool>(rxo::detail::any<T, boolFromT>([value](T n) { return n == value; }));
static_assert(sizeof...(AN) == 0, "contains(value) was passed too many arguments.");
return observable_member(contains_tag{}, *this, std::forward<AN>(an)...);
}

/*! @copydoc rx-filter.hpp
Expand Down
11 changes: 10 additions & 1 deletion Rx/v2/src/rxcpp/rx-operators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ class operator_factory
}

#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"
Expand Down Expand Up @@ -148,7 +147,17 @@ struct all_tag {
static_assert(Included::value, "missing include: please #include <rxcpp/operators/rx-all.hpp>");
};
};

struct any_tag {
template<class Included>
struct include_header{
static_assert(Included::value, "missing include: please #include <rxcpp/operators/rx-any.hpp>");
};
};

struct exists_tag : any_tag {};
struct contains_tag : any_tag {};

struct combine_latest_tag {
template<class Included>
struct include_header{
Expand Down
5 changes: 3 additions & 2 deletions Rx/v2/test/operators/contains.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "../test.h"
#include <rxcpp/operators/rx-any.hpp>

SCENARIO("contains emits true if an item satisfies the given condition", "[contains][operators]"){
GIVEN("a source") {
Expand All @@ -18,8 +19,8 @@ SCENARIO("contains emits true if an item satisfies the given condition", "[conta
auto res = w.start(
[xs]() {
return xs
.contains(2)
.as_dynamic(); // forget type to workaround lambda deduction bug on msvc 2013
| rxo::contains(2)
| rxo::as_dynamic(); // forget type to workaround lambda deduction bug on msvc 2013
}
);

Expand Down
5 changes: 3 additions & 2 deletions Rx/v2/test/operators/exists.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "../test.h"
#include <rxcpp/operators/rx-any.hpp>

SCENARIO("exists emits true if an item satisfies the given condition", "[exists][operators]"){
GIVEN("a source") {
Expand All @@ -18,8 +19,8 @@ SCENARIO("exists emits true if an item satisfies the given condition", "[exists]
auto res = w.start(
[xs]() {
return xs
.exists([](int n) { return n == 2; })
.as_dynamic(); // forget type to workaround lambda deduction bug on msvc 2013
| rxo::exists([](int n) { return n == 2; })
| rxo::as_dynamic(); // forget type to workaround lambda deduction bug on msvc 2013
}
);

Expand Down

0 comments on commit 4ab756b

Please sign in to comment.