From 00249736fe1078a91f06e8e1c343bf6def0d7954 Mon Sep 17 00:00:00 2001 From: Aleksey Loginov Date: Wed, 21 Feb 2024 18:33:16 +0300 Subject: [PATCH 1/3] Document dynamic_ --- docs/readme.md | 47 ++++++++++++++++++++++++++ src/rpp/rpp/observables/observable.hpp | 2 ++ src/rpp/rpp/observers/observer.hpp | 2 +- 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/docs/readme.md b/docs/readme.md index b9a6d53d4..9ccd450ff 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -397,3 +397,50 @@ rpp::source::just(my_custom_variable); makes only 1 copy/move to shared_ptr and then uses it instead. As a a result, users can select preferable way of handling of their types. + +## ReactivePlusPlus specific +### dynamic_* versions to keep classes as variables + +Most of the classes inside rpp library including `observable`, `observer` and others are heavy-templated classes. It means, it could has a lot of template params. In most cases you shouldn't worry about it due to it is purely internal problem. + +But in some cases you want to keep observable or observer inside your classes or return it from function. In most cases I strongly recommend you to use `auto` to deduce type automatically. But in some cases it is not possible (for example, to keep observable as member variable). For such an usage you could use `dynamic_observable` and `dynamic_observer`: +- they are type-erased wrappers over regular observable/observer with goal to hide all unnecessary stuff from user's code. For example, you can easily use it as: +```cpp +#include + +#include + +struct some_data +{ + rpp::dynamic_observable observable; + rpp::dynamic_observer observer; +}; + +int main() { + some_data v{rpp::source::just(1,2,3), + rpp::make_lambda_observer([](int value){ + std::cout << value << std::endl; + }) + }; + + v.observable.subscribe(v.observer); +} +``` +- to convert observable/observer to dynamic_* version you could manually call `as_dynamic()` member function or just pass them to ctor +- actually they are similar to rxcpp's `observer` and `observable` but provides EXPLICIT definition of `dynamic` fact +- due to type-erasure mechanism `dynamic_` provides some minor performance penalties due to extra usage of `shared_ptr` to keep internal state + indirect calls. It is not critical in case of storing it as member function, but could be important in case of using it on hot paths like this: +```cpp +rpp::source::just(1,2,3) +| rpp::ops::map([](int v) { return rpp::source::just(v); }) +| rpp::ops::flat_map([](rpp::dynamic_observable observable) { + return observable | rpp::ops::filter([](int v){ return v % 2 == 0;}); +}); +``` +^^^ while it is fully valid code, `flat_map` have to convert observable to dynamic version via extra heap, but it is unnecessary. It is better to use `auto` in this case. +```cpp +rpp::source::just(1,2,3) +| rpp::ops::map([](int v) { return rpp::source::just(v); }) +| rpp::ops::flat_map([](const rpp::constraint::observable_of_type auto& observable) { // or just `const auto& observable` +return observable | rpp::ops::filter([](int v){ return v % 2 == 0;}); +}); +``` \ No newline at end of file diff --git a/src/rpp/rpp/observables/observable.hpp b/src/rpp/rpp/observables/observable.hpp index a403351bb..4f1023435 100644 --- a/src/rpp/rpp/observables/observable.hpp +++ b/src/rpp/rpp/observables/observable.hpp @@ -26,6 +26,8 @@ namespace rpp * @warning Actually observable "doesn't emit nothing", it only **invokes Strategy!** Strategy COULD emit emissions immediately OR place observer to some queue or something like this to obtain emissions later (for example subjects) * @warning Expected that observable's strategy would work with observer in serialized way * + * @note In case of you are need to keep some "abstract" observable of `Type`, you can use type-erased wrapper version: `rpp::dynamic_observable` + * * @tparam Type of value this observable would provide. Only observers of same type can be subscribed to this observable. * @tparam Strategy used to provide logic over observable's callbacks. * diff --git a/src/rpp/rpp/observers/observer.hpp b/src/rpp/rpp/observers/observer.hpp index c6466ac51..e62a67156 100644 --- a/src/rpp/rpp/observers/observer.hpp +++ b/src/rpp/rpp/observers/observer.hpp @@ -241,7 +241,7 @@ namespace rpp public: template TStrategy> requires (!std::same_as>) - explicit observer(observer&& other) + observer(observer&& other) : details::observer_impl, details::observers::none_disposable_strategy>{details::observers::none_disposable_strategy{}, std::move(other)} { } From cbff2b705d42ccd8632a218e484372f05b78a2b9 Mon Sep 17 00:00:00 2001 From: Aleksey Loginov Date: Wed, 21 Feb 2024 18:37:23 +0300 Subject: [PATCH 2/3] update --- docs/readme.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/readme.md b/docs/readme.md index 9ccd450ff..5562ae73f 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -419,9 +419,8 @@ struct some_data int main() { some_data v{rpp::source::just(1,2,3), rpp::make_lambda_observer([](int value){ - std::cout << value << std::endl; - }) - }; + std::cout << value << std::endl; + })}; v.observable.subscribe(v.observer); } From 5d656714020d584d24d93f7f61b0232e80c97de4 Mon Sep 17 00:00:00 2001 From: Aleksey Loginov Date: Wed, 21 Feb 2024 18:37:57 +0300 Subject: [PATCH 3/3] fix comment --- src/rpp/rpp/observables/observable.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpp/rpp/observables/observable.hpp b/src/rpp/rpp/observables/observable.hpp index 4f1023435..3b1b06c00 100644 --- a/src/rpp/rpp/observables/observable.hpp +++ b/src/rpp/rpp/observables/observable.hpp @@ -26,7 +26,7 @@ namespace rpp * @warning Actually observable "doesn't emit nothing", it only **invokes Strategy!** Strategy COULD emit emissions immediately OR place observer to some queue or something like this to obtain emissions later (for example subjects) * @warning Expected that observable's strategy would work with observer in serialized way * - * @note In case of you are need to keep some "abstract" observable of `Type`, you can use type-erased wrapper version: `rpp::dynamic_observable` + * @note In case of you are need to keep some "abstract" observable of `Type`, you can use type-erased version: `rpp::dynamic_observable` * * @tparam Type of value this observable would provide. Only observers of same type can be subscribed to this observable. * @tparam Strategy used to provide logic over observable's callbacks.