Skip to content

Commit b697b94

Browse files
authoredNov 11, 2021
Merge pull request #3 from cschreib/enable_observer_from_this
Add `enable_observer_from_this` and fix self assignment/swap
2 parents c8acb1f + 91ef2e1 commit b697b94

File tree

4 files changed

+645
-52
lines changed

4 files changed

+645
-52
lines changed
 

‎README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ 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.
87+
8688

8789
## Limitations
8890

@@ -192,4 +194,4 @@ Detail of the benchmarks:
192194

193195
## Alternative implementation
194196

195-
An alternative implementation of an "observable unique pointer" can be found [here](https://www.codeproject.com/articles/1011134/smart-observers-to-use-with-unique-ptr). It does not compile out of the box with gcc unfortunately, but it does contain more features (like creating an observer pointer from a raw `this`) and lacks others (their `make_observable` always performs two allocations). Have a look to check if this better suits your needs.
197+
An alternative implementation of an "observable unique pointer" can be found [here](https://www.codeproject.com/articles/1011134/smart-observers-to-use-with-unique-ptr). It does not compile out of the box with gcc unfortunately and lacks certain features (their `make_observable` always performs two allocations). Have a look to check if this better suits your needs.

‎include/oup/observable_unique_ptr.hpp

+129-51
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ namespace oup {
1111
template<typename T>
1212
class observer_ptr;
1313

14+
template<typename T>
15+
class enable_observer_from_this;
16+
1417
namespace details {
18+
1519
struct control_block {
1620
enum flag_elements {
1721
flag_none = 0,
@@ -34,6 +38,7 @@ template<typename T, typename Deleter>
3438
struct ptr_and_deleter : Deleter {
3539
T* data = nullptr;
3640
};
41+
3742
}
3843

3944
/// Simple default deleter
@@ -75,6 +80,9 @@ struct placement_delete
7580
};
7681

7782
namespace details {
83+
84+
struct enable_observer_from_this_base {};
85+
7886
template<typename T, typename Deleter = oup::default_delete<T>>
7987
class observable_unique_ptr_base {
8088
protected:
@@ -106,6 +114,16 @@ class observable_unique_ptr_base {
106114
delete_and_pop_ref_(block, ptr_deleter.data, ptr_deleter);
107115
}
108116

117+
/// Fill in the observer pointer for objects inheriting from enable_observer_from_this.
118+
void set_this_observer_() noexcept {
119+
if constexpr (std::is_base_of_v<details::enable_observer_from_this_base, T>) {
120+
if (ptr_deleter.data) {
121+
ptr_deleter.data->this_observer.set_data_(block, ptr_deleter.data);
122+
++block->refcount;
123+
}
124+
}
125+
}
126+
109127
/// Private constructor using pre-allocated control block.
110128
/** \param ctrl The control block pointer
111129
* \param value The pointer to own
@@ -292,6 +310,10 @@ class observable_unique_ptr_base {
292310
/** \param other The other pointer to swap with
293311
*/
294312
void swap(observable_unique_ptr_base& other) noexcept {
313+
if (&other == this) {
314+
return;
315+
}
316+
295317
using std::swap;
296318
swap(block, other.block);
297319
swap(ptr_deleter, other.ptr_deleter);
@@ -345,6 +367,7 @@ class observable_unique_ptr_base {
345367
return ptr_deleter.data != nullptr;
346368
}
347369
};
370+
348371
}
349372

350373
/// Unique-ownership smart pointer, can be observed by observer_ptr, ownership can be released.
@@ -381,21 +404,6 @@ class observable_unique_ptr :
381404
return new control_block_type;
382405
}
383406

384-
static void pop_ref_(control_block_type* block) noexcept {
385-
--block->refcount;
386-
if (block->refcount == 0) {
387-
delete block;
388-
}
389-
}
390-
391-
static void delete_and_pop_ref_(control_block_type* block, T* data, Deleter& deleter) noexcept {
392-
deleter(data);
393-
394-
block->set_expired();
395-
396-
pop_ref_(block);
397-
}
398-
399407
// Friendship is required for conversions.
400408
template<typename U>
401409
friend class observer_ptr;
@@ -433,7 +441,9 @@ class observable_unique_ptr :
433441
* using make_observable_unique() instead of this constructor.
434442
*/
435443
explicit observable_unique_ptr(T* value) :
436-
base(value != nullptr ? allocate_block_() : nullptr, value) {}
444+
base(value != nullptr ? allocate_block_() : nullptr, value) {
445+
base::set_this_observer_();
446+
}
437447

438448
/// Explicit ownership capture of a raw pointer, with customer deleter.
439449
/** \param value The raw pointer to take ownership of
@@ -443,7 +453,9 @@ class observable_unique_ptr :
443453
* using make_observable_unique() instead of this constructor.
444454
*/
445455
explicit observable_unique_ptr(T* value, Deleter del) :
446-
base(value != nullptr ? allocate_block_() : nullptr, value, std::move(del)) {}
456+
base(value != nullptr ? allocate_block_() : nullptr, value, std::move(del)) {
457+
base::set_this_observer_();
458+
}
447459

448460
/// Transfer ownership by implicit casting
449461
/** \param value The pointer to take ownership from
@@ -474,7 +486,9 @@ class observable_unique_ptr :
474486
*/
475487
template<typename U, typename D>
476488
observable_unique_ptr(observable_unique_ptr<U,D>&& manager, T* value) noexcept :
477-
base(std::move(manager), value) {}
489+
base(std::move(manager), value) {
490+
base::set_this_observer_();
491+
}
478492

479493
/// Transfer ownership by explicit casting
480494
/** \param manager The smart pointer to take ownership from
@@ -485,7 +499,9 @@ class observable_unique_ptr :
485499
*/
486500
template<typename U, typename D>
487501
observable_unique_ptr(observable_unique_ptr<U,D>&& manager, T* value, Deleter del) noexcept :
488-
base(std::move(manager), value, del) {}
502+
base(std::move(manager), value, del) {
503+
base::set_this_observer_();
504+
}
489505

490506
/// Transfer ownership by implicit casting
491507
/** \param value The pointer to take ownership from
@@ -544,8 +560,10 @@ class observable_unique_ptr :
544560
// Delete the old pointer
545561
// (this follows std::unique_ptr specs)
546562
if (old_ptr) {
547-
delete_and_pop_ref_(old_block, old_ptr, base::ptr_deleter);
563+
base::delete_and_pop_ref_(old_block, old_ptr, base::ptr_deleter);
548564
}
565+
566+
base::set_this_observer_();
549567
}
550568

551569
/// Releases ownership of the managed object and mark observers as expired.
@@ -608,7 +626,9 @@ class observable_sealed_ptr :
608626
* \note This is used by make_observable_sealed().
609627
*/
610628
observable_sealed_ptr(control_block_type* ctrl, T* value) noexcept :
611-
base(ctrl, value, oup::placement_delete<T>{}) {}
629+
base(ctrl, value, oup::placement_delete<T>{}) {
630+
base::set_this_observer_();
631+
}
612632

613633
// Friendship is required for conversions.
614634
template<typename U>
@@ -744,7 +764,7 @@ observable_sealed_ptr<T> make_observable_sealed(Args&& ... args) {
744764
// Allocate memory
745765
constexpr std::size_t block_size = sizeof(block_type);
746766
constexpr std::size_t object_size = sizeof(T);
747-
std::byte* buffer = new std::byte[block_size + object_size];
767+
std::byte* buffer = reinterpret_cast<std::byte*>(operator new(block_size + object_size));
748768

749769
try {
750770
// Construct control block and object
@@ -756,7 +776,7 @@ observable_sealed_ptr<T> make_observable_sealed(Args&& ... args) {
756776
} catch (...) {
757777
// Exception thrown during object construction,
758778
// clean up memory and let exception propagate
759-
delete[] buffer;
779+
delete buffer;
760780
throw;
761781
}
762782
}
@@ -835,6 +855,9 @@ class observer_ptr {
835855
// Friendship is required for conversions.
836856
template<typename U>
837857
friend class observer_ptr;
858+
// Friendship is required for enable_observer_from_this.
859+
template<typename U, typename D>
860+
friend class details::observable_unique_ptr_base;
838861

839862
using control_block = details::control_block;
840863

@@ -848,6 +871,15 @@ class observer_ptr {
848871
}
849872
}
850873

874+
void set_data_(control_block* b, T* d) noexcept {
875+
if (data) {
876+
pop_ref_();
877+
}
878+
879+
block = b;
880+
data = d;
881+
}
882+
851883
public:
852884
/// Type of the pointed object
853885
using element_type = T;
@@ -936,12 +968,8 @@ class observer_ptr {
936968
*/
937969
template<typename U, typename D, typename enable = std::enable_if_t<std::is_convertible_v<U*, T*>>>
938970
observer_ptr& operator=(const observable_unique_ptr<U,D>& owner) noexcept {
939-
if (data) {
940-
pop_ref_();
941-
}
971+
set_data_(owner.block, owner.ptr_deleter.data);
942972

943-
block = owner.block;
944-
data = owner.ptr_deleter.data;
945973
if (block) {
946974
++block->refcount;
947975
}
@@ -956,12 +984,8 @@ class observer_ptr {
956984
*/
957985
template<typename U, typename enable = std::enable_if_t<std::is_convertible_v<U*, T*>>>
958986
observer_ptr& operator=(const observable_sealed_ptr<U>& owner) noexcept {
959-
if (data) {
960-
pop_ref_();
961-
}
987+
set_data_(owner.block, owner.ptr_deleter.data);
962988

963-
block = owner.block;
964-
data = owner.ptr_deleter.data;
965989
if (block) {
966990
++block->refcount;
967991
}
@@ -973,12 +997,12 @@ class observer_ptr {
973997
/** \param value The existing weak pointer to copy
974998
*/
975999
observer_ptr& operator=(const observer_ptr& value) noexcept {
976-
if (data) {
977-
pop_ref_();
1000+
if (&value == this) {
1001+
return *this;
9781002
}
9791003

980-
block = value.block;
981-
data = value.data;
1004+
set_data_(value.block, value.data);
1005+
9821006
if (block) {
9831007
++block->refcount;
9841008
}
@@ -993,12 +1017,12 @@ class observer_ptr {
9931017
*/
9941018
template<typename U, typename enable = std::enable_if_t<std::is_convertible_v<U*, T*>>>
9951019
observer_ptr& operator=(const observer_ptr<U>& value) noexcept {
996-
if (data) {
997-
pop_ref_();
1020+
if (&value == this) {
1021+
return *this;
9981022
}
9991023

1000-
block = value.block;
1001-
data = value.data;
1024+
set_data_(value.block, value.data);
1025+
10021026
if (block) {
10031027
++block->refcount;
10041028
}
@@ -1012,13 +1036,9 @@ class observer_ptr {
10121036
* pointer is set to null and looses ownership.
10131037
*/
10141038
observer_ptr& operator=(observer_ptr&& value) noexcept {
1015-
if (data) {
1016-
pop_ref_();
1017-
}
1039+
set_data_(value.block, value.data);
10181040

1019-
block = value.block;
10201041
value.block = nullptr;
1021-
data = value.data;
10221042
value.data = nullptr;
10231043

10241044
return *this;
@@ -1033,13 +1053,9 @@ class observer_ptr {
10331053
*/
10341054
template<typename U, typename enable = std::enable_if_t<std::is_convertible_v<U*, T*>>>
10351055
observer_ptr& operator=(observer_ptr<U>&& value) noexcept {
1036-
if (data) {
1037-
pop_ref_();
1038-
}
1056+
set_data_(value.block, value.data);
10391057

1040-
block = value.block;
10411058
value.block = nullptr;
1042-
data = value.data;
10431059
value.data = nullptr;
10441060

10451061
return *this;
@@ -1114,6 +1130,10 @@ class observer_ptr {
11141130
/** \param other The other pointer to swap with
11151131
*/
11161132
void swap(observer_ptr& other) noexcept {
1133+
if (&other == this) {
1134+
return;
1135+
}
1136+
11171137
using std::swap;
11181138
swap(block, other.block);
11191139
swap(data, other.data);
@@ -1153,6 +1173,64 @@ bool operator!= (const observer_ptr<T>& first, const observer_ptr<U>& second) no
11531173
return first.get() != second.get();
11541174
}
11551175

1176+
/// Enables creating an observer pointer from 'this'.
1177+
/** If an object must be able to create an observer pointer to itself,
1178+
* without having direct access to the owner pointer (unique or sealed),
1179+
* then the object's class can inherit from enable_observer_from_this.
1180+
* This provides the observer_from_this() member function, which returns
1181+
* a new observer pointer to the object. For this mechanism to work,
1182+
* the class must inherit publicly from enable_observer_from_this,
1183+
* and the object must be owned by a unique or sealed pointer when
1184+
* calling observer_from_this(). If the latter condition is not satisfied,
1185+
* i.e., the object was allocated on the stack, or is owned by another
1186+
* type of smart pointer, then observer_from_this() will return nullptr.
1187+
*/
1188+
template<typename T>
1189+
class enable_observer_from_this : public details::enable_observer_from_this_base {
1190+
mutable observer_ptr<T> this_observer;
1191+
1192+
// Friendship is required for assignment of the observer.
1193+
template<typename U, typename D>
1194+
friend class details::observable_unique_ptr_base;
1195+
1196+
protected:
1197+
enable_observer_from_this() noexcept = default;
1198+
1199+
enable_observer_from_this(const enable_observer_from_this&) noexcept {
1200+
// Do not copy the other object's observer, this would be an
1201+
// invalid reference.
1202+
};
1203+
1204+
enable_observer_from_this(enable_observer_from_this&&) noexcept {
1205+
// Do not move the other object's observer, this would be an
1206+
// invalid reference.
1207+
};
1208+
1209+
~enable_observer_from_this() noexcept = default;
1210+
1211+
public:
1212+
1213+
/// Return an observer pointer to 'this'.
1214+
/** \return A new observer pointer pointing to 'this'.
1215+
* \note If 'this' is not owned by a unique or sealed pointer, i.e., if
1216+
* the object was allocated on the stack, or if it is owned by another
1217+
* type of smart pointer, then this function will return nullptr.
1218+
*/
1219+
observer_ptr<T> observer_from_this() {
1220+
return this_observer;
1221+
}
1222+
1223+
/// Return a const observer pointer to 'this'.
1224+
/** \return A new observer pointer pointing to 'this'.
1225+
* \note If 'this' is not owned by a unique or sealed pointer, i.e., if
1226+
* the object was allocated on the stack, or if it is owned by another
1227+
* type of smart pointer, then this function will return nullptr.
1228+
*/
1229+
observer_ptr<const T> observer_from_this() const {
1230+
return this_observer;
1231+
}
1232+
};
1233+
11561234
}
11571235

11581236
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.