@@ -39,6 +39,28 @@ using find_key_t =
3939 std::remove_cv_t <
4040 std::remove_reference_t <T>>;
4141
42+ // Polystore lookup key: also strips pointer
43+ template <class T >
44+ using lookup_key_t =
45+ std::remove_cv_t <
46+ std::remove_pointer_t <
47+ find_key_t <T>>>;
48+
49+ // True when parameter is a pointer (optional dependency)
50+ template <class T >
51+ constexpr bool is_optional_v =
52+ std::is_pointer_v<find_key_t <T>>;
53+
54+ // Resolve a polystore pointer to the handler's expected arg
55+ template <class Arg , class T >
56+ auto resolve_arg (T* p)
57+ {
58+ if constexpr (is_optional_v<Arg>)
59+ return static_cast <find_key_t <Arg>>(p);
60+ else
61+ return static_cast <Arg>(*p);
62+ }
63+
4264// ------------------------------------------------
4365
4466template <class F , class ... Args>
@@ -49,43 +71,47 @@ dynamic_invoke_impl(
4971 type_list<Args...> const &)
5072{
5173 static_assert (
52- are_unique<find_key_t <Args>...>::value,
74+ are_unique<lookup_key_t <Args>...>::value,
5375 " callable has duplicate parameter types" );
5476
5577 auto ptrs = std::make_tuple (
56- ps.find <find_key_t <Args>>()...);
57-
58- bool all_found = std::apply (
59- [](auto *... p)
60- {
61- return (... && (p != nullptr ));
62- }, ptrs);
63-
64- if (! all_found)
65- return route_next;
78+ ps.find <lookup_key_t <Args>>()...);
6679
67- return std::apply (
68- [&](auto *... p) -> route_result
80+ return [&]<std::size_t ... I>(
81+ std::index_sequence<I...>) -> route_result
82+ {
83+ if constexpr (!(is_optional_v<Args> && ...))
6984 {
70- return f (*p...);
71- }, ptrs);
85+ if (! (... && (is_optional_v<Args> ||
86+ std::get<I>(ptrs) != nullptr )))
87+ return route_next;
88+ }
89+ return f (resolve_arg<Args>(
90+ std::get<I>(ptrs))...);
91+ }(std::index_sequence_for<Args...>{});
7292}
7393
7494/* * Invoke a callable, resolving arguments from a polystore.
7595
7696 Each parameter type of the callable is looked up in the
77- polystore via @ref polystore::find. If all parameters are
78- found, the callable is invoked with the resolved arguments
79- and its result is returned. If any parameter is not found,
80- @ref route_next is returned without invoking the callable.
97+ polystore via @ref polystore::find. If all required
98+ parameters are found, the callable is invoked with the
99+ resolved arguments and its result is returned. If any
100+ required parameter is not found, @ref route_next is
101+ returned without invoking the callable.
102+
103+ Parameters declared as pointer types (e.g. `A*`) are
104+ optional: `nullptr` is passed when the type is absent.
105+ Rvalue reference parameters (e.g. `A&&`) are supported
106+ and receive a moved reference to the stored object.
81107
82- Duplicate parameter types (after stripping cv-ref) produce
83- a compile-time error.
108+ Duplicate parameter types (after stripping cv-ref and
109+ pointer) produce a compile-time error.
84110
85111 @param ps The polystore to resolve arguments from.
86112 @param f The callable to invoke.
87113 @return The result of the callable, or @ref route_next
88- if any parameter was not found.
114+ if any required parameter was not found.
89115*/
90116template <class F >
91117route_result
@@ -101,26 +127,139 @@ dynamic_invoke(
101127
102128// ------------------------------------------------
103129
130+ /* * Wraps a callable whose first parameter is `route_params&`
131+ and whose remaining parameters are resolved from
132+ @ref route_params::route_data at dispatch time.
133+
134+ Produced by @ref dynamic_transform. Stored inside
135+ the router's handler table.
136+ */
104137template <class F >
105138struct dynamic_handler
106139{
107140 F f;
108141
142+ // No extra parameters -- just forward to the callable
143+ template <class First >
144+ route_task
145+ invoke_impl (
146+ route_params& p,
147+ type_list<First> const &) const
148+ {
149+ static_assert (
150+ std::is_convertible_v<route_params&, First>,
151+ " first parameter must accept route_params&" );
152+ using R = std::invoke_result_t <
153+ F const &, route_params&>;
154+ if constexpr (std::is_same_v<R, route_task>)
155+ return f (p);
156+ else
157+ return wrap_result (f (p));
158+ }
159+
160+ static route_task
161+ make_route_next ()
162+ {
163+ co_return route_next;
164+ }
165+
166+ static route_task
167+ wrap_result (route_result r)
168+ {
169+ co_return r;
170+ }
171+
172+ // Extra parameters resolved from route_data
173+ template <class First , class E1 , class ... Extra>
174+ route_task
175+ invoke_impl (
176+ route_params& p,
177+ type_list<First, E1 , Extra...> const &) const
178+ {
179+ static_assert (
180+ std::is_convertible_v<route_params&, First>,
181+ " first parameter must accept route_params&" );
182+ return invoke_extras (p, type_list<E1 , Extra...>{});
183+ }
184+
185+ template <class ... Extras>
186+ route_task
187+ invoke_extras (
188+ route_params& p,
189+ type_list<Extras...> const &) const
190+ {
191+ static_assert (
192+ are_unique<
193+ lookup_key_t <Extras>...>::value,
194+ " callable has duplicate parameter types" );
195+
196+ auto ptrs = std::make_tuple (
197+ p.route_data .template find <
198+ lookup_key_t <Extras>>()...);
199+
200+ return [this , &p, &ptrs]<std::size_t ... I>(
201+ std::index_sequence<I...>) -> route_task
202+ {
203+ if constexpr (!(is_optional_v<Extras> && ...))
204+ {
205+ if (! (... && (is_optional_v<Extras> ||
206+ std::get<I>(ptrs) != nullptr )))
207+ return make_route_next ();
208+ }
209+
210+ using R = std::invoke_result_t <
211+ F const &, route_params&, Extras...>;
212+ if constexpr (std::is_same_v<R, route_task>)
213+ return f (p, resolve_arg<Extras>(
214+ std::get<I>(ptrs))...);
215+ else
216+ return wrap_result (f (p,
217+ resolve_arg<Extras>(
218+ std::get<I>(ptrs))...));
219+ }(std::index_sequence_for<Extras...>{});
220+ }
221+
109222 route_task
110223 operator ()(route_params& p) const
111224 {
112- co_return dynamic_invoke (
113- p.route_data , f);
225+ return invoke_impl (p,
226+ typename call_traits<
227+ std::decay_t <F>>::arg_types{});
114228 }
115229};
116230
117- /* * A handler transform that resolves parameters from route_data.
231+ /* * A handler transform that resolves extra parameters from route_data.
118232
119233 When used with @ref router::with_transform, handlers may
120- declare parameters of arbitrary types. At dispatch time,
121- each parameter type is looked up in @ref route_params::route_data.
122- If all parameters are found the handler is invoked; otherwise
123- @ref route_next is returned.
234+ declare a first parameter of type `route_params&` followed
235+ by additional parameters of arbitrary types. At dispatch time,
236+ each extra parameter type is looked up in
237+ @ref route_params::route_data via @ref polystore::find.
238+ If all required parameters are found the handler is invoked;
239+ otherwise @ref route_next is returned.
240+
241+ Parameters declared as pointer types (e.g. `A*`) are
242+ optional: `nullptr` is passed when the type is absent.
243+ Rvalue reference parameters (e.g. `A&&`) are supported
244+ and receive a moved reference to the stored object.
245+
246+ Duplicate extra parameter types (after stripping cv-ref
247+ and pointer) produce a compile-time error.
248+
249+ @par Example
250+ @code
251+ router<route_params> base;
252+ auto r = base.with_transform( dynamic_transform{} );
253+
254+ r.get( "/users", [](
255+ route_params& p,
256+ UserService& svc,
257+ Config const& cfg) -> route_result
258+ {
259+ // svc and cfg resolved from p.route_data
260+ return route_done;
261+ });
262+ @endcode
124263*/
125264struct dynamic_transform
126265{
0 commit comments