Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 53 additions & 57 deletions src/rpp/rpp/observers/dynamic_observer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,35 +55,64 @@ std::shared_ptr<dynamic_observer_state_base<T>> make_dynamic_observer_state(Args
{
return std::make_shared<dynamic_observer_state<T, std::decay_t<TObserver>>>(std::forward<Args>(args)...);
}
} // namespace rpp::details

template<constraint::decayed_type T, typename ...Args>
std::shared_ptr<dynamic_observer_state_base<T>> make_dynamic_observer_state_from_fns(Args&& ...args)
namespace rpp
{
return make_dynamic_observer_state<T, details::state_observer<T, std::decay_t<Args>...>>(std::forward<Args>(args)...);
}

template<constraint::decayed_type T, constraint::decayed_type ...States>
class dynamic_state_observer : public details::typed_observer_tag<T>
/**
* \brief Dynamic (type-erased) version of observer (comparing to specific_observer)
* \details It uses type-erasure mechanism to hide types of OnNext, OnError and OnCompleted callbacks. But it has higher cost in the terms of performance due to usage of heap.
* Use it only when you need to store observer as member variable or make copy of original subscriber. In other cases prefer using "auto" to avoid converting to dynamic_observer
* \tparam T is type of value handled by this observer
* \ingroup observers
*/
template<constraint::decayed_type T>
class dynamic_observer final : public details::typed_observer_tag<T>
{
public:
template<typename ...TStates>
requires (constraint::decayed_same_as<States, TStates> && ...)
dynamic_state_observer(std::invocable<T, States...> auto&& on_next,
std::invocable<std::exception_ptr, States...> auto&& on_error,
std::invocable<States...> auto&& on_completed,
TStates&& ... states)
: m_state{make_dynamic_observer_state_from_fns<T>(std::forward<decltype(on_next)>(on_next),
std::forward<decltype(on_error)>(on_error),
std::forward<decltype(on_completed)>(on_completed),
std::forward<TStates>(states)...)} {}
template<typename ...States,
std::invocable<T, std::decay_t<States>...> OnNext,
std::invocable<std::exception_ptr, std::decay_t<States>...> OnError,
std::invocable<std::decay_t<States>...> OnCompleted>
dynamic_observer(OnNext&& on_next,
OnError&& on_error,
OnCompleted&& on_completed,
States&& ... states)
: m_state{details::make_dynamic_observer_state<T,
details::state_observer<T,
std::decay_t<OnNext>,
std::decay_t<OnError>,
std::decay_t<OnCompleted>,
std::decay_t<States>...>>(
std::forward<OnNext>(on_next),
std::forward<OnError>(on_error),
std::forward<OnCompleted>(on_completed),
std::forward<States>(states)...)} {}

template<constraint::on_next_fn<T> OnNext = utils::empty_function_t<T>,
constraint::on_error_fn OnError = utils::rethrow_error_t,
constraint::on_completed_fn OnCompleted = utils::empty_function_t<>>
dynamic_observer(OnNext&& on_next = {}, OnError&& on_error = {}, OnCompleted&& on_completed = {})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ bugprone-forwarding-reference-overload ⚠️
constructor accepting a forwarding reference can hide the copy and move constructors

: m_state{details::make_dynamic_observer_state<T,
details::state_observer<T,
std::decay_t<OnNext>,
std::decay_t<OnError>,
std::decay_t<OnCompleted>>>(
std::forward<OnNext>(on_next),
std::forward<OnError>(on_error),
std::forward<OnCompleted>(on_completed))} {}

dynamic_state_observer(std::shared_ptr<details::dynamic_observer_state_base<T>> state)
: m_state{ std::move(state) } {}
dynamic_observer(constraint::on_next_fn<T> auto&& on_next, constraint::on_completed_fn auto&& on_completed)
: dynamic_observer<T>{std::forward<decltype(on_next)>(on_next),
utils::rethrow_error_t{},
std::forward<decltype(on_completed)>(on_completed)} {}

dynamic_observer(std::shared_ptr<details::dynamic_observer_state_base<T>> state)
: m_state{std::move(state)} {}

template<constraint::observer_of_type<T> TObserver>
requires (!std::is_same_v<std::decay_t<TObserver>, dynamic_state_observer<T, States...>>)
dynamic_state_observer(TObserver&& obs)
requires (!std::is_same_v<std::decay_t<TObserver>, dynamic_observer<T>>)
dynamic_observer(TObserver&& obs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ bugprone-forwarding-reference-overload ⚠️
constructor accepting a forwarding reference can hide the copy and move constructors

: m_state{details::make_dynamic_observer_state<T, std::decay_t<TObserver>>(std::forward<TObserver>(obs))} {}


Expand Down Expand Up @@ -126,46 +155,13 @@ class dynamic_state_observer : public details::typed_observer_tag<T>
m_state->on_completed();
}

private:
std::shared_ptr<dynamic_observer_state_base<T>> m_state{};
};
} // namespace rpp::details

namespace rpp
{
/**
* \brief Dynamic (type-erased) version of observer (comparing to specific_observer)
* \details It uses type-erasure mechanism to hide types of OnNext, OnError and OnCompleted callbacks. But it has higher cost in the terms of performance due to usage of heap.
* Use it only when you need to store observer as member variable or make copy of original subscriber. In other cases prefer using "auto" to avoid converting to dynamic_observer
* \tparam T is type of value handled by this observer
* \ingroup observers
*/
template<constraint::decayed_type T>
class dynamic_observer final : public details::dynamic_state_observer<T>
{
public:
template<constraint::on_next_fn<T> OnNext = utils::empty_function_t<T>,
constraint::on_error_fn OnError = utils::rethrow_error_t,
constraint::on_completed_fn OnCompleted = utils::empty_function_t<>>
dynamic_observer(OnNext&& on_next = {}, OnError&& on_error = {}, OnCompleted&& on_completed = {})
: details::dynamic_state_observer<T>{std::forward<OnNext>(on_next),
std::forward<OnError>(on_error),
std::forward<OnCompleted>(on_completed)} {}

dynamic_observer(constraint::on_next_fn<T> auto&& on_next, constraint::on_completed_fn auto&& on_completed)
: details::dynamic_state_observer<T>{std::forward<decltype(on_next)>(on_next),
utils::rethrow_error_t{},
std::forward<decltype(on_completed)>(on_completed)} {}

template<constraint::observer_of_type<T> TObserver>
requires (!std::is_same_v<std::decay_t<TObserver>, dynamic_observer<T>>)
dynamic_observer(TObserver&& obs)
: details::dynamic_state_observer<T>{std::forward<TObserver>(obs)} {}

/**
* \brief Do nothing for rpp::dynamic_observer. Created only for unification of interfaces with rpp::specific_observer
*/
const dynamic_observer<T>& as_dynamic() const { return *this; }

private:
std::shared_ptr<details::dynamic_observer_state_base<T>> m_state{};
};

template<constraint::observer TObserver>
Expand Down
4 changes: 3 additions & 1 deletion src/rpp/rpp/observers/specific_observer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class specific_observer : public details::state_observer<T, OnNext, OnError, OnC
{
using base = details::state_observer<T, OnNext, OnError, OnCompleted>;

using base::base;
public:
template<constraint::on_next_fn<T> TOnNext = utils::empty_function_t<T>,
constraint::on_error_fn TOnError = utils::rethrow_error_t,
Expand All @@ -50,6 +49,9 @@ class specific_observer : public details::state_observer<T, OnNext, OnError, OnC
utils::rethrow_error_t{},
std::forward<decltype(on_completed)>(on_completed)} {}

specific_observer(const specific_observer&) = default;
specific_observer(specific_observer&&) noexcept = default;

/**
* \brief Converting current rpp::specific_observer to rpp::dynamic_observer alternative with erasing of type (and using heap)
* \return converted rpp::dynamic_observer
Expand Down
Loading