ALib C++ Library
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
sharedptr.inl
Go to the documentation of this file.
1//==================================================================================================
2/// \file
3/// This header-file is part of module \alib_containers of the \aliblong.
4///
5/// \emoji :copyright: 2013-2025 A-Worx GmbH, Germany.
6/// Published under \ref mainpage_license "Boost Software License".
7//==================================================================================================
8ALIB_EXPORT namespace alib { namespace containers {
9
10//==================================================================================================
11/// This templated class lifts restrictions imposed by (otherwise slightly more efficient)
12/// class \alib{containers;SharedVal} and is an almost full-featured alternative for
13/// C++ standard library type
14/// \https{std::shared_ptr,en.cppreference.com/w/cpp/memory/shared_ptr}.
15///
16/// What is missing compared to <c>std::shared_ptr</c>:
17/// - Pointers to externally allocated objects cannot be assigned.
18/// The shared objects are always created with construction of this class or with method
19/// #InsertDerived .
20/// - Special method #InsertDerived is to be used, in case types derived from \p{T} are to be
21/// placed inside.
22/// - Overall, the interface is more explicit and seems less convenient.
23/// - This implementation misses the coexistence of sibling type <c>std::weak_ptr</c> and
24/// corresponding functionality.
25/// - This implementation misses an equivalent to method <c>owner_before</c> and corresponding
26/// comparison operators.
27/// - This implementation misses dedicated array support (at least as of today).
28///
29/// Advantages are:
30/// - The type has a footprint of only <c>sizeof(void*)</c>, where the standard's type has
31/// a size of two pointers.
32/// - The type performs only one allocation.
33/// (Common implementations of the standard's type perform two allocations.)
34/// - The type supports storing references to \alib {lang;Allocator;allocators} which are
35/// used for allocation and freeing of memory. (In this case, of course, the footprint
36/// increases to two times the <c>sizeof(void*)</c>.)
37///
38/// \see
39/// - Sibling class \alib{containers;SharedVal}, with is a restricted but slightly more efficient
40/// version of this class.
41/// - Class \alib{monomem;TSharedMonoVal} of module \alib_monomem, which incorporates
42/// an own embedded instance of class \alib{monomem;TMonoAllocator}.
43/// This allocator can be used for further monotonic allocations by the contained type or
44/// other code entities that receive the shared pointer.
45/// @tparam T The custom type that is shared with this pointer.
46/// @tparam TAllocator The allocator that is used to allocate an instance of \p{T} together
47/// with a reference counter and optionally a reference to such allocator
48/// if passed with construction.
49//==================================================================================================
50template<typename T, typename TAllocator= HeapAllocator>
52{
53 protected:
54 /// The combined struct of members which are from the \p{TAllocator} passed in the constructor.
55 /// Note, that in the case that \alib{lang;HeapAllocator} is used parent class
56 /// \b AllocatorMember will be empty.
57 template<typename U>
59 {
60 /// The duly cast pointer to the custom type behind us.
62
63 /// The size of the allocated pair of these fields and the custom type.
64 size_t allocSize;
65
66 /// The reference counter used to implement the <c>std::shared_ptr</c> behavior.
67 std::atomic<unsigned> refCount;
68
69 /// The instance, either derived or T.
70 U u;
71
72 /// Constructor.
73 /// @param pAllocator The allocator, stored in parent \b AllocatorMember.
74 /// @param pCustom The pointer to #custom, rightfully cast.
75 /// @param pAllocSize The size of the allocated pair, containing this and the
76 /// potentially derived object \p{T}.
77 FieldMembers( TAllocator& pAllocator, T* pCustom, size_t pAllocSize)
78 : lang::AllocatorMember<TAllocator>( pAllocator )
79 , custom (pCustom)
80 , allocSize(pAllocSize)
81 , refCount (1) {}
82
83 /// Alternative constructor missing the allocator instance.
84 /// @param pAllocSize The size of the allocated pair, containing this and the
85 /// potentially derived object \p{T}.
86 /// @param pCustom The pointer to #custom, rightfully cast.
87 FieldMembers(T* pCustom, size_t pAllocSize)
88 : custom (pCustom)
89 , allocSize(pAllocSize)
90 , refCount (1) {}
91 }; // struct FieldMembers;
92
93 /// The allocated stored data. Note that \p{T} can be a derived type.
94 FieldMembers<void*>* members;
95
96 /// Internal shortcut to receive the custom member.
97 /// @return The pointer to the contained type, or \c nullptr in case this is empty.
98 T* getP() const noexcept { return members ? reinterpret_cast<T*>(&members->u) : nullptr; }
99
100 /// Internal shortcut to receive a reference to the custom member.
101 /// Asserts in debug-compilations.
102 /// @return A reference the contained type.
103 T& getR() const noexcept {
104 ALIB_ASSERT_ERROR( members, "CONTAINERS", "Accessing nulled SharedVal." )
105 return reinterpret_cast<T&>(members->u);
106 }
107
108
109 public:
110 /// Exposes the allocator as given with template parameter \p{TAllocator}.
111 using AllocatorType= TAllocator;
112
113 /// Exposes the stored type specified with template parameter \p{T}.
114 using StoredType = T;
115
116 /// Default Constructor. Leaves this object \e nulled.
117 SharedPtr() noexcept : members(nullptr) {}
118
119 /// Constructs an empty instance from \c std::nullptr.
120 /// This constructor is necessary to allow assignment of \c std::nullptr to values of this type,
121 /// which clears the automatic pointer.
122 SharedPtr(std::nullptr_t) noexcept : members(nullptr) {}
123
124 /// Copy Constructor. Increases the reference counter of the shared pointer (in case given
125 /// \p{other} is not nulled).
126 /// @param other The object to copy.
127 SharedPtr(const SharedPtr& other) noexcept
128 : members(other.members) { if(members) ++(members->refCount); }
129
130
131 /// Move Constructor. Does not increase the reference counter, instead nulls the \p{other}.
132 /// @param other The object to copy.
133 SharedPtr(SharedPtr&& other) noexcept
134 : members(other.members) { other.members= nullptr; }
135
136 /// Copy Assignment Operator. Cares for self-assignment and assignment of a shared pointer with
137 /// the same content.
138 /// Otherwise, the reference counter of the current object is decreased, disposed if
139 /// necessary, and then the object in \p{other} is copied to this object.
140 /// @param other The object to copy into this one.
141 /// @return A reference to \c this.
142 SharedPtr& operator=(const SharedPtr& other) noexcept {
143 // handle self assignment and assignment with same contents
144 if (this == &other || this->members == other.members)
145 return *this;
146
147 // decrement the old reference count and delete the old data if needed
148 if (members && members->refCount.fetch_sub(1) == 1) {
150 members->GetAllocator().free( members, members->allocSize );
151 }
152
153 // copy the new data
154 if((members= other.members) != nullptr)
155 ++(members->refCount);
156
157 return *this;
158 }
159
160 /// Move Assignment Operator. Cares for self-assignment.
161 /// Otherwise, the object in \p{other} is copied to this.
162 /// @param other The object to move into this one.
163 /// @return A reference to \c this.
164 SharedPtr& operator=(SharedPtr&& other) noexcept {
165 // handle self assignment
166 if (this == &other)
167 return *this;
168
169 // decrement the old reference count and delete the old data if needed
170 if (members && members != other.members && members->refCount.fetch_sub(1) == 1) {
172 members->GetAllocator().free( members, members->allocSize );
173 }
174
175 // move members
176 members= other.members;
177 other.members= nullptr;
178 return *this;
179 }
180
181 /// Constructor taking an allocator along with the construction parameters for the instance
182 /// of \p{T}. The allocator is used allocate the needed memory (one allocation) and the
183 /// reference to it is internally stored, to be able to free the memory later.
184 ///
185 /// \note
186 /// This constructor is accepted by the compiler only if template type \p{TAllocator}
187 /// is \b not default-constructible.
188 /// @tparam TArgs The argument types used for constructing \p{T}.
189 /// @tparam TRequires Defaulted template parameter. Must not be specified.
190 /// @param allocator The allocator used to allocate and free needed storage.
191 /// @param args The arguments for constructing \p{T}.
192 template< typename... TArgs, typename TRequires= TAllocator>
193 requires ( !std::default_initializable<TRequires> )
194 SharedPtr( TAllocator& allocator, TArgs&&... args ) {
195 auto* mem= allocator().template Alloc<FieldMembers<T>>();
196 members = reinterpret_cast<FieldMembers<void*>*>(mem);
197 new (members) FieldMembers<void*>(allocator, &mem->u, sizeof(FieldMembers<T>));
198 new (&mem->u) T(std::forward<TArgs>(args)...);
199 }
200
201 /// Constructor missing the allocator instance.
202 /// To be used only with allocators that are default-constructible
203 /// (like \alib{lang;HeapAllocator} is).
204 ///
205 /// \note
206 /// This constructor is accepted by the compiler only if
207 /// - \p{TAllocator} is default-constructible, and
208 /// - the variadic argument list is not empty (here default construction is chosen), and
209 /// - the variadic arguments would not match to the copy or move constructor, and
210 /// - the variadic arguments are constructing type \p{T}.
211 ///
212 /// @tparam TRequires Defaulted template parameter. Must not be specified.
213 /// @tparam TArgs The argument types used for constructing \p{T}.
214 /// @param args The arguments for constructing \p{T}.
215 template <typename... TArgs, typename TRequires= TAllocator>
216 requires( std::is_default_constructible_v<TRequires>
217 && !std::is_same_v<std::decay_t<TArgs>... , SharedPtr>
218 && (sizeof...(TArgs) > 0) )
219 SharedPtr(TArgs&&... args) {
220 auto* mem= TAllocator()().template Alloc<FieldMembers<T>>();
221 members = reinterpret_cast<FieldMembers<void*>*>(mem);
222 new (members) FieldMembers<void*>(&mem->u, sizeof(FieldMembers<T>));
223 new (&mem->u) T(std::forward<TArgs>(args)...);
224 }
225
226 /// Disposes any currently held data (in case this was the last user, the current object
227 /// is deleted and memory freed) and places a new instance of (potentially) derived
228 /// type \p{TDerived} in this object.
229 ///
230 /// This overload of the method is accepted by the compiler only if type \p{TAllocator} is
231 /// default-constructible.
232 ///
233 /// @tparam TRequires Defaulted template parameter. Must not be specified.
234 /// @tparam TArgs The argument types used for constructing \p{T}.
235 /// @param args The arguments for constructing \p{T}.
236 template <typename TDerived, typename TRequires= TAllocator, typename... TArgs>
237 requires std::is_default_constructible_v<TRequires>
238 void InsertDerived(TArgs&&... args) {
239 // delete any existing
240 lang::Destruct(*this);
241
242 auto* mem= TAllocator()().template Alloc<FieldMembers<TDerived>>();
243 members = reinterpret_cast<FieldMembers<void*>*>(mem);
244 new (members) FieldMembers<void*>( lang::SafeCast<T>(&mem->u),
245 sizeof(FieldMembers<TDerived>) );
246 new (&mem->u) TDerived(std::forward<TArgs>(args)...);
247 }
248
249 /// Disposes any currently held data (in case this was the last user, the current object
250 /// is deleted and memory freed) and places a new instance of (potentially) derived
251 /// type \p{TDerived} in this object.
252 ///
253 /// This overload of the method is accepted by the compiler only if type \p{TAllocator} is
254 /// not default-constructible.
255 ///
256 /// @tparam TRequires Defaulted template parameter. Must not be specified.
257 /// @tparam TArgs The argument types used for constructing \p{T}.
258 /// @param allocator The allocator to use.
259 /// @param args The arguments for constructing \p{T}.
260 template <typename TDerived, typename TRequires= TAllocator, typename... TArgs>
261 requires( !std::is_default_constructible_v<TRequires> )
262 void InsertDerived(TAllocator& allocator, TArgs&&... args) {
263 // delete any existing
264 lang::Destruct(*this);
265 new (this) SharedPtr(); // note: without this, some compilers complain about
266 // uninitialized member 'members'!
267
268 auto* mem= allocator().template Alloc<FieldMembers<TDerived>>();
269 members = reinterpret_cast<FieldMembers<void*>*>(mem);
270 new (members) FieldMembers<void*>( allocator,
271 lang::SafeCast<T>(&mem->u),
272 sizeof(FieldMembers<TDerived>) );
273 new (&mem->u) TDerived(std::forward<TArgs>(args)...);
274 }
275
276 /// Destructor. If this is the last copy, the destructor of \p{T} is invoked and the
277 /// memory is freed to \p{TAllocator}.
279 if (members && members->refCount.fetch_sub(1) == 1) {
281 members->GetAllocator().free( members, members->allocSize );
282 } }
283
284 /// @return The size of the memory that is allocated for the \p{T} as well as for
285 /// the reference counter and the allocator member.
286 /// (To whom it may concern.)
287 template<typename TStored= T>
288 static constexpr size_t SizeOfAllocation() noexcept { return sizeof(FieldMembers<TStored>); }
289
290 /// @return The allocator given with construction that will be used to free the memory
291 /// that had been allocated, at the moment the use counter becomes \c 0.
293 ALIB_ASSERT_ERROR( members, "CONTAINERS", "Accessing nulled SharedVal." )
294 return members->GetAllocator();
295 }
296
297 /// @return The allocator interface of the allocator received with construction.
298 lang::AllocatorInterface<TAllocator> AI() const noexcept { return GetAllocator()(); }
299
300 /// Returns the number of shared usages.
301 /// In a multithreaded environment, the value returned is approximate.
302 /// @return \c The number of shared usages.
303 /// If this instance was default-constructed, moved, method #SetNulled was called,
304 /// or \c nullptr was assigned, then \c 0 is returned.
305 unsigned UseCount() const noexcept
306 { return members != nullptr ? members->refCount.load() : 0; }
307
308 /// Returns \c true if the #UseCount is \c 1.
309 /// @return \c true if this instance is set but not shared.
310 bool Unique() const noexcept { return members && members->refCount.load() == 1; }
311
312 /// Sets this object to \e nulled state, as if default constructed or \c nullptr was assigned.
313 /// If no shared copy exists, all data is destructed and memory is freed.<br>
314 /// As an alternative to this method, \c nullptr can be assigned.
315 void SetNulled() { lang::Destruct(*this); members= nullptr; }
316
317 /// Returns \c true if this is an empty instance.
318 /// @return \c true if #UseCount is \c 0, \c false otherwise.
319 bool IsNulled() const noexcept { return members == nullptr; }
320
321 /// Assignment of <c>nullptr</c>. Same as #SetNulled.
322 void operator=(std::nullptr_t) { lang::Destruct(*this); members= nullptr; }
323
324 /// Comparison with <c>nullptr</c>.
325 /// @return \c true if #UseCount is greater than \c 0.
326 bool operator==(std::nullptr_t) const noexcept { return members == nullptr; }
327
328 /// Comparison with <c>nullptr</c>.
329 /// @return \c false if #UseCount is greater than \c 0.
330 bool operator!=(std::nullptr_t) const noexcept { return members != nullptr; }
331
332 /// @return \c true if this instance is not \e nulled, \c false otherwise.
333 operator bool() const noexcept { return members != nullptr; }
334
335 /// Returns a non-constant pointer to the stored object of type \p{T}.
336 /// @return A pointer to \p{T}.
337 T* Get() const { return getP(); }
338
339 /// Overloaded operator to access members of custom type \p{T}
340 /// @return A pointer to \p{T}.
341 T* operator->() const { return getP(); }
342
343 /// Overloaded operator to access members of custom type \p{T}
344 /// @return A pointer to \p{T}.
345 T& operator*() const { return getR(); }
346
347}; // class SharedPtr
348
349} // namespace alib[::containers]
350
351/// Type alias in namespace \b alib.
352template<typename T, typename TAllocator= lang::HeapAllocator>
354
355} // namespace [alib]
T StoredType
Exposes the stored type specified with template parameter T.
void InsertDerived(TAllocator &allocator, TArgs &&... args)
bool operator==(std::nullptr_t) const noexcept
AllocatorType & GetAllocator() const
static constexpr size_t SizeOfAllocation() noexcept
bool Unique() const noexcept
SharedPtr(const SharedPtr &other) noexcept
lang::AllocatorInterface< TAllocator > AI() const noexcept
TAllocator AllocatorType
Exposes the allocator as given with template parameter TAllocator.
T & getR() const noexcept
SharedPtr & operator=(SharedPtr &&other) noexcept
SharedPtr(TAllocator &allocator, TArgs &&... args)
void InsertDerived(TArgs &&... args)
SharedPtr & operator=(const SharedPtr &other) noexcept
SharedPtr(std::nullptr_t) noexcept
unsigned UseCount() const noexcept
SharedPtr(TArgs &&... args)
void operator=(std::nullptr_t)
Assignment of nullptr. Same as SetNulled.
bool IsNulled() const noexcept
bool operator!=(std::nullptr_t) const noexcept
SharedPtr() noexcept
Default Constructor. Leaves this object nulled.
T * getP() const noexcept
Definition sharedptr.inl:98
SharedPtr(SharedPtr &&other) noexcept
#define ALIB_EXPORT
Definition alib.inl:497
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1066
ALIB_WARNINGS_RESTORE TTo * SafeCast(TFrom *derived)
Definition tmp.inl:31
void Destruct(T &object)
Definition tmp.inl:82
containers::SharedPtr< T, TAllocator > SharedPtr
Type alias in namespace alib.
FieldMembers(TAllocator &pAllocator, T *pCustom, size_t pAllocSize)
Definition sharedptr.inl:77
size_t allocSize
The size of the allocated pair of these fields and the custom type.
Definition sharedptr.inl:64
std::atomic< unsigned > refCount
The reference counter used to implement the std::shared_ptr behavior.
Definition sharedptr.inl:67
T * custom
The duly cast pointer to the custom type behind us.
Definition sharedptr.inl:61
FieldMembers(T *pCustom, size_t pAllocSize)
Definition sharedptr.inl:87
U u
The instance, either derived or T.
Definition sharedptr.inl:70