Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * @file lib.SharedPointer.hpp | ||
3 | * @author Sergey Baigudin, sergey@baigudin.software | ||
4 | * @copyright 2020-2022, Sergey Baigudin, Baigudin Software | ||
5 | */ | ||
6 | #ifndef LIB_SHAREDPOINTER_HPP_ | ||
7 | #define LIB_SHAREDPOINTER_HPP_ | ||
8 | |||
9 | #include "lib.Object.hpp" | ||
10 | #include "api.SmartPointer.hpp" | ||
11 | #include "lib.SmartPointerDeleter.hpp" | ||
12 | #include "lib.MutexGuard.hpp" | ||
13 | #include "lib.NonCopyable.hpp" | ||
14 | |||
15 | namespace eoos | ||
16 | { | ||
17 | namespace lib | ||
18 | { | ||
19 | |||
20 | #ifdef EOOS_ENABLE_DYNAMIC_HEAP_MEMORY | ||
21 | |||
22 | /** | ||
23 | * @class SharedPointer<T,D,A> | ||
24 | * @brief Shared pointer. | ||
25 | * | ||
26 | * @tparam T Data type of an owning object. | ||
27 | * @tparam D Deleter type for an owning object. | ||
28 | * @tparam A Heap memory allocator class. | ||
29 | */ | ||
30 | template <typename T, class D = SmartPointerDeleter<T>, class A = Allocator> | ||
31 | class SharedPointer : public Object<A>, public api::SmartPointer<T> | ||
32 | { | ||
33 | typedef SharedPointer<T,D,A> Self; | ||
34 | typedef Object<A> Parent; | ||
35 | |||
36 | public: | ||
37 | |||
38 | /** | ||
39 | * @brief Constructor an empty shared object. | ||
40 | */ | ||
41 | 24 | SharedPointer() | |
42 | : Object<A>() | ||
43 | , api::SmartPointer<T>() | ||
44 | 24 | , cb_(NULLPTR) { | |
45 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
24 | bool_t const isConstructed( construct() ); |
46 | 24 | setConstructed(isConstructed); | |
47 | 24 | } | |
48 | |||
49 | /** | ||
50 | * @brief Constructor. | ||
51 | * | ||
52 | * @note If the shared object is not able to be constructed, an object passed by the pointer will be deleted. | ||
53 | * | ||
54 | * @param pointer A pointer to get ownership. | ||
55 | */ | ||
56 | 82 | explicit SharedPointer(T* const pointer) | |
57 | : Object<A>() | ||
58 | , api::SmartPointer<T>() | ||
59 | 82 | , cb_(NULLPTR) { | |
60 |
1/2✓ Branch 1 taken 41 times.
✗ Branch 2 not taken.
|
82 | bool_t const isConstructed( construct(pointer) ); |
61 | 82 | setConstructed(isConstructed); | |
62 | 82 | } | |
63 | |||
64 | /** | ||
65 | * @brief Destructor. | ||
66 | */ | ||
67 | 132 | virtual ~SharedPointer() | |
68 | { | ||
69 |
2/2✓ Branch 0 taken 53 times.
✓ Branch 1 taken 9 times.
|
124 | if( isConstructed() ) |
70 | { | ||
71 | 106 | release(); | |
72 | } | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * @copydoc eoos::Object::Object(Object const&) | ||
77 | */ | ||
78 | 16 | SharedPointer(SharedPointer const& obj) ///< SCA MISRA-C++:2008 Justified Rule 12-8-1 | |
79 | : Object<A>(obj) | ||
80 | , api::SmartPointer<T>() | ||
81 | 16 | , cb_(obj.cb_){ | |
82 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
16 | acquire(); |
83 | 16 | } | |
84 | |||
85 | /** | ||
86 | * @copydoc eoos::Object::operator=(Object const&) | ||
87 | */ | ||
88 | 18 | SharedPointer& operator=(SharedPointer const& obj) | |
89 | { | ||
90 |
5/6✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 1 times.
|
18 | if( isConstructed() && (this != &obj) ) |
91 | { | ||
92 | 16 | release(); | |
93 | 16 | cb_ = obj.cb_; | |
94 | 16 | acquire(); | |
95 | 16 | Parent::operator=(obj); | |
96 | } | ||
97 | 18 | return *this; | |
98 | } | ||
99 | |||
100 | #if EOOS_CPP_STANDARD >= 2011 | ||
101 | |||
102 | /** | ||
103 | * @copydoc eoos::Object::Object(Object&&) | ||
104 | */ | ||
105 | 1 | SharedPointer(SharedPointer&& obj) noexcept | |
106 | 1 | : Object<A>( move(obj) ) | |
107 | , api::SmartPointer<T>() | ||
108 | 1 | , cb_(obj.cb_) { | |
109 | 1 | } | |
110 | |||
111 | /** | ||
112 | * @copydoc eoos::Object::operator=(Object&&) | ||
113 | */ | ||
114 | 5 | SharedPointer& operator=(SharedPointer&& obj) & noexcept | |
115 | { | ||
116 |
5/6✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 1 times.
|
5 | if( isConstructed() && (this != &obj) ) |
117 | { | ||
118 | 4 | release(); | |
119 | 4 | cb_ = obj.cb_; | |
120 | 4 | Parent::operator=( move(obj) ); | |
121 | } | ||
122 | 5 | return *this; | |
123 | } | ||
124 | |||
125 | #endif // EOOS_CPP_STANDARD >= 2011 | ||
126 | |||
127 | /** | ||
128 | * @copydoc eoos::api::Object::isConstructed() | ||
129 | */ | ||
130 | 828 | virtual bool_t isConstructed() const ///< SCA MISRA-C++:2008 Defected Rule 9-3-3 | |
131 | { | ||
132 | 828 | return Parent::isConstructed(); | |
133 | } | ||
134 | |||
135 | /** | ||
136 | * @brief Casts to boolean data type comparing if the stored pointer does not equal to null. | ||
137 | * | ||
138 | * @return Comparation the stored pointer does not equal to null. | ||
139 | */ | ||
140 | 3 | operator bool_t() const | |
141 | { | ||
142 | 3 | return get() != NULLPTR; | |
143 | } | ||
144 | |||
145 | /** | ||
146 | * @brief Returns the result of dereferencing the stored pointer. | ||
147 | * | ||
148 | * @return The dereferenced stored pointer. | ||
149 | */ | ||
150 | 1 | T& operator*() const | |
151 | { | ||
152 | 1 | return *get(); | |
153 | } | ||
154 | |||
155 | /** | ||
156 | * @brief Returns the stored pointer. | ||
157 | * | ||
158 | * @return The stored pointer or NULLPTR if no pointer stored. | ||
159 | */ | ||
160 | 30 | T* operator->() const | |
161 | { | ||
162 | 30 | return get(); | |
163 | } | ||
164 | |||
165 | /** | ||
166 | * @brief Returns an element of the stored array. | ||
167 | * | ||
168 | * @param index An element index. | ||
169 | * @return An element. | ||
170 | */ | ||
171 | 9 | T& operator[](uint32_t const index) const | |
172 | { | ||
173 | 9 | T* const pointer( get() ); | |
174 | 9 | return pointer[index]; | |
175 | } | ||
176 | |||
177 | /** | ||
178 | * @copydoc eoos::api::SmartPointer::get() | ||
179 | */ | ||
180 | 218 | virtual T* get() const | |
181 | { | ||
182 | 218 | T* pointer( NULLPTR ); | |
183 |
2/2✓ Branch 1 taken 106 times.
✓ Branch 2 taken 3 times.
|
218 | if( isConstructed() ) |
184 | { | ||
185 | 212 | pointer = cb_->getPointer(); | |
186 | } | ||
187 | 218 | return pointer; | |
188 | } | ||
189 | |||
190 | /** | ||
191 | * @copydoc eoos::api::SmartPointer::reset() | ||
192 | */ | ||
193 | 4 | virtual void reset() | |
194 | { | ||
195 | 4 | reset(NULLPTR); | |
196 | } | ||
197 | |||
198 | /** | ||
199 | * @copydoc eoos::api::SmartPointer::reset(T*) | ||
200 | */ | ||
201 | 8 | virtual void reset(T* ptr) | |
202 | { | ||
203 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | if (get() != ptr) |
204 | { | ||
205 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | Self temp(ptr); |
206 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | swap(temp); |
207 | } | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * @copydoc eoos::api::SmartPointer::getCount() | ||
212 | */ | ||
213 | 200 | virtual int32_t getCount() const | |
214 | { | ||
215 | 200 | int32_t counter( 0 ); | |
216 |
2/2✓ Branch 1 taken 88 times.
✓ Branch 2 taken 12 times.
|
200 | if( isConstructed() ) |
217 | { | ||
218 |
2/2✓ Branch 1 taken 66 times.
✓ Branch 2 taken 22 times.
|
176 | if( cb_->getPointer() != NULLPTR ) |
219 | { | ||
220 | 132 | counter = cb_->getCounter(); | |
221 | } | ||
222 | } | ||
223 | 200 | return counter; | |
224 | } | ||
225 | |||
226 | /** | ||
227 | * @copydoc eoos::api::SmartPointer::isNull() | ||
228 | */ | ||
229 | 56 | virtual bool_t isNull() const | |
230 | { | ||
231 | 56 | return get() == NULLPTR; | |
232 | } | ||
233 | |||
234 | /** | ||
235 | * @copydoc eoos::api::SmartPointer::isUnique() | ||
236 | */ | ||
237 | 56 | virtual bool_t isUnique() const | |
238 | { | ||
239 | 56 | return getCount() == 1; | |
240 | } | ||
241 | |||
242 | /** | ||
243 | * @brief Swaps this managed object with an object managed by given smart object. | ||
244 | * | ||
245 | * @param obj A smart object to swap managed objects. | ||
246 | */ | ||
247 | 12 | void swap(SharedPointer& obj) | |
248 | { | ||
249 |
3/6✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
|
12 | if( isConstructed() && obj.isConstructed() ) |
250 | { | ||
251 | 12 | ControlBlock<T,D,A>* const cb( cb_ ); | |
252 | 12 | cb_ = obj.cb_; | |
253 | 12 | obj.cb_ = cb; | |
254 | } | ||
255 | } | ||
256 | |||
257 | protected: | ||
258 | |||
259 | using Parent::setConstructed; | ||
260 | |||
261 | private: | ||
262 | |||
263 | /** | ||
264 | * @brief Constructs this object. | ||
265 | * | ||
266 | * @param pointer A pointer to get ownership. | ||
267 | * @return True if this object has been constructed successfully. | ||
268 | */ | ||
269 | 106 | bool_t construct(T* const pointer = NULLPTR) | |
270 | { | ||
271 | 106 | bool_t res( false ); | |
272 | do | ||
273 | { | ||
274 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 53 times.
|
106 | if( !isConstructed() ) |
275 | { ///< UT Justified Branch: HW dependency | ||
276 | ✗ | D::free(pointer); | |
277 | ✗ | break; | |
278 | } | ||
279 |
3/4✓ Branch 1 taken 51 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 51 times.
✗ Branch 5 not taken.
|
106 | cb_ = new ControlBlock<T,D,A>(pointer); |
280 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 51 times.
|
106 | if(cb_ == NULLPTR) |
281 | { | ||
282 | 4 | D::free(pointer); | |
283 | 4 | break; | |
284 | } | ||
285 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
|
102 | if( !cb_->isConstructed() ) |
286 | { ///< UT Justified Branch: HW dependency | ||
287 | ✗ | D::free(pointer); | |
288 | ✗ | delete cb_; | |
289 | ✗ | cb_ = NULLPTR; | |
290 | ✗ | break; | |
291 | } | ||
292 | 102 | res = true; | |
293 | } while(false); | ||
294 | 106 | return res; | |
295 | } | ||
296 | |||
297 | /** | ||
298 | * @brief Releases the managed object by control block. | ||
299 | */ | ||
300 | 130 | void release() | |
301 | { | ||
302 |
1/2✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
|
130 | if( cb_ != NULLPTR ) |
303 | { | ||
304 | 130 | int32_t const counter( cb_->decrease() ); | |
305 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 15 times.
|
130 | if(counter == 0) |
306 | { | ||
307 | 100 | D::free(cb_->getPointer()); | |
308 |
1/2✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
|
100 | delete cb_; |
309 | 100 | cb_ = NULLPTR; | |
310 | } | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * @brief Acquires a managed object by control block. | ||
316 | */ | ||
317 | 32 | void acquire() | |
318 | { | ||
319 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
|
32 | if( cb_ != NULLPTR ) |
320 | { | ||
321 | 30 | cb_->increase(); | |
322 | } | ||
323 | 32 | } | |
324 | |||
325 | /** | ||
326 | * @class ControlBlock<TT,DD,AA> | ||
327 | * @brief Primary template implementation of shared pointer control block class. | ||
328 | * | ||
329 | * @tparam TT Data type of owning the object. | ||
330 | * @tparam DD Deleter type for owning the object. | ||
331 | * @tparam AA Heap memory allocator class. | ||
332 | * | ||
333 | * @note This class is implemented as an auxiliry class for SharedPointer | ||
334 | * and is tested for construction before usage. Therefore, some checks | ||
335 | * are skipped in public interface to speedup performence. | ||
336 | */ | ||
337 | template <typename TT, class DD, class AA> | ||
338 | class ControlBlock : public NonCopyable<AA> | ||
339 | { | ||
340 | typedef NonCopyable<AA> Parent; | ||
341 | |||
342 | public: | ||
343 | |||
344 | /** | ||
345 | * @brief Constructor. | ||
346 | * | ||
347 | * @param pointer A pointer to get ownership. | ||
348 | */ | ||
349 | 102 | explicit ControlBlock(T* const pointer) | |
350 | : NonCopyable<AA>() | ||
351 | 102 | , pointer_(pointer) | |
352 | 102 | , counter_(1) | |
353 |
1/2✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
|
102 | , mutex_() { |
354 |
1/2✓ Branch 1 taken 51 times.
✗ Branch 2 not taken.
|
102 | bool_t const isConstructed( construct() ); |
355 | 102 | setConstructed(isConstructed); | |
356 | } | ||
357 | |||
358 | /** | ||
359 | * @copydoc eoos::api::Object::isConstructed() | ||
360 | */ | ||
361 | 204 | virtual bool_t isConstructed() const | |
362 | { | ||
363 | 204 | return Parent::isConstructed(); | |
364 | } | ||
365 | |||
366 | /** | ||
367 | * @brief Destructor. | ||
368 | */ | ||
369 | 200 | virtual ~ControlBlock() {} | |
370 | |||
371 | /** | ||
372 | * @brief Increases the counter on one. | ||
373 | */ | ||
374 | 30 | void increase() | |
375 | { | ||
376 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
30 | MutexGuard<AA> const guard(mutex_); |
377 | static_cast<void>(guard); | ||
378 | 30 | ++counter_; | |
379 | } | ||
380 | |||
381 | /** | ||
382 | * @brief Decreases the counter on one. | ||
383 | * | ||
384 | * @return A value of the counter after decreasing. | ||
385 | */ | ||
386 | 130 | int32_t decrease() | |
387 | { | ||
388 |
1/2✓ Branch 1 taken 65 times.
✗ Branch 2 not taken.
|
130 | MutexGuard<AA> const guard(mutex_); |
389 | static_cast<void>(guard); | ||
390 | 130 | return --counter_; | |
391 | } | ||
392 | |||
393 | /** | ||
394 | * @brief Returns the counter. | ||
395 | * | ||
396 | * @return A value of the counter. | ||
397 | */ | ||
398 | 132 | int32_t getCounter() const | |
399 | { | ||
400 | 132 | return counter_; | |
401 | } | ||
402 | |||
403 | /** | ||
404 | * @brief Returns the managed raw pointer. | ||
405 | * | ||
406 | * @return The managed raw pointer. | ||
407 | */ | ||
408 | 488 | TT* getPointer() const | |
409 | { | ||
410 | 488 | return pointer_; | |
411 | } | ||
412 | |||
413 | private: | ||
414 | |||
415 | /** | ||
416 | * @brief Constructs this object. | ||
417 | */ | ||
418 | 102 | bool_t construct() | |
419 | { | ||
420 | 102 | bool_t res( false ); | |
421 | do | ||
422 | { | ||
423 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
|
102 | if( !isConstructed() ) |
424 | { ///< UT Justified Branch: HW dependency | ||
425 | ✗ | break; | |
426 | } | ||
427 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
|
102 | if( !mutex_.isConstructed() ) |
428 | { ///< UT Justified Branch: HW dependency | ||
429 | ✗ | break; | |
430 | } | ||
431 | 102 | res = true; | |
432 | } while(false); | ||
433 | 102 | return res; | |
434 | } | ||
435 | |||
436 | /** | ||
437 | * @brief An owned pointer. | ||
438 | */ | ||
439 | TT* pointer_; | ||
440 | |||
441 | /** | ||
442 | * @brief Counter of copies of the shared objects. | ||
443 | */ | ||
444 | int32_t counter_; | ||
445 | |||
446 | /** | ||
447 | * @brief Mutex to protect the counter. | ||
448 | */ | ||
449 | Mutex<AA> mutex_; | ||
450 | }; | ||
451 | |||
452 | /** | ||
453 | * @brief Control block of the managed object. | ||
454 | */ | ||
455 | ControlBlock<T,D,A>* cb_; | ||
456 | |||
457 | }; | ||
458 | |||
459 | /** | ||
460 | * @brief Comparison operator to equal. | ||
461 | * | ||
462 | * @param obj1 Reference to object. | ||
463 | * @param obj2 Reference to object. | ||
464 | * @return True if objects are equal. | ||
465 | */ | ||
466 | template <typename T, class D = SmartPointerDeleter<T>, class A = Allocator> | ||
467 | 2 | inline bool_t operator==(SharedPointer<T,D,A> const& obj1, SharedPointer<T,D,A> const& obj2) | |
468 | { | ||
469 | 2 | return obj1.get() == obj2.get(); | |
470 | } | ||
471 | |||
472 | /** | ||
473 | * @brief Comparison operator to unequal. | ||
474 | * | ||
475 | * @param obj1 Reference to object. | ||
476 | * @param obj2 Reference to object. | ||
477 | * @return True if objects are not equal. | ||
478 | */ | ||
479 | template <typename T, class D = SmartPointerDeleter<T>, class A = Allocator> | ||
480 | 2 | inline bool_t operator!=(SharedPointer<T,D,A> const& obj1, SharedPointer<T,D,A> const& obj2) | |
481 | { | ||
482 | 2 | return obj1.get() != obj2.get(); | |
483 | } | ||
484 | |||
485 | #endif // EOOS_ENABLE_DYNAMIC_HEAP_MEMORY | ||
486 | |||
487 | } // namespace lib | ||
488 | } // namespace eoos | ||
489 | #endif // LIB_SHAREDPOINTER_HPP_ | ||
490 |