Skip to content

Commit 7ca887d

Browse files
committed
Added support for multiple inheritance with enable_observable_from_this
1 parent acf661c commit 7ca887d

File tree

4 files changed

+138
-22
lines changed

4 files changed

+138
-22
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ int main() {
8383
}
8484
```
8585

86-
As with `std::shared_ptr`/`std::weak_ptr`, if you need to obtain an observer pointer to an object when you only have `this` (i.e., from a member function), you can inherit from `oup::enable_observer_from_this<T>` to gain access to the `observer_from_this()` member function. This function will return a valid observer pointer as long as the object is owned by a unique or sealed pointer, and will return `nullptr` in all other cases.
86+
As with `std::shared_ptr`/`std::weak_ptr`, if you need to obtain an observer pointer to an object when you only have `this` (i.e., from a member function), you can inherit from `oup::enable_observer_from_this<T>` to gain access to the `observer_from_this()` member function. This function will return a valid observer pointer as long as the object is owned by a unique or sealed pointer, and will return `nullptr` in all other cases. Contrary to `std::enable_shared_from_this<T>`, this feature naturally supports multiple inheritance.
8787

8888

8989
## Limitations

include/oup/observable_unique_ptr.hpp

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,38 @@ struct placement_delete
7979
}
8080
};
8181

82+
template<typename T>
83+
class enable_observer_from_this;
84+
8285
namespace details {
8386

84-
struct enable_observer_from_this_base {};
87+
struct enable_observer_from_this_base {
88+
using control_block = details::control_block;
89+
90+
mutable control_block* this_control_block = nullptr;
91+
92+
void pop_ref_() noexcept {
93+
--this_control_block->refcount;
94+
if (this_control_block->refcount == 0) {
95+
delete this_control_block;
96+
}
97+
}
98+
99+
void set_control_block_(control_block* b) noexcept {
100+
if (this_control_block) {
101+
pop_ref_();
102+
}
103+
104+
this_control_block = b;
105+
if (this_control_block) {
106+
++this_control_block->refcount;
107+
}
108+
}
109+
110+
// Friendship is required for assignment of the observer.
111+
template<typename U, typename D>
112+
friend class observable_unique_ptr_base;
113+
};
85114

86115
template<typename T, typename Deleter = oup::default_delete<T>>
87116
class observable_unique_ptr_base {
@@ -122,13 +151,8 @@ class observable_unique_ptr_base {
122151
template<typename U>
123152
void set_this_observer_(U* p) noexcept {
124153
if constexpr (std::is_convertible_v<U*,const details::enable_observer_from_this_base*>) {
125-
using input_observer_type = std::decay_t<decltype(p->this_observer)>;
126-
using observer_element_type = typename input_observer_type::element_type;
127-
static_assert(std::is_convertible_v<U*, observer_element_type*>,
128-
"incompatible type specified in enable_observer_from_this");
129-
130154
if (p) {
131-
p->this_observer.set_data_(block, p);
155+
p->this_control_block = block;
132156
++block->refcount;
133157
}
134158
}
@@ -876,6 +900,9 @@ class observer_ptr {
876900
// Friendship is required for enable_observer_from_this.
877901
template<typename U, typename D>
878902
friend class details::observable_unique_ptr_base;
903+
// Friendship is required for enable_observer_from_this.
904+
template<typename U>
905+
friend class enable_observer_from_this;
879906

880907
using control_block = details::control_block;
881908

@@ -898,6 +925,13 @@ class observer_ptr {
898925
data = d;
899926
}
900927

928+
// For enable_observer_from_this
929+
observer_ptr(control_block* b, T* d) noexcept : block(b), data(d) {
930+
if (block) {
931+
++block->refcount;
932+
}
933+
}
934+
901935
public:
902936
/// Type of the pointed object
903937
using element_type = T;
@@ -1206,20 +1240,46 @@ bool operator!= (const observer_ptr<T>& first, const observer_ptr<U>& second) no
12061240
* **Corner cases.**
12071241
* - If a class A inherits from both another class B and enable_observer_from_this<A>,
12081242
* and it is owned by an observable_unique_ptr<B>. The function observer_from_this()
1209-
* will always return nullptr if ownership is taken from a pointer to B*, but will
1210-
* be a valid pointer if ownership is taken from a pointer to A*.
1243+
* will always return nullptr if ownership is acquired from a pointer to B*, but will
1244+
* return a valid pointer if ownership is acquired from a pointer to A*. Therefore,
1245+
* make sure to always acquire ownership on the most derived type, or simply use the
1246+
* factory function make_observable_unique() which will enforce this automatically.
1247+
*
1248+
* struct B {
1249+
* virtual ~B() = default;
1250+
* };
12111251
*
1212-
* observable_unique_ptr<B> p(new A); // observer pointer is valid
1213-
* observable_unique_ptr<B> p(static_cast<B*>(new A)); // observer pointer is nullptr
1252+
* struct A : B, enable_observer_from_this<A> {};
1253+
*
1254+
*
1255+
* observable_unique_ptr<B> good1(new A);
1256+
* dynamic_cast<A*>(good1.get())->observer_from_this(); // valid pointer
1257+
*
1258+
* observable_unique_ptr<B> good2(make_observable_unique<A>());
1259+
* dynamic_cast<A*>(good2.get())->observer_from_this(); // valid pointer
1260+
*
1261+
* // Bad: do not do this
1262+
* observable_unique_ptr<B> bad(static_cast<B*>(new A));
1263+
* dynamic_cast<A*>(bad.get())->observer_from_this(); // nullptr
1264+
*
1265+
* - Multiple inheritance. If a class A inherits from both another class B and
1266+
* enable_observer_from_this<A>, and if B also inherits from
1267+
* enable_observer_from_this<B>, then observer_from_this() will be an ambiguous
1268+
* call. But it can be resolved, and (contrary to std::shared_ptr and
1269+
* std::enable_shared_from_this) will return a valid pointer:
1270+
*
1271+
* struct B : enable_observer_from_this<B> {
1272+
* virtual ~B() = default;
1273+
* };
1274+
*
1275+
* struct A : B, enable_observer_from_this<A> {};
1276+
*
1277+
* observable_sealed_ptr<A> ptr = make_observable_sealed<A>();
1278+
* ptr->enable_observer_from_this<A>::observer_from_this(); // valid A* pointer
1279+
* ptr->enable_observer_from_this<B>::observer_from_this(); // valid B* pointer
12141280
*/
12151281
template<typename T>
1216-
class enable_observer_from_this : public details::enable_observer_from_this_base {
1217-
mutable observer_ptr<T> this_observer;
1218-
1219-
// Friendship is required for assignment of the observer.
1220-
template<typename U, typename D>
1221-
friend class details::observable_unique_ptr_base;
1222-
1282+
class enable_observer_from_this : public virtual details::enable_observer_from_this_base {
12231283
protected:
12241284
enable_observer_from_this() noexcept = default;
12251285

@@ -1233,18 +1293,39 @@ class enable_observer_from_this : public details::enable_observer_from_this_base
12331293
// invalid reference.
12341294
};
12351295

1236-
~enable_observer_from_this() noexcept = default;
1296+
~enable_observer_from_this() noexcept {
1297+
if (this_control_block) {
1298+
pop_ref_();
1299+
}
1300+
1301+
this_control_block = nullptr;
1302+
}
1303+
1304+
enable_observer_from_this& operator=(const enable_observer_from_this&) noexcept {
1305+
// Do not copy the other object's observer, this would be an
1306+
// invalid reference.
1307+
return *this;
1308+
};
1309+
1310+
enable_observer_from_this& operator=(enable_observer_from_this&&) noexcept {
1311+
// Do not move the other object's observer, this would be an
1312+
// invalid reference.
1313+
return *this;
1314+
};
12371315

12381316
public:
12391317

1318+
using observer_element_type = T;
1319+
12401320
/// Return an observer pointer to 'this'.
12411321
/** \return A new observer pointer pointing to 'this'.
12421322
* \note If 'this' is not owned by a unique or sealed pointer, i.e., if
12431323
* the object was allocated on the stack, or if it is owned by another
12441324
* type of smart pointer, then this function will return nullptr.
12451325
*/
12461326
observer_ptr<T> observer_from_this() {
1247-
return this_observer;
1327+
return observer_ptr<T>{this_control_block,
1328+
this_control_block ? static_cast<T*>(this) : nullptr};
12481329
}
12491330

12501331
/// Return a const observer pointer to 'this'.
@@ -1254,7 +1335,8 @@ class enable_observer_from_this : public details::enable_observer_from_this_base
12541335
* type of smart pointer, then this function will return nullptr.
12551336
*/
12561337
observer_ptr<const T> observer_from_this() const {
1257-
return this_observer;
1338+
return observer_ptr<const T>{this_control_block,
1339+
this_control_block ? static_cast<const T*>(this) : nullptr};
12581340
}
12591341
};
12601342

tests/runtime_tests.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3245,3 +3245,29 @@ TEST_CASE("observer from this heap", "[observer_from_this]") {
32453245
REQUIRE(mem_track.leaks() == 0u);
32463246
REQUIRE(mem_track.double_del() == 0u);
32473247
}
3248+
3249+
TEST_CASE("observer from this multiple inheritance", "[observer_from_this]") {
3250+
memory_tracker mem_track;
3251+
3252+
{
3253+
using this_base = oup::enable_observer_from_this<test_object_observer_from_this>;
3254+
using this_deriv = oup::enable_observer_from_this<test_object_observer_from_this_multi>;
3255+
3256+
test_object_observer_from_this_multi* raw_ptr_deriv = new test_object_observer_from_this_multi;
3257+
test_object_observer_from_this* raw_ptr_base = raw_ptr_deriv;
3258+
test_ptr_from_this_multi ptr(raw_ptr_deriv);
3259+
3260+
test_optr_from_this optr_from_this_base = ptr->this_base::observer_from_this();
3261+
test_optr_from_this_multi optr_from_this_deriv = ptr->this_deriv::observer_from_this();
3262+
3263+
REQUIRE(instances == 1);
3264+
REQUIRE(optr_from_this_base.expired() == false);
3265+
REQUIRE(optr_from_this_deriv.expired() == false);
3266+
REQUIRE(optr_from_this_base.get() == raw_ptr_base);
3267+
REQUIRE(optr_from_this_deriv.get() == raw_ptr_deriv);
3268+
}
3269+
3270+
REQUIRE(instances == 0);
3271+
REQUIRE(mem_track.leaks() == 0u);
3272+
REQUIRE(mem_track.double_del() == 0u);
3273+
}

tests/tests_common.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ struct test_object_observer_from_this :
4545
struct test_object_observer_from_this_derived :
4646
public test_object_observer_from_this {};
4747

48+
struct test_object_observer_from_this_multi :
49+
public test_object_observer_from_this,
50+
public oup::enable_observer_from_this<test_object_observer_from_this_multi> {};
51+
4852
struct test_deleter {
4953
int state_ = 0;
5054

@@ -86,10 +90,14 @@ using test_cptr_from_this = oup::observable_unique_ptr<const test_object_observe
8690
using test_csptr_from_this = oup::observable_sealed_ptr<const test_object_observer_from_this>;
8791
using test_ptr_from_this_derived = oup::observable_unique_ptr<test_object_observer_from_this_derived>;
8892
using test_sptr_from_this_derived = oup::observable_sealed_ptr<test_object_observer_from_this_derived>;
93+
using test_ptr_from_this_multi = oup::observable_unique_ptr<test_object_observer_from_this_multi>;
94+
using test_sptr_from_this_multi = oup::observable_sealed_ptr<test_object_observer_from_this_multi>;
8995

9096
using test_optr = oup::observer_ptr<test_object>;
9197
using test_optr_derived = oup::observer_ptr<test_object_derived>;
9298
using test_optr_from_this = oup::observer_ptr<test_object_observer_from_this>;
9399
using test_optr_from_this_const = oup::observer_ptr<const test_object_observer_from_this>;
94100
using test_optr_from_this_derived = oup::observer_ptr<test_object_observer_from_this_derived>;
95101
using test_optr_from_this_derived_const = oup::observer_ptr<const test_object_observer_from_this_derived>;
102+
using test_optr_from_this_multi = oup::observer_ptr<test_object_observer_from_this_multi>;
103+
using test_optr_from_this_multi_const = oup::observer_ptr<const test_object_observer_from_this_multi>;

0 commit comments

Comments
 (0)