Skip to content

Commit b2961da

Browse files
committed
Updated readme
1 parent f5f6dac commit b2961da

File tree

1 file changed

+16
-8
lines changed

1 file changed

+16
-8
lines changed

README.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Built and tested on:
1414
- [Introduction](#introduction)
1515
- [Usage](#usage)
1616
- [enable_observer_from_this](#enable_observer_from_this)
17+
- [Policies](#policies)
1718
- [Limitations](#limitations)
1819
- [Comparison spreadsheet](#comparison-spreadsheet)
1920
- [Speed benchmarks](#speed-benchmarks)
@@ -89,9 +90,14 @@ int main() {
8990

9091
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_unique<T>` or `oup::enable_observer_from_this_sealed<T>` (depending on the type of the owner pointer) to gain access to the `observer_from_this()` member function. Contrary to `std::enable_shared_from_this<T>`, this function is `noexcept` and is able to return a valid observer pointer at all times, even if the object is being constructed or is not owned by a unique or sealed pointer. Also contrary to `std::enable_shared_from_this<T>`, this feature naturally supports multiple inheritance.
9192

92-
To achieve this, the price to pay is that `oup::enable_observer_from_this_unique<T>` uses virtual inheritance, while `oup::enable_observer_from_this_sealed<T>` requires `T`'s constructor to take a control block as input (thereby preventing `T` from being default-constructible, copiable, or movable).
93+
To achieve this, the price to pay is that `oup::enable_observer_from_this_unique<T>` uses virtual inheritance, while `oup::enable_observer_from_this_sealed<T>` requires `T`'s constructor to take a control block as input (thereby preventing `T` from being default-constructible, copiable, or movable). If needed, these trade-offs can be controlled by policies, see below.
9394

94-
If these trade-offs are not appropriate for your use cases, they can be fine-tuned using the generic classes `basic_observable_pointer<T,Deleter,Policy>`, `basic_observer_ptr<T,Policy>`, and `basic_enable_observer_from_this<T,Policy>`. Please refer to the API documentation for more information on policies.
95+
96+
## Policies
97+
98+
Similarly to `std::string` and `std::basic_string`, this library provides both "convenience" types (`oup::observable_unique_ptr<T,Deleter>`, `oup::observable_sealed_ptr<T>`, `oup::observer_ptr<T>`, `oup::enable_observable_from_this_unique<T>`, `oup::enable_observable_from_this_sealed<T>`) and "generic" types (`oup::basic_observable_ptr<T,Deleter,Policy>`, `oup::basic_observer_ptr<T,ObsPolicy>`, `oup::basic_enable_observable_from_this<T,Policy>`).
99+
100+
If the trade-offs chosen to defined the "convenience" types are not appropriate for your use cases, they can be fine-tuned using the generic classes and providing your own choice of policies. Please refer to the documentation for more information on policies. In particular, policies will control most of the API and behavior of the `enable_observable_from_this` feature, as well as allowing you to tune the size of the reference counting object (speed/memory trade-off).
95101

96102

97103
## Limitations
@@ -125,23 +131,25 @@ Labels:
125131
| Support arrays | yes | yes | no | yes | yes | no | no |
126132
| Support custom allocator | N/A | yes | no | yes | yes | no | no |
127133
| Support custom deleter | N/A | N/A | N/A | yes | yes(4) | yes | no |
128-
| Number of heap alloc. | 0 | 0 | 0 | 1 | 1/2(5) | 2 | 1 |
134+
| Max number of observers | inf. | ?(5) | 2^31 - 1 | 1 | ?(5) | 1 | 1 |
135+
| Number of heap alloc. | 0 | 0 | 0 | 1 | 1/2(6) | 2 | 1 |
129136
| Size in bytes (64 bit) | | | | | | | |
130137
| - Stack (per instance) | 8 | 16 | 16 | 8 | 16 | 16 | 16 |
131-
| - Heap (shared) | 0 | 0 | 0 | 0 | 24 | 8 | 8 |
132-
| - Total | 8 | 16 | 16 | 8 | 40 | 24 | 24 |
138+
| - Heap (shared) | 0 | 0 | 0 | 0 | 24 | 4 | 4 |
139+
| - Total | 8 | 16 | 16 | 8 | 40 | 20 | 20 |
133140
| Size in bytes (32 bit) | | | | | | | |
134141
| - Stack (per instance) | 4 | 8 | 8 | 4 | 8 | 8 | 8 |
135-
| - Heap (shared) | 0 | 0 | 0 | 0 | 16 | 8 | 8 |
136-
| - Total | 4 | 8 | 8 | 4 | 24 | 16 | 16 |
142+
| - Heap (shared) | 0 | 0 | 0 | 0 | 16 | 4 | 4 |
143+
| - Total | 4 | 8 | 8 | 4 | 24 | 12 | 12 |
137144

138145
Notes:
139146

140147
- (1) If `expired()` returns true, the pointer is guaranteed to remain `nullptr` forever, with no race condition. If `expired()` returns false, the pointer could still expire on the next instant, which can lead to race conditions.
141148
- (2) By construction, only one thread can own the pointer, therefore deletion is thread-safe.
142149
- (3) Yes if using `std::atomic<std::shared_ptr<T>>` and `std::atomic<std::weak_ptr<T>>`.
143150
- (4) Not if using `std::make_shared()`.
144-
- (5) 2 by default, or 1 if using `std::make_shared()`.
151+
- (5) Not defined by the C++ standard. In practice, libstdc++ stores its reference count on an `_Atomic_word`, which for a common 64bit linux platform is a 4 byte signed integer, hence the limit will be 2^31 - 1. Microsoft's STL uses `_Atomic_counter_t`, which for a 64bit Windows platform is 4 bytes unsigned integer, hence the limit will be 2^32 - 1.
152+
- (6) 2 by default, or 1 if using `std::make_shared()`.
145153

146154

147155
## Speed benchmarks

0 commit comments

Comments
 (0)