GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/core/polystore.hpp
Date: 2026-01-22 22:47:31
Exec Total Coverage
Lines: 76 89 85.4%
Functions: 120 175 68.6%
Branches: 22 23 95.7%

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