LCOV - code coverage report
Current view: top level - boost/capy - small_unique_ptr.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 99.0 % 102 101
Test Date: 2025-12-15 05:33:30 Functions: 91.4 % 35 32

            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/capy
       8              : //
       9              : 
      10              : #ifndef BOOST_CAPY_SMALL_UNIQUE_PTR_HPP
      11              : #define BOOST_CAPY_SMALL_UNIQUE_PTR_HPP
      12              : 
      13              : #include <boost/capy/detail/config.hpp>
      14              : #include <cstddef>
      15              : #include <type_traits>
      16              : #include <utility>
      17              : #include <new>
      18              : 
      19              : namespace boost {
      20              : namespace capy {
      21              : 
      22              : /** A smart pointer with small buffer optimization.
      23              : 
      24              :     This class provides unique ownership semantics similar
      25              :     to `std::unique_ptr`, but with an embedded buffer for
      26              :     small object optimization. Objects that fit within the
      27              :     buffer are constructed in-place, avoiding heap allocation.
      28              :     Larger objects fall back to heap allocation.
      29              : 
      30              :     The SBO path requires the managed type to be nothrow
      31              :     move constructible. Types that do not meet this requirement
      32              :     or exceed the buffer size are heap-allocated.
      33              : 
      34              :     @tparam T The base type of the managed object. Pointers
      35              :     to derived types may be stored if convertible to `T*`.
      36              : 
      37              :     @tparam N The size of the internal buffer in bytes.
      38              :     Defaults to `sizeof(T)`.
      39              : 
      40              :     @par Example
      41              :     @code
      42              :     struct Base { virtual ~Base() = default; };
      43              :     struct Derived : Base { int x; };
      44              : 
      45              :     // Uses SBO if Derived fits in buffer
      46              :     auto p = make_small_unique<Base, 32, Derived>(42);
      47              :     @endcode
      48              : 
      49              :     @see make_small_unique
      50              : */
      51              : template<class T, std::size_t N = sizeof(T)>
      52              : class small_unique_ptr
      53              : {
      54              :     static_assert(N >= sizeof(T), "Buffer too small for base type");
      55              : 
      56              :     template<class U>
      57              :     struct fits_in_buffer
      58              :         : std::integral_constant<
      59              :             bool,
      60              :             (sizeof(U) <= N) &&
      61              :             (alignof(U) <= alignof(std::max_align_t))>
      62              :     {
      63              :     };
      64              : 
      65              :     alignas(std::max_align_t) unsigned char buffer_[N];
      66              :     T* ptr_;
      67              :     void (*destroy_)(small_unique_ptr*);
      68              :     void (*relocate_)(small_unique_ptr*, small_unique_ptr*);
      69              : 
      70              :     template<class U>
      71            6 :     static void destroy_small(small_unique_ptr* self)
      72              :     {
      73            6 :         static_cast<U*>(self->ptr_)->~U();
      74            6 :     }
      75              : 
      76              :     template<class U>
      77            6 :     static void destroy_large(small_unique_ptr* self)
      78              :     {
      79            6 :         delete static_cast<U*>(self->ptr_);
      80            6 :     }
      81              : 
      82              :     template<class U>
      83            5 :     static void relocate_small(
      84              :         small_unique_ptr* src,
      85              :         small_unique_ptr* dst)
      86              :     {
      87              :         static_assert(
      88              :             std::is_nothrow_move_constructible<U>::value,
      89              :             "U must be nothrow move constructible for SBO");
      90            5 :         U* p = static_cast<U*>(src->ptr_);
      91            5 :         dst->ptr_ = ::new(static_cast<void*>(&dst->buffer_)) U(std::move(*p));
      92            5 :         dst->destroy_ = src->destroy_;
      93            5 :         dst->relocate_ = src->relocate_;
      94            5 :         p->~U();
      95            5 :         src->ptr_ = nullptr;
      96            5 :         src->destroy_ = nullptr;
      97            5 :         src->relocate_ = nullptr;
      98            5 :     }
      99              : 
     100              :     template<class U>
     101            5 :     static void relocate_large(
     102              :         small_unique_ptr* src,
     103              :         small_unique_ptr* dst)
     104              :     {
     105            5 :         dst->ptr_ = src->ptr_;
     106            5 :         dst->destroy_ = src->destroy_;
     107            5 :         dst->relocate_ = src->relocate_;
     108            5 :         src->ptr_ = nullptr;
     109            5 :         src->destroy_ = nullptr;
     110            5 :         src->relocate_ = nullptr;
     111            5 :     }
     112              : 
     113           13 :     void clear() noexcept
     114              :     {
     115           13 :         ptr_ = nullptr;
     116           13 :         destroy_ = nullptr;
     117           13 :         relocate_ = nullptr;
     118           13 :     }
     119              : 
     120              : public:
     121              :     /** The type of the managed object.
     122              :     */
     123              :     typedef T element_type;
     124              : 
     125              :     /** The pointer type.
     126              :     */
     127              :     typedef T* pointer;
     128              : 
     129              :     /** Default constructor.
     130              : 
     131              :         Constructs an empty smart pointer that owns nothing.
     132              : 
     133              :         @post `get() == nullptr`
     134              :     */
     135           13 :     small_unique_ptr() noexcept
     136           13 :         : ptr_(nullptr)
     137           13 :         , destroy_(nullptr)
     138           13 :         , relocate_(nullptr)
     139              :     {
     140           13 :     }
     141              : 
     142              :     /** Construct from nullptr.
     143              : 
     144              :         Constructs an empty smart pointer that owns nothing.
     145              : 
     146              :         @post `get() == nullptr`
     147              :     */
     148            1 :     small_unique_ptr(std::nullptr_t) noexcept
     149            1 :         : small_unique_ptr()
     150              :     {
     151            1 :     }
     152              : 
     153              :     /** Construct from a raw pointer.
     154              : 
     155              :         Takes ownership of a heap-allocated object. The
     156              :         pointer must have been allocated with `new` and
     157              :         will be deleted when this smart pointer is destroyed.
     158              : 
     159              :         @note This constructor always uses the heap path.
     160              :         Use @ref emplace or @ref make_small_unique to
     161              :         benefit from small buffer optimization.
     162              : 
     163              :         @param p A pointer to a heap-allocated object,
     164              :         or `nullptr`.
     165              : 
     166              :         @tparam U The dynamic type. Must be convertible to `T*`.
     167              : 
     168              :         @post `get() == p`
     169              :     */
     170              :     template<class U
     171              :         ,class = typename std::enable_if<
     172              :             std::is_convertible<U*, T*>::value>::type>
     173              :     explicit
     174            3 :     small_unique_ptr(
     175              :         U* p) noexcept
     176            3 :         : ptr_(p)
     177            3 :         , destroy_(p ? &destroy_large<U> : nullptr)
     178            3 :         , relocate_(p ? &relocate_large<U> : nullptr)
     179              :     {
     180            3 :     }
     181              : 
     182              :     /** Copy constructor (deleted).
     183              : 
     184              :         small_unique_ptr is move-only.
     185              :     */
     186              :     small_unique_ptr(small_unique_ptr const&) = delete;
     187              : 
     188              :     /** Copy assignment (deleted).
     189              : 
     190              :         small_unique_ptr is move-only.
     191              :     */
     192              :     small_unique_ptr& operator=(small_unique_ptr const&) = delete;
     193              : 
     194              :     /** Move constructor.
     195              : 
     196              :         Transfers ownership from `other` to `*this`.
     197              :         For SBO objects, the managed object is move-constructed
     198              :         into this instance's buffer. For heap objects, the
     199              :         pointer is transferred.
     200              : 
     201              :         @param other The source smart pointer. Will be empty
     202              :         after this call.
     203              : 
     204              :         @post `other.get() == nullptr`
     205              :     */
     206            5 :     small_unique_ptr(small_unique_ptr&& other) noexcept
     207            5 :         : ptr_(nullptr)
     208            5 :         , destroy_(nullptr)
     209            5 :         , relocate_(nullptr)
     210              :     {
     211            5 :         if(other.ptr_)
     212            4 :             other.relocate_(&other, this);
     213            5 :     }
     214              : 
     215              :     /** Move assignment.
     216              : 
     217              :         Destroys any currently managed object, then transfers
     218              :         ownership from `other` to `*this`.
     219              : 
     220              :         @param other The source smart pointer. Will be empty
     221              :         after this call.
     222              : 
     223              :         @return `*this`
     224              : 
     225              :         @post `other.get() == nullptr`
     226              :     */
     227            7 :     small_unique_ptr& operator=(small_unique_ptr&& other) noexcept
     228              :     {
     229            7 :         if(this != &other)
     230              :         {
     231            6 :             reset();
     232            6 :             if(other.ptr_)
     233            6 :                 other.relocate_(&other, this);
     234              :         }
     235            7 :         return *this;
     236              :     }
     237              : 
     238              :     /** Assign nullptr.
     239              : 
     240              :         Destroys any currently managed object and resets
     241              :         to empty state.
     242              : 
     243              :         @return `*this`
     244              : 
     245              :         @post `get() == nullptr`
     246              :     */
     247            1 :     small_unique_ptr& operator=(std::nullptr_t) noexcept
     248              :     {
     249            1 :         reset();
     250            1 :         return *this;
     251              :     }
     252              : 
     253              :     /** Destructor.
     254              : 
     255              :         Destroys the managed object if one exists. SBO
     256              :         objects are destroyed in-place; heap objects
     257              :         are deleted.
     258              :     */
     259           21 :     ~small_unique_ptr() noexcept
     260              :     {
     261           21 :         reset();
     262           21 :     }
     263              : 
     264              :     /** Destroy the managed object.
     265              : 
     266              :         If a managed object exists, it is destroyed (either
     267              :         in-place for SBO or deleted for heap). After this
     268              :         call, the smart pointer is empty.
     269              : 
     270              :         @post `get() == nullptr`
     271              :     */
     272           29 :     void reset() noexcept
     273              :     {
     274           29 :         if(ptr_)
     275              :         {
     276           12 :             destroy_(this);
     277           12 :             clear();
     278              :         }
     279           29 :     }
     280              : 
     281              :     /** Release ownership.
     282              : 
     283              :         Returns the managed pointer and relinquishes
     284              :         ownership without destroying the object. The
     285              :         caller is responsible for deleting the returned
     286              :         pointer.
     287              : 
     288              :         @warning For SBO objects, the returned pointer
     289              :         points to memory inside this object's buffer,
     290              :         which becomes invalid when this object is
     291              :         destroyed or reused.
     292              : 
     293              :         @return The previously managed pointer, or
     294              :         `nullptr` if empty.
     295              : 
     296              :         @post `get() == nullptr`
     297              :     */
     298            1 :     pointer release() noexcept
     299              :     {
     300            1 :         pointer p = ptr_;
     301            1 :         clear();
     302            1 :         return p;
     303              :     }
     304              : 
     305              :     /** Get the managed pointer.
     306              : 
     307              :         @return The stored pointer, or `nullptr` if empty.
     308              :     */
     309            1 :     pointer get() const noexcept
     310              :     {
     311            1 :         return ptr_;
     312              :     }
     313              : 
     314              :     /** Check if non-empty.
     315              : 
     316              :         @return `true` if this manages an object,
     317              :         `false` otherwise.
     318              :     */
     319           19 :     explicit operator bool() const noexcept
     320              :     {
     321           19 :         return ptr_ != nullptr;
     322              :     }
     323              : 
     324              :     /** Dereference the managed object.
     325              : 
     326              :         @return A reference to the managed object.
     327              : 
     328              :         @pre `get() != nullptr`
     329              :     */
     330              :     typename std::add_lvalue_reference<T>::type
     331            1 :     operator*() const noexcept
     332              :     {
     333            1 :         return *ptr_;
     334              :     }
     335              : 
     336              :     /** Member access.
     337              : 
     338              :         @return The stored pointer.
     339              : 
     340              :         @pre `get() != nullptr`
     341              :     */
     342           12 :     pointer operator->() const noexcept
     343              :     {
     344           12 :         return ptr_;
     345              :     }
     346              : 
     347              :     /** Swap with another small_unique_ptr.
     348              : 
     349              :         Exchanges the managed objects between `*this`
     350              :         and `other`.
     351              : 
     352              :         @param other The smart pointer to swap with.
     353              :     */
     354            2 :     void swap(small_unique_ptr& other) noexcept
     355              :     {
     356            2 :         small_unique_ptr tmp(std::move(other));
     357            2 :         other = std::move(*this);
     358            2 :         *this = std::move(tmp);
     359            2 :     }
     360              : 
     361              :     /** Construct a managed object in-place.
     362              : 
     363              :         Creates an object of type `U` with the given
     364              :         arguments. If `U` fits in the buffer and is
     365              :         nothrow move constructible, SBO is used;
     366              :         otherwise the object is heap-allocated.
     367              : 
     368              :         @param args Constructor arguments for `U`.
     369              : 
     370              :         @tparam U The concrete type to construct.
     371              :         Must be convertible to `T*`.
     372              : 
     373              :         @tparam Args Constructor argument types.
     374              : 
     375              :         @return A small_unique_ptr managing the new object.
     376              : 
     377              :         @throws Any exception thrown by `U`'s constructor.
     378              :         For heap allocation, may also throw `std::bad_alloc`.
     379              :     */
     380              :     // SBO path
     381              :     template<class U, class... Args>
     382              :     static
     383              :     typename std::enable_if<
     384              :         fits_in_buffer<U>::value &&
     385              :         std::is_convertible<U*, T*>::value,
     386              :         small_unique_ptr>::type
     387            6 :     emplace(Args&&... args)
     388              :     {
     389              :         static_assert(
     390              :             std::is_nothrow_move_constructible<U>::value,
     391              :             "U must be nothrow move constructible for SBO");
     392            6 :         small_unique_ptr p;
     393           12 :         p.ptr_ = ::new(static_cast<void*>(&p.buffer_)) U(
     394            6 :             std::forward<Args>(args)...);
     395            6 :         p.destroy_ = &destroy_small<U>;
     396            6 :         p.relocate_ = &relocate_small<U>;
     397            6 :         return p;
     398              :     }
     399              : 
     400              :     /** Construct a managed object in-place (heap path).
     401              : 
     402              :         Creates an object of type `U` on the heap when
     403              :         `U` does not fit in the buffer or does not meet
     404              :         SBO requirements.
     405              : 
     406              :         @param args Constructor arguments for `U`.
     407              : 
     408              :         @tparam U The concrete type to construct.
     409              :         Must be convertible to `T*`.
     410              : 
     411              :         @tparam Args Constructor argument types.
     412              : 
     413              :         @return A small_unique_ptr managing the new object.
     414              : 
     415              :         @throws Any exception thrown by `U`'s constructor,
     416              :         or `std::bad_alloc` on allocation failure.
     417              :     */
     418              :     // Heap path
     419              :     template<class U, class... Args>
     420              :     static
     421              :     typename std::enable_if<
     422              :         !fits_in_buffer<U>::value &&
     423              :         std::is_convertible<U*, T*>::value,
     424              :         small_unique_ptr>::type
     425            4 :     emplace(Args&&... args)
     426              :     {
     427            4 :         small_unique_ptr p;
     428            4 :         p.ptr_ = new U(std::forward<Args>(args)...);
     429            4 :         p.destroy_ = &destroy_large<U>;
     430            4 :         p.relocate_ = &relocate_large<U>;
     431            4 :         return p;
     432            0 :     }
     433              : };
     434              : 
     435              : /** Swap two small_unique_ptr objects.
     436              : 
     437              :     Exchanges the managed objects between `lhs` and `rhs`.
     438              : 
     439              :     @param lhs The first smart pointer.
     440              :     @param rhs The second smart pointer.
     441              : 
     442              :     @see small_unique_ptr::swap
     443              : */
     444              : template<class T, std::size_t N>
     445            2 : void swap(
     446              :     small_unique_ptr<T, N>& lhs,
     447              :     small_unique_ptr<T, N>& rhs) noexcept
     448              : {
     449            2 :     lhs.swap(rhs);
     450            2 : }
     451              : 
     452              : /** Create a small_unique_ptr with in-place construction.
     453              : 
     454              :     Constructs an object of type `U` managed by a
     455              :     small_unique_ptr. Uses small buffer optimization
     456              :     if `U` fits within `N` bytes and is nothrow move
     457              :     constructible; otherwise heap-allocates.
     458              : 
     459              :     @param args Constructor arguments forwarded to `U`.
     460              : 
     461              :     @tparam T The base type for the smart pointer.
     462              : 
     463              :     @tparam N The buffer size in bytes.
     464              : 
     465              :     @tparam U The concrete type to construct.
     466              :     Must be convertible to `T*`.
     467              : 
     468              :     @tparam Args Constructor argument types.
     469              : 
     470              :     @return A small_unique_ptr<T, N> managing the new object.
     471              : 
     472              :     @throws Any exception thrown by `U`'s constructor.
     473              :     For heap allocation, may also throw `std::bad_alloc`.
     474              : 
     475              :     @par Example
     476              :     @code
     477              :     struct Base { virtual ~Base() = default; };
     478              :     struct Derived : Base { int value; Derived(int v) : value(v) {} };
     479              : 
     480              :     auto p = make_small_unique<Base, 64, Derived>(42);
     481              :     @endcode
     482              : 
     483              :     @see small_unique_ptr, small_unique_ptr::emplace
     484              : */
     485              : template<class T, std::size_t N, class U, class... Args>
     486              : typename std::enable_if<
     487              :     std::is_convertible<U*, T*>::value,
     488              :     small_unique_ptr<T, N>>::type
     489           10 : make_small_unique(Args&&... args)
     490              : {
     491              :     return small_unique_ptr<T, N>::template emplace<U>(
     492           10 :         std::forward<Args>(args)...);
     493              : }
     494              : 
     495              : } // capy
     496              : } // boost
     497              : 
     498              : #endif
        

Generated by: LCOV version 2.1