qxLib
sbo_poly.inl
Go to the documentation of this file.
1 /**
2 
3  @file sbo_poly.inl
4  @author Khrapov
5  @date 20.12.2025
6  @copyright (c) Nick Khrapov, 2025. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 template<class base_t, size_t nSBOSize_>
14  requires(nSBOSize_ > sizeof(void*))
15 template<sbo_poly_assignable_c<base_t> derived_t>
16 sbo_poly<base_t, nSBOSize_>::sbo_poly(derived_t&& object)
17 {
18  assign(std::forward<derived_t>(object));
19 }
20 
21 template<class base_t, size_t nSBOSize_>
22  requires(nSBOSize_ > sizeof(void*))
24 {
25  *this = std::move(other);
26 }
27 
28 template<class base_t, size_t nSBOSize_>
29  requires(nSBOSize_ > sizeof(void*))
31 {
32  // may be empty if an object was moved
33  if (m_Operations)
34  m_Operations->Destroy(m_Data);
35 }
36 
37 template<class base_t, size_t nSBOSize_>
38  requires(nSBOSize_ > sizeof(void*))
39 template<sbo_poly_assignable_c<base_t> derived_t>
41 {
42  assign(std::forward<derived_t>(object));
43  return *this;
44 }
45 
46 template<class base_t, size_t nSBOSize_>
47  requires(nSBOSize_ > sizeof(void*))
49 {
50  if (this == &other)
51  return *this;
52 
53  // destroy the current object
54 
55  if (m_Operations)
56  m_Operations->Destroy(m_Data);
57 
58  // required for exception safety if the move operation throws an exception
59  m_Operations = nullptr;
60 
61  // move the other object to this one
62 
63  if (!other.m_Operations)
64  return *this;
65 
66  if (other.m_Data.is_small())
67  {
68  other.m_Operations->Move(other.m_Data, m_Data);
69  other.m_Operations->Destroy(other.m_Data);
70  }
71  else
72  {
73  // Heap-backed objects can transfer ownership without moving the object itself.
74  m_Data = std::move(other.m_Data);
75  }
76 
77  m_Operations = other.m_Operations;
78  other.m_Operations = nullptr;
79 
80  return *this;
81 }
82 
83 template<class base_t, size_t nSBOSize_>
84  requires(nSBOSize_ > sizeof(void*))
85 template<sbo_poly_assignable_c<base_t> derived_t>
86 void sbo_poly<base_t, nSBOSize_>::assign(derived_t&& object)
87 {
88  using stored_t = std::remove_cvref_t<derived_t>;
89 
90  if (m_Operations)
91  m_Operations->Destroy(m_Data);
92 
93  m_Operations = nullptr;
94 
95  if (!m_Data.resize(get_storage_size<stored_t>()))
96  throw std::bad_alloc();
97 
98  new (get_object<stored_t>(m_Data)) stored_t(std::forward<derived_t>(object));
99  m_Operations = &get_operations<stored_t>();
100 }
101 
102 template<class base_t, size_t nSBOSize_>
103  requires(nSBOSize_ > sizeof(void*))
104 template<sbo_poly_assignable_c<base_t> derived_t>
105 constexpr size_t sbo_poly<base_t, nSBOSize_>::get_storage_size() noexcept
106 {
107  if constexpr (alignof(derived_t) <= alignof(sbo_bytes_type))
108  return sizeof(derived_t);
109  else
110  return sizeof(derived_t) + alignof(derived_t) - 1;
111 }
112 
113 template<class base_t, size_t nSBOSize_>
114  requires(nSBOSize_ > sizeof(void*))
115 template<sbo_poly_assignable_c<base_t> derived_t>
116 derived_t* sbo_poly<base_t, nSBOSize_>::get_object(sbo_bytes_type& data) noexcept
117 {
118  void* pObject = data.data();
119  size_t nSpace = data.size();
120  return static_cast<derived_t*>(std::align(alignof(derived_t), sizeof(derived_t), pObject, nSpace));
121 }
122 
123 template<class base_t, size_t nSBOSize_>
124  requires(nSBOSize_ > sizeof(void*))
125 template<sbo_poly_assignable_c<base_t> derived_t>
127 {
128  static constexpr operations table { [](sbo_bytes_type& object) noexcept -> base_t*
129  {
130  return static_cast<base_t*>(get_object<derived_t>(object));
131  },
132  [](sbo_bytes_type& from, sbo_bytes_type& to) noexcept
133  {
134  if (!to.resize(get_storage_size<derived_t>()))
135  std::terminate();
136 
137  new (get_object<derived_t>(to))
138  derived_t(std::move(*get_object<derived_t>(from)));
139  },
140  [](sbo_bytes_type& object) noexcept
141  {
142  get_object<derived_t>(object)->~derived_t();
143  } };
144 
145  return table;
146 }
147 
148 template<class base_t, size_t nSBOSize_>
149  requires(nSBOSize_ > sizeof(void*))
151 {
152  return &get();
153 }
154 
155 template<class base_t, size_t nSBOSize_>
156  requires(nSBOSize_ > sizeof(void*))
157 const base_t* sbo_poly<base_t, nSBOSize_>::operator->() const noexcept
158 {
159  return &get();
160 }
161 
162 template<class base_t, size_t nSBOSize_>
163  requires(nSBOSize_ > sizeof(void*))
164 base_t& sbo_poly<base_t, nSBOSize_>::get() noexcept
165 {
166  return *m_Operations->Get(m_Data);
167 }
168 
169 template<class base_t, size_t nSBOSize_>
170  requires(nSBOSize_ > sizeof(void*))
171 const base_t& sbo_poly<base_t, nSBOSize_>::get() const noexcept
172 {
173  return QX_CONST_CAST_THIS()->get();
174 }
175 
176 } // namespace qx
Small Buffer Object for polymorphic classes.
requires(same_variadic_args_v< args_t... >) const expr auto coalesce(args_t &&... args)
Coalesce function, C# a ?? b analogue.
Definition: coalesce.inl:57
#define QX_CONST_CAST_THIS()
This macro is made for situations where you have a const method, and you need exactly the same method...
Definition: common.h:63