| 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 |