ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
sharedval.hpp
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/// Copyright 2013-2026 A-Worx GmbH, Germany.
6/// Published under #"mainpage_license".
7//==================================================================================================
8ALIB_EXPORT namespace alib { namespace containers {
9
10//==================================================================================================
11/// This templated class is an alternative for C++ standard library type
12/// \https{std::shared_ptr,en.cppreference.com/w/cpp/memory/shared_ptr}
13/// with important restrictions:
14/// - Instead of managing a pointer, a \b value is managed.
15/// - It is not possible to store derived types within this type, which is a common use case with
16/// <c>std::shared_ptr</c>, especially in consideration with dynamic (virtual) types.<br>
17/// This implies that no abstract types can be stored.
18/// - This implementation misses the coexistence of sibling type <c>std::weak_ptr</c> and
19/// corresponding functionality.
20/// - This implementation misses an equivalent to method <c>owner_before</c> and corresponding
21/// comparison operators.
22/// - This implementation misses dedicated array support (at least as of today).
23///
24/// The advantages are:
25/// - The type has a footprint of only <c>sizeof(void*)</c>, where the standard's type has
26/// a size of two pointers.
27/// - An internal second pointer-dereferencing is avoided when accessing the shared value.
28/// - The type performs only one allocation.
29/// (Common implementations of the standard's type perform two allocations.)
30/// - The type supports storing references to \alib {lang;Allocator;allocators} which are
31/// used for allocation and freeing of memory.
32/// Allocators can be of "heavy" weight and are never copied by value.
33///
34/// Note that despite being named <b>Shared<em><u>Val</u></em></b>, which is in contrast to
35/// sibling type <b>Shared<em><u>Ptr</u></em></b>, the type still behaves like a pointer.
36/// It can be \e nulled, value <c>nullptr</c> can be assigned, and member access is performed
37/// with the <c>operator-></c>. And of course the object is destructed, and the memory is freed in
38/// case the last copy of an instance is nulled or gets out of scope. A different naming
39/// proposal could have been <b>Shared<em><u>Static</u></em>Ptr</b> to indicate that no
40/// dynamic conversions and abstract types are applicable.
41///
42/// \see
43/// - Class #"SharedPtr" which allows storing derived, dynamic types.
44/// - Class #"TSharedMonoVal" of module \alib_monomem, which incorporates
45/// an own embedded instance of class #"TMonoAllocator".
46/// This allocator can be used for further monotonic allocations by the contained type or
47/// other code entities that receive the shared pointer.
48/// @tparam T The custom type that is shared with this pointer.
49/// @tparam TAllocator The allocator that is used to allocate an instance of \p{T} together
50/// with a reference counter and optionally a reference to such allocator
51/// if passed with construction.
52//==================================================================================================
53template<typename T, typename TAllocator= HeapAllocator>
54class SharedVal {
55 protected:
56 /// Fields if \p{TAllocator} is default-constructible (e.g., #"HeapAllocator").
58 /// The instance of the custom type.
60
61 /// The reference counter used to implement the <c>std::shared_ptr</c> behavior.
62 std::atomic<unsigned> refCount;
63
64 /// Alternative constructor missing the allocator instance.
65 /// This is used only with allocators that are default-constructible
66 /// (like #"HeapAllocator" is).
67 /// @tparam TArgs The argument types used for constructing \p{T}.
68 /// @tparam TRequires Defaulted template parameter. Must not be specified.
69 /// @param args The arguments for constructing \p{T}.
70 template<typename... TArgs, typename TRequires= TAllocator>
71 requires std::default_initializable<TRequires>
72 FieldMembersNoTA( TArgs&&... args )
73 : custom(std::forward<TArgs>(args)...)
74 , refCount(1) {}
75
76 ///@return A heap allocator value.
77 static constexpr TAllocator GetAllocator() noexcept { return lang::HeapAllocator(); }
78 }; // struct FieldMembers;
79
80 /// Fields if \p{TAllocator} is not default-constructible (not #"HeapAllocator").
82 /// The instance of the custom type.
84
85 /// The reference counter used to implement the <c>std::shared_ptr</c> behavior.
86 std::atomic<unsigned> refCount;
87
88 /// The allocator used.
89 TAllocator& allocator;
90
91 /// Constructor.
92 /// @param pAllocator The allocator, stored in parent #"%AllocatorMember".
93 /// @tparam TArgs The argument types used for constructing \p{T}.
94 /// @param args The arguments for constructing \p{T}.
95 template<typename... TArgs>
96 FieldMembersWithAllocator( TAllocator& pAllocator, TArgs&&... args )
97 : custom(std::forward<TArgs>(args)...)
98 , refCount(1)
99 , allocator(pAllocator) {}
100
101 ///@return The stored allocator.
102 TAllocator& GetAllocator() const { return allocator; }
103
104 }; // struct FieldMembers;
105
106 /// The type of the stored data. Note that we cannot use helper #"AllocatorMember",
107 /// because type T could be derived from the same base class and this would break
108 /// EBO rules, that force the compiler to give two distinguishable members, inherited or
109 /// not, a different address.
110 using FieldMembers = std::conditional_t< std::is_default_constructible<TAllocator>::value,
113
114 /// The stored data.
116
117 /// Internal shortcut to receive the custom member.
118 /// @return The pointer to the contained type, or \c nullptr in case this is empty.
119 T* getP() const noexcept { return members ? &members->custom : nullptr; }
120
121 /// Internal shortcut to receive a reference to the custom member.
122 /// Asserts in debug-compilations.
123 /// @return A reference the contained type.
124 T& getR() const noexcept
125 {
126 ALIB_ASSERT_ERROR( members, "CONTAINERS", "Accessing nulled SharedVal." )
127 return members->custom;
128 }
129 public:
130 /// Exposes the allocator as given with template parameter \p{TAllocator}.
131 using AllocatorType= TAllocator;
132
133 /// Exposes the stored type specified with template parameter \p{T}.
134 using StoredType = T;
135
136 /// Default Constructor. Leaves this object \e nulled.
137 SharedVal() noexcept : members(nullptr) {}
138
139 /// Constructs an empty instance from \c std::nullptr.
140 /// This constructor is necessary to allow assignment of \c std::nullptr to values of this type,
141 /// which clears the automatic pointer.
142 SharedVal(std::nullptr_t) noexcept : members(nullptr) {}
143
144 /// Copy Constructor. Increases the reference counter of the shared pointer (in case given
145 /// \p{other} is not nulled).
146 /// @param other The object to copy.
147 SharedVal(const SharedVal& other) noexcept
148 : members(other.members) { if(members) ++(members->refCount); }
149
150
151 /// Move Constructor. Does not increase the reference counter, instead nulls the \p{other}.
152 /// @param other The object to copy.
153 SharedVal(SharedVal&& other) noexcept
154 : members(other.members) { other.members= nullptr; }
155
156 /// Copy Assignment Operator. Cares for self-assignment and assignment of a shared pointer with
157 /// the same content.
158 /// Otherwise, the reference counter of the current object is decreased, disposed if
159 /// necessary, and then the object in \p{other} is copied to this object.
160 /// @param other The object to copy into this one.
161 /// @return A reference to \c this.
162 SharedVal& operator=(const SharedVal& other) noexcept
163 {
164 // handle self assignment and assignment with same contents
165 if (this == &other || this->members == other.members)
166 return *this;
167
168 // decrement the old reference count and delete the old data if needed
169 if (members && members->refCount.fetch_sub(1) == 1)
170 members->GetAllocator()().Delete(members);
171
172 // copy the new data
173 if((members= other.members) != nullptr)
174 ++(members->refCount);
175
176 return *this;
177 }
178
179 /// Move Assignment Operator. Cares for self-assignment.
180 /// Otherwise, the object in \p{other} is copied to this.
181 /// @param other The object to move into this one.
182 /// @return A reference to \c this.
183 SharedVal& operator=(SharedVal&& other) noexcept
184 {
185 // handle self assignment
186 if (this == &other)
187 return *this;
188
189 // decrement the old reference count and delete the old data if needed
190 if (members && members != other.members && members->refCount.fetch_sub(1) == 1)
191 members->GetAllocator()().Delete(members);
192
193 // move members
194 members= other.members;
195 other.members= nullptr;
196 return *this;
197 }
198
199 /// Constructor taking an allocator along with the construction parameters for the instance
200 /// of \p{T}. The allocator is used allocate the needed memory (one allocation) and the
201 /// reference to it is internally stored, to be able to free the memory later.
202 ///
203 /// \note
204 /// This constructor is accepted by the compiler only if template type \p{TAllocator}
205 /// is \b not default-constructible.
206 /// @tparam TArgs The argument types used for constructing \p{T}.
207 /// @tparam TRequires Defaulted template parameter. Must not be specified.
208 /// @param allocator The allocator used to allocate and free needed storage.
209 /// @param args The arguments for constructing \p{T}.
210 template< typename... TArgs, typename TRequires= TAllocator>
211 requires (!std::default_initializable<TRequires>)
212 SharedVal( TAllocator& allocator, TArgs&&... args )
213 : members( allocator().template New<FieldMembers>( allocator,
214 std::forward<TArgs>(args)...) ) {}
215
216 /// Constructor missing the allocator instance.
217 /// To be used only with allocators that are default-constructible
218 /// (like #"HeapAllocator" is).
219 ///
220 /// \note
221 /// This constructor is accepted by the compiler only if
222 /// - \p{TAllocator} is default-constructible, and
223 /// - the variadic argument list is not empty (here default construction is chosen), and
224 /// - the variadic arguments would not match to the copy or move constructor, and
225 /// - the variadic arguments are constructing type \p{T}.
226 ///
227 /// \note
228 /// If this was not done, this constructor was chosen for copy constructors of
229 /// this type. In this case, the compilation of this constructor would fail when
230 /// initializing field member.
231 ///
232 /// @tparam TArgs The argument types used for constructing \p{T}.
233 /// @tparam TRequires Defaulted template parameter. Must not be specified.
234 /// @param args The arguments for constructing \p{T}.
235 template <typename... TArgs, typename TRequires= TAllocator>
236 requires( std::default_initializable<TRequires>
237 && (sizeof...(TArgs) > 0)
238 && ( sizeof...(TArgs) != 1
239 || !std::same_as<std::decay_t<std::tuple_element_t<0, std::tuple<TArgs...>>>,
240 SharedVal> )
241 && std::is_constructible_v<T, TArgs...> )
242 SharedVal(TArgs&&... args)
243 : members( TAllocator()().template New<FieldMembers>(std::forward<TArgs>(args)...) ) {}
244
245 /// Destructor. If this is the last copy, the destructor of \p{T} is invoked and the
246 /// memory is freed to \p{TAllocator}.
248 {
249 if (members && members->refCount.fetch_sub(1) == 1)
250 members->GetAllocator()().Delete(members);
251 }
252
253 /// @return The size of the memory that is allocated for the \p{T} as well as for
254 /// the reference counter and the allocator member.
255 /// (To whom it may concern.)
256 static constexpr size_t SizeOfAllocation() { return sizeof(FieldMembers); }
257
258 /// @return The allocator given with construction that will be used to free the memory
259 /// that had been allocated, at the moment the use counter becomes \c 0.
261 {
262 ALIB_ASSERT_ERROR( members, "CONTAINERS", "Accessing nulled SharedVal." )
263 return members->GetAllocator();
264 }
265
266 /// Returns the number of shared usages.
267 /// In a multithreaded environment, the value returned is approximate.
268 /// @return \c The number of shared usages.
269 /// If this instance was default-constructed, moved, method #".SetNulled" was called,
270 /// or \c nullptr was assigned, then \c 0 is returned.
271 unsigned UseCount() const noexcept
272 { return members != nullptr ? members->refCount.load() : 0; }
273
274 /// Returns \c true if the #".UseCount" is \c 1.
275 /// @return \c true if this instance is set but not shared.
276 bool Unique() const noexcept { return members && members->refCount.load() == 1; }
277
278 /// Sets this object to \e nulled state, as if default constructed or \c nullptr was assigned.
279 /// If no shared copy exists, all data is destructed and memory is freed.<br>
280 /// As an alternative to this method, \c nullptr can be assigned.
281 void SetNulled() { lang::Destruct(*this); members= nullptr; }
282
283 /// Returns \c true if this is an empty instance.
284 /// @return \c true if #".UseCount" is \c 0, \c false otherwise.
285 bool IsNulled() const noexcept { return members == nullptr; }
286
287 /// Assignment of <c>nullptr</c>. Same as #".SetNulled".
288 void operator=(std::nullptr_t) { lang::Destruct(*this); members= nullptr; }
289
290 /// Comparison with <c>nullptr</c>.
291 /// @return \c true if #".UseCount" is greater than \c 0, \c false otherwise.
292 bool operator==(std::nullptr_t) const noexcept { return members == nullptr; }
293
294 /// Comparison with <c>nullptr</c>.
295 /// @return \c false if #".UseCount" is greater than \c 0, \c false otherwise.
296 bool operator!=(std::nullptr_t) const noexcept { return members != nullptr; }
297
298 /// @return \c true if this instance is not \e nulled, \c false otherwise.
299 operator bool() const noexcept { return members != nullptr; }
300
301 /// Returns a non-constant pointer to the stored object of type \p{T}.
302 /// @return A pointer to \p{T}.
303 T* Get() const { return getP(); }
304
305 /// Overloaded operator to access members of custom type \p{T}
306 /// @return A pointer to \p{T}.
307 T* operator->() const { return getP(); }
308
309 /// Overloaded operator to access members of custom type \p{T}
310 /// @return A pointer to \p{T}.
311 T& operator*() const { return getR(); }
312}; // class SharedVal
313
314} // namespace alib[::containers]
315
316/// Type alias in namespace #"%alib".
317template<typename T, typename TAllocator= lang::HeapAllocator>
319
320} // namespace [alib]
#define ALIB_EXPORT
#define ALIB_ASSERT_ERROR(cond, domain,...)
void operator=(std::nullptr_t)
Assignment of nullptr. Same as #".SetNulled".
SharedVal(std::nullptr_t) noexcept
SharedVal() noexcept
Default Constructor. Leaves this object nulled.
SharedVal & operator=(const SharedVal &other) noexcept
T StoredType
Exposes the stored type specified with template parameter T.
T & getR() const noexcept
T * getP() const noexcept
bool Unique() const noexcept
static constexpr size_t SizeOfAllocation()
bool IsNulled() const noexcept
AllocatorType & GetAllocator() const
bool operator==(std::nullptr_t) const noexcept
SharedVal(const SharedVal &other) noexcept
SharedVal & operator=(SharedVal &&other) noexcept
SharedVal(TArgs &&... args)
std::conditional_t< std::is_default_constructible< TAllocator >::value, FieldMembersNoTA, FieldMembersWithAllocator > FieldMembers
SharedVal(TAllocator &allocator, TArgs &&... args)
SharedVal(SharedVal &&other) noexcept
unsigned UseCount() const noexcept
bool operator!=(std::nullptr_t) const noexcept
TAllocator AllocatorType
Exposes the allocator as given with template parameter TAllocator.
void Destruct(T &object)
Definition tmp.hpp:82
Definition alox.cpp:14
containers::SharedVal< T, TAllocator > SharedVal
Type alias in namespace #"%alib".
Fields if TAllocator is default-constructible (e.g., #"HeapAllocator").
Definition sharedval.hpp:57
std::atomic< unsigned > refCount
The reference counter used to implement the std::shared_ptr behavior.
Definition sharedval.hpp:62
static constexpr TAllocator GetAllocator() noexcept
Definition sharedval.hpp:77
T custom
The instance of the custom type.
Definition sharedval.hpp:59
Fields if TAllocator is not default-constructible (not #"HeapAllocator").
Definition sharedval.hpp:81
FieldMembersWithAllocator(TAllocator &pAllocator, TArgs &&... args)
Definition sharedval.hpp:96
std::atomic< unsigned > refCount
The reference counter used to implement the std::shared_ptr behavior.
Definition sharedval.hpp:86