qxLib
sbo_poly.h
Go to the documentation of this file.
1 /**
2 
3  @file sbo_poly.h
4  @author Khrapov
5  @date 20.12.2025
6  @copyright (c) Nick Khrapov, 2025. All right reserved.
7 
8 **/
9 #pragma once
10 
11 #include <qx/memory/sbo_bytes.h>
12 
13 #include <exception>
14 #include <memory>
15 #include <new>
16 #include <type_traits>
17 #include <utility>
18 
19 namespace qx
20 {
21 
22 // Check that the derived type is suitable for storing in sbo_poly
23 template<class T, class base_t>
24 concept sbo_poly_assignable_c =
25  std::is_base_of_v<base_t, std::remove_cvref_t<T>> && std::is_nothrow_move_constructible_v<std::remove_cvref_t<T>>
26  && std::is_nothrow_destructible_v<std::remove_cvref_t<T>> && std::is_constructible_v<std::remove_cvref_t<T>, T&&>;
27 
28 // Check that the derived type fits into the SBO buffer
29 template<class sbo_poly_t, sbo_poly_assignable_c<typename sbo_poly_t::base_type> derived_t>
31 {
32  static constexpr size_t nAlignmentPadding =
33  alignof(derived_t) <= alignof(typename sbo_poly_t::sbo_bytes_type) ? 0 : alignof(derived_t) - 1;
34  static constexpr bool value = sizeof(derived_t) + nAlignmentPadding <= sbo_poly_t::sbo_bytes_type::nBufferSize;
35 };
36 
37 template<class sbo_poly_t, sbo_poly_assignable_c<typename sbo_poly_t::base_type> derived_t>
38 constexpr bool sbo_poly_fittable_type_v = sbo_poly_fittable_type<sbo_poly_t, derived_t>::value;
39 
40 // If you have several inheritors, you can check that they all fit into your SBO
41 template<class sbo_poly_t, class... args_t>
43 {
44  static constexpr bool value = ((sbo_poly_fittable_type_v<sbo_poly_t, args_t>) && ...);
45 };
46 
47 template<class sbo_poly_t, class... args_t>
48 constexpr bool sbo_poly_fittable_types_v = sbo_poly_fittable_types<sbo_poly_t, args_t...>::value;
49 
50 /**
51 
52  @class sbo_poly
53  @brief Small Buffer Object for polymorphic classes.
54  @details If the inherited class fits into the specified buffer, it stores it in place;
55  if not, it allocates memory for it.
56  Allows you to increase cache locality when storing in containers.
57  @tparam base_t - base class type, you'll be able to work with each element through its interface
58  @tparam nSBOSize_ - the size of the whole SBO type, including internal data
59  @author Khrapov
60  @date 24.12.2025
61 
62 **/
63 template<class base_t, size_t nSBOSize_>
64  requires(nSBOSize_ > sizeof(void*))
65 class sbo_poly
66 {
67  struct sbo_poly_traits
68  {
69  using size_type = size_t;
70  static constexpr size_type nSBOSize = nSBOSize_ - sizeof(void*);
71  static constexpr bool bShrinkToFitWhenSmall = true;
72  static constexpr bool bPreserveContents = false;
73  static constexpr size_type growth_strategy(size_type nOldCapacity) noexcept
74  {
75  return nOldCapacity;
76  }
77  };
78 
79 public:
80  using sbo_bytes_type = sbo_bytes<sbo_poly_traits>;
81  using base_type = base_t;
82 
83 private:
84  struct operations
85  {
86  base_t* (*Get)(sbo_bytes_type& object) noexcept;
87  void (*Move)(sbo_bytes_type& from, sbo_bytes_type& to) noexcept;
88  void (*Destroy)(sbo_bytes_type& object) noexcept;
89  };
90 
91 public:
92  /**
93  @brief sbo_poly object constructor
94  @tparam derived_t - type inherited from base_t
95  @param object - an object to store
96  **/
97  template<sbo_poly_assignable_c<base_t> derived_t>
98  sbo_poly(derived_t&& object);
99 
100  sbo_poly(sbo_poly&& other) noexcept;
101  ~sbo_poly() noexcept;
102 
103  /**
104  @brief operator=
105  @tparam derived_t - type inherited from base_t
106  @param object - an object to store
107  @retval - this object reference
108  **/
109  template<sbo_poly_assignable_c<base_t> derived_t>
110  sbo_poly& operator=(derived_t&& object);
111 
112  sbo_poly& operator=(sbo_poly&& other) noexcept;
113 
114  /**
115  @brief Assign a new object to this SBO
116  @tparam derived_t - type inherited from base_t
117  @param object - an object to store
118  **/
119  template<sbo_poly_assignable_c<base_t> derived_t>
120  void assign(derived_t&& object);
121 
122 
123  base_t* operator->() noexcept;
124  const base_t* operator->() const noexcept;
125 
126  /**
127  @brief Get object reference. Always valid.
128  @retval - object reference
129  **/
130  base_t& get() noexcept;
131 
132  /**
133  @brief Get object reference. Always valid.
134  @retval - object reference
135  **/
136  const base_t& get() const noexcept;
137 
138 private:
139  /**
140  @brief Get type operations table
141  @tparam derived_t - type inherited from base_t
142  @retval - operations
143  **/
144  template<sbo_poly_assignable_c<base_t> derived_t>
145  static const operations& get_operations() noexcept;
146 
147  /**
148  @brief Get the number of bytes required to store an object with proper alignment
149  @tparam derived_t - type inherited from base_t
150  @retval - object size plus the worst-case alignment padding when required
151  **/
152  template<sbo_poly_assignable_c<base_t> derived_t>
153  static constexpr size_t get_storage_size() noexcept;
154 
155  /**
156  @brief Get a properly aligned pointer to an object in the storage
157  @tparam derived_t - type inherited from base_t
158  @param data - storage containing the object
159  @retval - aligned pointer to the object
160  **/
161  template<sbo_poly_assignable_c<base_t> derived_t>
162  static derived_t* get_object(sbo_bytes_type& data) noexcept;
163 
164 private:
165  sbo_bytes_type m_Data;
166  const operations* m_Operations = nullptr;
167 };
168 
169 } // namespace qx
170 
171 #include <qx/memory/sbo_poly.inl>
A type erased small buffer object that works with raw data.
Definition: sbo_bytes.h:31
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