Skip to content

Commit a08d5cd

Browse files
committed
Updated documentation
1 parent 783f969 commit a08d5cd

File tree

1 file changed

+103
-87
lines changed

1 file changed

+103
-87
lines changed

include/oup/observable_unique_ptr.hpp

Lines changed: 103 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,6 @@ struct policy_queries {
119119
!Policy::eoft_constructor_takes_control_block;
120120
}
121121

122-
static constexpr bool eoft_allow_default_constructor() noexcept {
123-
return !eoft_base_constructor_needs_block();
124-
}
125-
126122
static constexpr bool owner_allow_release() noexcept {
127123
return !Policy::is_sealed;
128124
}
@@ -215,7 +211,7 @@ namespace details {
215211

216212
mutable control_block_type* this_control_block = nullptr;
217213

218-
enable_observer_from_this_base() noexcept(queries::eoft_constructor_allocates()) {
214+
enable_observer_from_this_base() noexcept(!queries::eoft_constructor_allocates()) {
219215
if constexpr (queries::eoft_constructor_allocates()) {
220216
this_control_block = new control_block_type;
221217
}
@@ -263,31 +259,40 @@ template<typename T, typename Policy>
263259
constexpr bool has_enable_observer_from_this = std::is_base_of_v<
264260
details::enable_observer_from_this_base<Policy>, T>;
265261

262+
static constexpr bool allow_eoft_in_constructor = true;
263+
static constexpr bool allow_eoft_multiple_inheritance = true;
264+
static constexpr bool eoft_constructor_takes_control_block = true;
265+
266266
/// Generic class for observable owning pointers.
267267
/** This is a generic class, configurable with policies. See @ref observable_unique_ptr and
268268
* @ref observable_sealed_ptr for more user-friendly usage and pre-configured policies.
269269
* The available policies are:
270270
* - `Policy::is_sealed`: This must evaluate to a constexpr boolean value, which is
271-
* `false` if a raw pointer can be acquired and released from this smart pointer, or
272-
* `true` if a raw pointer is forever "sealed" into this smart pointer. When this
273-
* 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
275-
* 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
277-
* @ref basic_enable_observer_from_this).
278-
* - `Policy::is_eoft_base_virtual`: This must evaluate to a constexpr boolean value,
279-
* which is `true` if @ref basic_enable_observer_from_this is allowed to use virtual
280-
* inheritance in its implementation, and `false` otherwise. Using virtual inheritance
281-
* offers more convenience, such as natural support for multiple inheritance of
282-
* @ref basic_enable_observer_from_this.
283-
* TODO: split this policy into:
284-
* - allow_eoft_multiple_inheritance
285-
* - allow_eoft_in_constructor
286-
* - then define the rest from that; policies should dictate requirements on API, not impl
287-
* - `Policy::observer_policy::refcount_type`: This must be an integer type
288-
* (signed or unsigned) holding the number of observer references. The larger the
289-
* type, the more concurrent references to the same object can exist, but the larger
290-
* the memory overhead.
271+
* `false` if a raw pointer can be acquired and released from this smart pointer, or
272+
* `true` if a raw pointer is forever "sealed" into this smart pointer. When this
273+
* 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
275+
* 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
277+
* @ref basic_enable_observer_from_this).
278+
* - `Policy::allow_eoft_in_constructor`: This must evaluate to a constexpr boolean value,
279+
* which is `true` if @ref basic_enable_observer_from_this::observer_from_this must return
280+
* a valid observer pointer if called within the object's constructor. If 'false', the
281+
* function is allowed to fail in this scenario (throw).
282+
* - `Policy::allow_eoft_multiple_inheritance`: This must evaluate to a constexpr boolean value,
283+
* which is `true` if @ref basic_enable_observer_from_this must support multiple inheritance,
284+
* such that an object may inherit from @ref basic_enable_observer_from_this, directly or
285+
* indirectly, from two or more of its base classes. If `false`, attempting to use multiple
286+
* inheritance in this fashion will cause a compiler error.
287+
* - `Policy::eoft_constructor_takes_control_block`: This must evaluate to a constexpr boolean
288+
* value, which is `true` if @ref basic_enable_observer_from_this must request its control
289+
* block through its constructor, forcing the object to accept a control block itself so it
290+
* can be forwarded to @ref basic_enable_observer_from_this. If `false`,
291+
* @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.
291296
*
292297
* This smart pointer is meant to be used alongside @ref basic_observer_ptr, which is able
293298
* to observe the lifetime of the stored raw pointer, without ownership.
@@ -1184,43 +1189,52 @@ namespace details {
11841189
* a new observer pointer to the object. For this mechanism to work,
11851190
* the class must inherit publicly from @ref basic_enable_observer_from_this.
11861191
*
1187-
* If `Policy::is_sealed` is true, then the object must be owned by a @ref basic_observable_ptr
1188-
* instance when calling @ref observer_from_this(). If the latter condition is not satisfied,
1189-
* then @ref observer_from_this() will throw @ref bad_observer_from_this.
1192+
* The behavior and API of this class are dependent on the chosen policies.
1193+
* Using the following shortcuts:
1194+
* - `Policy::is_sealed` = `S`
1195+
* - `Policy::allow_eoft_in_constructor` = `C`
1196+
* - `Policy::allow_eoft_multiple_inheritance` = `M`
1197+
* - `Policy::eoft_constructor_takes_control_block` = `B`
11901198
*
1191-
* If `Policy::is_eoft_base_virtual` is true, then:
1192-
* - if `Policy::is_sealed` is false, @ref basic_enable_observer_from_this will
1193-
* allocate its own control block in the default constructor, and this control
1194-
* block will be used by @ref basic_observable_ptr. This enables using
1195-
* @ref observer_from_this() at all times, even during the object's constructor,
1196-
* and even if the object is not owned by an instance of @ref basic_observable_ptr.
1197-
* The downside is that the default constructor allocates, and thus cannot be noexcept.
1198-
* The object `T` can have a default constructor.
1199-
* - if `Policy::is_sealed` is true, the control block will already be created by
1200-
* @ref make_observable, so @ref basic_enable_observer_from_this will not allocate its own
1201-
* control block in the default constructor, which is thus noexcept. The object `T` can have a
1202-
* default constructor. This means, however, that @ref observer_from_this() may only be called
1203-
* if `T` was created by @ref make_observable, and only after @ref make_observable has returned,
1204-
* otherwise @ref bad_observer_from_this will be thrown. In paticular, @ref observer_from_this()
1205-
* may not be called from within `T`'s constructor. If this is an issue, consider setting
1206-
* `Policy::is_eoft_base_virtual` to false.
1199+
* The behavior table is as follows:
1200+
* | S | C | M | B | API | Notes |
1201+
* |---|---|---|---|----------|-------------------|
1202+
* | 0 | 0 | 0 | 0 | a | |
1203+
* | 1 | 0 | 0 | 0 | a | |
1204+
* | 0 | 1 | 0 | 0 | b | |
1205+
* | 1 | 1 | 0 | 0 | *error* | |
1206+
* | 0 | 0 | 1 | 0 | c | |
1207+
* | 1 | 0 | 1 | 0 | c | |
1208+
* | 0 | 1 | 1 | 0 | d | `unique_policy` |
1209+
* | 1 | 1 | 1 | 0 | *error* | |
1210+
* | 0 | 0 | 0 | 1 | e | |
1211+
* | 1 | 0 | 0 | 1 | e | |
1212+
* | 0 | 1 | 0 | 1 | e | |
1213+
* | 1 | 1 | 0 | 1 | e | |
1214+
* | 0 | 0 | 1 | 1 | e | |
1215+
* | 1 | 0 | 1 | 1 | e | |
1216+
* | 0 | 1 | 1 | 1 | e | |
1217+
* | 1 | 1 | 1 | 1 | e | `sealed_policy` |
12071218
*
1208-
* If `Policy::is_eoft_base_virtual` is false, then `T`'s constructor must take
1209-
* a non-const reference to a control block object as first argument, and forward it to
1210-
* this class. Instances of `T` are then only constructible through @ref make_observable,
1211-
* which will take care of creating one control block and forwarding it to `T`'s constructor.
1212-
* This enables using @ref observer_from_this() at all times, even during the object's constructor,
1213-
* and (if `Policy::is_sealed` is false) even if `T` is later released from its
1214-
* @ref basic_observable_ptr. The downside is that the object `T` cannot have a default
1215-
* constructor, and that child classes of `T` must forward the control block reference in their
1216-
* own constructor. Likewise, for this reason, `T` cannot be copied nor moved.
1219+
* APIs:
1220+
* - `a`: No virtual inheritance. Default constructor is allowed and is noexcept
1221+
* (does not allocate). @ref observer_from_this can fail is thus not noexcept.
1222+
* - `b`: No virtual inheritance. Default constructor is allowed and is not noexcept
1223+
* (allocates). @ref observer_from_this cannot fail is thus noexcept.
1224+
* - `c`: Same as `a`, but virtual inheritance is used.
1225+
* - `d`: Same as `b`, but virtual inheritance is used.
1226+
* - `e`: No virtual inheritance. Default constructor is not allowed. The object `T`
1227+
* must take a control block as its first argument for all its constructors,
1228+
* and forward it to @ref basic_enable_observer_from_this. This prevents making `T`
1229+
* copiable or movable. Instances of `T` must be created using @ref make_observable.
12171230
*
12181231
* **Corner cases.**
12191232
* - Multiple inheritance. If a class `A` inherits from both another class `B` and
12201233
* `basic_enable_observer_from_this<A,...>`, and if `B` also inherits from
12211234
* `basic_enable_observer_from_this<B,...>`, then @ref observer_from_this() will be an
1222-
* ambiguous call. But it can be resolved, and (contrary to `std::shared_ptr` and
1223-
* `std::enable_shared_from_this`) will return a valid pointer regardless of the policy:
1235+
* ambiguous call. But, if `Policy::allow_eoft_multiple_inheritance` is true (and contrary to
1236+
* `std::shared_ptr` and `std::enable_shared_from_this`), this can be resolved and will return
1237+
* a valid pointer:
12241238
*
12251239
* ```
12261240
* struct B : basic_enable_observer_from_this<B,Policy> {
@@ -1234,10 +1248,9 @@ namespace details {
12341248
* ptr->basic_enable_observer_from_this<B,Policy>::observer_from_this(); // valid B*
12351249
* ```
12361250
*
1237-
* - Calling `observer_from_this()` from the object's constructor. Contrary to `std::shared_ptr`,
1238-
* this is valid in all cases, except if both `Policy::is_sealed` and
1239-
* `Policy::is_eoft_base_virtual` are true. Then, @ref bad_observer_from_this will
1240-
* be thrown.
1251+
* - Calling `observer_from_this()` from the object's constructor. If
1252+
* `Policy::allow_eoft_in_constructor` is true (and contrary to `std::shared_ptr`),
1253+
* this is possible. Otherwise, @ref bad_observer_from_this will be thrown.
12411254
*
12421255
* \see enable_observer_from_this_unique
12431256
* \see enable_observer_from_this_sealed
@@ -1264,63 +1277,57 @@ class basic_enable_observer_from_this : public
12641277
using control_block_type = basic_control_block<observer_policy>;
12651278

12661279
/// Default constructor.
1267-
/** \note This constructor is only enabled if `Policy::is_eoft_base_virtual` is true.
1268-
* If `Policy::is_sealed` is false, this will allocate the control block,
1269-
* which prevents this constructor from being `noexcept`.
1280+
/** \note This constructor is only enabled if `Policy::eoft_constructor_takes_control_block` is
1281+
* false. If `Policy::is_sealed` is false and `Policy::allow_eoft_in_constructor` is
1282+
* true, this will allocate the control block, which prevents this constructor from being
1283+
* `noexcept`.
12701284
*/
12711285
template<typename U = T, typename enable =
1272-
std::enable_if_t<std::is_same_v<U,T> && queries::eoft_allow_default_constructor()>>
1286+
std::enable_if_t<std::is_same_v<U,T> && !queries::eoft_base_constructor_needs_block()>>
12731287
basic_enable_observer_from_this() noexcept(!queries::eoft_constructor_allocates()) {};
12741288

12751289
/// Early assignment of control block.
12761290
/** \param block The pre-allocated control block
12771291
* \note This constructor allows setting the control block pointer early
12781292
* in the creation of the object. This enables calling @ref observer_from_this
1279-
* from the object's constructor. This may only be used if `Policy::is_sealed` is
1280-
* true, and with @ref make_observable. @ref make_observable will allocate the control
1281-
* block and, if the object's constructor can take the control block as its first
1282-
* constructor argument, the control block will be forwarded to the constructor.
1293+
* from the object's constructor. This may only be used if
1294+
* `Policy::eoft_constructor_takes_control_block` is true, and with @ref make_observable.
1295+
* @ref make_observable will allocate the control block and, if the object's constructor
1296+
* can take the control block as its first constructor argument, the control block will
1297+
* be forwarded to the constructor.
12831298
*/
12841299
explicit basic_enable_observer_from_this(control_block_type& block) noexcept : base(block) {}
12851300

12861301
/// Copy constructor.
1287-
/** \note This constructor is only enabled if `Policy::is_eoft_base_virtual` is true.
1302+
/** \note This constructor is only enabled if `Policy::eoft_constructor_takes_control_block` is false.
12881303
*/
12891304
template<typename U = T, typename enable =
1290-
std::enable_if_t<std::is_same_v<U,T> && queries::eoft_base_is_virtual()>>
1305+
std::enable_if_t<std::is_same_v<U,T> && !queries::eoft_base_constructor_needs_block()>>
12911306
basic_enable_observer_from_this(const basic_enable_observer_from_this&)
12921307
noexcept(!queries::eoft_constructor_allocates()) {
12931308
// Do not copy the other object's observer, this would be an
12941309
// invalid reference.
12951310
};
12961311

12971312
/// Move constructor.
1298-
/** \note This constructor is only enabled if `Policy::is_eoft_base_virtual` is true.
1313+
/** \note This constructor is only enabled if `Policy::eoft_constructor_takes_control_block` is false.
12991314
*/
13001315
template<typename U = T, typename enable =
1301-
std::enable_if_t<std::is_same_v<U,T> && queries::eoft_base_is_virtual()>>
1316+
std::enable_if_t<std::is_same_v<U,T> && !queries::eoft_base_constructor_needs_block()>>
13021317
basic_enable_observer_from_this(basic_enable_observer_from_this&&)
13031318
noexcept(!queries::eoft_constructor_allocates()) {
13041319
// Do not move the other object's observer, this would be an
13051320
// invalid reference.
13061321
};
13071322

13081323
/// Copy assignment operator.
1309-
/** \note This operator is only enabled if `Policy::is_eoft_base_virtual` is true.
1310-
*/
1311-
template<typename U = T, typename enable =
1312-
std::enable_if_t<std::is_same_v<U,T> && queries::eoft_base_is_virtual()>>
13131324
basic_enable_observer_from_this& operator=(const basic_enable_observer_from_this&) noexcept {
13141325
// Do not copy the other object's observer, this would be an
13151326
// invalid reference.
13161327
return *this;
13171328
};
13181329

13191330
/// Move assignment operator.
1320-
/** \note This operator is only enabled if `Policy::is_eoft_base_virtual` is true.
1321-
*/
1322-
template<typename U = T, typename enable =
1323-
std::enable_if_t<std::is_same_v<U,T> && queries::eoft_base_is_virtual()>>
13241331
basic_enable_observer_from_this& operator=(basic_enable_observer_from_this&&) noexcept {
13251332
// Do not move the other object's observer, this would be an
13261333
// invalid reference.
@@ -1339,13 +1346,18 @@ class basic_enable_observer_from_this : public
13391346
* the object was allocated on the stack, or if it is owned by another
13401347
* type of smart pointer, then this function will return nullptr.
13411348
*/
1342-
observer_type observer_from_this() noexcept(queries::eoft_constructor_allocates()) {
1349+
observer_type observer_from_this()
1350+
noexcept(queries::eoft_constructor_allocates() ||
1351+
queries::eoft_base_constructor_needs_block()) {
1352+
13431353
static_assert(std::is_base_of_v<basic_enable_observer_from_this,std::decay_t<T>>,
13441354
"T must inherit from basic_enable_observer_from_this<T>");
13451355

1346-
if constexpr (!queries::eoft_constructor_allocates()) {
1347-
// This check is not needed if the constructor allocates; then we
1348-
// always have a valid control block and this cannot fail.
1356+
if constexpr (!queries::eoft_constructor_allocates() &&
1357+
!queries::eoft_base_constructor_needs_block()) {
1358+
// This check is not needed if the constructor allocates or if we ask for the
1359+
// control block in the constructor; then we always have a valid control block and
1360+
// this cannot fail.
13491361
if (!this->this_control_block) {
13501362
throw bad_observer_from_this{};
13511363
}
@@ -1360,14 +1372,18 @@ class basic_enable_observer_from_this : public
13601372
* the object was allocated on the stack, or if it is owned by another
13611373
* type of smart pointer, then this function will return nullptr.
13621374
*/
1363-
const_observer_type observer_from_this() const noexcept(queries::eoft_constructor_allocates()) {
1375+
const_observer_type observer_from_this() const
1376+
noexcept(queries::eoft_constructor_allocates() ||
1377+
queries::eoft_base_constructor_needs_block()) {
13641378

13651379
static_assert(std::is_base_of_v<basic_enable_observer_from_this,std::decay_t<T>>,
13661380
"T must inherit from basic_enable_observer_from_this<T>");
13671381

1368-
if constexpr (!queries::eoft_constructor_allocates()) {
1369-
// This check is not needed if the constructor allocates; then we
1370-
// always have a valid control block and this cannot fail.
1382+
if constexpr (!queries::eoft_constructor_allocates() &&
1383+
!queries::eoft_base_constructor_needs_block()) {
1384+
// This check is not needed if the constructor allocates or if we ask for the
1385+
// control block in the constructor; then we always have a valid control block and
1386+
// this cannot fail.
13711387
if (!this->this_control_block) {
13721388
throw bad_observer_from_this{};
13731389
}

0 commit comments

Comments
 (0)