| 11 | // ![simple_custom_map] |
| 12 | template<typename Fn> |
| 13 | struct simple_map |
| 14 | { |
| 15 | simple_map(const Fn& fn) |
| 16 | : fn(fn) |
| 17 | { |
| 18 | } |
| 19 | |
| 20 | Fn fn{}; |
| 21 | |
| 22 | // 1: define traits for the operator with upstream (previous type) type |
| 23 | template<rpp::constraint::decayed_type T> |
| 24 | struct operator_traits |
| 25 | { |
| 26 | // 1.1: it could have static asserts to be sure T is applicable for this operator |
| 27 | static_assert(std::invocable<Fn, T>, "Fn is not invocable with T"); |
| 28 | |
| 29 | // 1.2: it should have `result_type` is type of new observable after applying this operator |
| 30 | using result_type = std::invoke_result_t<Fn, T>; |
| 31 | }; |
| 32 | |
| 33 | // 2: define updated optimal disposables strategy. Set to `rpp::details::observables::default_disposables_strategy` if you don't know what is that. |
| 34 | template<rpp::details::observables::constraint::disposables_strategy Prev> |
| 35 | using updated_optimal_disposables_strategy = Prev; |
| 36 | |
| 37 | |
| 38 | // 3: implement core logic of operator: accept downstream observer (of result_type) and convert it to upstream observer (of T). |
| 39 | template<typename Upstream, rpp::constraint::observer Observer> |
| 40 | auto lift(Observer&& observer) const |
| 41 | { |
| 42 | const auto dynamic_observer = std::forward<Observer>(observer).as_dynamic(); |
| 43 | return rpp::make_lambda_observer<Upstream>([dynamic_observer, fn = fn](const auto& v) { dynamic_observer.on_next(fn(v)); }, |
| 44 | [dynamic_observer](const std::exception_ptr& err) { dynamic_observer.on_error(err); }, |
| 45 | [dynamic_observer]() { dynamic_observer.on_completed(); }); |
| 46 | } |
| 47 | }; |
| 48 | |
| 49 | template<typename Fn> |
| 50 | simple_map(const Fn& fn) -> simple_map<Fn>; |