LCOV - code coverage report
Current view: top level - boost/beast2/polystore.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 82.8 % 87 72
Test Date: 2025-11-13 15:50:43 Functions: 59.4 % 202 120

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

Generated by: LCOV version 2.1