@@ -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,44 @@ 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
69- {
70- return f (*p...);
71- }, ptrs);
80+ return [&]<std::size_t ... I>(
81+ std::index_sequence<I...>) -> route_result
82+ {
83+ if (! (... && (is_optional_v<Args> ||
84+ std::get<I>(ptrs) != nullptr )))
85+ return route_next;
86+ return f (resolve_arg<Args>(
87+ std::get<I>(ptrs))...);
88+ }(std::index_sequence_for<Args...>{});
7289}
7390
7491/* * Invoke a callable, resolving arguments from a polystore.
7592
7693 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.
94+ polystore via @ref polystore::find. If all required
95+ parameters are found, the callable is invoked with the
96+ resolved arguments and its result is returned. If any
97+ required parameter is not found, @ref route_next is
98+ returned without invoking the callable.
99+
100+ Parameters declared as pointer types (e.g. `A*`) are
101+ optional: `nullptr` is passed when the type is absent.
102+ Rvalue reference parameters (e.g. `A&&`) are supported
103+ and receive a moved reference to the stored object.
81104
82- Duplicate parameter types (after stripping cv-ref) produce
83- a compile-time error.
105+ Duplicate parameter types (after stripping cv-ref and
106+ pointer) produce a compile-time error.
84107
85108 @param ps The polystore to resolve arguments from.
86109 @param f The callable to invoke.
87110 @return The result of the callable, or @ref route_next
88- if any parameter was not found.
111+ if any required parameter was not found.
89112*/
90113template <class F >
91114route_result
@@ -101,26 +124,136 @@ dynamic_invoke(
101124
102125// ------------------------------------------------
103126
127+ /* * Wraps a callable whose first parameter is `route_params&`
128+ and whose remaining parameters are resolved from
129+ @ref route_params::route_data at dispatch time.
130+
131+ Produced by @ref dynamic_transform. Stored inside
132+ the router's handler table.
133+ */
104134template <class F >
105135struct dynamic_handler
106136{
107137 F f;
108138
139+ // No extra parameters -- just forward to the callable
140+ template <class First >
141+ route_task
142+ invoke_impl (
143+ route_params& p,
144+ type_list<First> const &) const
145+ {
146+ static_assert (
147+ std::is_convertible_v<route_params&, First>,
148+ " first parameter must accept route_params&" );
149+ using R = std::invoke_result_t <
150+ F const &, route_params&>;
151+ if constexpr (std::is_same_v<R, route_task>)
152+ return f (p);
153+ else
154+ return wrap_result (f (p));
155+ }
156+
157+ static route_task
158+ make_route_next ()
159+ {
160+ co_return route_next;
161+ }
162+
163+ static route_task
164+ wrap_result (route_result r)
165+ {
166+ co_return r;
167+ }
168+
169+ // Extra parameters resolved from route_data
170+ template <class First , class E1 , class ... Extra>
171+ route_task
172+ invoke_impl (
173+ route_params& p,
174+ type_list<First, E1 , Extra...> const &) const
175+ {
176+ static_assert (
177+ std::is_convertible_v<route_params&, First>,
178+ " first parameter must accept route_params&" );
179+ return invoke_extras (p, type_list<E1 , Extra...>{});
180+ }
181+
182+ template <class ... Extras>
183+ route_task
184+ invoke_extras (
185+ route_params& p,
186+ type_list<Extras...> const &) const
187+ {
188+ static_assert (
189+ are_unique<
190+ lookup_key_t <Extras>...>::value,
191+ " callable has duplicate parameter types" );
192+
193+ auto ptrs = std::make_tuple (
194+ p.route_data .template find <
195+ lookup_key_t <Extras>>()...);
196+
197+ return [this , &p, &ptrs]<std::size_t ... I>(
198+ std::index_sequence<I...>) -> route_task
199+ {
200+ if (! (... && (is_optional_v<Extras> ||
201+ std::get<I>(ptrs) != nullptr )))
202+ return make_route_next ();
203+
204+ using R = std::invoke_result_t <
205+ F const &, route_params&, Extras...>;
206+ if constexpr (std::is_same_v<R, route_task>)
207+ return f (p, resolve_arg<Extras>(
208+ std::get<I>(ptrs))...);
209+ else
210+ return wrap_result (f (p,
211+ resolve_arg<Extras>(
212+ std::get<I>(ptrs))...));
213+ }(std::index_sequence_for<Extras...>{});
214+ }
215+
109216 route_task
110217 operator ()(route_params& p) const
111218 {
112- co_return dynamic_invoke (
113- p.route_data , f);
219+ return invoke_impl (p,
220+ typename call_traits<
221+ std::decay_t <F>>::arg_types{});
114222 }
115223};
116224
117- /* * A handler transform that resolves parameters from route_data.
225+ /* * A handler transform that resolves extra parameters from route_data.
118226
119227 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.
228+ declare a first parameter of type `route_params&` followed
229+ by additional parameters of arbitrary types. At dispatch time,
230+ each extra parameter type is looked up in
231+ @ref route_params::route_data via @ref polystore::find.
232+ If all required parameters are found the handler is invoked;
233+ otherwise @ref route_next is returned.
234+
235+ Parameters declared as pointer types (e.g. `A*`) are
236+ optional: `nullptr` is passed when the type is absent.
237+ Rvalue reference parameters (e.g. `A&&`) are supported
238+ and receive a moved reference to the stored object.
239+
240+ Duplicate extra parameter types (after stripping cv-ref
241+ and pointer) produce a compile-time error.
242+
243+ @par Example
244+ @code
245+ router<route_params> base;
246+ auto r = base.with_transform( dynamic_transform{} );
247+
248+ r.get( "/users", [](
249+ route_params& p,
250+ UserService& svc,
251+ Config const& cfg) -> route_result
252+ {
253+ // svc and cfg resolved from p.route_data
254+ return route_done;
255+ });
256+ @endcode
124257*/
125258struct dynamic_transform
126259{
0 commit comments