ALib C++ Library
Library Version: 2510 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 int> 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
99 {
100 return members ? reinterpret_cast<T*>(&members->u)
101 : nullptr;
102 }
103
104 /// Internal shortcut to receive a reference to the custom member.
105 /// Asserts in debug-compilations.
106 /// @return A reference the contained type.
107 T& getR() const noexcept
108 {
109 ALIB_ASSERT_ERROR( members, "CONTAINERS", "Accessing nulled SharedVal." )
110 return reinterpret_cast<T&>(members->u);
111 }
112
113
114 public:
115 /// Exposes the allocator as given with template parameter \p{TAllocator}.
116 using AllocatorType= TAllocator;
117
118 /// Exposes the stored type specified with template parameter \p{T}.
119 using StoredType = T;
120
121 /// Default Constructor. Leaves this object \e nulled.
122 SharedPtr() noexcept : members(nullptr) {}
123
124 /// Constructs an empty instance from \c std::nullptr.
125 /// This constructor is necessary to allow assignment of \c std::nullptr to values of this type,
126 /// which clears the automatic pointer.
127 SharedPtr(std::nullptr_t) noexcept : members(nullptr) {}
128
129 /// Copy Constructor. Increases the reference counter of the shared pointer (in case given
130 /// \p{other} is not nulled).
131 /// @param other The object to copy.
132 SharedPtr(const SharedPtr& other) noexcept
133 : members(other.members) { if(members) ++(members->refCount); }
134
135
136 /// Move Constructor. Does not increase the reference counter, instead nulls the \p{other}.
137 /// @param other The object to copy.
138 SharedPtr(SharedPtr&& other) noexcept
139 : members(other.members) { other.members= nullptr; }
140
141 /// Copy Assignment Operator. Cares for self-assignment and assignment of a shared pointer with
142 /// the same content.
143 /// Otherwise, the reference counter of the current object is decreased, disposed if
144 /// necessary, and then the object in \p{other} is copied to this object.
145 /// @param other The object to copy into this one.
146 /// @return A reference to \c this.
147 SharedPtr& operator=(const SharedPtr& other) noexcept
148 {
149 // handle self assignment and assignment with same contents
150 if (this == &other || this->members == other.members)
151 return *this;
152
153 // decrement the old reference count and delete the old data if needed
154 if (members && members->refCount.fetch_sub(1) == 1)
155 {
157 members->GetAllocator().free( members, members->allocSize );
158 }
159
160 // copy the new data
161 if((members= other.members) != nullptr)
162 ++(members->refCount);
163
164 return *this;
165 }
166
167 /// Move Assignment Operator. Cares for self-assignment.
168 /// Otherwise, the object in \p{other} is copied to this.
169 /// @param other The object to move into this one.
170 /// @return A reference to \c this.
171 SharedPtr& operator=(SharedPtr&& other) noexcept
172 {
173 // handle self assignment
174 if (this == &other)
175 return *this;
176
177 // decrement the old reference count and delete the old data if needed
178 if (members && members != other.members && members->refCount.fetch_sub(1) == 1)
179 {
181 members->GetAllocator().free( members, members->allocSize );
182 }
183
184 // move members
185 members= other.members;
186 other.members= nullptr;
187 return *this;
188 }
189
190 /// Constructor taking an allocator along with the construction parameters for the instance
191 /// of \p{T}. The allocator is used allocate the needed memory (one allocation) and the
192 /// reference to it is internally stored, to be able to free the memory later.
193 ///
194 /// \note
195 /// This constructor is accepted by the compiler only if template type \p{TAllocator}
196 /// is \b not default-constructible.
197 /// @tparam TArgs The argument types used for constructing \p{T}.
198 /// @tparam TRequires Defaulted template parameter. Must not be specified.
199 /// @param allocator The allocator used to allocate and free needed storage.
200 /// @param args The arguments for constructing \p{T}.
201 template< typename... TArgs, typename TRequires= TAllocator>
202 requires ( !std::default_initializable<TRequires> )
203 SharedPtr( TAllocator& allocator, TArgs&&... args )
204 {
205 auto* mem= allocator().template Alloc<FieldMembers<T>>();
206 members = reinterpret_cast<FieldMembers<void*>*>(mem);
207 new (members) FieldMembers<void*>(allocator, &mem->u, sizeof(FieldMembers<T>));
208 new (&mem->u) T(std::forward<TArgs>(args)...);
209 }
210
211 /// Constructor missing the allocator instance.
212 /// To be used only with allocators that are default-constructible
213 /// (like \alib{lang;HeapAllocator} is).
214 ///
215 /// \note
216 /// This constructor is accepted by the compiler only if
217 /// - \p{TAllocator} is default-constructible, and
218 /// - the variadic argument list is not empty (here default construction is chosen), and
219 /// - the variadic arguments would not match to the copy or move constructor, and
220 /// - the variadic arguments are constructing type \p{T}.
221 ///
222 /// @tparam TRequires Defaulted template parameter. Must not be specified.
223 /// @tparam TArgs The argument types used for constructing \p{T}.
224 /// @param args The arguments for constructing \p{T}.
225 template <typename... TArgs, typename TRequires= TAllocator>
226 requires( std::is_default_constructible_v<TRequires>
227 && !std::is_same_v<std::decay_t<TArgs>... , SharedPtr>
228 && (sizeof...(TArgs) > 0) )
229 SharedPtr(TArgs&&... args)
230 {
231 auto* mem= TAllocator()().template Alloc<FieldMembers<T>>();
232 members = reinterpret_cast<FieldMembers<void*>*>(mem);
233 new (members) FieldMembers<void*>(&mem->u, sizeof(FieldMembers<T>));
234 new (&mem->u) T(std::forward<TArgs>(args)...);
235 }
236
237 /// Disposes any currently held data (in case this was the last user, the current object
238 /// is deleted and memory freed) and places a new instance of (potentially) derived
239 /// type \p{TDerived} in this object.
240 ///
241 /// This overload of the method is accepted by the compiler only if type \p{TAllocator} is
242 /// default-constructible.
243 ///
244 /// @tparam TRequires Defaulted template parameter. Must not be specified.
245 /// @tparam TArgs The argument types used for constructing \p{T}.
246 /// @param args The arguments for constructing \p{T}.
247 template <typename TDerived, typename TRequires= TAllocator, typename... TArgs>
248 requires std::is_default_constructible_v<TRequires>
249 void InsertDerived(TArgs&&... args)
250 {
251 // delete any existing
252 lang::Destruct(*this);
253
254 auto* mem= TAllocator()().template Alloc<FieldMembers<TDerived>>();
255 members = reinterpret_cast<FieldMembers<void*>*>(mem);
256 new (members) FieldMembers<void*>( lang::SafeCast<T>(&mem->u),
257 sizeof(FieldMembers<TDerived>) );
258 new (&mem->u) TDerived(std::forward<TArgs>(args)...);
259 }
260
261 /// Disposes any currently held data (in case this was the last user, the current object
262 /// is deleted and memory freed) and places a new instance of (potentially) derived
263 /// type \p{TDerived} in this object.
264 ///
265 /// This overload of the method is accepted by the compiler only if type \p{TAllocator} is
266 /// not default-constructible.
267 ///
268 /// @tparam TRequires Defaulted template parameter. Must not be specified.
269 /// @tparam TArgs The argument types used for constructing \p{T}.
270 /// @param allocator The allocator to use.
271 /// @param args The arguments for constructing \p{T}.
272 template <typename TDerived, typename TRequires= TAllocator, typename... TArgs>
273 requires( !std::is_default_constructible_v<TRequires> )
274 void InsertDerived(TAllocator& allocator, TArgs&&... args)
275 {
276 // delete any existing
277 lang::Destruct(*this);
278 new (this) SharedPtr(); // note: without this, some compilers complain about
279 // uninitialized member 'members'!
280
281 auto* mem= allocator().template Alloc<FieldMembers<TDerived>>();
282 members = reinterpret_cast<FieldMembers<void*>*>(mem);
283 new (members) FieldMembers<void*>( allocator,
284 lang::SafeCast<T>(&mem->u),
285 sizeof(FieldMembers<TDerived>) );
286 new (&mem->u) TDerived(std::forward<TArgs>(args)...);
287 }
288
289 /// Destructor. If this is the last copy, the destructor of \p{T} is invoked and the
290 /// memory is freed to \p{TAllocator}.
292 {
293 if (members && members->refCount.fetch_sub(1) == 1)
294 {
296 members->GetAllocator().free( members, members->allocSize );
297 }
298 }
299
300 /// @return The size of the memory that is allocated for the \p{T} as well as for
301 /// the reference counter and the allocator member.
302 /// (To whom it may concern.)
303 template<typename TStored= T>
304 static constexpr size_t SizeOfAllocation() noexcept { return sizeof(FieldMembers<TStored>); }
305
306 /// @return The allocator given with construction that will be used to free the memory
307 /// that had been allocated, at the moment the use counter becomes \c 0.
309 {
310 ALIB_ASSERT_ERROR( members, "CONTAINERS", "Accessing nulled SharedVal." )
311 return members->GetAllocator();
312 }
313
314 /// @return The allocator interface of the allocator received with construction.
315 lang::AllocatorInterface<TAllocator> AI() const noexcept { return GetAllocator()(); }
316
317 /// Returns the number of shared usages.
318 /// In a multithreaded environment, the value returned is approximate.
319 /// @return \c The number of shared usages.
320 /// If this instance was default-constructed, moved, method #SetNulled was called,
321 /// or \c nullptr was assigned, then \c 0 is returned.
322 unsigned int UseCount() const noexcept
323 { return members != nullptr ? members->refCount.load() : 0; }
324
325 /// Returns \c true if the #UseCount is \c 1.
326 /// @return \c true if this instance is set but not shared.
327 bool Unique() const noexcept
328 { return members && members->refCount.load() == 1; }
329
330 /// Sets this object to \e nulled state, as if default constructed or \c nullptr was assigned.
331 /// If no shared copy exists, all data is destructed and memory is freed.<br>
332 /// As an alternative to this method, \c nullptr can be assigned.
333 void SetNulled() { lang::Destruct(*this); members= nullptr; }
334
335 /// Returns \c true if this is an empty instance.
336 /// @return \c true if #UseCount is \c 0, \c false otherwise.
337 bool IsNulled() const noexcept { return members == nullptr; }
338
339 /// Assignment of <c>nullptr</c>. Same as #SetNulled.
340 void operator=(std::nullptr_t) { lang::Destruct(*this); members= nullptr; }
341
342 /// Comparison with <c>nullptr</c>.
343 /// @return \c true if #UseCount is greater than \c 0.
344 bool operator==(std::nullptr_t) const noexcept { return members == nullptr; }
345
346 /// Comparison with <c>nullptr</c>.
347 /// @return \c false if #UseCount is greater than \c 0.
348 bool operator!=(std::nullptr_t) const noexcept { return members != nullptr; }
349
350 /// @return \c true if this instance is not \e nulled, \c false otherwise.
351 operator bool() const noexcept { return members != nullptr; }
352
353 /// Returns a non-constant pointer to the stored object of type \p{T}.
354 /// @return A pointer to \p{T}.
355 T* Get() const { return getP(); }
356
357 /// Overloaded operator to access members of custom type \p{T}
358 /// @return A pointer to \p{T}.
359 T* operator->() const { return getP(); }
360
361 /// Overloaded operator to access members of custom type \p{T}
362 /// @return A pointer to \p{T}.
363 T& operator*() const { return getR(); }
364
365}; // class SharedPtr
366
367} // namespace alib[::containers]
368
369/// Type alias in namespace \b alib.
370template<typename T, typename TAllocator= lang::HeapAllocator>
372
373} // namespace [alib]
374
375
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
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
unsigned int UseCount() const noexcept
#define ALIB_EXPORT
Definition alib.inl:488
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1049
ALIB_WARNINGS_RESTORE TTo * SafeCast(TFrom *derived)
Definition tmp.inl:31
void Destruct(T &object)
Definition tmp.inl:83
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 int > 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
AllocatorMember()=delete
Deleted default constructor. (The allocator has to be given with construction)