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