From 90f9f2f5f12e305d9f30f19ef5915104e3f7186a Mon Sep 17 00:00:00 2001 From: Aleksey Loginov Date: Thu, 14 Nov 2024 21:53:24 +0300 Subject: [PATCH] Update disposables docs (#679) * disposables * one more * Update src/rpp/rpp/disposables.hpp Co-authored-by: Markus Werle * minor update --------- Co-authored-by: Markus Werle --- docs/readme.md | 41 +------------------ src/rpp/rpp/disposables.hpp | 25 ++++++++++- .../rpp/disposables/disposable_wrapper.hpp | 39 ++++++++++++++++-- 3 files changed, 60 insertions(+), 45 deletions(-) diff --git a/docs/readme.md b/docs/readme.md index 83bf032de..e7a4894f3 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -193,15 +193,10 @@ See for more details about s ### Disposable -In reactive programming, a **disposable** is an object that represents a resource that needs to be released or disposed of when it is no longer needed. This can include things like file handles, network connections, or any other resource that needs to be cleaned up after use. +\copydoc disposables -The purpose of a disposable is to provide a way to manage resources in a safe and efficient manner. By using disposables, you can ensure that resources are released in a timely manner, preventing memory leaks and other issues that can arise from resource leaks. +Check API reference of @link disposables @endlink for more details -In most cases disposables are placed in observers. RPP's observer can use two types of disposables: - -1. **Upstream disposable** - This is a disposable that the observable puts into the observer. The upstream disposable keeps some state or callback that should be disposed of when the observer is disposed. This ensures that any resources used by the observable are properly cleaned up when the observer obtains on_error/on_completed or disposed in any other way. - -2. **External disposable** - This is a disposable that allows the observer to be disposed of from outside the observer itself. This can be useful in situations where you need to cancel an ongoing operation or release resources before the observable has completed its work. ### Exception Guarantee @@ -252,38 +247,6 @@ All disposable in RPP should be created and used via `rpp::disposable_wrapper_im - `disposable_wrapper` - wrapper over `interface_disposable` - `composite_disposable_wrapper` - wrapper over `interface_composite_disposable` -`disposable_wrapper` is kind of smart_pointer (like std::unique_ptr) but for disposables. So, default constructed wrapper is empty wrapper. -```cpp -auto d = rpp::disposable_wrapper{}; -``` -Comparing to unique_ptr wrapper's methods are safe to use for empty wrapper. -To construct wrapper you have to use `make` method: -```cpp -auto d = rpp::disposable_wrapper::make(some_arguments, to_construct_it); -``` - -Wrapper has popluar methods to work with disposable: `dispose()`, `is_disposed()` and `add()`/`remove()`/`clear()` (for `interface_composite_disposable`). - -In case of you want to obtain original disposable, you can use `lock()` method returning shared_ptr. - -`disposable_wrapper` can be strong and weak: -- strong (it is default behavior) is keeping disposable as shared_ptr, so, such an instance of wrapper is extending life-time is underlying disposable -- weak (disposable_wrapper can be forced to weak via `as_weak()` method) is keeping disposable as weak_ptr, so, such an instance of wrapper is **NOT** extendning life-time is underlying disposable - -This wrapper is needed for 2 goals: -- provide safe usage of disposables avoiding manual handling of empty/weak disposables -- automatically call `dispose()` during destruction of any disposable - -To achieve desired performance RPP is avoiding to returning disposable by default. So, it is why `subscribe` method is not returning anything by default. If you want to attach disposable to observer you can use overloading method accepting disposable as first argument like this: -```cpp -auto d = rpp::composite_disposable_wrapper::make(); -observable.subscribe(d, [](int v){}); -``` -or use `subscribe_with_disposable` method instead -```cpp -auto d = observable.subscribe_with_disposable([](int){}); -``` - ### 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. diff --git a/src/rpp/rpp/disposables.hpp b/src/rpp/rpp/disposables.hpp index d9cecc81a..8d423fbe1 100644 --- a/src/rpp/rpp/disposables.hpp +++ b/src/rpp/rpp/disposables.hpp @@ -12,8 +12,29 @@ /** * @defgroup disposables Disposables - * @brief Disposable owns some resource and provides ability to `dispose()` it: destroy/remove/disconnect and etc. - * @details In RPP it used as "inverted subscription": observable sets disposable to observer via `set_upstream(disposable)` with meaning "if you want to cancel me -> dispose this disposable" + * + * @brief Disposable is handle/resource passed from observable to observer via the `set_upstream` method. Observer disposes this disposable when it wants to unsubscribe from observable. + * + * @details In reactive programming, a **disposable** is an object that represents a resource that needs to be released or disposed of when it is no longer needed. + * This can include things like file handles, network connections, or any other resource that needs to be cleaned up after use. + * The purpose of a disposable is to provide a way to manage resources in a safe and efficient manner. + * By using disposables, you can ensure that resources are released in a timely manner, preventing memory leaks and other issues that can arise from resource leaks. + * + * There are 2 main purposes of disposables: + * 1. **Upstream disposable**
+ * This is a disposable that the observable puts into the observer. + * The upstream disposable keeps some state or callback that should be disposed of when the observer is disposed (== no longer wants to receive emissions, for example, was completed/errored or just unsubscribed) + * This ensures that any resources used by the observable are properly cleaned up when the observer obtains on_error/on_completed or disposed in any other way. + * + * 2. **External disposable**
+ * This is a disposable that allows the observer to be disposed of from outside the observer itself. + * This can be useful in situations where you need to cancel an ongoing operation or release resources before the observable has completed its work. + * To achieve this in rpp you can pass disposable to `subscribe` method or use `subscribe_with_disposable` overload instead. + * + * @note In rpp all disposables should be created via @link rpp::disposable_wrapper_impl @endlink instead of manually. + * + * @warning From user of rpp library it is not really expected to handle disposables manually somehow **except** of case where user want to control lifetime of observable-observer connection manually. + * * @ingroup rpp */ diff --git a/src/rpp/rpp/disposables/disposable_wrapper.hpp b/src/rpp/rpp/disposables/disposable_wrapper.hpp index 26477d79b..a9b7fb693 100644 --- a/src/rpp/rpp/disposables/disposable_wrapper.hpp +++ b/src/rpp/rpp/disposables/disposable_wrapper.hpp @@ -105,9 +105,35 @@ namespace rpp::details namespace rpp { /** - * @brief Wrapper to keep disposable. Any disposable have to be created right from this wrapper with help of `make` function. - * @details Member functions is safe to call even if internal disposable is gone. Also it provides access to "raw" shared_ptr and it can be nullptr in case of disposable empty/ptr gone. - * @details Can keep weak_ptr in case of not owning disposable + * @brief Main RPP wrapper over @link disposables @endlink. + * @details This wrapper invented to provide safe and easy-to-use access to disposables. It has next core points: + * - disposable_wrapper is kind of smart_pointer (like std::shared_ptr) but for disposables. So, default constructed wrapper is empty wrapper. + * - disposable_wrapper shares ownership like std::shared_ptr + * - any disposable created via disposable_wrapper would have call `dispose()` during it's destruction (during destruction of last disposable_wrapper owning it) + * - disposable_wrapper's methods is safe to use over empty/gone/disposed/weak disposables. + * - as soon as disposable can be actually "any internal state" it provides access to "raw" shared_ptr and it can be nullptr in case of disposable empty/ptr gone. + * - disposable_wrapper can be strong or weak (same as std::shared_ptr). weak disposable is important, for example, when it keeps observer and this observer should keep this disposable at the same time. + * - disposable_wrapper has popluar methods to work with disposable: `dispose()`, `is_disposed()` and `add()`/`remove()`/`clear()` (for `interface_composite_disposable`). + * + * To construct wrapper you have to use `make` method: + * @code{cpp} + * auto d = rpp::disposable_wrapper::make(some_arguments, to_construct_it); + * @endcode + * + * To achieve desired performance RPP is avoiding to returning disposable by default. So, it is why `subscribe` method is not returning anything by default. If you want to attach disposable to observer you can use overloading method accepting disposable as first argument like this: + * @code{cpp} + * auto d = rpp::composite_disposable_wrapper::make(); + * observable.subscribe(d, [](int v){}); + * @endcode + + * or use `subscribe_with_disposable` method instead + * @code{cpp} + * auto d = observable.subscribe_with_disposable([](int){}); + * @endcode + * + * @note rpp has 2 predefined disposable_wrappers for most popular cases: + * - @link rpp::disposable_wrapper @endlink is wrapper for simple @link rpp::interface_disposable @endlink + * - @link rpp::composite_disposable_wrapper @endlink is wrapper for @link rpp::composite_disposable @endlink * * @ingroup disposables */ @@ -126,7 +152,12 @@ namespace rpp bool operator==(const disposable_wrapper_impl&) const = default; /** - * @brief Way to create disposable_wrapper. Passed `TTarget` type can be any type derived from `TDisposable`. + * @brief Main way to create disposable_wrapper. Passed `TTarget` type can be any type derived from `TDisposable`. + * @par Example: + * + * \code{cpp} + * rpp::disposable_wrapper::make(); + * \endcode */ template TTarget = TDefaultMake, typename... TArgs> requires (std::constructible_from)