ALib C++ Library
Library Version: 2510 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
sharedval.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 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 \alib{containers;SharedPtr} which allows storing derived, dynamic types.
44/// - Class \alib{monomem;TSharedMonoVal} of module \alib_monomem, which incorporates
45/// an own embedded instance of class \alib{monomem;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>
55{
56 protected:
57 /// Fields if \p{TAllocator} is default-constructible (e.g., \alib{lang;HeapAllocator}).
59 {
60 /// The instance of the custom type.
62
63 /// The reference counter used to implement the <c>std::shared_ptr</c> behavior.
64 std::atomic<unsigned int> refCount;
65
66 /// Alternative constructor missing the allocator instance.
67 /// This is used only with allocators that are default-constructible
68 /// (like \alib{lang;HeapAllocator} is).
69 /// @tparam TArgs The argument types used for constructing \p{T}.
70 /// @tparam TRequires Defaulted template parameter. Must not be specified.
71 /// @param args The arguments for constructing \p{T}.
72 template<typename... TArgs, typename TRequires= TAllocator>
73 requires std::default_initializable<TRequires>
74 FieldMembersNoTA( TArgs&&... args )
75 : custom(std::forward<TArgs>(args)...)
76 , refCount(1) {}
77
78 ///@return A heap allocator value.
79 static constexpr TAllocator GetAllocator() noexcept { return lang::HeapAllocator(); }
80 }; // struct FieldMembers;
81
82 /// Fields if \p{TAllocator} is not default-constructible (not \alib{lang;HeapAllocator}).
84 {
85 /// The instance of the custom type.
87
88 /// The reference counter used to implement the <c>std::shared_ptr</c> behavior.
89 std::atomic<unsigned int> refCount;
90
91 /// The allocator used.
92 TAllocator& allocator;
93
94 /// Constructor.
95 /// @param pAllocator The allocator, stored in parent \b AllocatorMember.
96 /// @tparam TArgs The argument types used for constructing \p{T}.
97 /// @param args The arguments for constructing \p{T}.
98 template<typename... TArgs>
99 FieldMembersWithAllocator( TAllocator& pAllocator, TArgs&&... args )
100 : custom(std::forward<TArgs>(args)...)
101 , refCount(1)
102 , allocator(pAllocator) {}
103
104 ///@return The stored allocator.
105 TAllocator& GetAllocator() const { return allocator; }
106
107 }; // struct FieldMembers;
108
109 /// The type of the stored data. Note that we cannot use helper \alib{lang;AllocatorMember},
110 /// because type T could be derived from the same base class and this would break
111 /// EBO rules, that force the compiler to give two distinguishable members, inherited or
112 /// not, a different address.
113 using FieldMembers = std::conditional_t< std::is_default_constructible<TAllocator>::value,
116
117 /// The stored data.
119
120 /// Internal shortcut to receive the custom member.
121 /// @return The pointer to the contained type, or \c nullptr in case this is empty.
122 T* getP() const noexcept
123 { return members ? &members->custom : nullptr; }
124
125 /// Internal shortcut to receive a reference to the custom member.
126 /// Asserts in debug-compilations.
127 /// @return A reference the contained type.
128 T& getR() const noexcept
129 {
130 ALIB_ASSERT_ERROR( members, "CONTAINERS", "Accessing nulled SharedVal." )
131 return members->custom;
132 }
133 public:
134 /// Exposes the allocator as given with template parameter \p{TAllocator}.
135 using AllocatorType= TAllocator;
136
137 /// Exposes the stored type specified with template parameter \p{T}.
138 using StoredType = T;
139
140 /// Default Constructor. Leaves this object \e nulled.
141 SharedVal() noexcept : members(nullptr) {}
142
143 /// Constructs an empty instance from \c std::nullptr.
144 /// This constructor is necessary to allow assignment of \c std::nullptr to values of this type,
145 /// which clears the automatic pointer.
146 SharedVal(std::nullptr_t) noexcept : members(nullptr) {}
147
148 /// Copy Constructor. Increases the reference counter of the shared pointer (in case given
149 /// \p{other} is not nulled).
150 /// @param other The object to copy.
151 SharedVal(const SharedVal& other) noexcept
152 : members(other.members) { if(members) ++(members->refCount); }
153
154
155 /// Move Constructor. Does not increase the reference counter, instead nulls the \p{other}.
156 /// @param other The object to copy.
157 SharedVal(SharedVal&& other) noexcept
158 : members(other.members) { other.members= nullptr; }
159
160 /// Copy Assignment Operator. Cares for self-assignment and assignment of a shared pointer with
161 /// the same content.
162 /// Otherwise, the reference counter of the current object is decreased, disposed if
163 /// necessary, and then the object in \p{other} is copied to this object.
164 /// @param other The object to copy into this one.
165 /// @return A reference to \c this.
166 SharedVal& operator=(const SharedVal& other) noexcept
167 {
168 // handle self assignment and assignment with same contents
169 if (this == &other || this->members == other.members)
170 return *this;
171
172 // decrement the old reference count and delete the old data if needed
173 if (members && members->refCount.fetch_sub(1) == 1)
174 members->GetAllocator()().Delete(members);
175
176 // copy the new data
177 if((members= other.members) != nullptr)
178 ++(members->refCount);
179
180 return *this;
181 }
182
183 /// Move Assignment Operator. Cares for self-assignment.
184 /// Otherwise, the object in \p{other} is copied to this.
185 /// @param other The object to move into this one.
186 /// @return A reference to \c this.
187 SharedVal& operator=(SharedVal&& other) noexcept
188 {
189 // handle self assignment
190 if (this == &other)
191 return *this;
192
193 // decrement the old reference count and delete the old data if needed
194 if (members && members != other.members && members->refCount.fetch_sub(1) == 1)
195 members->GetAllocator()().Delete(members);
196
197 // move members
198 members= other.members;
199 other.members= nullptr;
200 return *this;
201 }
202
203 /// Constructor taking an allocator along with the construction parameters for the instance
204 /// of \p{T}. The allocator is used allocate the needed memory (one allocation) and the
205 /// reference to it is internally stored, to be able to free the memory later.
206 ///
207 /// \note
208 /// This constructor is accepted by the compiler only if template type \p{TAllocator}
209 /// is \b not default-constructible.
210 /// @tparam TArgs The argument types used for constructing \p{T}.
211 /// @tparam TRequires Defaulted template parameter. Must not be specified.
212 /// @param allocator The allocator used to allocate and free needed storage.
213 /// @param args The arguments for constructing \p{T}.
214 template< typename... TArgs, typename TRequires= TAllocator>
215 requires (!std::default_initializable<TRequires>)
216 SharedVal( TAllocator& allocator, TArgs&&... args )
217 : members( allocator().template New<FieldMembers>( allocator,
218 std::forward<TArgs>(args)...) ) {}
219
220 /// Constructor missing the allocator instance.
221 /// To be used only with allocators that are default-constructible
222 /// (like \alib{lang;HeapAllocator} is).
223 ///
224 /// \note
225 /// This constructor is accepted by the compiler only if
226 /// - \p{TAllocator} is default-constructible, and
227 /// - the variadic argument list is not empty (here default construction is chosen), and
228 /// - the variadic arguments would not match to the copy or move constructor, and
229 /// - the variadic arguments are constructing type \p{T}.
230 ///
231 /// \note
232 /// If this was not done, this constructor was chosen for copy constructors of
233 /// this type. In this case, the compilation of this constructor would fail when
234 /// initializing field member.
235 ///
236 /// @tparam TArgs The argument types used for constructing \p{T}.
237 /// @tparam TRequires Defaulted template parameter. Must not be specified.
238 /// @param args The arguments for constructing \p{T}.
239 template <typename... TArgs, typename TRequires= TAllocator>
240 requires( std::default_initializable<TRequires>
241 && (sizeof...(TArgs) > 0)
242 && ( sizeof...(TArgs) != 1
243 || !std::same_as<std::decay_t<std::tuple_element_t<0, std::tuple<TArgs...>>>,
244 SharedVal> )
245 && std::is_constructible_v<T, TArgs...> )
246 SharedVal(TArgs&&... args)
247 : members( TAllocator()().template New<FieldMembers>(std::forward<TArgs>(args)...) ) {}
248
249 /// Destructor. If this is the last copy, the destructor of \p{T} is invoked and the
250 /// memory is freed to \p{TAllocator}.
252 {
253 if (members && members->refCount.fetch_sub(1) == 1)
254 members->GetAllocator()().Delete(members);
255 }
256
257 /// @return The size of the memory that is allocated for the \p{T} as well as for
258 /// the reference counter and the allocator member.
259 /// (To whom it may concern.)
260 static constexpr size_t SizeOfAllocation() { return sizeof(FieldMembers); }
261
262 /// @return The allocator given with construction that will be used to free the memory
263 /// that had been allocated, at the moment the use counter becomes \c 0.
265 {
266 ALIB_ASSERT_ERROR( members, "CONTAINERS", "Accessing nulled SharedVal." )
267 return members->GetAllocator();
268 }
269
270 /// Returns the number of shared usages.
271 /// In a multithreaded environment, the value returned is approximate.
272 /// @return \c The number of shared usages.
273 /// If this instance was default-constructed, moved, method #SetNulled was called,
274 /// or \c nullptr was assigned, then \c 0 is returned.
275 unsigned int UseCount() const noexcept
276 { return members != nullptr ? members->refCount.load() : 0; }
277
278 /// Returns \c true if the #UseCount is \c 1.
279 /// @return \c true if this instance is set but not shared.
280 bool Unique() const noexcept
281 { return members && members->refCount.load() == 1; }
282
283 /// Sets this object to \e nulled state, as if default constructed or \c nullptr was assigned.
284 /// If no shared copy exists, all data is destructed and memory is freed.<br>
285 /// As an alternative to this method, \c nullptr can be assigned.
286 void SetNulled() { lang::Destruct(*this); members= nullptr; }
287
288 /// Returns \c true if this is an empty instance.
289 /// @return \c true if #UseCount is \c 0, \c false otherwise.
290 bool IsNulled() const noexcept { return members == nullptr; }
291
292 /// Assignment of <c>nullptr</c>. Same as #SetNulled.
293 void operator=(std::nullptr_t) { lang::Destruct(*this); members= nullptr; }
294
295 /// Comparison with <c>nullptr</c>.
296 /// @return \c true if #UseCount is greater than \c 0, \c false otherwise.
297 bool operator==(std::nullptr_t) const noexcept { return members == nullptr; }
298
299 /// Comparison with <c>nullptr</c>.
300 /// @return \c false if #UseCount is greater than \c 0, \c false otherwise.
301 bool operator!=(std::nullptr_t) const noexcept { return members != nullptr; }
302
303 /// @return \c true if this instance is not \e nulled, \c false otherwise.
304 operator bool() const noexcept { return members != nullptr; }
305
306 /// Returns a non-constant pointer to the stored object of type \p{T}.
307 /// @return A pointer to \p{T}.
308 T* Get() const { return getP(); }
309
310 /// Overloaded operator to access members of custom type \p{T}
311 /// @return A pointer to \p{T}.
312 T* operator->() const { return getP(); }
313
314 /// Overloaded operator to access members of custom type \p{T}
315 /// @return A pointer to \p{T}.
316 T& operator*() const { return getR(); }
317}; // class SharedVal
318
319} // namespace alib[::containers]
320
321/// Type alias in namespace \b alib.
322template<typename T, typename TAllocator= lang::HeapAllocator>
324
325} // namespace [alib]
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(TArgs &&... args)
SharedVal & operator=(const SharedVal &other) noexcept
SharedVal(TAllocator &allocator, TArgs &&... args)
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
std::conditional_t< std::is_default_constructible< TAllocator >::value, FieldMembersNoTA, FieldMembersWithAllocator > FieldMembers
AllocatorType & GetAllocator() const
bool operator==(std::nullptr_t) const noexcept
SharedVal(const SharedVal &other) noexcept
SharedVal & operator=(SharedVal &&other) noexcept
unsigned int UseCount() const noexcept
SharedVal(SharedVal &&other) noexcept
bool operator!=(std::nullptr_t) const noexcept
TAllocator AllocatorType
Exposes the allocator as given with template parameter TAllocator.
#define ALIB_EXPORT
Definition alib.inl:488
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1049
void Destruct(T &object)
Definition tmp.inl:83
containers::SharedVal< T, TAllocator > SharedVal
Type alias in namespace alib.
Fields if TAllocator is default-constructible (e.g., HeapAllocator).
Definition sharedval.inl:59
static constexpr TAllocator GetAllocator() noexcept
Definition sharedval.inl:79
T custom
The instance of the custom type.
Definition sharedval.inl:61
std::atomic< unsigned int > refCount
The reference counter used to implement the std::shared_ptr behavior.
Definition sharedval.inl:64
Fields if TAllocator is not default-constructible (not HeapAllocator).
Definition sharedval.inl:84
FieldMembersWithAllocator(TAllocator &pAllocator, TArgs &&... args)
Definition sharedval.inl:99
std::atomic< unsigned int > refCount
The reference counter used to implement the std::shared_ptr behavior.
Definition sharedval.inl:89