INSTINCT Code Coverage Report


Directory: src/
File: util/Container/TsDeque.hpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 36 49 73.5%
Functions: 11 14 78.6%
Branches: 7 20 35.0%

Line Branch Exec Source
1 // This file is part of INSTINCT, the INS Toolkit for Integrated
2 // Navigation Concepts and Training by the Institute of Navigation of
3 // the University of Stuttgart, Germany.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9 /// @file TsDeque.hpp
10 /// @brief Thread-safe deque
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @date 2022-09-12
13
14 #pragma once
15
16 #include <deque>
17 #include <mutex>
18
19 namespace NAV
20 {
21
22 /// @brief Thread-safe deque
23 /// @tparam T The type of the elements.
24 /// @tparam Alloc Allocator that is used to acquire/release memory and to construct/destroy the elements in that memory.
25 template<class T, class Alloc = std::allocator<T>>
26 class TsDeque
27 {
28 public:
29 // #######################################################################################################
30 // Member functions
31 // #######################################################################################################
32
33 /// @brief Default Constructor. Constructs an empty container with a default-constructed allocator.
34 5856 TsDeque() = default;
35
36 /// Destructor
37 8990 ~TsDeque() = default;
38
39 /// @brief Constructs an empty container with the given allocator alloc.
40 /// @param alloc Allocator to use for all memory allocations of this container
41 explicit TsDeque(const Alloc& alloc)
42 : _queue(alloc) {}
43
44 /// @brief Constructs the container with count copies of elements with value value.
45 /// @param count The size of the container
46 /// @param value The value to initialize elements of the container with
47 /// @param alloc Allocator to use for all memory allocations of this container
48 TsDeque(typename std::deque<T, Alloc>::size_type count, const T& value, const Alloc& alloc = Alloc())
49 : _queue(count, value, alloc) {}
50
51 /// @brief Constructs the container with count default-inserted instances of T. No copies are made.
52 /// @param count The size of the container
53 /// @param alloc Allocator to use for all memory allocations of this container
54 explicit TsDeque(typename std::deque<T, Alloc>::size_type count, const Alloc& alloc = Alloc())
55 : _queue(count, alloc) {}
56
57 /// @brief Constructs the container with the contents of the range [first, last).
58 /// @param first First element of the range to copy the elements from
59 /// @param last Last element the range to copy the elements from
60 /// @param alloc Allocator to use for all memory allocations of this container
61 template<class InputIt>
62 TsDeque(InputIt first, InputIt last, const Alloc& alloc = Alloc())
63 : _queue(first, last, alloc)
64 {}
65
66 /// @brief Copy constructor. Constructs the container with the copy of the contents of other.
67 /// @param other Another container to be used as source to initialize the elements of the container with
68 3135 TsDeque(const TsDeque& other)
69 3135 {
70
1/2
✓ Branch 1 taken 3135 times.
✗ Branch 2 not taken.
3135 std::scoped_lock lk(other._mutex);
71
1/2
✓ Branch 1 taken 3135 times.
✗ Branch 2 not taken.
3135 _queue = std::deque<T, Alloc>(other._queue); // NOLINT(cppcoreguidelines-prefer-member-initializer)
72 3135 }
73 /// @brief Constructs the container with the copy of the contents of other, using alloc as the allocator.
74 /// @param other Another container to be used as source to initialize the elements of the container with
75 /// @param alloc Allocator to use for all memory allocations of this container
76 TsDeque(const TsDeque& other, const Alloc& alloc)
77 {
78 std::scoped_lock lk(other._mutex);
79 _queue = std::deque<T, Alloc>(other._queue, alloc); // NOLINT(cppcoreguidelines-prefer-member-initializer)
80 }
81 /// @brief Move constructor. Constructs the container with the contents of other using move semantics.
82 /// Allocator is obtained by move-construction from the allocator belonging to other.
83 /// @param other Another container to be used as source to initialize the elements of the container with
84 TsDeque(TsDeque&& other) noexcept
85 {
86 std::scoped_lock lk(other._mutex);
87 _queue = std::deque<T, Alloc>(std::move(other._queue)); // NOLINT(cppcoreguidelines-prefer-member-initializer)
88 }
89 /// @brief Allocator-extended move constructor. Using alloc as the allocator for the new container, moving the contents from other;
90 /// if alloc != other.get_allocator(), this results in an element-wise move.
91 /// @param other Another container to be used as source to initialize the elements of the container with
92 /// @param alloc Allocator to use for all memory allocations of this container
93 TsDeque(TsDeque&& other, const Alloc& alloc) noexcept
94 {
95 std::scoped_lock lk(other._mutex);
96 _queue = std::deque<T, Alloc>(std::move(other._queue), alloc); // NOLINT(cppcoreguidelines-prefer-member-initializer)
97 }
98 /// @brief Constructs the container with the contents of the initializer list init.
99 /// @param init Initializer list to initialize the elements of the container with
100 /// @param alloc Allocator to use for all memory allocations of this container
101 TsDeque(std::initializer_list<T> init, const Alloc& alloc = Alloc())
102 : _queue(init, alloc) {}
103
104 /// @brief Copy assignment operator
105 TsDeque& operator=(const TsDeque& other)
106 {
107 if (this != &other)
108 {
109 std::scoped_lock lk(_mutex);
110 std::scoped_lock lko(other._mutex);
111 _queue = other._queue;
112 }
113 return *this;
114 }
115 /// @brief Move assignment operator
116 TsDeque& operator=(TsDeque&& other) noexcept
117 {
118 if (this != &other)
119 {
120 std::scoped_lock lk(_mutex);
121 std::scoped_lock lko(other._mutex);
122 _queue = std::move(other._queue);
123 }
124 return *this;
125 }
126 /// @brief Replaces the contents with those identified by initializer list ilist.
127 /// @param ilist Initializer list to use as data source
128 TsDeque& operator=(std::initializer_list<T> ilist)
129 {
130 _queue = ilist;
131 }
132
133 /// @brief Replaces the contents with count copies of value value
134 ///
135 /// All iterators, pointers and references to the elements of the container are invalidated. The past-the-end iterator is also invalidated.
136 /// @param count The new size of the container
137 /// @param value The value to initialize elements of the container with
138 void assign(typename std::deque<T, Alloc>::size_type count, const T& value)
139 {
140 std::scoped_lock lk(_mutex);
141 _queue.assign(count, value);
142 }
143
144 /// @brief Replaces the contents with copies of those in the range [first, last). The behavior is undefined if either argument is an iterator into *this.
145 ///
146 /// All iterators, pointers and references to the elements of the container are invalidated. The past-the-end iterator is also invalidated.
147 /// @param[in] first The first element of the range to copy the elements from
148 /// @param[in] last The last element of the range to copy the elements from
149 template<class InputIt>
150 void assign(InputIt first, InputIt last)
151 {
152 std::scoped_lock lk(_mutex);
153 _queue.assign(first, last);
154 }
155 /// @brief Replaces the contents with the elements from the initializer list ilist.
156 ///
157 /// All iterators, pointers and references to the elements of the container are invalidated. The past-the-end iterator is also invalidated.
158 /// @param ilist Initializer list to copy the values from
159 void assign(std::initializer_list<T> ilist)
160 {
161 std::scoped_lock lk(_mutex);
162 _queue.assign(ilist);
163 }
164
165 /// @brief Returns the allocator associated with the container.
166 /// @return The associated allocator.
167 typename std::deque<T, Alloc>::allocator_type get_allocator() const noexcept
168 {
169 return _queue.get_allocator();
170 }
171
172 // #######################################################################################################
173 // Element access
174 // #######################################################################################################
175
176 /// @brief Returns a reference to the element at specified location pos, with bounds checking.
177 /// If pos is not within the range of the container, an exception of type std::out_of_range is thrown.
178 /// @param[in] pos Position of the element to return
179 /// @return Reference to the requested element.
180 auto& at(typename std::deque<T, Alloc>::size_type pos) { return _queue.at(pos); }
181 /// @brief Returns a reference to the element at specified location pos, with bounds checking.
182 /// If pos is not within the range of the container, an exception of type std::out_of_range is thrown.
183 /// @param[in] pos Position of the element to return
184 /// @return Reference to the requested element.
185 const auto& at(typename std::deque<T, Alloc>::size_type pos) const { return _queue.at(pos); }
186
187 /// @brief Returns a reference to the element at specified location pos. No bounds checking is performed.
188 /// @param[in] pos Position of the element to return
189 /// @return Reference to the requested element.
190 auto& operator[](typename std::deque<T, Alloc>::size_type pos) { return _queue[pos]; }
191 /// @brief Returns a reference to the element at specified location pos. No bounds checking is performed.
192 /// @param[in] pos Position of the element to return
193 /// @return Reference to the requested element.
194 const auto& operator[](typename std::deque<T, Alloc>::size_type pos) const { return _queue[pos]; }
195
196 /// @brief Returns a reference to the first element in the container.
197 /// @return Reference to the first element
198 1827825 auto& front() { return _queue.front(); }
199 /// @brief Returns a reference to the first element in the container.
200 /// @return Reference to the first element
201 59376 const auto& front() const { return _queue.front(); }
202
203 /// @brief Returns a reference to the last element in the container.
204 /// @return Reference to the last element
205 auto& back() { return _queue.back(); }
206 /// @brief Returns a reference to the last element in the container.
207 /// @return Reference to the last element
208 const auto& back() const { return _queue.back(); }
209
210 // #######################################################################################################
211 // Iterators
212 // #######################################################################################################
213
214 /// @brief Returns an iterator to the first element of the deque.
215 /// If the deque is empty, the returned iterator will be equal to end().
216 /// @return Iterator to the first element.
217 typename std::deque<T>::iterator begin() noexcept { return _queue.begin(); }
218 /// @brief Returns an iterator to the first element of the deque.
219 /// If the deque is empty, the returned iterator will be equal to end().
220 /// @return Iterator to the first element.
221 typename std::deque<T>::const_iterator begin() const noexcept { return _queue.begin(); }
222 /// @brief Returns an iterator to the first element of the deque.
223 /// If the deque is empty, the returned iterator will be equal to end().
224 /// @return Iterator to the first element.
225 typename std::deque<T>::const_iterator cbegin() const noexcept { return _queue.cbegin(); }
226
227 /// @brief Returns an iterator to the element following the last element of the deque.
228 /// This element acts as a placeholder; attempting to access it results in undefined behavior.
229 /// @return Iterator to the element following the last element.
230 typename std::deque<T>::iterator end() noexcept { return _queue.end(); }
231 /// @brief Returns an iterator to the element following the last element of the deque.
232 /// This element acts as a placeholder; attempting to access it results in undefined behavior.
233 /// @return Iterator to the element following the last element.
234 typename std::deque<T>::const_iterator end() const noexcept { return _queue.end(); }
235 /// @brief Returns an iterator to the element following the last element of the deque.
236 /// This element acts as a placeholder; attempting to access it results in undefined behavior.
237 /// @return Iterator to the element following the last element.
238 typename std::deque<T>::const_iterator cend() const noexcept { return _queue.cend(); }
239
240 /// @brief Returns a reverse iterator to the first element of the reversed deque. It corresponds to the last element of the non-reversed deque.
241 /// If the deque is empty, the returned iterator is equal to rend().
242 /// @return Reverse iterator to the first element.
243 typename std::deque<T>::reverse_iterator rbegin() noexcept { return _queue.rbegin(); }
244 /// @brief Returns a reverse iterator to the first element of the reversed deque. It corresponds to the last element of the non-reversed deque.
245 /// If the deque is empty, the returned iterator is equal to rend().
246 /// @return Reverse iterator to the first element.
247 typename std::deque<T>::const_reverse_iterator rbegin() const noexcept { return _queue.rbegin(); }
248 /// @brief Returns a reverse iterator to the first element of the reversed deque. It corresponds to the last element of the non-reversed deque.
249 /// If the deque is empty, the returned iterator is equal to rend().
250 /// @return Reverse iterator to the first element.
251 typename std::deque<T>::const_reverse_iterator crbegin() const noexcept { return _queue.crbegin(); }
252
253 /// @brief Returns a reverse iterator to the element following the last element of the reversed deque.
254 /// It corresponds to the element preceding the first element of the non-reversed deque.
255 /// This element acts as a placeholder, attempting to access it results in undefined behavior.
256 /// @return Reverse iterator to the element following the last element.
257 typename std::deque<T>::reverse_iterator rend() noexcept { return _queue.rend(); }
258 /// @brief Returns a reverse iterator to the element following the last element of the reversed deque.
259 /// It corresponds to the element preceding the first element of the non-reversed deque.
260 /// This element acts as a placeholder, attempting to access it results in undefined behavior.
261 /// @return Reverse iterator to the element following the last element.
262 typename std::deque<T>::const_reverse_iterator rend() const noexcept { return _queue.rend(); }
263 /// @brief Returns a reverse iterator to the element following the last element of the reversed deque.
264 /// It corresponds to the element preceding the first element of the non-reversed deque.
265 /// This element acts as a placeholder, attempting to access it results in undefined behavior.
266 /// @return Reverse iterator to the element following the last element.
267 typename std::deque<T>::const_reverse_iterator crend() const noexcept { return _queue.crend(); }
268
269 // #######################################################################################################
270 // Capacity
271 // #######################################################################################################
272
273 /// @brief Checks if the container has no elements, i.e. whether 'begin() == end()'.
274 /// @return true if the container is empty, false otherwise
275 4109341 [[nodiscard]] bool empty() const noexcept
276 {
277 4109341 std::scoped_lock lk(_mutex);
278 4112447 return _queue.empty();
279 4111740 }
280
281 /// @brief Returns the number of elements in the container, i.e. std::distance(begin(), end()).
282 /// @return The number of elements in the container.
283 56800 typename std::deque<T, Alloc>::size_type size() const noexcept
284 {
285 56800 std::scoped_lock lk(_mutex);
286 56800 return _queue.size();
287 56800 }
288
289 /// @brief Returns the maximum number of elements the container is able to hold due to system or library implementation limitations, i.e. std::distance(begin(), end()) for the largest container.
290 /// @return Maximum number of elements.
291 typename std::deque<T, Alloc>::size_type max_size() const noexcept
292 {
293 std::scoped_lock lk(_mutex);
294 return _queue.max_size();
295 }
296
297 /// @brief Requests the removal of unused capacity.
298 void shrink_to_fit()
299 {
300 std::scoped_lock lk(_mutex);
301 _queue.shrink_to_fit();
302 }
303
304 // #######################################################################################################
305 // Modifiers
306 // #######################################################################################################
307
308 /// Erases all elements from the container. After this call, size() returns zero.
309 /// Invalidates any references, pointers, or iterators referring to contained elements. Any past-the-end iterators are also invalidated.
310 831 void clear() noexcept
311 {
312 831 std::scoped_lock lk(_mutex);
313 831 _queue.clear();
314 829 }
315
316 /// @brief Insert value before pos in the container.
317 /// @param[in] pos Iterator before which the content will be inserted. pos may be the end() iterator
318 /// @param[in] value Element value to insert
319 /// @return Iterator pointing to the inserted value
320 typename std::deque<T>::iterator insert(typename std::deque<T>::const_iterator pos, const T& value)
321 {
322 std::scoped_lock lk(_mutex);
323 return _queue.insert(pos, value);
324 }
325
326 /// @brief Insert value before pos in the container.
327 /// @param[in] pos Iterator before which the content will be inserted. pos may be the end() iterator
328 /// @param[in] value Element value to insert
329 /// @return Iterator pointing to the inserted value
330 typename std::deque<T>::iterator insert(typename std::deque<T>::const_iterator pos, T&& value)
331 {
332 std::scoped_lock lk(_mutex);
333 return _queue.insert(pos, value);
334 }
335
336 /// @brief inserts count copies of the value before pos
337 /// @param[in] pos Iterator before which the content will be inserted. pos may be the end() iterator
338 /// @param[in] count Number of elements to insert
339 /// @param[in] value Element value to insert
340 /// @return Linear in count plus linear in the lesser of the distances between pos and either of the ends of the container.
341 typename std::deque<T>::iterator insert(typename std::deque<T>::const_iterator pos, typename std::deque<T, Alloc>::size_type count, const T& value)
342 {
343 std::scoped_lock lk(_mutex);
344 return _queue.insert(pos, count, value);
345 }
346
347 /// @brief inserts elements from range [first, last) before pos.
348 /// @tparam InputIt Input iterator type
349 /// @param[in] pos Iterator before which the content will be inserted. pos may be the end() iterator
350 /// @param[in] first The first element of the range of elements to insert, can't be iterators into container for which insert is called
351 /// @param[in] last The last element of the range of elements to insert, can't be iterators into container for which insert is called
352 /// @return Iterator pointing to the first element inserted, or pos if first==last.
353 template<class InputIt>
354 typename std::deque<T>::iterator insert(typename std::deque<T>::const_iterator pos, InputIt first, InputIt last)
355 {
356 std::scoped_lock lk(_mutex);
357 return _queue.insert(pos, first, last);
358 }
359
360 /// @brief Inserts elements from initializer list ilist before pos.
361 /// @param[in] pos Iterator before which the content will be inserted. pos may be the end() iterator
362 /// @param[in] ilist Initializer list to insert the values from
363 /// @return Iterator pointing to the first element inserted, or pos if ilist is empty.
364 typename std::deque<T>::iterator insert(typename std::deque<T>::const_iterator pos, std::initializer_list<T> ilist)
365 {
366 std::scoped_lock lk(_mutex);
367 return _queue.insert(pos, ilist);
368 }
369
370 /// @brief Inserts a new element into the container directly before pos.
371 /// @param[in] pos Iterator before which the new element will be constructed
372 /// @param[in] args Arguments to forward to the constructor of the element
373 /// @return Iterator pointing to the emplaced element.
374 template<class... Args>
375 typename std::deque<T>::iterator emplace(typename std::deque<T>::const_iterator pos, Args&&... args)
376 {
377 std::scoped_lock lk(_mutex);
378 return _queue.emplace(pos, std::forward<Args>(args)...);
379 }
380
381 /// @brief Removes the element at pos.
382 /// @param[in] pos Iterator to the element to remove
383 /// @return Iterator following the last removed element.
384 typename std::deque<T>::iterator erase(typename std::deque<T>::const_iterator pos)
385 {
386 std::scoped_lock lk(_mutex);
387 return _queue.erase(pos);
388 }
389 /// @brief Removes the elements in the range [first, last).
390 /// All iterators and references are invalidated, unless the erased elements are at the end or the beginning of the container,
391 /// in which case only the iterators and references to the erased elements are invalidated.
392 /// @param[in] first First element of the range of elements to remove
393 /// @param[in] last Last element of the range of elements to remove
394 /// @return Iterator following the last removed element.
395 typename std::deque<T>::iterator erase(typename std::deque<T>::const_iterator first, typename std::deque<T>::const_iterator last)
396 {
397 std::scoped_lock lk(_mutex);
398 return _queue.erase(first, last);
399 }
400
401 /// @brief Appends the given element value to the end of the container. The new element is initialized as a copy of value.
402 /// @param[in] value The value of the element to append
403 467104 void push_back(const T& value)
404 {
405
1/2
✓ Branch 1 taken 466986 times.
✗ Branch 2 not taken.
467104 std::scoped_lock lk(_mutex);
406
1/2
✓ Branch 1 taken 466871 times.
✗ Branch 2 not taken.
466986 _queue.push_back(value);
407 466871 }
408 /// @brief Appends the given element value to the end of the container. Value is moved into the new element.
409 /// @param[in] value The value of the element to append
410 void push_back(T&& value)
411 {
412 std::scoped_lock lk(_mutex);
413 _queue.push_back(value);
414 }
415
416 /// @brief Appends a new element to the end of the container.
417 /// @param[in] args Arguments to forward to the constructor of the element
418 /// @return A reference to the inserted element.
419 template<class... Args>
420 auto& emplace_back(Args&&... args)
421 {
422 std::scoped_lock lk(_mutex);
423 return _queue.emplace_back(std::forward<Args>(args)...);
424 }
425
426 /// @brief Removes the last element of the container.
427 void pop_back()
428 {
429 std::scoped_lock lk(_mutex);
430 _queue.pop_back();
431 }
432
433 /// @brief Prepends the given element value to the beginning of the container. The new element is initialized as a copy of value.
434 /// All iterators, including the past-the-end iterator, are invalidated. No references are invalidated.
435 /// @param[in] value The value of the element to prepend
436 void push_front(const T& value)
437 {
438 std::scoped_lock lk(_mutex);
439 _queue.push_front(value);
440 }
441 /// @brief Prepends the given element value to the beginning of the container. Value is moved into the new element.
442 /// All iterators, including the past-the-end iterator, are invalidated. No references are invalidated.
443 /// @param[in] value The value of the element to prepend
444 void push_front(T&& value)
445 {
446 std::scoped_lock lk(_mutex);
447 _queue.push_front(value);
448 }
449
450 /// @brief Inserts a new element to the beginning of the container.
451 /// @param[in] args Arguments to forward to the constructor of the element
452 /// @return A reference to the inserted element.
453 template<class... Args>
454 auto& emplace_front(Args&&... args)
455 {
456 std::scoped_lock lk(_mutex);
457 return _queue.emplace_front(std::forward<Args>(args)...);
458 }
459
460 /// @brief Removes the first element of the container. If there are no elements in the container, the behavior is undefined.
461 465716 void pop_front()
462 {
463
1/2
✓ Branch 1 taken 466264 times.
✗ Branch 2 not taken.
465716 std::scoped_lock lk(_mutex);
464 466264 _queue.pop_front();
465 466498 }
466
467 /// @brief Resizes the container to contain count elements.
468 /// @param[in] count New size of the container
469 void resize(typename std::deque<T, Alloc>::size_type count)
470 {
471 std::scoped_lock lk(_mutex);
472 _queue.resize(count);
473 }
474 /// @brief Resizes the container to contain count elements.
475 /// @param[in] count New size of the container
476 /// @param[in] value The value to initialize the new elements with
477 void resize(typename std::deque<T, Alloc>::size_type count, const T& value)
478 {
479 std::scoped_lock lk(_mutex);
480 _queue.resize(count, value);
481 }
482
483 /// @brief Exchanges the contents of the container with those of other. Does not invoke any move, copy, or swap operations on individual elements.
484 /// All iterators and references remain valid. The past-the-end iterator is invalidated.
485 /// @param[in, out] other Container to exchange the contents with
486 void swap(std::deque<T>& other) noexcept
487 {
488 std::scoped_lock lk(_mutex);
489 _queue.swap(other);
490 }
491
492 /// @brief Returns a copy of the first element in the container and removes it from the container.
493 /// @return Copy of the first element
494 459482 auto extract_front()
495 {
496 459482 T front;
497 {
498
1/2
✓ Branch 1 taken 460268 times.
✗ Branch 2 not taken.
459482 std::scoped_lock lk(_mutex);
499 460268 front = _queue.front();
500 459970 }
501
1/2
✓ Branch 1 taken 459446 times.
✗ Branch 2 not taken.
459784 pop_front();
502 459446 return front;
503 }
504
505 private:
506 /// Queue with received data
507 std::deque<T, Alloc> _queue;
508
509 /// Mutex to interact with the queue object
510 mutable std::mutex _mutex;
511 };
512
513 } // namespace NAV
514