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 © 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_ > 2 * sizeof(void*))
15 template<sbo_poly_assignable_c<base_t> derived_t>
16 sbo_poly<base_t, nSBOSize_>::sbo_poly(derived_t object) noexcept
17 {
18  assign(std::move(object));
19 }
20 
21 template<class base_t, size_t nSBOSize_>
22  requires(nSBOSize_ > 2 * sizeof(void*))
24 {
25  *this = std::move(other);
26 }
27 
28 template<class base_t, size_t nSBOSize_>
29  requires(nSBOSize_ > 2 * sizeof(void*))
31 {
32  // may be empty if an object was moved
33  if (m_Deleter)
34  m_Deleter(m_Data);
35 }
36 
37 template<class base_t, size_t nSBOSize_>
38  requires(nSBOSize_ > 2 * sizeof(void*))
39 template<sbo_poly_assignable_c<base_t> derived_t>
41 {
42  assign(std::move(object));
43  return *this;
44 }
45 
46 template<class base_t, size_t nSBOSize_>
47  requires(nSBOSize_ > 2 * sizeof(void*))
49 {
50  if (!m_Data.is_small() && !other.m_Data.is_small())
51  {
52  // both objects are big, it's safe to simply swap pointers
53  std::swap(m_Data, other.m_Data);
54  }
55  else
56  {
57  // one or both objects is small, we should call move constructors for them,
58  // because it isn't safe to use memmove for them.
59  // do it that way that we avoid allocations.
60 
61  sbo_bytes<sbo_poly_traits> temp;
62 
63  if (m_Data.is_small() && other.m_Data.is_small())
64  {
65  if (m_Assigner)
66  m_Assigner(m_Data, temp);
67 
68  other.m_Assigner(other.m_Data, m_Data);
69 
70  if (m_Assigner)
71  m_Assigner(temp, other.m_Data);
72  }
73  else if (m_Data.is_small())
74  {
75  if (m_Assigner)
76  m_Assigner(m_Data, temp);
77 
78  other.m_Data = std::move(m_Data);
79 
80  if (m_Assigner)
81  m_Assigner(temp, other.m_Data);
82  }
83  else
84  {
85  other.m_Assigner(other.m_Data, temp);
86  m_Data = std::move(other.m_Data);
87  other.m_Assigner(temp, m_Data);
88  }
89  }
90 
91  std::swap(m_Assigner, other.m_Assigner);
92  std::swap(m_Deleter, other.m_Deleter);
93 
94  return *this;
95 }
96 
97 template<class base_t, size_t nSBOSize_>
98  requires(nSBOSize_ > 2 * sizeof(void*))
99 template<sbo_poly_assignable_c<base_t> derived_t>
100 void sbo_poly<base_t, nSBOSize_>::assign(derived_t object) noexcept
101 {
102  if (m_Data.size() > 0)
103  get().~base_t();
104 
105  m_Data.resize(sizeof(derived_t));
106  new (m_Data.data()) derived_t(std::move(object));
107 
108  m_Assigner = [](sbo_bytes_type& from, sbo_bytes_type& to)
109  {
110  to.resize(sizeof(derived_t));
111  new (to.data()) derived_t(std::move(*reinterpret_cast<derived_t*>(from.data())));
112  };
113 
114  m_Deleter = [](sbo_bytes_type& object)
115  {
116  reinterpret_cast<derived_t*>(object.data())->~derived_t();
117  };
118 }
119 
120 template<class base_t, size_t nSBOSize_>
121  requires(nSBOSize_ > 2 * sizeof(void*))
123 {
124  return &get();
125 }
126 
127 template<class base_t, size_t nSBOSize_>
128  requires(nSBOSize_ > 2 * sizeof(void*))
129 const base_t* sbo_poly<base_t, nSBOSize_>::operator->() const noexcept
130 {
131  return &get();
132 }
133 
134 template<class base_t, size_t nSBOSize_>
135  requires(nSBOSize_ > 2 * sizeof(void*))
136 base_t& sbo_poly<base_t, nSBOSize_>::get() noexcept
137 {
138  return *reinterpret_cast<base_t*>(m_Data.data());
139 }
140 
141 template<class base_t, size_t nSBOSize_>
142  requires(nSBOSize_ > 2 * sizeof(void*))
143 const base_t& sbo_poly<base_t, nSBOSize_>::get() const noexcept
144 {
145  return QX_CONST_CAST_THIS()->get();
146 }
147 
148 } // 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