22#define OBSERVABLE_UNIQUE_PTR_INCLUDED
33
44#include < cstddef>
5+ #include < cstdint>
6+ #include < cmath>
57#include < type_traits>
68#include < utility>
79#include < new>
@@ -37,6 +39,14 @@ namespace details {
3739 struct ptr_and_deleter : Deleter {
3840 T* data = nullptr ;
3941 };
42+
43+ // Struct providing an unsigned integer with at least `Bits` bits.
44+ template <std::size_t Bits>
45+ struct unsigned_least : std::conditional<
46+ Bits <= 8 , std::uint_least8_t , std::conditional_t <
47+ Bits <= 16 , std::uint_least16_t , std::conditional_t <
48+ Bits <= 32 , std::uint_least32_t , std::conditional_t <
49+ Bits <= 64 , std::uint_least64_t , std::size_t >>>> {};
4050}
4151
4252// / Simple default deleter
@@ -72,7 +82,7 @@ struct placement_delete
7282* observed pointer has expired.
7383*/
7484struct default_observer_policy {
75- using refcount_type = int ; // TODO: change this into a request on max number of observer
85+ static constexpr std:: size_t max_observers = 2'000'000'000 ;
7686};
7787
7888// / Unique ownership (with release) policy
@@ -105,6 +115,10 @@ struct policy_queries {
105115 " enable_observer_from_this() must take a control block in its constructor if the "
106116 " policy is sealed and requires support for observer_from_this() in constructors." );
107117
118+ using observer_policy = typename Policy::observer_policy;
119+ using control_block_storage_type = typename details::unsigned_least<
120+ static_cast <std::size_t >(std::ceil(std::log2(observer_policy::max_observers)))>::type;
121+
108122 static constexpr bool eoft_base_is_virtual () noexcept {
109123 return Policy::allow_eoft_multiple_inheritance &&
110124 !Policy::eoft_constructor_takes_control_block;
@@ -128,6 +142,12 @@ struct policy_queries {
128142 }
129143};
130144
145+ template <typename Policy>
146+ struct observer_policy_queries {
147+ using control_block_storage_type = typename details::unsigned_least<1 +
148+ static_cast <std::size_t >(std::ceil(std::log2(Policy::max_observers)))>::type;
149+ };
150+
131151namespace details {
132152 template <typename Policy>
133153 struct enable_observer_from_this_base ;
@@ -152,15 +172,12 @@ class basic_control_block final {
152172 template <typename U, typename P, typename ... Args>
153173 friend auto oup::make_observable (Args&& ... args);
154174
155- using refcount_type = typename Policy::refcount_type ;
175+ using control_block_storage_type = typename observer_policy_queries< Policy>::control_block_storage_type ;
156176
157- enum flag_elements {
158- flag_none = 0 ,
159- flag_expired = 1
160- };
177+ static constexpr control_block_storage_type highest_bit_mask =
178+ 1 << (sizeof (control_block_storage_type) * 8 - 1 );
161179
162- refcount_type refcount = 1 ;
163- int flags = flag_none;
180+ control_block_storage_type storage = 1 ;
164181
165182 basic_control_block () noexcept = default ;
166183 basic_control_block (const basic_control_block&) = delete ;
@@ -169,30 +186,30 @@ class basic_control_block final {
169186 basic_control_block& operator =(basic_control_block&&) = delete ;
170187
171188 void push_ref () noexcept {
172- ++refcount ;
189+ ++storage ;
173190 }
174191
175192 void pop_ref () noexcept {
176- --refcount ;
193+ --storage ;
177194 if (has_no_ref ()) {
178195 delete this ;
179196 }
180197 }
181198
182199 bool has_no_ref () const noexcept {
183- return refcount == 0 ;
200+ return (storage ^ highest_bit_mask) == 0 ;
184201 }
185202
186203 bool expired () const noexcept {
187- return (flags & flag_expired ) != 0 ;
204+ return (storage & highest_bit_mask ) != 0 ;
188205 }
189206
190207 void set_not_expired () noexcept {
191- flags = flags & ~flag_expired ;
208+ storage = storage & ~highest_bit_mask ;
192209 }
193210
194211 void set_expired () noexcept {
195- flags = flags | flag_expired ;
212+ storage = storage | highest_bit_mask ;
196213 }
197214};
198215
@@ -271,9 +288,9 @@ constexpr bool has_enable_observer_from_this = std::is_base_of_v<
271288* `false` if a raw pointer can be acquired and released from this smart pointer, or
272289* `true` if a raw pointer is forever "sealed" into this smart pointer. When this
273290* policy is set to `true`, the control block and the managed object are allocated
274- * separately into a single buffer. This allows memory optimisations , but introduces
291+ * separately into a single buffer. This allows memory optimizations , but introduces
275292* multiple limitations on the API (no @ref release or @ref reset, and cannot create
276- * @ref observer_ptr from the object's constructor if inheriting from
293+ * @ref basic_observer_ptr from the object's constructor if inheriting from
277294* @ref basic_enable_observer_from_this).
278295* - `Policy::allow_eoft_in_constructor`: This must evaluate to a constexpr boolean value,
279296* which is `true` if @ref basic_enable_observer_from_this::observer_from_this must return
@@ -289,10 +306,11 @@ constexpr bool has_enable_observer_from_this = std::is_base_of_v<
289306* block through its constructor, forcing the object to accept a control block itself so it
290307* can be forwarded to @ref basic_enable_observer_from_this. If `false`,
291308* @ref basic_enable_observer_from_this only has a default constructor.
292- * - `Policy::observer_policy::refcount_type`: This must be an integer type
293- * (signed or unsigned) holding the number of observer references. The larger the
294- * type, the more concurrent references to the same object can exist, but the larger
295- * the memory overhead.
309+ * - `Policy::observer_policy::max_observers`: This must evaluate to a constexpr integer value,
310+ * representing the maximum number of observers for a given object that the library will
311+ * support. This is used to define the integer type holding the number of observer references.
312+ * The larger the type, the more concurrent references to the same object can exist, but the
313+ * larger the memory overhead.
296314*
297315* This smart pointer is meant to be used alongside @ref basic_observer_ptr, which is able
298316* to observe the lifetime of the stored raw pointer, without ownership.
0 commit comments