@@ -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>
263259constexpr 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