GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/small_unique_ptr.hpp
Date: 2025-12-15 05:33:30
Exec Total Coverage
Lines: 101 102 99.0%
Functions: 28 31 90.3%
Branches: 11 15 73.3%

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_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 12 static void destroy_small(small_unique_ptr* self)
72 {
73 12 static_cast<U*>(self->ptr_)->~U();
74 12 }
75
76 template<class U>
77 12 static void destroy_large(small_unique_ptr* self)
78 {
79
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 delete static_cast<U*>(self->ptr_);
80 12 }
81
82 template<class U>
83 10 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 10 U* p = static_cast<U*>(src->ptr_);
91 10 dst->ptr_ = ::new(static_cast<void*>(&dst->buffer_)) U(std::move(*p));
92 10 dst->destroy_ = src->destroy_;
93 10 dst->relocate_ = src->relocate_;
94 10 p->~U();
95 10 src->ptr_ = nullptr;
96 10 src->destroy_ = nullptr;
97 10 src->relocate_ = nullptr;
98 10 }
99
100 template<class U>
101 10 static void relocate_large(
102 small_unique_ptr* src,
103 small_unique_ptr* dst)
104 {
105 10 dst->ptr_ = src->ptr_;
106 10 dst->destroy_ = src->destroy_;
107 10 dst->relocate_ = src->relocate_;
108 10 src->ptr_ = nullptr;
109 10 src->destroy_ = nullptr;
110 10 src->relocate_ = nullptr;
111 10 }
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 6 small_unique_ptr(
175 U* p) noexcept
176 6 : ptr_(p)
177
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 , destroy_(p ? &destroy_large<U> : nullptr)
178
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 , relocate_(p ? &relocate_large<U> : nullptr)
179 {
180 6 }
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
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
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
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if(this != &other)
230 {
231 6 reset();
232
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
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
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 17 times.
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 12 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 12 small_unique_ptr p;
393 24 p.ptr_ = ::new(static_cast<void*>(&p.buffer_)) U(
394 12 std::forward<Args>(args)...);
395 12 p.destroy_ = &destroy_small<U>;
396 12 p.relocate_ = &relocate_small<U>;
397 12 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 8 emplace(Args&&... args)
426 {
427 8 small_unique_ptr p;
428
1/1
✓ Branch 1 taken 4 times.
8 p.ptr_ = new U(std::forward<Args>(args)...);
429 8 p.destroy_ = &destroy_large<U>;
430 8 p.relocate_ = &relocate_large<U>;
431 8 return p;
432 }
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
499