GCC Code Coverage Report


Directory: libs/beast2/
File: include/boost/beast2/polystore.hpp
Date: 2025-11-13 15:50:44
Exec Total Coverage
Lines: 72 87 82.8%
Functions: 110 192 57.3%
Branches: 26 55 47.3%

Line Branch Exec Source
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 126 T* find() const noexcept
113 {
114 126 return static_cast<T*>(find(
115 126 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 68 T& get() const
137 {
138
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 1 times.
68 if(auto t = find<T>())
139 66 return *t;
140 2 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
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
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 6 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
1/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6 auto p = make_any<T>(std::forward<Args>(args)...);
223 6 keyset<T, typename get_key<T>::type> ks(
224 6 *static_cast<T*>(p->get()));
225
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
12 return *static_cast<T*>(insert_impl(
226 14 std::move(p), ks.kn, ks.N));
227 6 }
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 16 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
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 4 not taken.
16 auto p = make_any<T>(std::forward<Args>(args)...);
260 16 keyset<T, Keys...> ks(*static_cast<T*>(p->get()));
261
2/2
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 3 times.
32 return *static_cast<T*>(insert_impl(
262 36 std::move(p), ks.kn, ks.N));
263 16 }
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/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if(auto t = find<T>())
297 1 return *t;
298
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
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
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
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/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if(auto t = find<T>())
337 1 return *t;
338
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 auto p = make_any<T>(std::forward<Args>(args)...);
339 1 keyset<T, Keys...> ks(*static_cast<T*>(p->get()));
340
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
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 4 T& use()
395 {
396 // T must be default constructible
397 BOOST_CORE_STATIC_ASSERT(
398 std::is_default_constructible<T>::value);
399
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
4 if(auto t = find<T>())
400 return *t;
401 4 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 14 explicit keyset(T& t) noexcept
450 14 : kn{{ &detail::get_type_info<T>(), &t }}
451 {
452 14 }
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 12 explicit keyset(T& t) noexcept
462 60 : kn{
463 12 { &detail::get_type_info<T>(),
464 12 std::addressof(t) },
465 16 { &detail::get_type_info<Keys>(),
466 8 &static_cast<Keys&>(t) }...,
467 }
468 {
469 12 }
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 30 make_any(Args&&... args) ->
479 std::unique_ptr<any_impl<T>>
480 {
481
0/2
✗ Branch 3 not taken.
✗ Branch 4 not taken.
32 return std::unique_ptr<any_impl<T>>(new
482
0/5
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 3 not taken.
32 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 std::size_t size() const noexcept
514 {
515 return n_;
516 }
517
518 any& operator[](
519 std::size_t i) noexcept
520 {
521 return ps_.get(i);
522 }
523
524 private:
525 friend class polystore;
526
527 elements(
528 std::size_t n,
529 polystore& ps)
530 : n_(n)
531 , ps_(ps)
532 {
533 }
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 30 explicit any_impl(Args&&... args)
548
0/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
30 : t(std::forward<Args>(args)...)
549 {
550 30 }
551 56 void* get() noexcept override { return std::addressof(t); }
552 void start() override { do_start(has_start<T>{}); }
553 void stop() override { do_stop(has_stop<T>{}); }
554 void do_start(std::true_type) { t.start(); }
555 void do_start(std::false_type) {}
556 void do_stop(std::true_type) { t.stop(); }
557 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 16 T& operator()(polystore& ps) const
570 {
571 16 return ps.get<T>();
572 }
573 };
574 template<class T> struct arg<T*>
575 {
576 10 T* operator()(polystore& ps) const
577 {
578 10 return ps.find<T>();
579 }
580 };
581
582 template<class F, class... Args>
583 auto
584 20 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
6/8
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 3 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
20 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
626