@@ -101,26 +101,133 @@ dynamic_invoke(
101101
102102// ------------------------------------------------
103103
104+ template <class F , class Tuple , std::size_t ... I>
105+ auto
106+ apply_tuple_deref (
107+ F const & f,
108+ route_params& p,
109+ Tuple& t,
110+ std::index_sequence<I...>)
111+ {
112+ return f (p, *std::get<I>(t)...);
113+ }
114+
115+ // ------------------------------------------------
116+
117+ /* * Wraps a callable whose first parameter is `route_params&`
118+ and whose remaining parameters are resolved from
119+ @ref route_params::route_data at dispatch time.
120+
121+ Produced by @ref dynamic_transform. Stored inside
122+ the router's handler table.
123+ */
104124template <class F >
105125struct dynamic_handler
106126{
107127 F f;
108128
129+ // No extra parameters -- just forward to the callable
130+ template <class First >
131+ route_task
132+ invoke_impl (
133+ route_params& p,
134+ type_list<First> const &) const
135+ {
136+ static_assert (
137+ std::is_convertible_v<route_params&, First>,
138+ " first parameter must accept route_params&" );
139+ return f (p);
140+ }
141+
142+ static route_task
143+ make_route_next ()
144+ {
145+ co_return route_next;
146+ }
147+
148+ static route_task
149+ wrap_result (route_result r)
150+ {
151+ co_return r;
152+ }
153+
154+ // Extra parameters resolved from route_data
155+ template <class First , class E1 , class ... Extra>
156+ route_task
157+ invoke_impl (
158+ route_params& p,
159+ type_list<First, E1 , Extra...> const &) const
160+ {
161+ static_assert (
162+ std::is_convertible_v<route_params&, First>,
163+ " first parameter must accept route_params&" );
164+ static_assert (
165+ are_unique<
166+ find_key_t <E1 >,
167+ find_key_t <Extra>...>::value,
168+ " callable has duplicate parameter types" );
169+
170+ auto ptrs = std::make_tuple (
171+ p.route_data .template find <find_key_t <E1 >>(),
172+ p.route_data .template find <find_key_t <Extra>>()...);
173+
174+ bool all_found = std::apply (
175+ [](auto *... pp)
176+ {
177+ return (... && (pp != nullptr ));
178+ }, ptrs);
179+
180+ if (! all_found)
181+ return make_route_next ();
182+
183+ using R = std::invoke_result_t <
184+ F const &, route_params&,
185+ find_key_t <E1 >&,
186+ find_key_t <Extra>&...>;
187+ if constexpr (std::is_same_v<R, route_task>)
188+ return apply_tuple_deref (
189+ f, p, ptrs, std::index_sequence_for<E1 , Extra...>{});
190+ else
191+ return wrap_result (apply_tuple_deref (
192+ f, p, ptrs, std::index_sequence_for<E1 , Extra...>{}));
193+ }
194+
109195 route_task
110196 operator ()(route_params& p) const
111197 {
112- co_return dynamic_invoke (
113- p.route_data , f);
198+ return invoke_impl (p,
199+ typename call_traits<
200+ std::decay_t <F>>::arg_types{});
114201 }
115202};
116203
117- /* * A handler transform that resolves parameters from route_data.
204+ /* * A handler transform that resolves extra parameters from route_data.
118205
119206 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.
207+ declare a first parameter of type `route_params&` followed
208+ by additional parameters of arbitrary types. At dispatch time,
209+ each extra parameter type is looked up in
210+ @ref route_params::route_data via @ref polystore::find.
211+ If all extra parameters are found the handler is invoked;
212+ otherwise @ref route_next is returned.
213+
214+ Duplicate extra parameter types (after stripping cv-ref)
215+ produce a compile-time error.
216+
217+ @par Example
218+ @code
219+ router<route_params> base;
220+ auto r = base.with_transform( dynamic_transform{} );
221+
222+ r.get( "/users", [](
223+ route_params& p,
224+ UserService& svc,
225+ Config const& cfg) -> route_result
226+ {
227+ // svc and cfg resolved from p.route_data
228+ return route_done;
229+ });
230+ @endcode
124231*/
125232struct dynamic_transform
126233{
0 commit comments