1-
21export module libfork.core:concepts_indirect;
32
43import std;
54
6- import libfork.utils;
7-
85import :concepts_invocable;
9-
10- /* *
11- * The purpose of this file is to define async versions of:
12- *
13- * `indirectly_unary_invocable`
14- *
15- * This requires `indirect-value-t` which is in turn requires an async version
16- * of `projected` which in turns requires an async version of
17- * `indirect_result_t`
18- *
19- */
6+ import :concepts_context;
207
218namespace lf {
229
23- // ========= Forward decl =========
10+ // A type can derive from this to opt-into indirect-value-t customization
11+ struct indirect_value_customization {};
2412
25- template <typename T >
13+ template <typename I >
2614struct indirect_value {
27- using type = std::iter_value_t <T > &;
15+ using type = std::iter_value_t <I > &;
2816};
2917
18+ // strip cv-ref qualifiers
3019template <typename T>
31- using indirect_value_t = indirect_value<T>::type;
20+ requires (!std::same_as<T, std::remove_cvref_t <T>>)
21+ struct indirect_value <T> : indirect_value<std::remove_cvref_t <T>> {};
22+
23+ // Specialization for types that customize
24+ template <std::derived_from<indirect_value_customization> T>
25+ requires std::same_as<T, std::remove_cvref_t <T>>
26+ struct indirect_value <T> {
27+ using type = T::indirect_value_type;
28+ };
29+
30+ template <typename I>
31+ using indirect_value_t = indirect_value<I>::type;
3232
3333// ========= Core concepts =========
3434
35+ /* *
36+ * @brief A version of `std::indirectly_unary_invocable` that supports
37+ * libfork's projection type.
38+ */
39+ export template <typename Fn, typename I>
40+ concept indirectly_sync_unary_invocable = //
41+ std::indirectly_readable<I> && //
42+ std::copy_constructible<Fn> && //
43+ std::invocable<Fn &, indirect_value_t <I>> && //
44+ std::invocable<Fn &, std::iter_reference_t <I>> && //
45+ std::common_reference_with< //
46+ std::invoke_result_t <Fn &, indirect_value_t <I>>, //
47+ std::invoke_result_t <Fn &, std::iter_reference_t <I>> //
48+ >; //
49+
50+ /* *
51+ * @brief A version of `std::indirectly_regular_unary_invocable` that supports
52+ * libfork's projection type.
53+ */
54+ export template <typename Fn, typename I>
55+ concept indirectly_sync_regular_unary_invocable = //
56+ std::indirectly_readable<I> && //
57+ std::copy_constructible<Fn> && //
58+ std::regular_invocable<Fn &, indirect_value_t <I>> && //
59+ std::regular_invocable<Fn &, std::iter_reference_t <I>> && //
60+ std::common_reference_with< //
61+ std::invoke_result_t <Fn &, indirect_value_t <I>>, //
62+ std::invoke_result_t <Fn &, std::iter_reference_t <I>> //
63+ >; //
64+
65+ /* *
66+ * @brief A variant of `std::indirectly_unary_invocable` that supports
67+ * libfork's projection type and requires an async invocable.
68+ */
3569export template <typename Fn, typename Context, typename I>
36- concept indirectly_unary_async_invocable = //
70+ concept indirectly_async_unary_invocable = //
3771 worker_context<Context> && //
3872 std::indirectly_readable<I> && //
3973 std::copy_constructible<Fn> && //
@@ -44,8 +78,12 @@ concept indirectly_unary_async_invocable = //
4478 async_result_t <Fn &, Context, std::iter_reference_t <I>> //
4579 >; //
4680
81+ /* *
82+ * @brief A variant of `std::indirectly_regular_unary_invocable` that supports
83+ * libfork's projection type and requires an async invocable.
84+ */
4785export template <typename Fn, typename Context, typename I>
48- concept indirectly_regular_unary_async_invocable =
86+ concept indirectly_async_regular_unary_invocable = //
4987 worker_context<Context> && //
5088 std::indirectly_readable<I> && //
5189 std::copy_constructible<Fn> && //
@@ -56,77 +94,30 @@ concept indirectly_regular_unary_async_invocable =
5694 async_result_t <Fn &, Context, std::iter_reference_t <I>> //
5795 >; //
5896
59- export template <typename Fn, typename Context, typename I>
60- concept indirectly_unary_invocable =
61- indirectly_unary_async_invocable<Fn, Context, I> || std::indirectly_unary_invocable<Fn, I>;
62-
63- export template <typename Fn, typename Context, typename I>
64- concept indirectly_regular_unary_invocable = indirectly_regular_unary_async_invocable<Fn, Context, I> ||
65- std::indirectly_regular_unary_invocable<Fn, I>;
66-
67- // ========= indirect_result =========
68-
6997/* *
70- * @brief A version of `std::invoke_result` that supports both regular invocables and async invocables.
98+ * @brief A variant of `std::indirectly_unary_invocable` that supports either
99+ * sync or async invocables.
71100 *
72- * This gives preference to async invocation.
101+ * In general if a function is both sync and async invocable it is expected
102+ * that the async version will be preferred.
73103 */
74- template <typename Proj, typename Context, typename ... Args>
75- struct invoke_result : std::invoke_result<Proj, Args...> {};
76-
77- // More constrained so should be selected if both regular and async invocations are possible.
78- template <typename Proj, typename Context, typename ... Args>
79- requires async_invocable<Proj, Context, Args...>
80- struct invoke_result <Proj, Context, Args...> {
81- using type = async_result_t <Proj, Context, Args...>;
82- };
83-
84- template <class F , typename Context, typename ... Args>
85- using invoke_result_t = invoke_result<F, Context, Args...>::type;
86-
87- template <class F , typename Context, std::indirectly_readable... Is>
88- using indirect_result_t = invoke_result<F, Context, std::iter_reference_t <Is>...>::type;
89-
90- // ========= Projected =========
91-
92- // C++26 ADL firewalled implementation.
93-
94- struct hidden_projected_base {};
95-
96- template <bool WeaklyIncrementable, typename I, typename Proj, typename Context>
97- struct projected_impl {
98-
99- static_assert (!WeaklyIncrementable, " Should hit specialization for weakly incrementable" );
100-
101- struct type : hidden_projected_base {
102-
103- // Used by indirect_value
104- using hidden_indirect_value = invoke_result<Proj &, Context, indirect_value_t <I>>;
105-
106- using value_type = std::remove_cvref_t <indirect_result_t <Proj &, Context, I>>;
107- auto operator *() const -> indirect_result_t <Proj &, Context, I>;
108- };
109- };
104+ export template <typename Fn, typename Context, typename I>
105+ concept indirectly_unary_invocable =
106+ indirectly_async_unary_invocable<Fn, Context, I> || indirectly_sync_unary_invocable<Fn, I>;
110107
111- template <std::weakly_incrementable I, typename Proj, typename Context>
112- struct projected_impl <true , I, Proj, Context> {
113- struct type : projected_impl<false , I, Proj, Context>::type {
114- using difference_type = std::iter_difference_t <I>;
115- };
116- };
108+ // clang-format off
117109
118110/* *
119- * @brief A version of `std::projected` that supports both regular invocables and async invocables.
111+ * @brief A variant of `std::indirectly_regular_unary_invocable` that supports
112+ * either sync or async invocables.
120113 *
121- * Note: this mandates regularly invocable projections.
114+ * In general if a function is both sync and async invocable it is expected
115+ * that the async version will be preferred.
122116 */
123- export template <std::indirectly_readable I, typename Proj, worker_context Context>
124- requires indirectly_regular_unary_invocable<Proj, Context, I>
125- using projected = projected_impl<std::weakly_incrementable<I>, I, Proj, Context>::type;
126-
127- // Specialization of indirect_value
128- template <typename P>
129- requires std::derived_from<P, hidden_projected_base>
130- struct indirect_value <P> : P::hidden_indirect_value {};
117+ export template <typename Fn, typename Context, typename I>
118+ concept indirectly_regular_unary_invocable =
119+ indirectly_async_regular_unary_invocable<Fn, Context, I> || indirectly_sync_regular_unary_invocable<Fn, I>;
120+
121+ // clang-format on
131122
132123} // namespace lf
0 commit comments