Skip to content

Commit f5f6dac

Browse files
committed
Using policy to set the max number of observers
1 parent bc8042b commit f5f6dac

File tree

1 file changed

+38
-20
lines changed

1 file changed

+38
-20
lines changed

include/oup/observable_unique_ptr.hpp

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
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
*/
7484
struct 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+
131151
namespace 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

Comments
 (0)