diff --git a/src/rpp/rpp/disposables/fwd.hpp b/src/rpp/rpp/disposables/fwd.hpp index 6cfb7344a..eb5bcb7e6 100644 --- a/src/rpp/rpp/disposables/fwd.hpp +++ b/src/rpp/rpp/disposables/fwd.hpp @@ -11,6 +11,7 @@ #pragma once #include +#include namespace rpp::details { diff --git a/src/rpp/rpp/operators/combine_latest.hpp b/src/rpp/rpp/operators/combine_latest.hpp index 2beeffc94..5c72dd3d9 100644 --- a/src/rpp/rpp/operators/combine_latest.hpp +++ b/src/rpp/rpp/operators/combine_latest.hpp @@ -99,7 +99,7 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/combinelatest.html */ template - requires (!rpp::constraint::observable && (!utils::is_not_template_callable || std::invocable, utils::extract_observable_type_t...>)) + requires (!rpp::constraint::observable && (constraint::template_callable_or_invocable, utils::extract_observable_type_t...>)) auto combine_latest(TSelector&& selector, TObservable&& observable, TObservables&&... observables) { return details::combine_latest_t, std::decay_t, std::decay_t...>{ diff --git a/src/rpp/rpp/operators/distinct_until_changed.hpp b/src/rpp/rpp/operators/distinct_until_changed.hpp index 91ecf0ccb..0f51c76cb 100644 --- a/src/rpp/rpp/operators/distinct_until_changed.hpp +++ b/src/rpp/rpp/operators/distinct_until_changed.hpp @@ -55,7 +55,7 @@ namespace rpp::operators::details template struct operator_traits { - static_assert(rpp::constraint::invocable_r_v, "EqualityFn is not invocable with T and T returning bool"); + static_assert(rpp::constraint::invocable_ret, "EqualityFn is not invocable with T and T returning bool"); using result_type = T; @@ -97,9 +97,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/distinct.html */ template - requires (!utils::is_not_template_callable || std::same_as>) auto distinct_until_changed(EqualityFn&& equality_fn) { + static_assert(constraint::template_callable_or_invocable_ret, "EqualityFn is not invocable with T and T returning bool"); return details::distinct_until_changed_t>{std::forward(equality_fn)}; } } // namespace rpp::operators diff --git a/src/rpp/rpp/operators/filter.hpp b/src/rpp/rpp/operators/filter.hpp index 32b842569..ba5ab3af1 100644 --- a/src/rpp/rpp/operators/filter.hpp +++ b/src/rpp/rpp/operators/filter.hpp @@ -91,9 +91,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/filter.html */ template - requires (!utils::is_not_template_callable || std::same_as>) auto filter(Fn&& predicate) { + static_assert(constraint::template_callable_or_invocable_ret, "Fn is not invocable with T returning bool"); return details::filter_t>{std::forward(predicate)}; } } // namespace rpp::operators diff --git a/src/rpp/rpp/operators/flat_map.hpp b/src/rpp/rpp/operators/flat_map.hpp index 8044cd752..0c975548d 100644 --- a/src/rpp/rpp/operators/flat_map.hpp +++ b/src/rpp/rpp/operators/flat_map.hpp @@ -64,9 +64,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/flatmap.html */ template - requires (!utils::is_not_template_callable || rpp::constraint::observable>) auto flat_map(Fn&& callable) { + static_assert(!constraint::is_not_template_callable || rpp::constraint::observable>, "Fn is not invocable with T returning observable"); return details::flat_map_t>{std::forward(callable)}; } diff --git a/src/rpp/rpp/operators/fwd.hpp b/src/rpp/rpp/operators/fwd.hpp index 131f04036..dd44255a3 100644 --- a/src/rpp/rpp/operators/fwd.hpp +++ b/src/rpp/rpp/operators/fwd.hpp @@ -27,7 +27,7 @@ namespace rpp::operators auto concat(); template - requires (!rpp::constraint::observable && (!utils::is_not_template_callable || std::invocable, utils::extract_observable_type_t...>)) + requires (!rpp::constraint::observable && (constraint::template_callable_or_invocable, utils::extract_observable_type_t...>)) auto combine_latest(TSelector&& selector, TObservable&& observable, TObservables&&... observables); template @@ -42,7 +42,6 @@ namespace rpp::operators auto distinct(); template - requires (!utils::is_not_template_callable || std::same_as>) auto distinct_until_changed(EqualityFn&& equality_fn = {}); auto element_at(size_t index); @@ -50,27 +49,20 @@ namespace rpp::operators auto first(); template - requires (!utils::is_not_template_callable || std::same_as>) auto filter(Fn&& predicate); template LastFn> auto finally(LastFn&& lastFn); template - requires (!utils::is_not_template_callable || rpp::constraint::observable>) auto flat_map(Fn&& callable); - template - requires ( - (!utils::is_not_template_callable || !std::same_as>) && (!utils::is_not_template_callable || !std::same_as>) && (!utils::is_not_template_callable || std::strict_weak_order)) + template auto group_by(KeySelector&& key_selector, ValueSelector&& value_selector = {}, KeyComparator&& comparator = {}); auto last(); template - requires (!utils::is_not_template_callable || !std::same_as>) auto map(Fn&& callable); template @@ -79,13 +71,9 @@ namespace rpp::operators template typename Subject = rpp::subjects::publish_subject> auto multicast(); - template - requires (!utils::is_not_template_callable || rpp::constraint::observable>) - auto flat_map(Fn&& callable); - template - requires constraint::observables_of_same_type, std::decay_t...> auto merge_with(TObservable&& observable, TObservables&&... observables); + auto merge(); template @@ -94,7 +82,6 @@ namespace rpp::operators auto publish(); template - requires (!utils::is_not_template_callable || std::same_as, std::invoke_result_t &&, rpp::utils::convertible_to_any>>) auto reduce(Seed&& seed, Accumulator&& accumulator); template @@ -110,9 +97,8 @@ namespace rpp::operators auto retry(); - template - requires (!utils::is_not_template_callable || std::same_as, std::invoke_result_t &&, rpp::utils::convertible_to_any>>) - auto scan(InitialValue&& initial_value, Fn&& accumulator); + template + auto scan(Seed&& seed, Fn&& accumulator); template auto scan(Fn&& accumulator); @@ -149,14 +135,13 @@ namespace rpp::operators auto take_last(size_t count); template - requires (!utils::is_not_template_callable || std::same_as>) auto take_while(Fn&& predicate); template auto take_until(TObservable&& until_observable); template OnError = rpp::utils::empty_function_t> - requires utils::is_not_template_callable + requires (constraint::is_not_template_callable) auto tap(OnError&& on_error); template OnCompleted = rpp::utils::empty_function_t<>> @@ -170,7 +155,7 @@ namespace rpp::operators template OnError = rpp::utils::empty_function_t, std::invocable<> OnCompleted = rpp::utils::empty_function_t<>> - requires utils::is_not_template_callable + requires (constraint::is_not_template_callable) auto tap(OnNext&& on_next = {}, OnError&& on_error = {}, OnCompleted&& on_completed = {}); @@ -185,15 +170,13 @@ namespace rpp::operators auto throttle(rpp::schedulers::duration period); template - requires rpp::constraint::observable> auto on_error_resume_next(Selector&& selector); template - requires rpp::constraint::observable> auto retry_when(Notifier&& notifier); template - requires (!rpp::constraint::observable && (!utils::is_not_template_callable || std::invocable, utils::extract_observable_type_t...>)) + requires (!rpp::constraint::observable && (constraint::template_callable_or_invocable, utils::extract_observable_type_t...>)) auto with_latest_from(TSelector&& selector, TObservable&& observable, TObservables&&... observables); template @@ -206,7 +189,7 @@ namespace rpp::operators auto window_toggle(TOpeningsObservable&& openings, TClosingsSelectorFn&& closings_selector); template - requires (!rpp::constraint::observable && (!utils::is_not_template_callable || std::invocable, utils::extract_observable_type_t...>)) + requires (!rpp::constraint::observable && (constraint::template_callable_or_invocable, utils::extract_observable_type_t...>)) auto zip(TSelector&& selector, TObservable&& observable, TObservables&&... observables); template diff --git a/src/rpp/rpp/operators/group_by.hpp b/src/rpp/rpp/operators/group_by.hpp index 4e1d8d1fc..bd9796679 100644 --- a/src/rpp/rpp/operators/group_by.hpp +++ b/src/rpp/rpp/operators/group_by.hpp @@ -215,10 +215,12 @@ namespace rpp::operators template - requires ( - (!utils::is_not_template_callable || !std::same_as>) && (!utils::is_not_template_callable || !std::same_as>) && (!utils::is_not_template_callable || std::strict_weak_order)) auto group_by(KeySelector&& key_selector, ValueSelector&& value_selector, KeyComparator&& comparator) { + static_assert(constraint::template_callable_or_invocable_ret_non_void, "KeySelector is not invocable with T returning non-void"); + static_assert(constraint::template_callable_or_invocable_ret_non_void, "ValueSelector is not invocable with T returning non-void"); + static_assert(!constraint::is_not_template_callable || std::strict_weak_order, "KeyComparator is not invocable with (result of KeySelector, result of KeySelector) returning bool"); + return details::group_by_t, std::decay_t, std::decay_t>{ std::forward(key_selector), std::forward(value_selector), diff --git a/src/rpp/rpp/operators/map.hpp b/src/rpp/rpp/operators/map.hpp index 74e3fad4d..0baea934a 100644 --- a/src/rpp/rpp/operators/map.hpp +++ b/src/rpp/rpp/operators/map.hpp @@ -94,9 +94,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/map.html */ template - requires (!utils::is_not_template_callable || !std::same_as>) auto map(Fn&& callable) { + static_assert(constraint::template_callable_or_invocable_ret_non_void, "Fn is not invocable with T returning non-void"); return details::map_t>{std::forward(callable)}; } } // namespace rpp::operators diff --git a/src/rpp/rpp/operators/merge.hpp b/src/rpp/rpp/operators/merge.hpp index 791a65f84..fccbcbf61 100644 --- a/src/rpp/rpp/operators/merge.hpp +++ b/src/rpp/rpp/operators/merge.hpp @@ -253,9 +253,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/merge.html */ template - requires constraint::observables_of_same_type, std::decay_t...> auto merge_with(TObservable&& observable, TObservables&&... observables) { + static_assert(constraint::observables_of_same_type, std::decay_t...>, "observables should be of same type"); return details::merge_with_t, std::decay_t...>{ rpp::utils::tuple{std::forward(observable), std::forward(observables)...}}; } diff --git a/src/rpp/rpp/operators/on_error_resume_next.hpp b/src/rpp/rpp/operators/on_error_resume_next.hpp index 23d1c820b..5a91136a5 100644 --- a/src/rpp/rpp/operators/on_error_resume_next.hpp +++ b/src/rpp/rpp/operators/on_error_resume_next.hpp @@ -136,9 +136,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/catch.html */ template - requires rpp::constraint::observable> auto on_error_resume_next(Selector&& selector) { + static_assert(rpp::constraint::observable>, "Selector is not invocable with std::exception_ptr returning new observable"); return details::on_error_resume_next_t>{std::forward(selector)}; } } // namespace rpp::operators diff --git a/src/rpp/rpp/operators/reduce.hpp b/src/rpp/rpp/operators/reduce.hpp index fd15960ba..18b0a80a8 100644 --- a/src/rpp/rpp/operators/reduce.hpp +++ b/src/rpp/rpp/operators/reduce.hpp @@ -131,7 +131,7 @@ namespace rpp::operators operator "reduce: s=10, (s,x)=>s+x" : +------16| } * - * @param initial_value initial value for seed + * @param seed initial value for seed * @param accumulator function which accepts seed value and new value from observable and return new value of seed. Can accept seed by move-reference. * * @warning #include @@ -143,9 +143,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/reduce.html */ template - requires (!utils::is_not_template_callable || std::same_as, std::invoke_result_t &&, rpp::utils::convertible_to_any>>) auto reduce(Seed&& seed, Accumulator&& accumulator) { + static_assert(constraint::template_callable_or_invocable_ret, std::invoke_result_t&&, rpp::utils::convertible_to_any>>, "Accumulator is not invocable with Seed&& and T returning Seed"); return details::reduce_t, std::decay_t>{std::forward(seed), std::forward(accumulator)}; } @@ -160,7 +160,6 @@ namespace rpp::operators * * @details There is no initial value for seed, so, first value would be used as seed value and forwarded as is. * - * @param initial_value initial value for seed * @param accumulator function which accepts seed value and new value from observable and return new value of seed. Can accept seed by move-reference. * * @warning #include diff --git a/src/rpp/rpp/operators/retry_when.hpp b/src/rpp/rpp/operators/retry_when.hpp index d5ad56a69..b67dd5438 100644 --- a/src/rpp/rpp/operators/retry_when.hpp +++ b/src/rpp/rpp/operators/retry_when.hpp @@ -190,9 +190,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/retry.html */ template - requires rpp::constraint::observable> auto retry_when(TNotifier&& notifier) { + static_assert(rpp::constraint::observable>, "Notifier is not invocable with std::exception_ptr returning new observable"); return details::retry_when_t>{std::forward(notifier)}; } } // namespace rpp::operators diff --git a/src/rpp/rpp/operators/scan.hpp b/src/rpp/rpp/operators/scan.hpp index 66064e127..3b6c9cf6a 100644 --- a/src/rpp/rpp/operators/scan.hpp +++ b/src/rpp/rpp/operators/scan.hpp @@ -138,7 +138,7 @@ namespace rpp::operators * - No any heap allocations at all * - Keep actual seed/cache inside observable and updating it every emission * - * @param initial_value initial value for seed which would be sent during subscription and applied for first value from observable. Then it will be replaced with result and etc. + * @param seed initial value for seed which would be sent during subscription and applied for first value from observable. Then it will be replaced with result and etc. * @param accumulator function which accepts seed value and new value from observable and return new value of seed. Can accept seed by move-reference. * * @warning #include @@ -150,11 +150,11 @@ namespace rpp::operators * @ingroup transforming_operators * @see https://reactivex.io/documentation/operators/scan.html */ - template - requires (!utils::is_not_template_callable || std::same_as, std::invoke_result_t &&, rpp::utils::convertible_to_any>>) - auto scan(InitialValue&& initial_value, Fn&& accumulator) + template + auto scan(Seed&& seed, Fn&& accumulator) { - return details::scan_t, std::decay_t>{std::forward(initial_value), std::forward(accumulator)}; + static_assert(constraint::template_callable_or_invocable_ret, std::invoke_result_t&&, rpp::utils::convertible_to_any>>, "Accumulator is not invocable with Seed and T returning seed"); + return details::scan_t, std::decay_t>{std::forward(seed), std::forward(accumulator)}; } /** diff --git a/src/rpp/rpp/operators/subscribe.hpp b/src/rpp/rpp/operators/subscribe.hpp index 9f9c75813..ec87fa8d1 100644 --- a/src/rpp/rpp/operators/subscribe.hpp +++ b/src/rpp/rpp/operators/subscribe.hpp @@ -209,7 +209,7 @@ namespace rpp::operators::details subscribe_t(const Args&...) -> subscribe_t; template - concept on_next_like = (!rpp::utils::is_not_template_callable || std::invocable) && (!rpp::constraint::decayed_same_as && !rpp::constraint::observer_strategy_base && !rpp::constraint::observer); + concept on_next_like = (rpp::constraint::template_callable_or_invocable) && (!rpp::constraint::decayed_same_as && !rpp::constraint::observer_strategy_base && !rpp::constraint::observer); } // namespace rpp::operators::details namespace rpp::operators diff --git a/src/rpp/rpp/operators/take_while.hpp b/src/rpp/rpp/operators/take_while.hpp index 9d6d8d8d0..d861c5eaf 100644 --- a/src/rpp/rpp/operators/take_while.hpp +++ b/src/rpp/rpp/operators/take_while.hpp @@ -91,9 +91,9 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/takewhile.html */ template - requires (!utils::is_not_template_callable || std::same_as>) auto take_while(Fn&& predicate) { + static_assert(constraint::template_callable_or_invocable_ret, "Fn is not invocable with T returning bool"); return details::take_while_t>{std::forward(predicate)}; } } // namespace rpp::operators diff --git a/src/rpp/rpp/operators/tap.hpp b/src/rpp/rpp/operators/tap.hpp index be29f9117..ce6ea617a 100644 --- a/src/rpp/rpp/operators/tap.hpp +++ b/src/rpp/rpp/operators/tap.hpp @@ -66,7 +66,7 @@ namespace rpp::operators::details template struct operator_traits { - static_assert(rpp::constraint::invocable_r_v, "OnNext is not invocable with T"); + static_assert(rpp::constraint::invocable_ret, "OnNext is not invocable with T"); using result_type = T; @@ -90,7 +90,7 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/do.html */ template OnError /* = rpp::utils::empty_function_t */> - requires utils::is_not_template_callable + requires (constraint::is_not_template_callable) auto tap(OnError&& on_error) { using OnNext = rpp::utils::empty_function_any_t; @@ -157,7 +157,7 @@ namespace rpp::operators template OnError /* = rpp::utils::empty_function_t */, std::invocable<> OnCompleted /* = rpp::utils::empty_function_t<> */> - requires utils::is_not_template_callable + requires (constraint::is_not_template_callable) auto tap(OnNext&& on_next /* = {} */, OnError&& on_error /* = {} */, OnCompleted&& on_completed /* = {} */) diff --git a/src/rpp/rpp/operators/with_latest_from.hpp b/src/rpp/rpp/operators/with_latest_from.hpp index 1e0eff555..ead3c02aa 100644 --- a/src/rpp/rpp/operators/with_latest_from.hpp +++ b/src/rpp/rpp/operators/with_latest_from.hpp @@ -198,7 +198,7 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/combinelatest.html */ template - requires (!rpp::constraint::observable && (!utils::is_not_template_callable || std::invocable, utils::extract_observable_type_t...>)) + requires (!rpp::constraint::observable && (constraint::template_callable_or_invocable, utils::extract_observable_type_t...>)) auto with_latest_from(TSelector&& selector, TObservable&& observable, TObservables&&... observables) { return details::with_latest_from_t, std::decay_t, std::decay_t...>{ diff --git a/src/rpp/rpp/operators/zip.hpp b/src/rpp/rpp/operators/zip.hpp index f785dc7ad..2d06bcdbb 100644 --- a/src/rpp/rpp/operators/zip.hpp +++ b/src/rpp/rpp/operators/zip.hpp @@ -99,7 +99,7 @@ namespace rpp::operators * @see https://reactivex.io/documentation/operators/zip.html */ template - requires (!rpp::constraint::observable && (!utils::is_not_template_callable || std::invocable, utils::extract_observable_type_t...>)) + requires (!rpp::constraint::observable && (constraint::template_callable_or_invocable, utils::extract_observable_type_t...>)) auto zip(TSelector&& selector, TObservable&& observable, TObservables&&... observables) { return details::zip_t, std::decay_t, std::decay_t...>{ diff --git a/src/rpp/rpp/sources/fwd.hpp b/src/rpp/rpp/sources/fwd.hpp index feb77ae56..5837448fb 100644 --- a/src/rpp/rpp/sources/fwd.hpp +++ b/src/rpp/rpp/sources/fwd.hpp @@ -36,7 +36,7 @@ namespace rpp::source template OnSubscribe> auto create(OnSubscribe&& on_subscribe); - template>> + template>> auto create(OnSubscribe&& on_subscribe); template diff --git a/src/rpp/rpp/utils/constraints.hpp b/src/rpp/rpp/utils/constraints.hpp index 87e9d5792..0de24e899 100644 --- a/src/rpp/rpp/utils/constraints.hpp +++ b/src/rpp/rpp/utils/constraints.hpp @@ -46,12 +46,6 @@ namespace rpp::constraint } -> std::same_as; }; - template - concept invocable_r_v = std::invocable && std::same_as>; - - template - concept is_nothrow_invocable = std::is_nothrow_invocable_v; - template concept hashable = requires(T a) { { diff --git a/src/rpp/rpp/utils/function_traits.hpp b/src/rpp/rpp/utils/function_traits.hpp index 5d4a3205b..07d4eaa0b 100644 --- a/src/rpp/rpp/utils/function_traits.hpp +++ b/src/rpp/rpp/utils/function_traits.hpp @@ -41,11 +41,38 @@ namespace rpp::utils { }; +} // namespace rpp::utils + +namespace rpp::constraint +{ + template - concept is_not_template_callable = is_not_template_callable_t::value; + concept is_not_template_callable = utils::is_not_template_callable_t::value; + + template + concept invocable = std::invocable; + + template + concept invocable_ret = invocable && std::same_as>; + + template + concept template_callable_or_invocable = !is_not_template_callable || invocable; + + template + concept template_callable_or_invocable_ret_non_void = !is_not_template_callable || (invocable && !invocable_ret); + + template + concept template_callable_or_invocable_ret = !is_not_template_callable || invocable_ret; + + template + concept is_nothrow_invocable = std::is_nothrow_invocable_v; +} // namespace rpp::constraint + +namespace rpp::utils +{ // Lambda - template + template struct function_traits : function_traits { };