Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/http
8 : //
9 :
10 : #ifndef BOOST_HTTP_SERVER_DETAIL_DYNAMIC_INVOKE_HPP
11 : #define BOOST_HTTP_SERVER_DETAIL_DYNAMIC_INVOKE_HPP
12 :
13 : #include <boost/http/detail/config.hpp>
14 : #include <boost/http/core/polystore.hpp>
15 : #include <boost/http/server/route_handler.hpp>
16 : #include <tuple>
17 : #include <type_traits>
18 : #include <utility>
19 :
20 : namespace boost {
21 : namespace http {
22 : namespace detail {
23 :
24 : //------------------------------------------------
25 :
26 : template<class... Ts>
27 : struct are_unique : std::true_type {};
28 :
29 : template<class T, class... Ts>
30 : struct are_unique<T, Ts...>
31 : : std::bool_constant<
32 : (!std::is_same_v<T, Ts> && ...) &&
33 : are_unique<Ts...>::value> {};
34 :
35 : //------------------------------------------------
36 :
37 : template<class T>
38 : using find_key_t =
39 : std::remove_cv_t<
40 : std::remove_reference_t<T>>;
41 :
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 3 : auto resolve_arg(T* p)
57 : {
58 : if constexpr (is_optional_v<Arg>)
59 2 : return static_cast<find_key_t<Arg>>(p);
60 : else
61 1 : return static_cast<Arg>(*p);
62 : }
63 :
64 : //------------------------------------------------
65 :
66 : template<class F, class... Args>
67 : route_result
68 : dynamic_invoke_impl(
69 : polystore& ps,
70 : F const& f,
71 : type_list<Args...> const&)
72 : {
73 : static_assert(
74 : are_unique<lookup_key_t<Args>...>::value,
75 : "callable has duplicate parameter types");
76 :
77 : auto ptrs = std::make_tuple(
78 : ps.find<lookup_key_t<Args>>()...);
79 :
80 : return [&]<std::size_t... I>(
81 : std::index_sequence<I...>) -> route_result
82 : {
83 : if constexpr (!(is_optional_v<Args> && ...))
84 : {
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...>{});
92 : }
93 :
94 : /** Invoke a callable, resolving arguments from a polystore.
95 :
96 : Each parameter type of the callable is looked up in the
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.
107 :
108 : Duplicate parameter types (after stripping cv-ref and
109 : pointer) produce a compile-time error.
110 :
111 : @param ps The polystore to resolve arguments from.
112 : @param f The callable to invoke.
113 : @return The result of the callable, or @ref route_next
114 : if any required parameter was not found.
115 : */
116 : template<class F>
117 : route_result
118 : dynamic_invoke(
119 : polystore& ps,
120 : F const& f)
121 : {
122 : return dynamic_invoke_impl(
123 : ps, f,
124 : typename call_traits<
125 : std::decay_t<F>>::arg_types{});
126 : }
127 :
128 : //------------------------------------------------
129 :
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 : */
137 : template<class F>
138 : struct dynamic_handler
139 : {
140 : F f;
141 :
142 : // No extra parameters -- just forward to the callable
143 : template<class First>
144 : route_task
145 3 : 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 3 : return wrap_result(f(p));
158 : }
159 :
160 : static route_task
161 0 : make_route_next()
162 : {
163 : co_return route_next;
164 0 : }
165 :
166 : static route_task
167 6 : wrap_result(route_result r)
168 : {
169 : co_return r;
170 12 : }
171 :
172 : // Extra parameters resolved from route_data
173 : template<class First, class E1, class... Extra>
174 : route_task
175 3 : 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 3 : return invoke_extras(p, type_list<E1, Extra...>{});
183 : }
184 :
185 : template<class... Extras>
186 : route_task
187 3 : 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 3 : auto ptrs = std::make_tuple(
197 : p.route_data.template find<
198 3 : lookup_key_t<Extras>>()...);
199 :
200 6 : 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 1 : if(! (... && (is_optional_v<Extras> ||
206 1 : std::get<I>(ptrs) != nullptr)))
207 0 : 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 3 : return wrap_result(f(p,
217 2 : resolve_arg<Extras>(
218 4 : std::get<I>(ptrs))...));
219 6 : }(std::index_sequence_for<Extras...>{});
220 : }
221 :
222 : route_task
223 6 : operator()(route_params& p) const
224 : {
225 : return invoke_impl(p,
226 : typename call_traits<
227 6 : std::decay_t<F>>::arg_types{});
228 : }
229 : };
230 :
231 : /** A handler transform that resolves extra parameters from route_data.
232 :
233 : When used with @ref router::with_transform, handlers may
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
263 : */
264 : struct dynamic_transform
265 : {
266 : template<class F>
267 : auto
268 6 : operator()(F f) const ->
269 : dynamic_handler<std::decay_t<F>>
270 : {
271 6 : return { std::move(f) };
272 : }
273 : };
274 :
275 : } // detail
276 : } // http
277 : } // boost
278 :
279 : #endif
|