88.46% Lines (23/26) 88.89% Functions (8/9)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/http 7   // Official repository: https://github.com/cppalliance/http
8   // 8   //
9   9  
10   #ifndef BOOST_HTTP_SERVER_DETAIL_DYNAMIC_INVOKE_HPP 10   #ifndef BOOST_HTTP_SERVER_DETAIL_DYNAMIC_INVOKE_HPP
11   #define BOOST_HTTP_SERVER_DETAIL_DYNAMIC_INVOKE_HPP 11   #define BOOST_HTTP_SERVER_DETAIL_DYNAMIC_INVOKE_HPP
12   12  
13   #include <boost/http/detail/config.hpp> 13   #include <boost/http/detail/config.hpp>
14   #include <boost/http/core/polystore.hpp> 14   #include <boost/http/core/polystore.hpp>
15   #include <boost/http/server/route_handler.hpp> 15   #include <boost/http/server/route_handler.hpp>
16   #include <tuple> 16   #include <tuple>
17   #include <type_traits> 17   #include <type_traits>
18   #include <utility> 18   #include <utility>
19   19  
20   namespace boost { 20   namespace boost {
21   namespace http { 21   namespace http {
22   namespace detail { 22   namespace detail {
23   23  
24   //------------------------------------------------ 24   //------------------------------------------------
25   25  
26   template<class... Ts> 26   template<class... Ts>
27   struct are_unique : std::true_type {}; 27   struct are_unique : std::true_type {};
28   28  
29   template<class T, class... Ts> 29   template<class T, class... Ts>
30   struct are_unique<T, Ts...> 30   struct are_unique<T, Ts...>
31   : std::bool_constant< 31   : std::bool_constant<
32   (!std::is_same_v<T, Ts> && ...) && 32   (!std::is_same_v<T, Ts> && ...) &&
33   are_unique<Ts...>::value> {}; 33   are_unique<Ts...>::value> {};
34   34  
35   //------------------------------------------------ 35   //------------------------------------------------
36   36  
37   template<class T> 37   template<class T>
38   using find_key_t = 38   using find_key_t =
39   std::remove_cv_t< 39   std::remove_cv_t<
40   std::remove_reference_t<T>>; 40   std::remove_reference_t<T>>;
41   41  
42   // Polystore lookup key: also strips pointer 42   // Polystore lookup key: also strips pointer
43   template<class T> 43   template<class T>
44   using lookup_key_t = 44   using lookup_key_t =
45   std::remove_cv_t< 45   std::remove_cv_t<
46   std::remove_pointer_t< 46   std::remove_pointer_t<
47   find_key_t<T>>>; 47   find_key_t<T>>>;
48   48  
49   // True when parameter is a pointer (optional dependency) 49   // True when parameter is a pointer (optional dependency)
50   template<class T> 50   template<class T>
51   constexpr bool is_optional_v = 51   constexpr bool is_optional_v =
52   std::is_pointer_v<find_key_t<T>>; 52   std::is_pointer_v<find_key_t<T>>;
53   53  
54   // Resolve a polystore pointer to the handler's expected arg 54   // Resolve a polystore pointer to the handler's expected arg
55   template<class Arg, class T> 55   template<class Arg, class T>
HITCBC 56   3 auto resolve_arg(T* p) 56   3 auto resolve_arg(T* p)
57   { 57   {
58   if constexpr (is_optional_v<Arg>) 58   if constexpr (is_optional_v<Arg>)
HITCBC 59   2 return static_cast<find_key_t<Arg>>(p); 59   2 return static_cast<find_key_t<Arg>>(p);
60   else 60   else
HITCBC 61   1 return static_cast<Arg>(*p); 61   1 return static_cast<Arg>(*p);
62   } 62   }
63   63  
64   //------------------------------------------------ 64   //------------------------------------------------
65   65  
66   template<class F, class... Args> 66   template<class F, class... Args>
67   route_result 67   route_result
68   dynamic_invoke_impl( 68   dynamic_invoke_impl(
69   polystore& ps, 69   polystore& ps,
70   F const& f, 70   F const& f,
71   type_list<Args...> const&) 71   type_list<Args...> const&)
72   { 72   {
73   static_assert( 73   static_assert(
74   are_unique<lookup_key_t<Args>...>::value, 74   are_unique<lookup_key_t<Args>...>::value,
75   "callable has duplicate parameter types"); 75   "callable has duplicate parameter types");
76   76  
77   auto ptrs = std::make_tuple( 77   auto ptrs = std::make_tuple(
78   ps.find<lookup_key_t<Args>>()...); 78   ps.find<lookup_key_t<Args>>()...);
79   79  
80   return [&]<std::size_t... I>( 80   return [&]<std::size_t... I>(
81   std::index_sequence<I...>) -> route_result 81   std::index_sequence<I...>) -> route_result
82   { 82   {
83   if constexpr (!(is_optional_v<Args> && ...)) 83   if constexpr (!(is_optional_v<Args> && ...))
84   { 84   {
85   if(! (... && (is_optional_v<Args> || 85   if(! (... && (is_optional_v<Args> ||
86   std::get<I>(ptrs) != nullptr))) 86   std::get<I>(ptrs) != nullptr)))
87   return route_next; 87   return route_next;
88   } 88   }
89   return f(resolve_arg<Args>( 89   return f(resolve_arg<Args>(
90   std::get<I>(ptrs))...); 90   std::get<I>(ptrs))...);
91   }(std::index_sequence_for<Args...>{}); 91   }(std::index_sequence_for<Args...>{});
92   } 92   }
93   93  
94   /** Invoke a callable, resolving arguments from a polystore. 94   /** Invoke a callable, resolving arguments from a polystore.
95   95  
96   Each parameter type of the callable is looked up in the 96   Each parameter type of the callable is looked up in the
97   polystore via @ref polystore::find. If all required 97   polystore via @ref polystore::find. If all required
98   parameters are found, the callable is invoked with the 98   parameters are found, the callable is invoked with the
99   resolved arguments and its result is returned. If any 99   resolved arguments and its result is returned. If any
100   required parameter is not found, @ref route_next is 100   required parameter is not found, @ref route_next is
101   returned without invoking the callable. 101   returned without invoking the callable.
102   102  
103   Parameters declared as pointer types (e.g. `A*`) are 103   Parameters declared as pointer types (e.g. `A*`) are
104   optional: `nullptr` is passed when the type is absent. 104   optional: `nullptr` is passed when the type is absent.
105   Rvalue reference parameters (e.g. `A&&`) are supported 105   Rvalue reference parameters (e.g. `A&&`) are supported
106   and receive a moved reference to the stored object. 106   and receive a moved reference to the stored object.
107   107  
108   Duplicate parameter types (after stripping cv-ref and 108   Duplicate parameter types (after stripping cv-ref and
109   pointer) produce a compile-time error. 109   pointer) produce a compile-time error.
110   110  
111   @param ps The polystore to resolve arguments from. 111   @param ps The polystore to resolve arguments from.
112   @param f The callable to invoke. 112   @param f The callable to invoke.
113   @return The result of the callable, or @ref route_next 113   @return The result of the callable, or @ref route_next
114   if any required parameter was not found. 114   if any required parameter was not found.
115   */ 115   */
116   template<class F> 116   template<class F>
117   route_result 117   route_result
118   dynamic_invoke( 118   dynamic_invoke(
119   polystore& ps, 119   polystore& ps,
120   F const& f) 120   F const& f)
121   { 121   {
122   return dynamic_invoke_impl( 122   return dynamic_invoke_impl(
123   ps, f, 123   ps, f,
124   typename call_traits< 124   typename call_traits<
125   std::decay_t<F>>::arg_types{}); 125   std::decay_t<F>>::arg_types{});
126   } 126   }
127   127  
128   //------------------------------------------------ 128   //------------------------------------------------
129   129  
130   /** Wraps a callable whose first parameter is `route_params&` 130   /** Wraps a callable whose first parameter is `route_params&`
131   and whose remaining parameters are resolved from 131   and whose remaining parameters are resolved from
132   @ref route_params::route_data at dispatch time. 132   @ref route_params::route_data at dispatch time.
133   133  
134   Produced by @ref dynamic_transform. Stored inside 134   Produced by @ref dynamic_transform. Stored inside
135   the router's handler table. 135   the router's handler table.
136   */ 136   */
137   template<class F> 137   template<class F>
138   struct dynamic_handler 138   struct dynamic_handler
139   { 139   {
140   F f; 140   F f;
141   141  
142   // No extra parameters -- just forward to the callable 142   // No extra parameters -- just forward to the callable
143   template<class First> 143   template<class First>
144   route_task 144   route_task
HITCBC 145   3 invoke_impl( 145   3 invoke_impl(
146   route_params& p, 146   route_params& p,
147   type_list<First> const&) const 147   type_list<First> const&) const
148   { 148   {
149   static_assert( 149   static_assert(
150   std::is_convertible_v<route_params&, First>, 150   std::is_convertible_v<route_params&, First>,
151   "first parameter must accept route_params&"); 151   "first parameter must accept route_params&");
152   using R = std::invoke_result_t< 152   using R = std::invoke_result_t<
153   F const&, route_params&>; 153   F const&, route_params&>;
154   if constexpr (std::is_same_v<R, route_task>) 154   if constexpr (std::is_same_v<R, route_task>)
155   return f(p); 155   return f(p);
156   else 156   else
HITCBC 157   3 return wrap_result(f(p)); 157   3 return wrap_result(f(p));
158   } 158   }
159   159  
160   static route_task 160   static route_task
MISUBC 161   make_route_next() 161   make_route_next()
162   { 162   {
163   co_return route_next; 163   co_return route_next;
MISUBC 164   } 164   }
165   165  
166   static route_task 166   static route_task
HITCBC 167   6 wrap_result(route_result r) 167   6 wrap_result(route_result r)
168   { 168   {
169   co_return r; 169   co_return r;
HITCBC 170   12 } 170   12 }
171   171  
172   // Extra parameters resolved from route_data 172   // Extra parameters resolved from route_data
173   template<class First, class E1, class... Extra> 173   template<class First, class E1, class... Extra>
174   route_task 174   route_task
HITCBC 175   3 invoke_impl( 175   3 invoke_impl(
176   route_params& p, 176   route_params& p,
177   type_list<First, E1, Extra...> const&) const 177   type_list<First, E1, Extra...> const&) const
178   { 178   {
179   static_assert( 179   static_assert(
180   std::is_convertible_v<route_params&, First>, 180   std::is_convertible_v<route_params&, First>,
181   "first parameter must accept route_params&"); 181   "first parameter must accept route_params&");
HITCBC 182   3 return invoke_extras(p, type_list<E1, Extra...>{}); 182   3 return invoke_extras(p, type_list<E1, Extra...>{});
183   } 183   }
184   184  
185   template<class... Extras> 185   template<class... Extras>
186   route_task 186   route_task
HITCBC 187   3 invoke_extras( 187   3 invoke_extras(
188   route_params& p, 188   route_params& p,
189   type_list<Extras...> const&) const 189   type_list<Extras...> const&) const
190   { 190   {
191   static_assert( 191   static_assert(
192   are_unique< 192   are_unique<
193   lookup_key_t<Extras>...>::value, 193   lookup_key_t<Extras>...>::value,
194   "callable has duplicate parameter types"); 194   "callable has duplicate parameter types");
195   195  
HITCBC 196   3 auto ptrs = std::make_tuple( 196   3 auto ptrs = std::make_tuple(
197   p.route_data.template find< 197   p.route_data.template find<
HITCBC 198   3 lookup_key_t<Extras>>()...); 198   3 lookup_key_t<Extras>>()...);
199   199  
HITCBC 200   6 return [this, &p, &ptrs]<std::size_t... I>( 200   6 return [this, &p, &ptrs]<std::size_t... I>(
201   std::index_sequence<I...>) -> route_task 201   std::index_sequence<I...>) -> route_task
202   { 202   {
203   if constexpr (!(is_optional_v<Extras> && ...)) 203   if constexpr (!(is_optional_v<Extras> && ...))
204   { 204   {
HITCBC 205   1 if(! (... && (is_optional_v<Extras> || 205   1 if(! (... && (is_optional_v<Extras> ||
HITCBC 206   1 std::get<I>(ptrs) != nullptr))) 206   1 std::get<I>(ptrs) != nullptr)))
MISUBC 207   return make_route_next(); 207   return make_route_next();
208   } 208   }
209   209  
210   using R = std::invoke_result_t< 210   using R = std::invoke_result_t<
211   F const&, route_params&, Extras...>; 211   F const&, route_params&, Extras...>;
212   if constexpr (std::is_same_v<R, route_task>) 212   if constexpr (std::is_same_v<R, route_task>)
213   return f(p, resolve_arg<Extras>( 213   return f(p, resolve_arg<Extras>(
214   std::get<I>(ptrs))...); 214   std::get<I>(ptrs))...);
215   else 215   else
HITCBC 216   3 return wrap_result(f(p, 216   3 return wrap_result(f(p,
HITCBC 217   2 resolve_arg<Extras>( 217   2 resolve_arg<Extras>(
HITCBC 218   4 std::get<I>(ptrs))...)); 218   4 std::get<I>(ptrs))...));
HITCBC 219   6 }(std::index_sequence_for<Extras...>{}); 219   6 }(std::index_sequence_for<Extras...>{});
220   } 220   }
221   221  
222   route_task 222   route_task
HITCBC 223   6 operator()(route_params& p) const 223   6 operator()(route_params& p) const
224   { 224   {
225   return invoke_impl(p, 225   return invoke_impl(p,
226   typename call_traits< 226   typename call_traits<
HITCBC 227   6 std::decay_t<F>>::arg_types{}); 227   6 std::decay_t<F>>::arg_types{});
228   } 228   }
229   }; 229   };
230   230  
231   /** A handler transform that resolves extra parameters from route_data. 231   /** A handler transform that resolves extra parameters from route_data.
232   232  
233   When used with @ref router::with_transform, handlers may 233   When used with @ref router::with_transform, handlers may
234   declare a first parameter of type `route_params&` followed 234   declare a first parameter of type `route_params&` followed
235   by additional parameters of arbitrary types. At dispatch time, 235   by additional parameters of arbitrary types. At dispatch time,
236   each extra parameter type is looked up in 236   each extra parameter type is looked up in
237   @ref route_params::route_data via @ref polystore::find. 237   @ref route_params::route_data via @ref polystore::find.
238   If all required parameters are found the handler is invoked; 238   If all required parameters are found the handler is invoked;
239   otherwise @ref route_next is returned. 239   otherwise @ref route_next is returned.
240   240  
241   Parameters declared as pointer types (e.g. `A*`) are 241   Parameters declared as pointer types (e.g. `A*`) are
242   optional: `nullptr` is passed when the type is absent. 242   optional: `nullptr` is passed when the type is absent.
243   Rvalue reference parameters (e.g. `A&&`) are supported 243   Rvalue reference parameters (e.g. `A&&`) are supported
244   and receive a moved reference to the stored object. 244   and receive a moved reference to the stored object.
245   245  
246   Duplicate extra parameter types (after stripping cv-ref 246   Duplicate extra parameter types (after stripping cv-ref
247   and pointer) produce a compile-time error. 247   and pointer) produce a compile-time error.
248   248  
249   @par Example 249   @par Example
250   @code 250   @code
251   router<route_params> base; 251   router<route_params> base;
252   auto r = base.with_transform( dynamic_transform{} ); 252   auto r = base.with_transform( dynamic_transform{} );
253   253  
254   r.get( "/users", []( 254   r.get( "/users", [](
255   route_params& p, 255   route_params& p,
256   UserService& svc, 256   UserService& svc,
257   Config const& cfg) -> route_result 257   Config const& cfg) -> route_result
258   { 258   {
259   // svc and cfg resolved from p.route_data 259   // svc and cfg resolved from p.route_data
260   return route_done; 260   return route_done;
261   }); 261   });
262   @endcode 262   @endcode
263   */ 263   */
264   struct dynamic_transform 264   struct dynamic_transform
265   { 265   {
266   template<class F> 266   template<class F>
267   auto 267   auto
HITCBC 268   6 operator()(F f) const -> 268   6 operator()(F f) const ->
269   dynamic_handler<std::decay_t<F>> 269   dynamic_handler<std::decay_t<F>>
270   { 270   {
HITCBC 271   6 return { std::move(f) }; 271   6 return { std::move(f) };
272   } 272   }
273   }; 273   };
274   274  
275   } // detail 275   } // detail
276   } // http 276   } // http
277   } // boost 277   } // boost
278   278  
279   #endif 279   #endif