85.39% Lines (76/89) 75.86% Functions (22/29)
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_POLYSTORE_HPP 10   #ifndef BOOST_HTTP_POLYSTORE_HPP
11   #define BOOST_HTTP_POLYSTORE_HPP 11   #define BOOST_HTTP_POLYSTORE_HPP
12   12  
13   #include <boost/http/detail/config.hpp> 13   #include <boost/http/detail/config.hpp>
14   #include <boost/http/detail/except.hpp> 14   #include <boost/http/detail/except.hpp>
15   #include <boost/core/typeinfo.hpp> 15   #include <boost/core/typeinfo.hpp>
16   #include <boost/core/detail/static_assert.hpp> 16   #include <boost/core/detail/static_assert.hpp>
17   #include <cstring> 17   #include <cstring>
18   #include <memory> 18   #include <memory>
19   #include <type_traits> 19   #include <type_traits>
20   #include <unordered_map> 20   #include <unordered_map>
21   #include <vector> 21   #include <vector>
22   22  
23   #if ! defined( BOOST_NO_TYPEID ) 23   #if ! defined( BOOST_NO_TYPEID )
24   #include <typeindex> 24   #include <typeindex>
25   #endif 25   #endif
26   26  
27   namespace boost { 27   namespace boost {
28   namespace http { 28   namespace http {
29   29  
30   namespace detail { 30   namespace detail {
31   31  
32   #if defined( BOOST_NO_TYPEID ) 32   #if defined( BOOST_NO_TYPEID )
33   33  
34   struct typeindex 34   struct typeindex
35   { 35   {
36   typeindex( 36   typeindex(
37   core::typeinfo const& ti) noexcept 37   core::typeinfo const& ti) noexcept
38   : n_(std::strlen(ti.name())) 38   : n_(std::strlen(ti.name()))
39   , ti_(&ti) 39   , ti_(&ti)
40   { 40   {
41   } 41   }
42   42  
43   std::size_t hash_code() const noexcept 43   std::size_t hash_code() const noexcept
44   { 44   {
45   constexpr std::size_t offset_basis = 45   constexpr std::size_t offset_basis =
46   (sizeof(std::size_t) == 8) 46   (sizeof(std::size_t) == 8)
47   ? 1469598103934665603ull 47   ? 1469598103934665603ull
48   : 2166136261u; 48   : 2166136261u;
49   constexpr std::size_t prime = 49   constexpr std::size_t prime =
50   (sizeof(std::size_t) == 8) 50   (sizeof(std::size_t) == 8)
51   ? 1099511628211ull 51   ? 1099511628211ull
52   : 16777619u; 52   : 16777619u;
53   auto const s = ti_->name(); 53   auto const s = ti_->name();
54   std::size_t h = offset_basis; 54   std::size_t h = offset_basis;
55   for(std::size_t i = 0; i < n_; ++i) 55   for(std::size_t i = 0; i < n_; ++i)
56   h = (h ^ static_cast<unsigned char>(s[i])) * prime; 56   h = (h ^ static_cast<unsigned char>(s[i])) * prime;
57   return h; 57   return h;
58   } 58   }
59   59  
60   bool operator==(typeindex const& other) const noexcept 60   bool operator==(typeindex const& other) const noexcept
61   { 61   {
62   return n_ == other.n_ && *ti_ == *other.ti_; 62   return n_ == other.n_ && *ti_ == *other.ti_;
63   } 63   }
64   64  
65   private: 65   private:
66   std::size_t n_; 66   std::size_t n_;
67   core::typeinfo const* ti_; 67   core::typeinfo const* ti_;
68   }; 68   };
69   69  
70   } // detail 70   } // detail
71   } // http 71   } // http
72   } // boost 72   } // boost
73   namespace std { 73   namespace std {
74   template<> 74   template<>
75   struct hash< boost::http::detail::typeindex > 75   struct hash< boost::http::detail::typeindex >
76   { 76   {
77   std::size_t operator()( 77   std::size_t operator()(
78   boost::http::detail::typeindex const& t) const noexcept 78   boost::http::detail::typeindex const& t) const noexcept
79   { 79   {
80   return t.hash_code(); 80   return t.hash_code();
81   } 81   }
82   }; 82   };
83   } // std 83   } // std
84   namespace boost { 84   namespace boost {
85   namespace http { 85   namespace http {
86   namespace detail { 86   namespace detail {
87   87  
88   #else 88   #else
89   89  
90   using typeindex = std::type_index; 90   using typeindex = std::type_index;
91   91  
92   #endif 92   #endif
93   93  
94   //------------------------------------------------ 94   //------------------------------------------------
95   // 95   //
96   // call_traits 96   // call_traits
97   // 97   //
98   //------------------------------------------------ 98   //------------------------------------------------
99   99  
100   template<class... Ts> 100   template<class... Ts>
101   struct type_list {}; 101   struct type_list {};
102   102  
103   template<class T> 103   template<class T>
104   struct call_traits : std::false_type {}; 104   struct call_traits : std::false_type {};
105   105  
106   template<class R, class... Args> 106   template<class R, class... Args>
107   struct call_traits<R(*)(Args...)> : std::true_type 107   struct call_traits<R(*)(Args...)> : std::true_type
108   { 108   {
109   using return_type = R; 109   using return_type = R;
110   using arg_types = type_list<Args...>; 110   using arg_types = type_list<Args...>;
111   }; 111   };
112   112  
113   template<class R, class... Args> 113   template<class R, class... Args>
114   struct call_traits<R(&)(Args...)> : std::true_type 114   struct call_traits<R(&)(Args...)> : std::true_type
115   { 115   {
116   using return_type = R; 116   using return_type = R;
117   using arg_types = type_list<Args...>; 117   using arg_types = type_list<Args...>;
118   }; 118   };
119   119  
120   template<class C, class R, class... Args> 120   template<class C, class R, class... Args>
121   struct call_traits<R(C::*)(Args...)> : std::true_type 121   struct call_traits<R(C::*)(Args...)> : std::true_type
122   { 122   {
123   using class_type = C; 123   using class_type = C;
124   using return_type = R; 124   using return_type = R;
125   using arg_types = type_list<Args...>; 125   using arg_types = type_list<Args...>;
126   }; 126   };
127   127  
128   template<class C, class R, class... Args> 128   template<class C, class R, class... Args>
129   struct call_traits<R(C::*)(Args...) const> : std::true_type 129   struct call_traits<R(C::*)(Args...) const> : std::true_type
130   { 130   {
131   using class_type = C; 131   using class_type = C;
132   using return_type = R; 132   using return_type = R;
133   using arg_types = type_list<Args...>; 133   using arg_types = type_list<Args...>;
134   }; 134   };
135   135  
136   template<class R, class... Args> 136   template<class R, class... Args>
137   struct call_traits<R(*)(Args...) noexcept> : std::true_type 137   struct call_traits<R(*)(Args...) noexcept> : std::true_type
138   { 138   {
139   using return_type = R; 139   using return_type = R;
140   using arg_types = type_list<Args...>; 140   using arg_types = type_list<Args...>;
141   }; 141   };
142   142  
143   template<class R, class... Args> 143   template<class R, class... Args>
144   struct call_traits<R(&)(Args...) noexcept> : std::true_type 144   struct call_traits<R(&)(Args...) noexcept> : std::true_type
145   { 145   {
146   using return_type = R; 146   using return_type = R;
147   using arg_types = type_list<Args...>; 147   using arg_types = type_list<Args...>;
148   }; 148   };
149   149  
150   template<class C, class R, class... Args> 150   template<class C, class R, class... Args>
151   struct call_traits<R(C::*)(Args...) noexcept> : std::true_type 151   struct call_traits<R(C::*)(Args...) noexcept> : std::true_type
152   { 152   {
153   using class_type = C; 153   using class_type = C;
154   using return_type = R; 154   using return_type = R;
155   using arg_types = type_list<Args...>; 155   using arg_types = type_list<Args...>;
156   }; 156   };
157   157  
158   template<class C, class R, class... Args> 158   template<class C, class R, class... Args>
159   struct call_traits<R(C::*)(Args...) const noexcept> : std::true_type 159   struct call_traits<R(C::*)(Args...) const noexcept> : std::true_type
160   { 160   {
161   using class_type = C; 161   using class_type = C;
162   using return_type = R; 162   using return_type = R;
163   using arg_types = type_list<Args...>; 163   using arg_types = type_list<Args...>;
164   }; 164   };
165   165  
166   template<class F> 166   template<class F>
167   requires requires { &F::operator(); } && 167   requires requires { &F::operator(); } &&
168   std::is_member_function_pointer_v<decltype(&F::operator())> 168   std::is_member_function_pointer_v<decltype(&F::operator())>
169   struct call_traits<F> : call_traits<decltype(&F::operator())> {}; 169   struct call_traits<F> : call_traits<decltype(&F::operator())> {};
170   170  
171   } // detail 171   } // detail
172   172  
173   /** A container of type-erased objects 173   /** A container of type-erased objects
174   174  
175   Objects are stored and retrieved by their type. 175   Objects are stored and retrieved by their type.
176   Each type may be stored at most once. Types 176   Each type may be stored at most once. Types
177   may specify a nested `key_type` to be used 177   may specify a nested `key_type` to be used
178   as the unique identifier instead of the type 178   as the unique identifier instead of the type
179   itself. In this case, a reference to the type 179   itself. In this case, a reference to the type
180   must be convertible to a reference to the key type. 180   must be convertible to a reference to the key type.
181   181  
182   @par Example 182   @par Example
183   @code 183   @code
184   struct A 184   struct A
185   { 185   {
186   int i = 1; 186   int i = 1;
187   }; 187   };
188   struct B 188   struct B
189   { 189   {
190   char c = '2'; 190   char c = '2';
191   }; 191   };
192   struct C 192   struct C
193   { 193   {
194   double d; 194   double d;
195   }; 195   };
196   struct D : C 196   struct D : C
197   { 197   {
198   using key_type = C; 198   using key_type = C;
199   D() 199   D()
200   { 200   {
201   d = 3.14; 201   d = 3.14;
202   } 202   }
203   }; 203   };
204   polystore ps; 204   polystore ps;
205   A& a = ps.emplace<A>(); 205   A& a = ps.emplace<A>();
206   B& b = ps.insert(B{}); 206   B& b = ps.insert(B{});
207   C& c = ps.emplace<C>(); 207   C& c = ps.emplace<C>();
208   assert(ps.get<A>().i == 1); 208   assert(ps.get<A>().i == 1);
209   assert(ps.get<B>().c == '2'); 209   assert(ps.get<B>().c == '2');
210   assert(ps.get<C>().d == 3.14); 210   assert(ps.get<C>().d == 3.14);
211   invoke(ps, [](A& a){ a.i = 0; }); 211   invoke(ps, [](A& a){ a.i = 0; });
212   invoke(ps, [](A const&, B& b){ b.c = 0; }); 212   invoke(ps, [](A const&, B& b){ b.c = 0; });
213   assert(ps.get<A>().i == 0); 213   assert(ps.get<A>().i == 0);
214   assert(ps.get<B>().c == 0); 214   assert(ps.get<B>().c == 0);
215   @endcode 215   @endcode
216   */ 216   */
217   class polystore 217   class polystore
218   { 218   {
219   template<class T, class = void> 219   template<class T, class = void>
220   struct get_key : std::false_type 220   struct get_key : std::false_type
221   { 221   {
222   }; 222   };
223   223  
224   template<class T> 224   template<class T>
225   struct get_key<T, typename std::enable_if< 225   struct get_key<T, typename std::enable_if<
226   ! std::is_same<T, typename T::key_type>::value>::type> 226   ! std::is_same<T, typename T::key_type>::value>::type>
227   : std::true_type 227   : std::true_type
228   { 228   {
229   using type = typename T::key_type; 229   using type = typename T::key_type;
230   }; 230   };
231   231  
232   public: 232   public:
233   /** Destructor 233   /** Destructor
234   234  
235   All objects stored in the container are destroyed in 235   All objects stored in the container are destroyed in
236   the reverse order of construction. 236   the reverse order of construction.
237   */ 237   */
238   BOOST_HTTP_DECL 238   BOOST_HTTP_DECL
239   ~polystore(); 239   ~polystore();
240   240  
241   /** Constructor 241   /** Constructor
242   The moved-from container will be empty. 242   The moved-from container will be empty.
243   */ 243   */
244   BOOST_HTTP_DECL 244   BOOST_HTTP_DECL
245   polystore(polystore&& other) noexcept; 245   polystore(polystore&& other) noexcept;
246   246  
247   /** Assignment operator 247   /** Assignment operator
248   The moved-from container will be empty. 248   The moved-from container will be empty.
249   @return A reference to `*this`. 249   @return A reference to `*this`.
250   */ 250   */
251   BOOST_HTTP_DECL 251   BOOST_HTTP_DECL
252   polystore& operator=(polystore&& other) noexcept; 252   polystore& operator=(polystore&& other) noexcept;
253   253  
254   /** Constructor 254   /** Constructor
255   The container is initially empty. 255   The container is initially empty.
256   */ 256   */
HITCBC 257   234 polystore() = default; 257   234 polystore() = default;
258   258  
259   /** Return a pointer to the object associated with type `T`, or `nullptr` 259   /** Return a pointer to the object associated with type `T`, or `nullptr`
260   260  
261   If no object associated with `T` exists in the container, 261   If no object associated with `T` exists in the container,
262   `nullptr` is returned. 262   `nullptr` is returned.
263   263  
264   @par Thread Safety 264   @par Thread Safety
265   `const` member function calls are thread-safe. 265   `const` member function calls are thread-safe.
266   Calls to non-`const` member functions must not run concurrently 266   Calls to non-`const` member functions must not run concurrently
267   with other member functions on the same object. 267   with other member functions on the same object.
268   268  
269   @tparam T The type of object to find. 269   @tparam T The type of object to find.
270   @return A pointer to the associated object, or `nullptr` if none exists. 270   @return A pointer to the associated object, or `nullptr` if none exists.
271   */ 271   */
272   template<class T> 272   template<class T>
HITCBC 273   69 T* find() const noexcept 273   69 T* find() const noexcept
274   { 274   {
HITCBC 275   69 return static_cast<T*>(find(BOOST_CORE_TYPEID(T))); 275   69 return static_cast<T*>(find(BOOST_CORE_TYPEID(T)));
276   } 276   }
277   277  
278   /** Assign the pointer for the object associated with `T`, or `nullptr`. 278   /** Assign the pointer for the object associated with `T`, or `nullptr`.
279   279  
280   If no object of type `T` is stored, @p t is set to `nullptr`. 280   If no object of type `T` is stored, @p t is set to `nullptr`.
281   281  
282   @par Thread Safety 282   @par Thread Safety
283   `const` member functions are thread-safe. Non-`const` functions 283   `const` member functions are thread-safe. Non-`const` functions
284   must not run concurrently with any other member function on the 284   must not run concurrently with any other member function on the
285   same instance. 285   same instance.
286   286  
287   @param t The pointer to assign. 287   @param t The pointer to assign.
288   @return `true` if an object of type `T` is present, otherwise `false`. 288   @return `true` if an object of type `T` is present, otherwise `false`.
289   */ 289   */
290   template<class T> 290   template<class T>
291   bool find(T*& t) const noexcept 291   bool find(T*& t) const noexcept
292   { 292   {
293   t = find<T>(); 293   t = find<T>();
294   return t != nullptr; 294   return t != nullptr;
295   } 295   }
296   296  
297   /** Return a reference to the object associated with type T 297   /** Return a reference to the object associated with type T
298   298  
299   If no such object exists in the container, an exception is thrown. 299   If no such object exists in the container, an exception is thrown.
300   300  
301   @par Exception Safety 301   @par Exception Safety
302   Strong guarantee. 302   Strong guarantee.
303   303  
304   @par Thread Safety 304   @par Thread Safety
305   Calls to `const` member functions are thread-safe. 305   Calls to `const` member functions are thread-safe.
306   Calls to non-`const` member functions must not run concurrently 306   Calls to non-`const` member functions must not run concurrently
307   with other member functions on the same object. 307   with other member functions on the same object.
308   308  
309   @throws std::bad_typeid 309   @throws std::bad_typeid
310   If no object associated with type `T` is present. 310   If no object associated with type `T` is present.
311   @tparam T The type of object to retrieve. 311   @tparam T The type of object to retrieve.
312   @return A reference to the associated object. 312   @return A reference to the associated object.
313   */ 313   */
314   template<class T> 314   template<class T>
HITCBC 315   34 T& get() const 315   34 T& get() const
316   { 316   {
HITCBC 317   34 if(auto t = find<T>()) 317   34 if(auto t = find<T>())
HITCBC 318   33 return *t; 318   33 return *t;
HITCBC 319   1 detail::throw_bad_typeid(); 319   1 detail::throw_bad_typeid();
320   } 320   }
321   321  
322   /** Construct and insert an anonymous object into the container 322   /** Construct and insert an anonymous object into the container
323   323  
324   A new object of type `T` is constructed in place using the provided 324   A new object of type `T` is constructed in place using the provided
325   arguments and inserted into the container without associating it 325   arguments and inserted into the container without associating it
326   with any key. A reference to the stored object is returned. 326   with any key. A reference to the stored object is returned.
327   327  
328   @par Exception Safety 328   @par Exception Safety
329   Strong guarantee. 329   Strong guarantee.
330   330  
331   @par Thread Safety 331   @par Thread Safety
332   Not thread-safe. 332   Not thread-safe.
333   333  
334   @tparam T The type of object to construct and insert. 334   @tparam T The type of object to construct and insert.
335   @param args Arguments forwarded to the constructor of `T`. 335   @param args Arguments forwarded to the constructor of `T`.
336   @return A reference to the inserted object. 336   @return A reference to the inserted object.
337   */ 337   */
338   template<class T, class... Args> 338   template<class T, class... Args>
HITCBC 339   2 T& emplace_anon(Args&&... args) 339   2 T& emplace_anon(Args&&... args)
340   { 340   {
HITCBC 341   4 return *static_cast<T*>(insert_impl( 341   4 return *static_cast<T*>(insert_impl(
HITCBC 342   4 make_any<T>(std::forward<Args>(args)...))); 342   4 make_any<T>(std::forward<Args>(args)...)));
343   } 343   }
344   344  
345   /** Insert an anonymous object by moving or copying it into the container 345   /** Insert an anonymous object by moving or copying it into the container
346   346  
347   A new object of type `T` is inserted into the container without 347   A new object of type `T` is inserted into the container without
348   associating it with any key. The object is move-constructed or 348   associating it with any key. The object is move-constructed or
349   copy-constructed from the provided argument, and a reference to 349   copy-constructed from the provided argument, and a reference to
350   the stored object is returned. 350   the stored object is returned.
351   351  
352   @par Exception Safety 352   @par Exception Safety
353   Strong guarantee. 353   Strong guarantee.
354   354  
355   @par Thread Safety 355   @par Thread Safety
356   Not thread-safe. 356   Not thread-safe.
357   357  
358   @tparam T The type of object to insert. 358   @tparam T The type of object to insert.
359   @param t The object to insert. 359   @param t The object to insert.
360   @return A reference to the inserted object. 360   @return A reference to the inserted object.
361   */ 361   */
362   template<class T> 362   template<class T>
363   T& insert_anon(T&& t) 363   T& insert_anon(T&& t)
364   { 364   {
365   return emplace_anon<typename 365   return emplace_anon<typename
366   std::remove_cv<T>::type>( 366   std::remove_cv<T>::type>(
367   std::forward<T>(t)); 367   std::forward<T>(t));
368   } 368   }
369   369  
370   /** Construct and insert an object with a nested key type 370   /** Construct and insert an object with a nested key type
371   371  
372   A new object of type `T` is constructed in place using the provided 372   A new object of type `T` is constructed in place using the provided
373   arguments and inserted into the container. The type `T` must define 373   arguments and inserted into the container. The type `T` must define
374   a nested type `key_type`, which is used as the key for insertion. 374   a nested type `key_type`, which is used as the key for insertion.
375   No additional key types may be specified. The type `T&` must be 375   No additional key types may be specified. The type `T&` must be
376   convertible to a reference to `key_type`. 376   convertible to a reference to `key_type`.
377   377  
378   @par Constraints 378   @par Constraints
379   `T::key_type` must name a type. 379   `T::key_type` must name a type.
380   380  
381   @par Exception Safety 381   @par Exception Safety
382   Strong guarantee. 382   Strong guarantee.
383   383  
384   @par Thread Safety 384   @par Thread Safety
385   Not thread-safe. 385   Not thread-safe.
386   386  
387   @throws std::invalid_argument On duplicate insertion. 387   @throws std::invalid_argument On duplicate insertion.
388   @tparam T The type of object to construct and insert. 388   @tparam T The type of object to construct and insert.
389   @param args Arguments forwarded to the constructor of `T`. 389   @param args Arguments forwarded to the constructor of `T`.
390   @return A reference to the inserted object. 390   @return A reference to the inserted object.
391   */ 391   */
392   template<class T, class... Keys, class... Args> 392   template<class T, class... Keys, class... Args>
HITCBC 393   3 auto emplace(Args&&... args) -> 393   3 auto emplace(Args&&... args) ->
394   typename std::enable_if<get_key<T>::value, T&>::type 394   typename std::enable_if<get_key<T>::value, T&>::type
395   { 395   {
396   // Can't have Keys with nested key_type 396   // Can't have Keys with nested key_type
397   BOOST_CORE_STATIC_ASSERT(sizeof...(Keys) == 0); 397   BOOST_CORE_STATIC_ASSERT(sizeof...(Keys) == 0);
398   // T& must be convertible to key_type& 398   // T& must be convertible to key_type&
399   BOOST_CORE_STATIC_ASSERT(std::is_convertible< 399   BOOST_CORE_STATIC_ASSERT(std::is_convertible<
400   T&, typename get_key<T>::type&>::value); 400   T&, typename get_key<T>::type&>::value);
HITCBC 401   3 auto p = make_any<T>(std::forward<Args>(args)...); 401   3 auto p = make_any<T>(std::forward<Args>(args)...);
HITCBC 402   3 keyset<T, typename get_key<T>::type> ks( 402   3 keyset<T, typename get_key<T>::type> ks(
HITCBC 403   3 *static_cast<T*>(p->get())); 403   3 *static_cast<T*>(p->get()));
HITCBC 404   6 return *static_cast<T*>(insert_impl( 404   6 return *static_cast<T*>(insert_impl(
HITCBC 405   7 std::move(p), ks.kn, ks.N)); 405   7 std::move(p), ks.kn, ks.N));
HITCBC 406   3 } 406   3 }
407   407  
408   /** Construct and insert an object into the container 408   /** Construct and insert an object into the container
409   409  
410   A new object of type `T` is constructed in place using the provided 410   A new object of type `T` is constructed in place using the provided
411   arguments and inserted into the container. The type `T` must not 411   arguments and inserted into the container. The type `T` must not
412   already exist in the container, nor may any of the additional key 412   already exist in the container, nor may any of the additional key
413   types refer to an existing object. The type `T&` must be convertible 413   types refer to an existing object. The type `T&` must be convertible
414   to a reference to each specified key type. 414   to a reference to each specified key type.
415   415  
416   @par Constraints 416   @par Constraints
417   `T::key_type` must not name a type. 417   `T::key_type` must not name a type.
418   418  
419   @par Exception Safety 419   @par Exception Safety
420   Strong guarantee. 420   Strong guarantee.
421   421  
422   @par Thread Safety 422   @par Thread Safety
423   Not thread-safe. 423   Not thread-safe.
424   424  
425   @throws std::invalid_argument On duplicate insertion. 425   @throws std::invalid_argument On duplicate insertion.
426   @tparam T The type of object to construct and insert. 426   @tparam T The type of object to construct and insert.
427   @tparam Keys Optional key types associated with the object. 427   @tparam Keys Optional key types associated with the object.
428   @param args Arguments forwarded to the constructor of `T`. 428   @param args Arguments forwarded to the constructor of `T`.
429   @return A reference to the inserted object. 429   @return A reference to the inserted object.
430   */ 430   */
431   template<class T, class... Keys, class... Args> 431   template<class T, class... Keys, class... Args>
HITCBC 432   11 auto emplace(Args&&... args) -> 432   11 auto emplace(Args&&... args) ->
433   typename std::enable_if<! get_key<T>::value, T&>::type 433   typename std::enable_if<! get_key<T>::value, T&>::type
434   { 434   {
435   // T& must be convertible to each of Keys& 435   // T& must be convertible to each of Keys&
436   BOOST_CORE_STATIC_ASSERT((std::is_convertible_v<T&, Keys&> && ...)); 436   BOOST_CORE_STATIC_ASSERT((std::is_convertible_v<T&, Keys&> && ...));
HITCBC 437   11 auto p = make_any<T>(std::forward<Args>(args)...); 437   11 auto p = make_any<T>(std::forward<Args>(args)...);
HITCBC 438   11 keyset<T, Keys...> ks(*static_cast<T*>(p->get())); 438   11 keyset<T, Keys...> ks(*static_cast<T*>(p->get()));
HITCBC 439   22 return *static_cast<T*>(insert_impl( 439   22 return *static_cast<T*>(insert_impl(
HITCBC 440   27 std::move(p), ks.kn, ks.N)); 440   27 std::move(p), ks.kn, ks.N));
HITCBC 441   11 } 441   11 }
442   442  
443   /** Return an existing object, creating it if necessary 443   /** Return an existing object, creating it if necessary
444   444  
445   If an object of the exact type `T` already exists in the container, 445   If an object of the exact type `T` already exists in the container,
446   a reference to that object is returned. Otherwise, a new object is 446   a reference to that object is returned. Otherwise, a new object is
447   constructed in place using the provided arguments, and a reference 447   constructed in place using the provided arguments, and a reference
448   to the newly created object is returned. The type `T` must not 448   to the newly created object is returned. The type `T` must not
449   already exist in the container, nor may any of the additional key 449   already exist in the container, nor may any of the additional key
450   types refer to an existing object. The type `T` must be convertible 450   types refer to an existing object. The type `T` must be convertible
451   to a reference to each additional key type. 451   to a reference to each additional key type.
452   452  
453   @par Exception Safety 453   @par Exception Safety
454   Strong guarantee. 454   Strong guarantee.
455   455  
456   @par Thread Safety 456   @par Thread Safety
457   Not thread-safe. 457   Not thread-safe.
458   458  
459   @throws std::invalid_argument On duplicate insertion. 459   @throws std::invalid_argument On duplicate insertion.
460   @tparam T The type of object to return or create. 460   @tparam T The type of object to return or create.
461   @tparam Keys Optional key types associated with the object. 461   @tparam Keys Optional key types associated with the object.
462   @param args Arguments forwarded to the constructor of `T`. 462   @param args Arguments forwarded to the constructor of `T`.
463   @return A reference to the existing or newly created object. 463   @return A reference to the existing or newly created object.
464   */ 464   */
465   template<class T, class... Keys, class... Args> 465   template<class T, class... Keys, class... Args>
HITCBC 466   2 auto try_emplace(Args&&... args) -> 466   2 auto try_emplace(Args&&... args) ->
467   typename std::enable_if<get_key<T>::value, T&>::type 467   typename std::enable_if<get_key<T>::value, T&>::type
468   { 468   {
469   // Can't have Keys with nested key_type 469   // Can't have Keys with nested key_type
470   BOOST_CORE_STATIC_ASSERT(sizeof...(Keys) == 0); 470   BOOST_CORE_STATIC_ASSERT(sizeof...(Keys) == 0);
471   // T& must be convertible to key_type& 471   // T& must be convertible to key_type&
472   BOOST_CORE_STATIC_ASSERT(std::is_convertible< 472   BOOST_CORE_STATIC_ASSERT(std::is_convertible<
473   T&, typename get_key<T>::type&>::value); 473   T&, typename get_key<T>::type&>::value);
HITCBC 474   2 if(auto t = find<T>()) 474   2 if(auto t = find<T>())
HITCBC 475   1 return *t; 475   1 return *t;
HITCBC 476   1 auto p = make_any<T>(std::forward<Args>(args)...); 476   1 auto p = make_any<T>(std::forward<Args>(args)...);
HITCBC 477   1 keyset<T, typename get_key<T>::type> ks( 477   1 keyset<T, typename get_key<T>::type> ks(
HITCBC 478   1 *static_cast<T*>(p->get())); 478   1 *static_cast<T*>(p->get()));
HITCBC 479   2 return *static_cast<T*>(insert_impl( 479   2 return *static_cast<T*>(insert_impl(
HITCBC 480   2 std::move(p), ks.kn, ks.N)); 480   2 std::move(p), ks.kn, ks.N));
HITCBC 481   1 } 481   1 }
482   482  
483   /** Return an existing object, creating it if necessary 483   /** Return an existing object, creating it if necessary
484   484  
485   If an object of the exact type `T` already exists in the container, 485   If an object of the exact type `T` already exists in the container,
486   a reference to that object is returned. Otherwise, a new object is 486   a reference to that object is returned. Otherwise, a new object is
487   constructed in place using the provided arguments, and a reference 487   constructed in place using the provided arguments, and a reference
488   to the newly created object is returned. The type `T` must not 488   to the newly created object is returned. The type `T` must not
489   already exist in the container, nor may any of the additional key 489   already exist in the container, nor may any of the additional key
490   types refer to an existing object. The type `T` must be convertible 490   types refer to an existing object. The type `T` must be convertible
491   to a reference to each additional key type. 491   to a reference to each additional key type.
492   492  
493   @par Exception Safety 493   @par Exception Safety
494   Strong guarantee. 494   Strong guarantee.
495   495  
496   @par Thread Safety 496   @par Thread Safety
497   `const` member function calls are thread-safe. 497   `const` member function calls are thread-safe.
498   Calls to non-`const` member functions must not run concurrently 498   Calls to non-`const` member functions must not run concurrently
499   with other member functions on the same object. 499   with other member functions on the same object.
500   500  
501   @throws std::invalid_argument On duplicate insertion. 501   @throws std::invalid_argument On duplicate insertion.
502   @tparam T The type of object to return or create. 502   @tparam T The type of object to return or create.
503   @tparam Keys Optional key types associated with the object. 503   @tparam Keys Optional key types associated with the object.
504   @param args Arguments forwarded to the constructor of `T`. 504   @param args Arguments forwarded to the constructor of `T`.
505   @return A reference to the existing or newly created object. 505   @return A reference to the existing or newly created object.
506   */ 506   */
507   template<class T, class... Keys, class... Args> 507   template<class T, class... Keys, class... Args>
HITCBC 508   2 auto try_emplace(Args&&... args) -> 508   2 auto try_emplace(Args&&... args) ->
509   typename std::enable_if<! get_key<T>::value, T&>::type 509   typename std::enable_if<! get_key<T>::value, T&>::type
510   { 510   {
511   // T& must be convertible to each of Keys& 511   // T& must be convertible to each of Keys&
512   BOOST_CORE_STATIC_ASSERT((std::is_convertible_v<T&, Keys&> && ...)); 512   BOOST_CORE_STATIC_ASSERT((std::is_convertible_v<T&, Keys&> && ...));
HITCBC 513   2 if(auto t = find<T>()) 513   2 if(auto t = find<T>())
HITCBC 514   1 return *t; 514   1 return *t;
HITCBC 515   1 auto p = make_any<T>(std::forward<Args>(args)...); 515   1 auto p = make_any<T>(std::forward<Args>(args)...);
HITCBC 516   1 keyset<T, Keys...> ks(*static_cast<T*>(p->get())); 516   1 keyset<T, Keys...> ks(*static_cast<T*>(p->get()));
HITCBC 517   2 return *static_cast<T*>(insert_impl( 517   2 return *static_cast<T*>(insert_impl(
HITCBC 518   2 std::move(p), ks.kn, ks.N)); 518   2 std::move(p), ks.kn, ks.N));
HITCBC 519   1 } 519   1 }
520   520  
521   /** Insert an object by moving or copying it into the container 521   /** Insert an object by moving or copying it into the container
522   522  
523   If an object of the same type `T` already exists in the container, 523   If an object of the same type `T` already exists in the container,
524   or if any of the additional key types would refer to an existing 524   or if any of the additional key types would refer to an existing
525   object, an exception is thrown. Otherwise, the object is inserted 525   object, an exception is thrown. Otherwise, the object is inserted
526   by move or copy construction, and a reference to the stored object 526   by move or copy construction, and a reference to the stored object
527   is returned. The type `T` must be convertible to a reference to each 527   is returned. The type `T` must be convertible to a reference to each
528   additional key type. 528   additional key type.
529   529  
530   @par Exception Safety 530   @par Exception Safety
531   Strong guarantee. 531   Strong guarantee.
532   532  
533   @par Thread Safety 533   @par Thread Safety
534   Not thread-safe. 534   Not thread-safe.
535   535  
536   @throws std::invalid_argument On duplicate insertion. 536   @throws std::invalid_argument On duplicate insertion.
537   @tparam T The type of object to insert. 537   @tparam T The type of object to insert.
538   @tparam Keys Optional key types associated with the object. 538   @tparam Keys Optional key types associated with the object.
539   @param t The object to insert. 539   @param t The object to insert.
540   @return A reference to the inserted object. 540   @return A reference to the inserted object.
541   */ 541   */
542   template<class T, class... Keys> 542   template<class T, class... Keys>
HITCBC 543   3 T& insert(T&& t) 543   3 T& insert(T&& t)
544   { 544   {
545   return emplace<typename 545   return emplace<typename
HITCBC 546   3 std::remove_cv<T>::type, Keys...>( 546   3 std::remove_cv<T>::type, Keys...>(
HITCBC 547   3 std::forward<T>(t)); 547   3 std::forward<T>(t));
548   } 548   }
549   549  
550   /** Return an existing object or create a new one 550   /** Return an existing object or create a new one
551   551  
552   If an object of the exact type `T` already exists in the container, 552   If an object of the exact type `T` already exists in the container,
553   a reference to that object is returned. Otherwise, a new object of 553   a reference to that object is returned. Otherwise, a new object of
554   type `T` is default-constructed in the container, and a reference 554   type `T` is default-constructed in the container, and a reference
555   to the newly created object is returned. This function ignores 555   to the newly created object is returned. This function ignores
556   nested key types and cannot be used to specify additional keys. 556   nested key types and cannot be used to specify additional keys.
557   557  
558   @par Constraints 558   @par Constraints
559   `T` must be default-constructible. 559   `T` must be default-constructible.
560   560  
561   @par Exception Safety 561   @par Exception Safety
562   Strong guarantee. 562   Strong guarantee.
563   563  
564   @par Thread Safety 564   @par Thread Safety
565   Not thread-safe. 565   Not thread-safe.
566   566  
567   @tparam T The type of object to retrieve or create. 567   @tparam T The type of object to retrieve or create.
568   @return A reference to the stored object. 568   @return A reference to the stored object.
569   */ 569   */
570   template<class T> 570   template<class T>
HITCBC 571   2 T& use() 571   2 T& use()
572   { 572   {
573   // T must be default constructible 573   // T must be default constructible
574   BOOST_CORE_STATIC_ASSERT( 574   BOOST_CORE_STATIC_ASSERT(
575   std::is_default_constructible<T>::value); 575   std::is_default_constructible<T>::value);
HITCBC 576   2 if(auto t = find<T>()) 576   2 if(auto t = find<T>())
MISUBC 577   return *t; 577   return *t;
HITCBC 578   2 return emplace<T>(); 578   2 return emplace<T>();
579   } 579   }
580   580  
581   protected: 581   protected:
582   struct any; 582   struct any;
583   class elements; 583   class elements;
584   584  
585   /** Remove and destroy all objects in the container. 585   /** Remove and destroy all objects in the container.
586   586  
587   All stored objects are destroyed in the reverse order 587   All stored objects are destroyed in the reverse order
588   of construction. The container is left empty. 588   of construction. The container is left empty.
589   */ 589   */
590   BOOST_HTTP_DECL 590   BOOST_HTTP_DECL
591   void 591   void
592   clear() noexcept; 592   clear() noexcept;
593   593  
594   /** Return a range of all stored elements 594   /** Return a range of all stored elements
595   @par Thread Safety 595   @par Thread Safety
596   `const` member function calls are thread-safe. 596   `const` member function calls are thread-safe.
597   Calls to non-`const` member functions must not run concurrently 597   Calls to non-`const` member functions must not run concurrently
598   with other member functions on the same object. 598   with other member functions on the same object.
599   @return An object representing the range of stored elements. 599   @return An object representing the range of stored elements.
600   */ 600   */
601   BOOST_HTTP_DECL 601   BOOST_HTTP_DECL
602   elements 602   elements
603   get_elements() noexcept; 603   get_elements() noexcept;
604   604  
605   private: 605   private:
606   template<class T, class = void> 606   template<class T, class = void>
607   struct has_start : std::false_type {}; 607   struct has_start : std::false_type {};
608   608  
609   template<class T> 609   template<class T>
610   struct has_start<T, typename std::enable_if< 610   struct has_start<T, typename std::enable_if<
611   std::is_same<decltype(std::declval<T>().start()), 611   std::is_same<decltype(std::declval<T>().start()),
612   void>::value>::type> : std::true_type {}; 612   void>::value>::type> : std::true_type {};
613   613  
614   template<class T, class = void> 614   template<class T, class = void>
615   struct has_stop : std::false_type {}; 615   struct has_stop : std::false_type {};
616   616  
617   template<class T> 617   template<class T>
618   struct has_stop<T, typename std::enable_if< 618   struct has_stop<T, typename std::enable_if<
619   std::is_same<decltype(std::declval<T>().stop()), 619   std::is_same<decltype(std::declval<T>().stop()),
620   void>::value>::type> : std::true_type {}; 620   void>::value>::type> : std::true_type {};
621   621  
622   struct key 622   struct key
623   { 623   {
624   detail::typeindex ti = 624   detail::typeindex ti =
625   detail::typeindex(BOOST_CORE_TYPEID(void)); 625   detail::typeindex(BOOST_CORE_TYPEID(void));
626   void* p = nullptr; 626   void* p = nullptr;
627   627  
HITCBC 628   6 key() = default; 628   6 key() = default;
HITCBC 629   24 key(detail::typeindex const& ti_, 629   24 key(detail::typeindex const& ti_,
HITCBC 630   24 void* p_) noexcept : ti(ti_) , p(p_) {} 630   24 void* p_) noexcept : ti(ti_) , p(p_) {}
631   }; 631   };
632   632  
633   template<class T, class... Key> 633   template<class T, class... Key>
634   struct keyset; 634   struct keyset;
635   635  
636   template<class T> 636   template<class T>
637   struct keyset<T> 637   struct keyset<T>
638   { 638   {
639   static constexpr std::size_t N = 1; 639   static constexpr std::size_t N = 1;
640   key kn[1]; 640   key kn[1];
641   641  
HITCBC 642   10 explicit keyset(T& t) noexcept 642   10 explicit keyset(T& t) noexcept
HITCBC 643   10 : kn{ key(detail::typeindex(BOOST_CORE_TYPEID(T)), &t) } 643   10 : kn{ key(detail::typeindex(BOOST_CORE_TYPEID(T)), &t) }
644   { 644   {
HITCBC 645   10 } 645   10 }
646   }; 646   };
647   647  
648   template<class T, class... Keys> 648   template<class T, class... Keys>
649   struct keyset 649   struct keyset
650   { 650   {
651   static constexpr std::size_t N = 1 + sizeof...(Keys); 651   static constexpr std::size_t N = 1 + sizeof...(Keys);
652   key kn[N + 1]; 652   key kn[N + 1];
653   653  
HITCBC 654   6 explicit keyset(T& t) noexcept 654   6 explicit keyset(T& t) noexcept
HITCBC 655   20 : kn{ 655   20 : kn{
HITCBC 656   6 key(detail::typeindex(BOOST_CORE_TYPEID(T)), 656   6 key(detail::typeindex(BOOST_CORE_TYPEID(T)),
HITCBC 657   6 std::addressof(t)), 657   6 std::addressof(t)),
HITCBC 658   8 key(detail::typeindex(BOOST_CORE_TYPEID(Keys)), 658   8 key(detail::typeindex(BOOST_CORE_TYPEID(Keys)),
HITCBC 659   4 &static_cast<Keys&>(t))..., } 659   4 &static_cast<Keys&>(t))..., }
660   { 660   {
HITCBC 661   6 } 661   6 }
662   }; 662   };
663   663  
664   template<class T> struct any_impl; 664   template<class T> struct any_impl;
665   665  
666   using any_ptr = std::unique_ptr<any>; 666   using any_ptr = std::unique_ptr<any>;
667   667  
668   template<class T, class... Args> 668   template<class T, class... Args>
669   auto 669   auto
HITCBC 670   18 make_any(Args&&... args) -> 670   18 make_any(Args&&... args) ->
671   std::unique_ptr<any_impl<T>> 671   std::unique_ptr<any_impl<T>>
672   { 672   {
HITCBC 673   21 return std::unique_ptr<any_impl<T>>(new 673   21 return std::unique_ptr<any_impl<T>>(new
HITCBC 674   21 any_impl<T>(std::forward<Args>(args)...)); 674   21 any_impl<T>(std::forward<Args>(args)...));
675   } 675   }
676   676  
677   void destroy() noexcept; 677   void destroy() noexcept;
678   BOOST_HTTP_DECL any& get(std::size_t i); 678   BOOST_HTTP_DECL any& get(std::size_t i);
679   BOOST_HTTP_DECL void* find( 679   BOOST_HTTP_DECL void* find(
680   core::typeinfo const& ti) const noexcept; 680   core::typeinfo const& ti) const noexcept;
681   BOOST_HTTP_DECL void* insert_impl(any_ptr, 681   BOOST_HTTP_DECL void* insert_impl(any_ptr,
682   key const* = nullptr, std::size_t = 0); 682   key const* = nullptr, std::size_t = 0);
683   683  
684   std::vector<any_ptr> v_; 684   std::vector<any_ptr> v_;
685   std::unordered_map< 685   std::unordered_map<
686   detail::typeindex, void*> m_; 686   detail::typeindex, void*> m_;
687   }; 687   };
688   688  
689   //------------------------------------------------ 689   //------------------------------------------------
690   690  
691   struct BOOST_HTTP_DECL 691   struct BOOST_HTTP_DECL
692   polystore::any 692   polystore::any
693   { 693   {
HITCBC 694   18 virtual ~any() = default; 694   18 virtual ~any() = default;
695   virtual void start() = 0; 695   virtual void start() = 0;
696   virtual void stop() = 0; 696   virtual void stop() = 0;
697   private: 697   private:
698   friend class polystore; 698   friend class polystore;
699   virtual void* get() noexcept = 0; 699   virtual void* get() noexcept = 0;
700   }; 700   };
701   701  
702   //------------------------------------------------ 702   //------------------------------------------------
703   703  
704   class polystore::elements 704   class polystore::elements
705   { 705   {
706   public: 706   public:
MISUBC 707   std::size_t size() const noexcept 707   std::size_t size() const noexcept
708   { 708   {
MISUBC 709   return n_; 709   return n_;
710   } 710   }
711   711  
MISUBC 712   any& operator[]( 712   any& operator[](
713   std::size_t i) noexcept 713   std::size_t i) noexcept
714   { 714   {
MISUBC 715   return ps_.get(i); 715   return ps_.get(i);
716   } 716   }
717   717  
718   private: 718   private:
719   friend class polystore; 719   friend class polystore;
720   720  
MISUBC 721   elements( 721   elements(
722   std::size_t n, 722   std::size_t n,
723   polystore& ps) 723   polystore& ps)
MISUBC 724   : n_(n) 724   : n_(n)
MISUBC 725   , ps_(ps) 725   , ps_(ps)
726   { 726   {
MISUBC 727   } 727   }
728   728  
729   std::size_t n_; 729   std::size_t n_;
730   polystore& ps_; 730   polystore& ps_;
731   }; 731   };
732   732  
733   //------------------------------------------------ 733   //------------------------------------------------
734   734  
735   template<class T> 735   template<class T>
736   struct polystore::any_impl : polystore::any 736   struct polystore::any_impl : polystore::any
737   { 737   {
738   T t; 738   T t;
739   739  
740   template<class... Args> 740   template<class... Args>
HITCBC 741   18 explicit any_impl(Args&&... args) 741   18 explicit any_impl(Args&&... args)
HITCBC 742   18 : t(std::forward<Args>(args)...) 742   18 : t(std::forward<Args>(args)...)
743   { 743   {
HITCBC 744   18 } 744   18 }
HITCBC 745   34 void* get() noexcept override { return std::addressof(t); } 745   34 void* get() noexcept override { return std::addressof(t); }
MISUBC 746   void start() override { do_start(has_start<T>{}); } 746   void start() override { do_start(has_start<T>{}); }
MISUBC 747   void stop() override { do_stop(has_stop<T>{}); } 747   void stop() override { do_stop(has_stop<T>{}); }
748   void do_start(std::true_type) { t.start(); } 748   void do_start(std::true_type) { t.start(); }
MISUBC 749   void do_start(std::false_type) {} 749   void do_start(std::false_type) {}
750   void do_stop(std::true_type) { t.stop(); } 750   void do_stop(std::true_type) { t.stop(); }
MISUBC 751   void do_stop(std::false_type) {} 751   void do_stop(std::false_type) {}
752   }; 752   };
753   753  
754   //------------------------------------------------ 754   //------------------------------------------------
755   755  
756   namespace detail { 756   namespace detail {
757   757  
758   template<class T> struct arg; 758   template<class T> struct arg;
759   template<class T> struct arg<T const&> : arg<T&> {}; 759   template<class T> struct arg<T const&> : arg<T&> {};
760   template<class T> struct arg<T const*> : arg<T*> {}; 760   template<class T> struct arg<T const*> : arg<T*> {};
761   template<class T> struct arg<T&> 761   template<class T> struct arg<T&>
762   { 762   {
HITCBC 763   8 T& operator()(polystore& ps) const 763   8 T& operator()(polystore& ps) const
764   { 764   {
HITCBC 765   8 return ps.get<T>(); 765   8 return ps.get<T>();
766   } 766   }
767   }; 767   };
768   template<class T> struct arg<T*> 768   template<class T> struct arg<T*>
769   { 769   {
HITCBC 770   5 T* operator()(polystore& ps) const 770   5 T* operator()(polystore& ps) const
771   { 771   {
HITCBC 772   5 return ps.find<T>(); 772   5 return ps.find<T>();
773   } 773   }
774   }; 774   };
775   775  
776   template<class F, class... Args> 776   template<class F, class... Args>
777   auto 777   auto
HITCBC 778   10 invoke(polystore& ps, F&& f, 778   10 invoke(polystore& ps, F&& f,
779   type_list<Args...> const&) -> 779   type_list<Args...> const&) ->
780   typename call_traits<std::decay_t<F>>::return_type 780   typename call_traits<std::decay_t<F>>::return_type
781   { 781   {
HITCBC 782   10 return std::forward<F>(f)(arg<Args>()(ps)...); 782   10 return std::forward<F>(f)(arg<Args>()(ps)...);
783   } 783   }
784   784  
785   } // detail 785   } // detail
786   786  
787   /** Invoke a callable, injecting stored objects as arguments 787   /** Invoke a callable, injecting stored objects as arguments
788   The callable is invoked with zero or more arguments. 788   The callable is invoked with zero or more arguments.
789   For each argument type, if an object of that type 789   For each argument type, if an object of that type
790   (or key type) is stored in the container, a reference 790   (or key type) is stored in the container, a reference
791   to that object is passed to the callable. 791   to that object is passed to the callable.
792   @par Example 792   @par Example
793   @code 793   @code
794   struct A { int i = 1; }; 794   struct A { int i = 1; };
795   polystore ps; 795   polystore ps;
796   ps.emplace<A>(); 796   ps.emplace<A>();
797   ps.invoke([](A& a){ assert(a.i == 1; }); 797   ps.invoke([](A& a){ assert(a.i == 1; });
798   @endcode 798   @endcode
799   @param f The callable to invoke. 799   @param f The callable to invoke.
800   @return The result of the invocation. 800   @return The result of the invocation.
801   @throws std::bad_typeid if any reference argument 801   @throws std::bad_typeid if any reference argument
802   types are not found in the container. 802   types are not found in the container.
803   */ 803   */
804   template<class F> 804   template<class F>
805   auto 805   auto
HITCBC 806   10 invoke(polystore& ps, F&& f) -> 806   10 invoke(polystore& ps, F&& f) ->
807   typename detail::call_traits<std::decay_t<F>>::return_type 807   typename detail::call_traits<std::decay_t<F>>::return_type
808   { 808   {
HITCBC 809   20 return detail::invoke(ps, std::forward<F>(f), 809   20 return detail::invoke(ps, std::forward<F>(f),
HITCBC 810   20 typename detail::call_traits<std::decay_t<F>>::arg_types{}); 810   20 typename detail::call_traits<std::decay_t<F>>::arg_types{});
811   } 811   }
812   812  
813   } // http 813   } // http
814   } // boost 814   } // boost
815   815  
816   #endif 816   #endif