diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index cd7a23f8c..c7269919b 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -74,7 +74,6 @@ endif() # ===================== Tests =================== if (RPP_BUILD_TESTS) rpp_fetch_library(Catch2 https://github.com/catchorg/Catch2.git v3.6.0) - target_compile_features(Catch2::Catch2WithMain INTERFACE cxx_std_20) rpp_fetch_library(trompeloeil https://github.com/rollbear/trompeloeil.git main) endif() diff --git a/src/benchmarks/benchmarks.cpp b/src/benchmarks/benchmarks.cpp index 796d019d5..e4ce85dc3 100644 --- a/src/benchmarks/benchmarks.cpp +++ b/src/benchmarks/benchmarks.cpp @@ -183,6 +183,17 @@ int main(int argc, char* argv[]) // NOLINT(bugprone-exception-escape) }); } + SECTION("concat_as_source of just(1 immediate) and just(1,2 immediate)create + subscribe") + { + TEST_RPP([&]() { + rpp::source::concat(rpp::source::just(rpp::schedulers::immediate{}, 1), rpp::source::just(rpp::schedulers::immediate{}, 1, 2)).subscribe([](int v) { ankerl::nanobench::doNotOptimizeAway(v); }); + }); + + TEST_RXCPP([&]() { + rxcpp::observable<>::from(rxcpp::identity_immediate(), rxcpp::observable<>::just(1, rxcpp::identity_immediate()).as_dynamic(), rxcpp::observable<>::from(rxcpp::identity_immediate(), 1, 2).as_dynamic()) | rxcpp::operators::concat() | rxcpp::operators::subscribe([](int v) { ankerl::nanobench::doNotOptimizeAway(v); }); + }); + } + SECTION("defer from array of 1 - defer + create + subscribe + immediate") { TEST_RPP([&]() { diff --git a/src/examples/rpp/doxygen/switch_on_next.cpp b/src/examples/rpp/doxygen/switch_on_next.cpp new file mode 100644 index 000000000..346310426 --- /dev/null +++ b/src/examples/rpp/doxygen/switch_on_next.cpp @@ -0,0 +1,20 @@ +#include + +#include + +/** + * \example switch_on_next.cpp + **/ +int main() +{ + //! [switch_on_next] + rpp::source::just(rpp::source::just(1).as_dynamic(), + rpp::source::never().as_dynamic(), + rpp::source::just(2).as_dynamic()) + | rpp::operators::switch_on_next() + | rpp::operators::subscribe([](int v) { std::cout << v << " "; }); + // Output: 1 2 + //! [switch_on_next] + + return 0; +} diff --git a/src/rpp/rpp/observables.hpp b/src/rpp/rpp/observables.hpp index 279cb5d45..1bf79618d 100644 --- a/src/rpp/rpp/observables.hpp +++ b/src/rpp/rpp/observables.hpp @@ -25,3 +25,4 @@ #include #include #include +#include diff --git a/src/rpp/rpp/observables/fwd.hpp b/src/rpp/rpp/observables/fwd.hpp index 7411fdca4..48a60147e 100644 --- a/src/rpp/rpp/observables/fwd.hpp +++ b/src/rpp/rpp/observables/fwd.hpp @@ -50,17 +50,9 @@ namespace rpp template Strategy> class observable; - - template - class dynamic_observable; - - template Strategy> - class blocking_observable; - - template Strategy> - class grouped_observable; } // namespace rpp + namespace rpp::constraint { template @@ -124,6 +116,22 @@ namespace rpp::constraint concept observables_of_same_type = rpp::constraint::observable && (rpp::constraint::observable && ...) && (std::same_as, rpp::utils::extract_observable_type_t> && ...); } // namespace rpp::constraint +namespace rpp +{ + template + class dynamic_observable; + + template Strategy> + class blocking_observable; + + template Strategy> + class grouped_observable; + + template... Observables> + class variant_observable; +} // namespace rpp + + #define RPP_CHECK_IF_TRAIT_ASSERTS_SATISFIED(Op, Type) \ /* operator_traits can be instantiated if all inner static_asserts are fine*/ \ if constexpr (requires { { typename std::decay_t::template operator_traits{}}; }) diff --git a/src/rpp/rpp/observables/variant_observable.hpp b/src/rpp/rpp/observables/variant_observable.hpp new file mode 100644 index 000000000..06e0bc3cb --- /dev/null +++ b/src/rpp/rpp/observables/variant_observable.hpp @@ -0,0 +1,62 @@ +// ReactivePlusPlus library +// +// Copyright Aleksey Loginov 2023 - present. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) +// +// Project home: https://github.com/victimsnino/ReactivePlusPlus + +#pragma once + +#include +#include + +#include + +namespace rpp::details +{ + template... Observables> + struct variant_observable_strategy + { + using value_type = Type; + template TT> + requires (!constraint::decayed_same_as) + explicit variant_observable_strategy(TT&& observable) // NOLINT + : observables(std::forward(observable)) + { + } + + + variant_observable_strategy(const variant_observable_strategy& other) = default; + variant_observable_strategy(variant_observable_strategy&& other) noexcept = default; + + utils::unique_variant observables; + + template TObs> + void subscribe(TObs&& obs) const + { + std::visit([&](const auto& o) { o.subscribe(std::forward(obs)); }, observables); + } + }; +} // namespace rpp::details + +namespace rpp +{ + /** + * @brief Extension over rpp::observable to provide ability statically keep one of multiple observables + * + * @ingroup observables + */ + template... Observables> + class variant_observable : public rpp::observable> + { + using base = rpp::observable>; + + public: + using base::base; + }; + + template>... Observables> + variant_observable(std::variant variant) -> variant_observable, T, Observables...>; +} // namespace rpp diff --git a/src/rpp/rpp/operators/switch_on_next.hpp b/src/rpp/rpp/operators/switch_on_next.hpp index 9cd494e83..b4e7649fe 100644 --- a/src/rpp/rpp/operators/switch_on_next.hpp +++ b/src/rpp/rpp/operators/switch_on_next.hpp @@ -157,6 +157,30 @@ namespace rpp::operators::details namespace rpp::operators { + /** + * @brief Converts observable of observables into observable of values which emits values from most recent underlying observable till new observable obtained + * + * @marble switch_on_next + { + source observable: + { + +--1-2-3-5--| + .....+4--6-9| + .......+7-8-| + } + operator "switch_on_next" : +--1-24-7-8| + } + * + * @details Actually this operator just unsubscribes from previous observable and subscribes on new observable when obtained in `on_next` + * + * @warning #include + * + * @par Example: + * @snippet switch_on_next.cpp switch_on_next + * + * @ingroup combining_operators + * @see https://reactivex.io/documentation/operators/switch.html + */ inline auto switch_on_next() { return details::switch_on_next_t{}; diff --git a/src/rpp/rpp/sources/concat.hpp b/src/rpp/rpp/sources/concat.hpp index 4a5e37a49..8ee5f537e 100644 --- a/src/rpp/rpp/sources/concat.hpp +++ b/src/rpp/rpp/sources/concat.hpp @@ -12,8 +12,8 @@ #include #include -#include #include +#include #include #include @@ -183,7 +183,10 @@ namespace rpp::source return rpp::details::make_concat_from_iterable(std::forward(obs), std::forward(others)...); } else - return concat(std::forward(obs).as_dynamic(), std::forward(others).as_dynamic()...); + { + using variant_observable_t = rpp::variant_observable, std::decay_t, std::decay_t...>; + return concat(variant_observable_t{std::forward(obs)}, variant_observable_t{std::forward(others)}...); + } } /** diff --git a/src/rpp/rpp/utils/utils.hpp b/src/rpp/rpp/utils/utils.hpp index 091af0d75..0fef33617 100644 --- a/src/rpp/rpp/utils/utils.hpp +++ b/src/rpp/rpp/utils/utils.hpp @@ -16,6 +16,7 @@ #include #include +#include namespace rpp::utils { @@ -312,6 +313,28 @@ namespace rpp::utils template using pointer_under_lock = typename value_with_mutex::pointer_under_lock; + namespace details + { + template + struct unique_variant_t : std::type_identity + { + }; + + template + requires (std::is_same_v || ...) + struct unique_variant_t, U, Us...> : unique_variant_t, Us...> + { + }; + + template + struct unique_variant_t, U, Us...> : public unique_variant_t, Us...> + { + }; + } // namespace details + + template + using unique_variant = typename details::unique_variant_t, Ts...>::type; // inspired by https://stackoverflow.com/a/57528226/17771792 + #define RPP_CALL_DURING_CONSTRUCTION(...) RPP_NO_UNIQUE_ADDRESS rpp::utils::none _ = [&]() { \ __VA_ARGS__; \