Skip to content

Commit 18b8c3f

Browse files
committed
feat: small_unique_ptr
1 parent 35c8a56 commit 18b8c3f

2 files changed

Lines changed: 451 additions & 0 deletions

File tree

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
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+
template<class T, std::size_t N = sizeof(T)>
23+
class small_unique_ptr
24+
{
25+
static_assert(N >= sizeof(T), "Buffer too small for base type");
26+
27+
template<class U>
28+
struct fits_in_buffer
29+
: std::integral_constant<
30+
bool,
31+
(sizeof(U) <= N) &&
32+
(alignof(U) <= alignof(std::max_align_t))>
33+
{
34+
};
35+
36+
alignas(std::max_align_t) unsigned char buffer_[N];
37+
T* ptr_;
38+
void (*destroy_)(small_unique_ptr*);
39+
void (*relocate_)(small_unique_ptr*, small_unique_ptr*);
40+
41+
template<class U>
42+
static void destroy_small(small_unique_ptr* self)
43+
{
44+
static_cast<U*>(self->ptr_)->~U();
45+
}
46+
47+
template<class U>
48+
static void destroy_large(small_unique_ptr* self)
49+
{
50+
delete static_cast<U*>(self->ptr_);
51+
}
52+
53+
template<class U>
54+
static void relocate_small(
55+
small_unique_ptr* src,
56+
small_unique_ptr* dst)
57+
{
58+
static_assert(
59+
std::is_nothrow_move_constructible<U>::value,
60+
"U must be nothrow move constructible for SBO");
61+
U* p = static_cast<U*>(src->ptr_);
62+
dst->ptr_ = ::new(static_cast<void*>(&dst->buffer_)) U(std::move(*p));
63+
dst->destroy_ = src->destroy_;
64+
dst->relocate_ = src->relocate_;
65+
p->~U();
66+
src->ptr_ = nullptr;
67+
src->destroy_ = nullptr;
68+
src->relocate_ = nullptr;
69+
}
70+
71+
template<class U>
72+
static void relocate_large(
73+
small_unique_ptr* src,
74+
small_unique_ptr* dst)
75+
{
76+
dst->ptr_ = src->ptr_;
77+
dst->destroy_ = src->destroy_;
78+
dst->relocate_ = src->relocate_;
79+
src->ptr_ = nullptr;
80+
src->destroy_ = nullptr;
81+
src->relocate_ = nullptr;
82+
}
83+
84+
void clear() noexcept
85+
{
86+
ptr_ = nullptr;
87+
destroy_ = nullptr;
88+
relocate_ = nullptr;
89+
}
90+
91+
public:
92+
typedef T element_type;
93+
typedef T* pointer;
94+
95+
small_unique_ptr() noexcept
96+
: ptr_(nullptr)
97+
, destroy_(nullptr)
98+
, relocate_(nullptr)
99+
{
100+
}
101+
102+
small_unique_ptr(std::nullptr_t) noexcept
103+
: small_unique_ptr()
104+
{
105+
}
106+
107+
template<class U
108+
,class = typename std::enable_if<
109+
std::is_convertible<U*, T*>::value>::type>
110+
explicit
111+
small_unique_ptr(
112+
U* p) noexcept
113+
: ptr_(p)
114+
, destroy_(p ? &destroy_large<U> : nullptr)
115+
, relocate_(p ? &relocate_large<U> : nullptr)
116+
{
117+
}
118+
119+
small_unique_ptr(small_unique_ptr const&) = delete;
120+
small_unique_ptr& operator=(small_unique_ptr const&) = delete;
121+
122+
small_unique_ptr(small_unique_ptr&& other) noexcept
123+
: ptr_(nullptr)
124+
, destroy_(nullptr)
125+
, relocate_(nullptr)
126+
{
127+
if(other.ptr_)
128+
other.relocate_(&other, this);
129+
}
130+
131+
small_unique_ptr& operator=(small_unique_ptr&& other) noexcept
132+
{
133+
if(this != &other)
134+
{
135+
reset();
136+
if(other.ptr_)
137+
other.relocate_(&other, this);
138+
}
139+
return *this;
140+
}
141+
142+
small_unique_ptr& operator=(std::nullptr_t) noexcept
143+
{
144+
reset();
145+
return *this;
146+
}
147+
148+
~small_unique_ptr() noexcept
149+
{
150+
reset();
151+
}
152+
153+
void reset() noexcept
154+
{
155+
if(ptr_)
156+
{
157+
destroy_(this);
158+
clear();
159+
}
160+
}
161+
162+
pointer release() noexcept
163+
{
164+
pointer p = ptr_;
165+
clear();
166+
return p;
167+
}
168+
169+
pointer get() const noexcept
170+
{
171+
return ptr_;
172+
}
173+
174+
explicit operator bool() const noexcept
175+
{
176+
return ptr_ != nullptr;
177+
}
178+
179+
typename std::add_lvalue_reference<T>::type
180+
operator*() const noexcept
181+
{
182+
return *ptr_;
183+
}
184+
185+
pointer operator->() const noexcept
186+
{
187+
return ptr_;
188+
}
189+
190+
void swap(small_unique_ptr& other) noexcept
191+
{
192+
small_unique_ptr tmp(std::move(other));
193+
other = std::move(*this);
194+
*this = std::move(tmp);
195+
}
196+
197+
// SBO path
198+
template<class U, class... Args>
199+
static
200+
typename std::enable_if<
201+
fits_in_buffer<U>::value &&
202+
std::is_convertible<U*, T*>::value,
203+
small_unique_ptr>::type
204+
emplace(Args&&... args)
205+
{
206+
static_assert(
207+
std::is_nothrow_move_constructible<U>::value,
208+
"U must be nothrow move constructible for SBO");
209+
small_unique_ptr p;
210+
p.ptr_ = ::new(static_cast<void*>(&p.buffer_)) U(
211+
std::forward<Args>(args)...);
212+
p.destroy_ = &destroy_small<U>;
213+
p.relocate_ = &relocate_small<U>;
214+
return p;
215+
}
216+
217+
// Heap path
218+
template<class U, class... Args>
219+
static
220+
typename std::enable_if<
221+
!fits_in_buffer<U>::value &&
222+
std::is_convertible<U*, T*>::value,
223+
small_unique_ptr>::type
224+
emplace(Args&&... args)
225+
{
226+
small_unique_ptr p;
227+
p.ptr_ = new U(std::forward<Args>(args)...);
228+
p.destroy_ = &destroy_large<U>;
229+
p.relocate_ = &relocate_large<U>;
230+
return p;
231+
}
232+
};
233+
234+
template<class T, std::size_t N>
235+
void swap(
236+
small_unique_ptr<T, N>& lhs,
237+
small_unique_ptr<T, N>& rhs) noexcept
238+
{
239+
lhs.swap(rhs);
240+
}
241+
242+
template<class T, std::size_t N, class U, class... Args>
243+
typename std::enable_if<
244+
std::is_convertible<U*, T*>::value,
245+
small_unique_ptr<T, N>>::type
246+
make_small_unique(Args&&... args)
247+
{
248+
return small_unique_ptr<T, N>::template emplace<U>(
249+
std::forward<Args>(args)...);
250+
}
251+
252+
} // capy
253+
} // boost
254+
255+
#endif

0 commit comments

Comments
 (0)