Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot 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/beast2
8 : //
9 :
10 : #ifndef BOOST_BEAST2_POLYSTORE_HPP
11 : #define BOOST_BEAST2_POLYSTORE_HPP
12 :
13 : #include <boost/beast2/detail/config.hpp>
14 : #include <boost/beast2/detail/call_traits.hpp>
15 : #include <boost/beast2/detail/except.hpp>
16 : #include <boost/beast2/detail/type_info.hpp>
17 : #include <boost/beast2/detail/type_traits.hpp>
18 : #include <boost/core/detail/static_assert.hpp>
19 : #include <memory>
20 : #include <type_traits>
21 :
22 : namespace boost {
23 : namespace beast2 {
24 :
25 : /** A container of type-erased objects
26 :
27 : Objects are stored and retrieved by their type.
28 : Each type may be stored at most once. Types
29 : may specify a nested `key_type` to be used
30 : as the unique identifier instead of the type
31 : itself. In this case, a reference to the type
32 : must be convertible to a reference to the key type.
33 :
34 : @par Example
35 : @code
36 : struct A
37 : {
38 : int i = 1;
39 : };
40 : struct B
41 : {
42 : char c = '2';
43 : };
44 : struct C
45 : {
46 : double d;
47 : };
48 : struct D : C
49 : {
50 : using key_type = C;
51 : D()
52 : {
53 : d = 3.14;
54 : }
55 : };
56 : polystore ps;
57 : A& a = ps.emplace<A>();
58 : B& b = ps.insert(B{});
59 : C& c = ps.emplace<C>();
60 : assert(ps.get<A>().i == 1);
61 : assert(ps.get<B>().c == '2');
62 : assert(ps.get<C>().d == 3.14);
63 : invoke(ps, [](A& a){ a.i = 0; });
64 : invoke(ps, [](A const&, B& b){ b.c = 0; });
65 : assert(ps.get<A>().i == 0);
66 : assert(ps.get<B>().c == 0);
67 : @endcode
68 : */
69 : class polystore
70 : {
71 : template<class T, class = void>
72 : struct get_key : std::false_type
73 : {
74 : };
75 :
76 : template<class T>
77 : struct get_key<T, typename std::enable_if<
78 : ! std::is_same<T, typename T::key_type>::value>::type>
79 : : std::true_type
80 : {
81 : using type = typename T::key_type;
82 : };
83 :
84 : public:
85 : /** Destructor
86 :
87 : All objects stored in the container are destroyed in
88 : the reverse order of construction.
89 : */
90 : BOOST_BEAST2_DECL
91 : ~polystore();
92 :
93 : /** Constructor
94 : */
95 : BOOST_BEAST2_DECL
96 : polystore();
97 :
98 : /** Return a pointer to the object associated with type `T`, or `nullptr`
99 :
100 : If no object associated with `T` exists in the container,
101 : `nullptr` is returned.
102 :
103 : @par Thread Safety
104 : `const` member function calls are thread-safe.
105 : Calls to non-`const` member functions must not run concurrently
106 : with other member functions on the same object.
107 :
108 : @tparam T The type of object to find.
109 : @return A pointer to the associated object, or `nullptr` if none exists.
110 : */
111 : template<class T>
112 63 : T* find() const noexcept
113 : {
114 63 : return static_cast<T*>(find(
115 63 : detail::get_type_info<T>()));
116 : }
117 :
118 : /** Return a reference to the object associated with type T
119 :
120 : If no such object exists in the container, an exception is thrown.
121 :
122 : @par Exception Safety
123 : Strong guarantee.
124 :
125 : @par Thread Safety
126 : Calls to `const` member functions are thread-safe.
127 : Calls to non-`const` member functions must not run concurrently
128 : with other member functions on the same object.
129 :
130 : @throws std::bad_typeid
131 : If no object associated with type `T` is present.
132 : @tparam T The type of object to retrieve.
133 : @return A reference to the associated object.
134 : */
135 : template<class T>
136 34 : T& get() const
137 : {
138 34 : if(auto t = find<T>())
139 33 : return *t;
140 1 : detail::throw_bad_typeid();
141 : }
142 :
143 : /** Construct and insert an anonymous object into the container
144 :
145 : A new object of type `T` is constructed in place using the provided
146 : arguments and inserted into the container without associating it
147 : with any key. A reference to the stored object is returned.
148 :
149 : @par Exception Safety
150 : Strong guarantee.
151 :
152 : @par Thread Safety
153 : Not thread-safe.
154 :
155 : @tparam T The type of object to construct and insert.
156 : @param args Arguments forwarded to the constructor of `T`.
157 : @return A reference to the inserted object.
158 : */
159 : template<class T, class... Args>
160 2 : T& emplace_anon(Args&&... args)
161 : {
162 4 : return *static_cast<T*>(insert_impl(
163 4 : make_any<T>(std::forward<Args>(args)...)));
164 : }
165 :
166 : /** Insert an anonymous object by moving or copying it into the container
167 :
168 : A new object of type `T` is inserted into the container without
169 : associating it with any key. The object is move-constructed or
170 : copy-constructed from the provided argument, and a reference to
171 : the stored object is returned.
172 :
173 : @par Exception Safety
174 : Strong guarantee.
175 :
176 : @par Thread Safety
177 : Not thread-safe.
178 :
179 : @tparam T The type of object to insert.
180 : @param t The object to insert.
181 : @return A reference to the inserted object.
182 : */
183 : template<class T>
184 : T& insert_anon(T&& t)
185 : {
186 : return emplace_anon<typename
187 : std::remove_cv<T>::type>(
188 : std::forward<T>(t));
189 : }
190 :
191 : /** Construct and insert an object with a nested key type
192 :
193 : A new object of type `T` is constructed in place using the provided
194 : arguments and inserted into the container. The type `T` must define
195 : a nested type `key_type`, which is used as the key for insertion.
196 : No additional key types may be specified. The type `T&` must be
197 : convertible to a reference to `key_type`.
198 :
199 : @par Constraints
200 : `T::key_type` must name a type.
201 :
202 : @par Exception Safety
203 : Strong guarantee.
204 :
205 : @par Thread Safety
206 : Not thread-safe.
207 :
208 : @throws std::invalid_argument On duplicate insertion.
209 : @tparam T The type of object to construct and insert.
210 : @param args Arguments forwarded to the constructor of `T`.
211 : @return A reference to the inserted object.
212 : */
213 : template<class T, class... Keys, class... Args>
214 3 : auto emplace(Args&&... args) ->
215 : typename std::enable_if<get_key<T>::value, T&>::type
216 : {
217 : // Can't have Keys with nested key_type
218 : BOOST_CORE_STATIC_ASSERT(sizeof...(Keys) == 0);
219 : // T& must be convertible to key_type&
220 : BOOST_CORE_STATIC_ASSERT(std::is_convertible<
221 : T&, typename get_key<T>::type&>::value);
222 3 : auto p = make_any<T>(std::forward<Args>(args)...);
223 3 : keyset<T, typename get_key<T>::type> ks(
224 3 : *static_cast<T*>(p->get()));
225 6 : return *static_cast<T*>(insert_impl(
226 7 : std::move(p), ks.kn, ks.N));
227 3 : }
228 :
229 : /** Construct and insert an object into the container
230 :
231 : A new object of type `T` is constructed in place using the provided
232 : arguments and inserted into the container. The type `T` must not
233 : already exist in the container, nor may any of the additional key
234 : types refer to an existing object. The type `T&` must be convertible
235 : to a reference to each specified key type.
236 :
237 : @par Constraints
238 : `T::key_type` must not name a type.
239 :
240 : @par Exception Safety
241 : Strong guarantee.
242 :
243 : @par Thread Safety
244 : Not thread-safe.
245 :
246 : @throws std::invalid_argument On duplicate insertion.
247 : @tparam T The type of object to construct and insert.
248 : @tparam Keys Optional key types associated with the object.
249 : @param args Arguments forwarded to the constructor of `T`.
250 : @return A reference to the inserted object.
251 : */
252 : template<class T, class... Keys, class... Args>
253 8 : auto emplace(Args&&... args) ->
254 : typename std::enable_if<! get_key<T>::value, T&>::type
255 : {
256 : // T& must be convertible to each of Keys&
257 : BOOST_CORE_STATIC_ASSERT(all_true<std::is_convertible<
258 : T&, Keys&>::value...>::value);
259 8 : auto p = make_any<T>(std::forward<Args>(args)...);
260 8 : keyset<T, Keys...> ks(*static_cast<T*>(p->get()));
261 16 : return *static_cast<T*>(insert_impl(
262 18 : std::move(p), ks.kn, ks.N));
263 8 : }
264 :
265 : /** Return an existing object, creating it if necessary
266 :
267 : If an object of the exact type `T` already exists in the container,
268 : a reference to that object is returned. Otherwise, a new object is
269 : constructed in place using the provided arguments, and a reference
270 : to the newly created object is returned. The type `T` must not
271 : already exist in the container, nor may any of the additional key
272 : types refer to an existing object. The type `T` must be convertible
273 : to a reference to each additional key type.
274 :
275 : @par Exception Safety
276 : Strong guarantee.
277 :
278 : @par Thread Safety
279 : Not thread-safe.
280 :
281 : @throws std::invalid_argument On duplicate insertion.
282 : @tparam T The type of object to return or create.
283 : @tparam Keys Optional key types associated with the object.
284 : @param args Arguments forwarded to the constructor of `T`.
285 : @return A reference to the existing or newly created object.
286 : */
287 : template<class T, class... Keys, class... Args>
288 2 : auto try_emplace(Args&&... args) ->
289 : typename std::enable_if<get_key<T>::value, T&>::type
290 : {
291 : // Can't have Keys with nested key_type
292 : BOOST_CORE_STATIC_ASSERT(sizeof...(Keys) == 0);
293 : // T& must be convertible to key_type&
294 : BOOST_CORE_STATIC_ASSERT(std::is_convertible<
295 : T&, typename get_key<T>::type&>::value);
296 2 : if(auto t = find<T>())
297 1 : return *t;
298 1 : auto p = make_any<T>(std::forward<Args>(args)...);
299 1 : keyset<T, typename get_key<T>::type> ks(
300 1 : *static_cast<T*>(p->get()));
301 2 : return *static_cast<T*>(insert_impl(
302 2 : std::move(p), ks.kn, ks.N));
303 1 : }
304 :
305 : /** Return an existing object, creating it if necessary
306 :
307 : If an object of the exact type `T` already exists in the container,
308 : a reference to that object is returned. Otherwise, a new object is
309 : constructed in place using the provided arguments, and a reference
310 : to the newly created object is returned. The type `T` must not
311 : already exist in the container, nor may any of the additional key
312 : types refer to an existing object. The type `T` must be convertible
313 : to a reference to each additional key type.
314 :
315 : @par Exception Safety
316 : Strong guarantee.
317 :
318 : @par Thread Safety
319 : `const` member function calls are thread-safe.
320 : Calls to non-`const` member functions must not run concurrently
321 : with other member functions on the same object.
322 :
323 : @throws std::invalid_argument On duplicate insertion.
324 : @tparam T The type of object to return or create.
325 : @tparam Keys Optional key types associated with the object.
326 : @param args Arguments forwarded to the constructor of `T`.
327 : @return A reference to the existing or newly created object.
328 : */
329 : template<class T, class... Keys, class... Args>
330 2 : auto try_emplace(Args&&... args) ->
331 : typename std::enable_if<! get_key<T>::value, T&>::type
332 : {
333 : // T& must be convertible to each of Keys&
334 : BOOST_CORE_STATIC_ASSERT(all_true<std::is_convertible<
335 : T&, Keys&>::value...>::value);
336 2 : if(auto t = find<T>())
337 1 : return *t;
338 1 : auto p = make_any<T>(std::forward<Args>(args)...);
339 1 : keyset<T, Keys...> ks(*static_cast<T*>(p->get()));
340 2 : return *static_cast<T*>(insert_impl(
341 2 : std::move(p), ks.kn, ks.N));
342 1 : }
343 :
344 : /** Insert an object by moving or copying it into the container
345 :
346 : If an object of the same type `T` already exists in the container,
347 : or if any of the additional key types would refer to an existing
348 : object, an exception is thrown. Otherwise, the object is inserted
349 : by move or copy construction, and a reference to the stored object
350 : is returned. The type `T` must be convertible to a reference to each
351 : additional key type.
352 :
353 : @par Exception Safety
354 : Strong guarantee.
355 :
356 : @par Thread Safety
357 : Not thread-safe.
358 :
359 : @throws std::invalid_argument On duplicate insertion.
360 : @tparam T The type of object to insert.
361 : @tparam Keys Optional key types associated with the object.
362 : @param t The object to insert.
363 : @return A reference to the inserted object.
364 : */
365 : template<class T, class... Keys>
366 1 : T& insert(T&& t)
367 : {
368 : return emplace<typename
369 1 : std::remove_cv<T>::type, Keys...>(
370 1 : std::forward<T>(t));
371 : }
372 :
373 : /** Return an existing object or create a new one
374 :
375 : If an object of the exact type `T` already exists in the container,
376 : a reference to that object is returned. Otherwise, a new object of
377 : type `T` is default-constructed in the container, and a reference
378 : to the newly created object is returned. This function ignores
379 : nested key types and cannot be used to specify additional keys.
380 :
381 : @par Constraints
382 : `T` must be default-constructible.
383 :
384 : @par Exception Safety
385 : Strong guarantee.
386 :
387 : @par Thread Safety
388 : Not thread-safe.
389 :
390 : @tparam T The type of object to retrieve or create.
391 : @return A reference to the stored object.
392 : */
393 : template<class T>
394 2 : T& use()
395 : {
396 : // T must be default constructible
397 : BOOST_CORE_STATIC_ASSERT(
398 : std::is_default_constructible<T>::value);
399 2 : if(auto t = find<T>())
400 0 : return *t;
401 2 : return emplace<T>();
402 : }
403 :
404 : protected:
405 : struct any;
406 : class elements;
407 :
408 : BOOST_BEAST2_DECL
409 : elements
410 : get_elements() noexcept;
411 :
412 : private:
413 : template<bool...> struct bool_pack {};
414 : template<bool... Bs>
415 : struct all_true : std::is_same<bool_pack<
416 : true, Bs...>, bool_pack<Bs..., true>> {};
417 :
418 : template<class T, class = void>
419 : struct has_start : std::false_type {};
420 :
421 : template<class T>
422 : struct has_start<T, typename std::enable_if<
423 : std::is_same<decltype(std::declval<T>().start()),
424 : void>::value>::type> : std::true_type {};
425 :
426 : template<class T, class = void>
427 : struct has_stop : std::false_type {};
428 :
429 : template<class T>
430 : struct has_stop<T, typename std::enable_if<
431 : std::is_same<decltype(std::declval<T>().stop()),
432 : void>::value>::type> : std::true_type {};
433 :
434 : struct key
435 : {
436 : detail::type_info const* ti;
437 : void* p;
438 : };
439 :
440 : template<class T, class... Key>
441 : struct keyset;
442 :
443 : template<class T>
444 : struct keyset<T>
445 : {
446 : static constexpr std::size_t N = 1;
447 : key kn[1];
448 :
449 7 : explicit keyset(T& t) noexcept
450 7 : : kn{{ &detail::get_type_info<T>(), &t }}
451 : {
452 7 : }
453 : };
454 :
455 : template<class T, class... Keys>
456 : struct keyset
457 : {
458 : static constexpr std::size_t N = 1 + sizeof...(Keys);
459 : key kn[N + 1];
460 :
461 6 : explicit keyset(T& t) noexcept
462 30 : : kn{
463 6 : { &detail::get_type_info<T>(),
464 6 : std::addressof(t) },
465 8 : { &detail::get_type_info<Keys>(),
466 4 : &static_cast<Keys&>(t) }...,
467 : }
468 : {
469 6 : }
470 : };
471 :
472 : template<class T> struct any_impl;
473 :
474 : using any_ptr = std::unique_ptr<any>;
475 :
476 : template<class T, class... Args>
477 : auto
478 15 : make_any(Args&&... args) ->
479 : std::unique_ptr<any_impl<T>>
480 : {
481 16 : return std::unique_ptr<any_impl<T>>(new
482 16 : any_impl<T>(std::forward<Args>(args)...));
483 : }
484 :
485 : BOOST_BEAST2_DECL any& get(std::size_t i);
486 : BOOST_BEAST2_DECL void* find(
487 : detail::type_info const& ti) const noexcept;
488 : BOOST_BEAST2_DECL void* insert_impl(any_ptr,
489 : key const* = nullptr, std::size_t = 0);
490 :
491 : struct impl;
492 : impl* impl_;
493 : };
494 :
495 : //------------------------------------------------
496 :
497 : struct BOOST_SYMBOL_VISIBLE
498 : polystore::any
499 : {
500 : BOOST_BEAST2_DECL virtual ~any();
501 : virtual void start() = 0;
502 : virtual void stop() = 0;
503 : private:
504 : friend class polystore;
505 : virtual void* get() noexcept = 0;
506 : };
507 :
508 : //------------------------------------------------
509 :
510 : class polystore::elements
511 : {
512 : public:
513 0 : std::size_t size() const noexcept
514 : {
515 0 : return n_;
516 : }
517 :
518 0 : any& operator[](
519 : std::size_t i) noexcept
520 : {
521 0 : return ps_.get(i);
522 : }
523 :
524 : private:
525 : friend class polystore;
526 :
527 0 : elements(
528 : std::size_t n,
529 : polystore& ps)
530 0 : : n_(n)
531 0 : , ps_(ps)
532 : {
533 0 : }
534 :
535 : std::size_t n_;
536 : polystore& ps_;
537 : };
538 :
539 : //------------------------------------------------
540 :
541 : template<class T>
542 : struct polystore::any_impl : polystore::any
543 : {
544 : T t;
545 :
546 : template<class... Args>
547 15 : explicit any_impl(Args&&... args)
548 15 : : t(std::forward<Args>(args)...)
549 : {
550 15 : }
551 28 : void* get() noexcept override { return std::addressof(t); }
552 0 : void start() override { do_start(has_start<T>{}); }
553 0 : void stop() override { do_stop(has_stop<T>{}); }
554 0 : void do_start(std::true_type) { t.start(); }
555 0 : void do_start(std::false_type) {}
556 0 : void do_stop(std::true_type) { t.stop(); }
557 0 : void do_stop(std::false_type) {}
558 : };
559 :
560 : //------------------------------------------------
561 :
562 : namespace detail {
563 :
564 : template<class T> struct arg;
565 : template<class T> struct arg<T const&> : arg<T&> {};
566 : template<class T> struct arg<T const*> : arg<T*> {};
567 : template<class T> struct arg<T&>
568 : {
569 8 : T& operator()(polystore& ps) const
570 : {
571 8 : return ps.get<T>();
572 : }
573 : };
574 : template<class T> struct arg<T*>
575 : {
576 5 : T* operator()(polystore& ps) const
577 : {
578 5 : return ps.find<T>();
579 : }
580 : };
581 :
582 : template<class F, class... Args>
583 : auto
584 10 : invoke(polystore& ps, F&& f,
585 : detail::type_list<Args...> const&) ->
586 : typename detail::call_traits<typename
587 : std::decay<F>::type>::return_type
588 : {
589 10 : return std::forward<F>(f)(arg<Args>()(ps)...);
590 : }
591 :
592 : } // detail
593 :
594 : /** Invoke a callable, injecting stored objects as arguments
595 : The callable is invoked with zero or more arguments.
596 : For each argument type, if an object of that type
597 : (or key type) is stored in the container, a reference
598 : to that object is passed to the callable.
599 : @par Example
600 : @code
601 : struct A { int i = 1; };
602 : polystore ps;
603 : ps.emplace<A>();
604 : ps.invoke([](A& a){ assert(a.i == 1; });
605 : @endcode
606 : @param f The callable to invoke.
607 : @return The result of the invocation.
608 : @throws std::bad_typeid if any reference argument
609 : types are not found in the container.
610 : */
611 : template<class F>
612 : auto
613 10 : invoke(polystore& ps, F&& f) ->
614 : typename detail::call_traits<
615 : typename std::decay<F>::type>::return_type
616 : {
617 20 : return detail::invoke(ps, std::forward<F>(f),
618 : typename detail::call_traits< typename
619 20 : std::decay<F>::type>::arg_types{});
620 : }
621 :
622 : } // beast2
623 : } // boost
624 :
625 : #endif
|