From f718c54396fe0446f0f483a89b3e35d33f691b4c Mon Sep 17 00:00:00 2001 From: Daniel Santos Date: Fri, 27 Mar 2026 10:17:56 +0000 Subject: [PATCH] Add bounds and empty checks to container classes (#1334) * add bounds and empty checks to containers * address code rabbit review * correct C++11 constexpr error * rename new constexpr macro and make it global * rename queue specializations' exceptions * change front() implementation in locked queue specializations * refactor usage of CONSTEXPR and NO_EXCEPT * expand intrusive queue tests * introduce lock guards on locked queues * Print test names at test time (#1343) * revert mutex and return changes on locking queues * finish reverting the locked queues --------- Co-authored-by: Roland Reichwein Co-authored-by: John Wellbelove --- include/etl/array_view.h | 31 +++++++++- include/etl/array_wrapper.h | 13 +++- include/etl/deque.h | 8 +++ include/etl/forward_list.h | 5 ++ include/etl/indirect_vector.h | 4 ++ include/etl/intrusive_forward_list.h | 5 ++ include/etl/intrusive_list.h | 9 +++ include/etl/intrusive_queue.h | 12 ++-- include/etl/intrusive_stack.h | 8 ++- include/etl/list.h | 9 +++ include/etl/priority_queue.h | 25 +++++++- include/etl/queue.h | 17 +++-- include/etl/queue_lockable.h | 73 ++++++++++++++++++++-- include/etl/queue_mpmc_mutex.h | 78 +++++++++++++++++++---- include/etl/queue_spsc_isr.h | 93 +++++++++++++++++++++++----- include/etl/queue_spsc_locked.h | 71 +++++++++++++++++++-- include/etl/stack.h | 9 ++- include/etl/string_view.h | 33 +++++++++- test/etl_profile.h | 2 + test/test_array_view.cpp | 26 ++++++++ test/test_array_wrapper.cpp | 15 +++++ test/test_deque.cpp | 18 ++++++ test/test_forward_list.cpp | 18 ++++++ test/test_intrusive_forward_list.cpp | 9 +++ test/test_intrusive_list.cpp | 26 ++++++++ test/test_intrusive_queue.cpp | 38 ++++++++++++ test/test_intrusive_stack.cpp | 19 ++++++ test/test_list.cpp | 36 +++++++++++ test/test_priority_queue.cpp | 19 ++++++ test/test_queue.cpp | 36 +++++++++++ test/test_queue_lockable.cpp | 19 ++++++ test/test_queue_mpmc_mutex.cpp | 19 ++++++ test/test_queue_spsc_isr.cpp | 23 +++++++ test/test_queue_spsc_locked.cpp | 23 +++++++ test/test_stack.cpp | 19 ++++++ test/test_string_view.cpp | 28 +++++++++ 36 files changed, 839 insertions(+), 57 deletions(-) diff --git a/include/etl/array_view.h b/include/etl/array_view.h index 5015d55e..da38c67d 100644 --- a/include/etl/array_view.h +++ b/include/etl/array_view.h @@ -93,6 +93,19 @@ namespace etl } }; + //*************************************************************************** + /// The exception thrown when the view is empty. + //*************************************************************************** + class array_view_empty : public array_view_exception + { + public: + + array_view_empty(string_type file_name_, numeric_type line_number_) + : array_view_exception(ETL_ERROR_TEXT("array_view:empty", ETL_ARRAY_VIEW_FILE_ID"C"), file_name_, line_number_) + { + } + }; + //*************************************************************************** /// Array view. //*************************************************************************** @@ -276,33 +289,41 @@ namespace etl //************************************************************************* /// Returns a reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::array_view_empty if the view is empty. //************************************************************************* reference front() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(array_view_empty)); return *mbegin; } //************************************************************************* /// Returns a const reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::array_view_empty if the view is empty. //************************************************************************* const_reference front() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(array_view_empty)); return *mbegin; } //************************************************************************* /// Returns a reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::array_view_empty if the view is empty. //************************************************************************* reference back() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(array_view_empty)); return *(mend - 1); } //************************************************************************* /// Returns a const reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::array_view_empty if the view is empty. //************************************************************************* const_reference back() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(array_view_empty)); return *(mend - 1); } @@ -476,18 +497,24 @@ namespace etl #if defined(ETL_ARRAY_VIEW_IS_MUTABLE) //************************************************************************* /// Returns a reference to the indexed value. + /// If asserts or exceptions are enabled, throws an etl::array_view_bounds if the index is out of bounds. //************************************************************************* - reference operator[](const size_t i) ETL_NOEXCEPT + reference operator[](const size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) { + ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds)); + return mbegin[i]; } #endif //************************************************************************* /// Returns a const reference to the indexed value. + /// If asserts or exceptions are enabled, throws an etl::array_view_bounds if the index is out of bounds. //************************************************************************* - const_reference operator[](const size_t i) const ETL_NOEXCEPT + const_reference operator[](const size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) { + ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(array_view_bounds)); + return mbegin[i]; } diff --git a/include/etl/array_wrapper.h b/include/etl/array_wrapper.h index b422634d..c56ca50a 100644 --- a/include/etl/array_wrapper.h +++ b/include/etl/array_wrapper.h @@ -268,18 +268,27 @@ namespace etl //************************************************************************* /// Returns a reference to the indexed value. + /// If asserts or exceptions are enabled, throws an etl::array_wrapper_bounds if the index is out of bounds. //************************************************************************* - reference operator[](size_t i) ETL_NOEXCEPT + reference operator[](size_t i) ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) { + ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(etl::array_wrapper_bounds)); return ARRAY_[i]; } //************************************************************************* /// Returns a const reference to the indexed value. + /// If asserts or exceptions are enabled, throws an etl::array_wrapper_bounds if the index is out of bounds. //************************************************************************* - ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT + ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) { + // Throwing from c++11 constexpr requires special syntax +#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR + return i < SIZE ? ARRAY_[i] : throw(ETL_ERROR(etl::array_wrapper_bounds)); +#else + ETL_ASSERT_CHECK_INDEX_OPERATOR(i < SIZE, ETL_ERROR(etl::array_wrapper_bounds)); return ARRAY_[i]; +#endif } //************************************************************************* diff --git a/include/etl/deque.h b/include/etl/deque.h index 44043cdc..9c11dff2 100644 --- a/include/etl/deque.h +++ b/include/etl/deque.h @@ -844,37 +844,45 @@ namespace etl //************************************************************************* /// Gets a reference to the item at the front of the deque. + /// If asserts or exceptions are enabled, throws an etl::deque_empty if the deque is empty. ///\return A reference to the item at the front of the deque. //************************************************************************* reference front() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(deque_empty)); return *_begin; } //************************************************************************* /// Gets a const reference to the item at the front of the deque. + /// If asserts or exceptions are enabled, throws an etl::deque_empty if the deque is empty. ///\return A const reference to the item at the front of the deque. //************************************************************************* const_reference front() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(deque_empty)); return *_begin; } //************************************************************************* /// Gets a reference to the item at the back of the deque. + /// If asserts or exceptions are enabled, throws an etl::deque_empty if the deque is empty. ///\return A reference to the item at the back of the deque. //************************************************************************* reference back() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(deque_empty)); return *(_end - 1); } //************************************************************************* /// Gets a const reference to the item at the back of the deque. + /// If asserts or exceptions are enabled, throws an etl::deque_empty if the deque is empty. ///\return A const reference to the item at the back of the deque. //************************************************************************* const_reference back() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(deque_empty)); return *(_end - 1); } diff --git a/include/etl/forward_list.h b/include/etl/forward_list.h index 58ef66f3..65d65a4f 100644 --- a/include/etl/forward_list.h +++ b/include/etl/forward_list.h @@ -130,6 +130,7 @@ namespace etl } }; + //*************************************************************************** /// The base class for all forward_lists. ///\ingroup forward_list @@ -624,17 +625,21 @@ namespace etl //************************************************************************* /// Gets a reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::forward_list_empty if the list is empty. //************************************************************************* reference front() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(forward_list_empty)); return data_cast(*get_head()).value; } //************************************************************************* /// Gets a const reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::forward_list_empty if the list is empty. //************************************************************************* const_reference front() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(forward_list_empty)); return data_cast(*get_head()).value; } diff --git a/include/etl/indirect_vector.h b/include/etl/indirect_vector.h index c0d41438..b92d3b69 100644 --- a/include/etl/indirect_vector.h +++ b/include/etl/indirect_vector.h @@ -664,6 +664,7 @@ namespace etl //********************************************************************* /// Returns a reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::vector_out_of_bounds if the vector is empty. ///\return A reference to the first element. //********************************************************************* reference front() @@ -673,6 +674,7 @@ namespace etl //********************************************************************* /// Returns a const reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::vector_out_of_bounds if the vector is empty. ///\return A const reference to the first element. //********************************************************************* const_reference front() const @@ -682,6 +684,7 @@ namespace etl //********************************************************************* /// Returns a reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::vector_out_of_bounds if the vector is empty. ///\return A reference to the last element. //********************************************************************* reference back() @@ -691,6 +694,7 @@ namespace etl //********************************************************************* /// Returns a const reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::vector_out_of_bounds if the vector is empty. ///\return A const reference to the last element. //********************************************************************* const_reference back() const diff --git a/include/etl/intrusive_forward_list.h b/include/etl/intrusive_forward_list.h index 210dd542..fd7e5679 100644 --- a/include/etl/intrusive_forward_list.h +++ b/include/etl/intrusive_forward_list.h @@ -131,6 +131,7 @@ namespace etl } }; + //*************************************************************************** /// Base for intrusive forward list. ///\ingroup intrusive_forward_list @@ -727,17 +728,21 @@ namespace etl //************************************************************************* /// Gets a reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::intrusive_forward_list_empty if the list is empty. //************************************************************************* reference front() { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_forward_list_empty)); return *static_cast(this->get_head()); } //************************************************************************* /// Gets a const reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::intrusive_forward_list_empty if the list is empty. //************************************************************************* const_reference front() const { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_forward_list_empty)); return *static_cast(this->get_head()); } diff --git a/include/etl/intrusive_list.h b/include/etl/intrusive_list.h index 7a6d057b..a4517d3c 100644 --- a/include/etl/intrusive_list.h +++ b/include/etl/intrusive_list.h @@ -118,6 +118,7 @@ namespace etl } }; + //*************************************************************************** /// Base for intrusive list. ///\ingroup intrusive_list @@ -769,33 +770,41 @@ namespace etl //************************************************************************* /// Gets a reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::intrusive_list_empty if the list is empty. //************************************************************************* reference front() { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_list_empty)); return *static_cast(this->get_head()); } //************************************************************************* /// Gets a const reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::intrusive_list_empty if the list is empty. //************************************************************************* const_reference front() const { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_list_empty)); return *static_cast(this->get_head()); } //************************************************************************* /// Gets a reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::intrusive_list_empty if the list is empty. //************************************************************************* reference back() { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_list_empty)); return *static_cast(this->get_tail()); } //************************************************************************* /// Gets a const reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::intrusive_list_empty if the list is empty. //************************************************************************* const_reference back() const { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_list_empty)); return *static_cast(this->get_tail()); } diff --git a/include/etl/intrusive_queue.h b/include/etl/intrusive_queue.h index 6b51598e..baef0966 100644 --- a/include/etl/intrusive_queue.h +++ b/include/etl/intrusive_queue.h @@ -241,41 +241,45 @@ namespace etl //************************************************************************* /// Gets a reference to the value at the front of the queue. - /// Undefined behaviour if the queue is empty. + /// If asserts or exceptions are enabled, throws an etl::intrusive_queue_empty if the queue is empty. /// \return A reference to the value at the front of the queue. //************************************************************************* reference front() { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_queue_empty)); return *static_cast(this->terminator.etl_next); } //************************************************************************* /// Gets a reference to the value at the back of the queue. - /// Undefined behaviour if the queue is empty. + /// If asserts or exceptions are enabled, throws an etl::intrusive_queue_empty if the queue is empty. /// \return A reference to the value at the back of the queue. //************************************************************************* reference back() { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_queue_empty)); return *static_cast(this->p_back); } //************************************************************************* /// Gets a const reference to the value at the front of the queue. - /// Undefined behaviour if the queue is empty. + /// If asserts or exceptions are enabled, throws an etl::intrusive_queue_empty if the queue is empty. /// \return A const reference to the value at the front of the queue. //************************************************************************* const_reference front() const { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_queue_empty)); return *static_cast(this->terminator.etl_next); } //************************************************************************* /// Gets a reference to the value at the back of the queue. - /// Undefined behaviour if the queue is empty. + /// If asserts or exceptions are enabled, throws an etl::intrusive_queue_empty if the queue is empty. /// \return A reference to the value at the back of the queue. //************************************************************************* const_reference back() const { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_queue_empty)); return *static_cast(this->p_back); } diff --git a/include/etl/intrusive_stack.h b/include/etl/intrusive_stack.h index c93c27cb..c956c490 100644 --- a/include/etl/intrusive_stack.h +++ b/include/etl/intrusive_stack.h @@ -82,6 +82,7 @@ namespace etl } }; + //*************************************************************************** ///\ingroup stack /// Base for intrusive stack. Stores elements derived any type that supports an 'etl_next' pointer member. @@ -242,20 +243,23 @@ namespace etl //************************************************************************* /// Gets a reference to the value at the top of the stack. - /// Undefined behaviour if the stack is empty. + /// If asserts or exceptions are enabled, throws an etl::intrusive_stack_empty if the stack is empty. /// \return A reference to the value at the top of the stack. //************************************************************************* reference top() { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_stack_empty)); return *static_cast(this->p_top); } //************************************************************************* - /// Gets a const reference to the value at the top of the stack.
+ /// Gets a const reference to the value at the top of the stack. + /// If asserts or exceptions are enabled, throws an etl::intrusive_stack_empty if the stack is empty. /// \return A const reference to the value at the top of the stack. //************************************************************************* const_reference top() const { + ETL_ASSERT_CHECK_EXTRA(!this->empty(), ETL_ERROR(intrusive_stack_empty)); return *static_cast(this->p_top); } diff --git a/include/etl/list.h b/include/etl/list.h index 63ab9fc3..5f8b6724 100644 --- a/include/etl/list.h +++ b/include/etl/list.h @@ -130,6 +130,7 @@ namespace etl } }; + //*************************************************************************** /// Unsorted exception for the list. ///\ingroup list @@ -757,33 +758,41 @@ namespace etl //************************************************************************* /// Gets a reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::list_empty if the list is empty. //************************************************************************* reference front() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(list_empty)); return data_cast(get_head()).value; } //************************************************************************* /// Gets a const reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::list_empty if the list is empty. //************************************************************************* const_reference front() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(list_empty)); return data_cast(get_head()).value; } //************************************************************************* /// Gets a reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::list_empty if the list is empty. //************************************************************************* reference back() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(list_empty)); return data_cast(get_tail()).value; } //************************************************************************* /// Gets a reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::list_empty if the list is empty. //************************************************************************* const_reference back() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(list_empty)); return data_cast(get_tail()).value; } diff --git a/include/etl/priority_queue.h b/include/etl/priority_queue.h index a920d753..032aab6c 100644 --- a/include/etl/priority_queue.h +++ b/include/etl/priority_queue.h @@ -95,6 +95,21 @@ namespace etl } }; + //*************************************************************************** + /// The exception thrown when the queue is empty. + ///\ingroup queue + //*************************************************************************** + class priority_queue_empty : public etl::priority_queue_exception + { + public: + + priority_queue_empty(string_type file_name_, numeric_type line_number_) + : priority_queue_exception(ETL_ERROR_TEXT("priority_queue:empty", ETL_PRIORITY_QUEUE_FILE_ID"C"), file_name_, line_number_) + { + } + }; + + //*************************************************************************** ///\ingroup queue ///\brief This is the base for all priority queues that contain a particular type. @@ -128,20 +143,24 @@ namespace etl typedef typename etl::iterator_traits::difference_type difference_type; //************************************************************************* - /// Gets a reference to the highest priority value in the priority queue.
+ /// Gets a reference to the highest priority value in the priority queue. + /// If asserts or exceptions are enabled, throws an etl::priority_queue_empty if the priority queue is empty. /// \return A reference to the highest priority value in the priority queue. //************************************************************************* reference top() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(priority_queue_empty)); return container.front(); } //************************************************************************* - /// Gets a const reference to the highest priority value in the priority queue.
+ /// Gets a const reference to the highest priority value in the priority queue. + /// If asserts or exceptions are enabled, throws an etl::priority_queue_empty if the priority queue is empty. /// \return A const reference to the highest priority value in the priority queue. //************************************************************************* const_reference top() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(priority_queue_empty)); return container.front(); } @@ -307,10 +326,12 @@ namespace etl //************************************************************************* /// Removes the oldest value from the back of the priority queue. + /// If asserts or exceptions are enabled, throws an etl::priority_queue_empty if the priority queue is empty. /// Does nothing if the priority queue is already empty. //************************************************************************* void pop() { + ETL_ASSERT_CHECK_PUSH_POP_OR_RETURN(!empty(), ETL_ERROR(priority_queue_empty)); // Move largest element to end etl::pop_heap(container.begin(), container.end(), compare); // Actually remove largest element at end diff --git a/include/etl/queue.h b/include/etl/queue.h index 6baa3e49..88e3e3f6 100644 --- a/include/etl/queue.h +++ b/include/etl/queue.h @@ -97,6 +97,7 @@ namespace etl } }; + //*************************************************************************** /// The base class for all queues. ///\ingroup queue @@ -266,38 +267,46 @@ namespace etl using base_t::del_out; //************************************************************************* - /// Gets a reference to the value at the front of the queue.
+ /// Gets a reference to the value at the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_empty if the queue is empty. /// \return A reference to the value at the front of the queue. //************************************************************************* reference front() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(queue_empty)); return p_buffer[out]; } //************************************************************************* - /// Gets a const reference to the value at the front of the queue.
+ /// Gets a const reference to the value at the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_empty if the queue is empty. /// \return A const reference to the value at the front of the queue. //************************************************************************* const_reference front() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(queue_empty)); return p_buffer[out]; } //************************************************************************* - /// Gets a reference to the value at the back of the queue.
+ /// Gets a reference to the value at the back of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_empty if the queue is empty. /// \return A reference to the value at the back of the queue. //************************************************************************* reference back() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(queue_empty)); return p_buffer[in == 0 ? CAPACITY - 1 : in - 1]; } //************************************************************************* - /// Gets a const reference to the value at the back of the queue.
+ /// Gets a const reference to the value at the back of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_empty if the queue is empty. /// \return A const reference to the value at the back of the queue. //************************************************************************* const_reference back() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(queue_empty)); return p_buffer[in == 0 ? CAPACITY - 1 : in - 1]; } diff --git a/include/etl/queue_lockable.h b/include/etl/queue_lockable.h index 7d467bf5..7aad8b85 100644 --- a/include/etl/queue_lockable.h +++ b/include/etl/queue_lockable.h @@ -39,12 +39,41 @@ SOFTWARE. #include "function.h" #include "utility.h" #include "placement_new.h" +#include "mutex.h" #include #include namespace etl { + //*************************************************************************** + /// The base class for queue exceptions. + ///\ingroup queue + //*************************************************************************** + class queue_lockable_exception : public exception + { + public: + + queue_lockable_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// The exception thrown when the queue is empty. + /// \ingroup queue + //*************************************************************************** + class queue_lockable_empty : public queue_lockable_exception + { + public: + + queue_lockable_empty(string_type file_name_, numeric_type line_number_) + : queue_lockable_exception(ETL_ERROR_TEXT("queue_lockable:empty", ETL_QUEUE_SPSC_LOCKABLE_FILE_ID"A"), file_name_, line_number_) + { + } + }; + template class queue_lockable_base { @@ -491,45 +520,77 @@ namespace etl //************************************************************************* /// Peek a value at the front of the queue without locking. + /// If asserts or exceptions are enabled, throws an etl::queue_lockable_empty if the queue is empty. //************************************************************************* reference front_unlocked() { + ETL_ASSERT_CHECK_EXTRA(!this->empty_unlocked(), ETL_ERROR(queue_lockable_empty)); return front_implementation(); } //************************************************************************* /// Peek a value at the front of the queue without locking. + /// If asserts or exceptions are enabled, throws an etl::queue_lockable_empty if the queue is empty. //************************************************************************* const_reference front_unlocked() const { + ETL_ASSERT_CHECK_EXTRA(!this->empty_unlocked(), ETL_ERROR(queue_lockable_empty)); return front_implementation(); } //************************************************************************* /// Peek a value at the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_lockable_empty if the queue is empty. //************************************************************************* reference front() { +#if ETL_CHECKING_EXTRA + this->lock(); + if (!this->empty_unlocked()) + { + reference inner_result = front_implementation(); + this->unlock(); + return inner_result; + } + else + { + this->unlock(); + ETL_ASSERT_FAIL(ETL_ERROR(queue_lockable_empty)); + // fall through to return something to satisfy the compiler, even + // though this should never be reached due to undefined behaviour. + } +#endif this->lock(); - reference result = front_implementation(); - this->unlock(); - return result; } //************************************************************************* /// Peek a value at the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_lockable_empty if the queue is empty. //************************************************************************* const_reference front() const { +#if ETL_CHECKING_EXTRA + this->lock(); + if (!this->empty_unlocked()) + { + const_reference inner_result = front_implementation(); + this->unlock(); + return inner_result; + } + else + { + this->unlock(); + ETL_ASSERT_FAIL(ETL_ERROR(queue_lockable_empty)); + // fall through to return something to satisfy the compiler, even + // though this should never be reached due to undefined behaviour. + } +#endif this->lock(); - const_reference result = front_implementation(); - this->unlock(); - return result; } diff --git a/include/etl/queue_mpmc_mutex.h b/include/etl/queue_mpmc_mutex.h index a2f73ef8..cf7a129e 100644 --- a/include/etl/queue_mpmc_mutex.h +++ b/include/etl/queue_mpmc_mutex.h @@ -48,6 +48,34 @@ SOFTWARE. namespace etl { + //*************************************************************************** + /// The base class for queue exceptions. + ///\ingroup queue + //*************************************************************************** + class queue_mpmc_exception : public exception + { + public: + + queue_mpmc_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// The exception thrown when the queue is empty. + /// \ingroup queue + //*************************************************************************** + class queue_mpmc_empty : public queue_mpmc_exception + { + public: + + queue_mpmc_empty(string_type file_name_, numeric_type line_number_) + : queue_mpmc_exception(ETL_ERROR_TEXT("queue_mpmc_mutex:empty", ETL_QUEUE_MPMC_MUTEX_FILE_ID"A"), file_name_, line_number_) + { + } + }; + template class queue_mpmc_mutex_base { @@ -138,13 +166,13 @@ namespace etl public: - typedef T value_type; ///< The type stored in the queue. - typedef T& reference; ///< A reference to the type used in the queue. - typedef const T& const_reference; ///< A const reference to the type used in the queue. + typedef T value_type; ///< The type stored in the queue. + typedef T& reference; ///< A reference to the type used in the queue. + typedef const T& const_reference; ///< A const reference to the type used in the queue. #if ETL_USING_CPP11 - typedef T&& rvalue_reference;///< An rvalue reference to the type used in the queue. + typedef T&& rvalue_reference; ///< An rvalue reference to the type used in the queue. #endif - typedef typename base_t::size_type size_type; ///< The type used for determining the size of the queue. + typedef typename base_t::size_type size_type; ///< The type used for determining the size of the queue. using base_t::write_index; using base_t::read_index; @@ -309,29 +337,57 @@ namespace etl //************************************************************************* /// Peek a value at the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_mpmc_empty if the queue is empty. //************************************************************************* reference front() { +#if ETL_CHECKING_EXTRA + access.lock(); + if (current_size != 0) + { + reference inner_result = front_implementation(); + access.unlock(); + return inner_result; + } + else + { + access.unlock(); + ETL_ASSERT_FAIL(ETL_ERROR(queue_mpmc_empty)); + // fall through to return something to satisfy the compiler, even + // though this should never be reached due to undefined behaviour. + } +#endif access.lock(); - reference result = front_implementation(); - access.unlock(); - return result; } //************************************************************************* /// Peek a value at the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_mpmc_empty if the queue is empty. //************************************************************************* const_reference front() const { +#if ETL_CHECKING_EXTRA + access.lock(); + if (current_size != 0) + { + const_reference inner_result = front_implementation(); + access.unlock(); + return inner_result; + } + else + { + access.unlock(); + ETL_ASSERT_FAIL(ETL_ERROR(queue_mpmc_empty)); + // fall through to return something to satisfy the compiler, even + // though this should never be reached due to undefined behaviour. + } +#endif access.lock(); - const_reference result = front_implementation(); - access.unlock(); - return result; } diff --git a/include/etl/queue_spsc_isr.h b/include/etl/queue_spsc_isr.h index bc6a8f6e..745adcae 100644 --- a/include/etl/queue_spsc_isr.h +++ b/include/etl/queue_spsc_isr.h @@ -38,12 +38,41 @@ SOFTWARE. #include "integral_limits.h" #include "utility.h" #include "placement_new.h" +#include "mutex.h" #include #include namespace etl { + //*************************************************************************** + /// The base class for queue exceptions. + ///\ingroup queue + //*************************************************************************** + class queue_spsc_isr_exception : public exception + { + public: + + queue_spsc_isr_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// The exception thrown when the queue is empty. + /// \ingroup queue + //*************************************************************************** + class queue_spsc_isr_empty : public queue_spsc_isr_exception + { + public: + + queue_spsc_isr_empty(string_type file_name_, numeric_type line_number_) + : queue_spsc_isr_exception(ETL_ERROR_TEXT("queue_spsc_isr:empty", ETL_QUEUE_SPSC_ISR_FILE_ID"A"), file_name_, line_number_) + { + } + }; + template class queue_spsc_isr_base { @@ -52,11 +81,11 @@ namespace etl /// The type used for determining the size of queue. typedef typename etl::size_type_lookup::type size_type; - typedef T value_type; ///< The type stored in the queue. - typedef T& reference; ///< A reference to the type used in the queue. - typedef const T& const_reference; ///< A const reference to the type used in the queue. + typedef T value_type; ///< The type stored in the queue. + typedef T& reference; ///< A reference to the type used in the queue. + typedef const T& const_reference; ///< A const reference to the type used in the queue. #if ETL_USING_CPP11 - typedef T&& rvalue_reference;///< An rvalue reference to the type used in the queue. + typedef T&& rvalue_reference; ///< An rvalue reference to the type used in the queue. #endif //************************************************************************* @@ -108,17 +137,23 @@ namespace etl //************************************************************************* /// Peek a value at the front of the queue from an ISR + /// If asserts or exceptions are enabled, throws an etl::queue_spsc_isr_empty if the queue is empty. //************************************************************************* reference front_from_isr() { + ETL_ASSERT_CHECK_EXTRA(!empty_from_isr(), ETL_ERROR(queue_spsc_isr_empty)); + return front_implementation(); } //************************************************************************* /// Peek a value at the front of the queue from an ISR + /// If asserts or exceptions are enabled, throws an etl::queue_spsc_isr_empty if the queue is empty. //************************************************************************* const_reference front_from_isr() const { + ETL_ASSERT_CHECK_EXTRA(!empty_from_isr(), ETL_ERROR(queue_spsc_isr_empty)); + return front_implementation(); } @@ -494,9 +529,9 @@ namespace etl public: - typedef typename base_t::value_type value_type; ///< The type stored in the queue. - typedef typename base_t::reference reference; ///< A reference to the type used in the queue. - typedef typename base_t::const_reference const_reference; ///< A const reference to the type used in the queue. + typedef typename base_t::value_type value_type; ///< The type stored in the queue. + typedef typename base_t::reference reference; ///< A reference to the type used in the queue. + typedef typename base_t::const_reference const_reference; ///< A const reference to the type used in the queue. #if ETL_USING_CPP11 typedef typename base_t::rvalue_reference rvalue_reference;///< A const reference to the type used in the queue. #endif @@ -659,29 +694,57 @@ namespace etl //************************************************************************* /// Peek a value at the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_spsc_isr_empty if the queue is empty. //************************************************************************* reference front() { +#if ETL_CHECKING_EXTRA + TAccess::lock(); + if (!this->empty_from_isr()) + { + reference inner_result = this->front_implementation(); + TAccess::unlock(); + return inner_result; + } + else + { + TAccess::unlock(); + ETL_ASSERT_FAIL(ETL_ERROR(queue_spsc_isr_empty)); + // fall through to return something to satisfy the compiler, even + // though this should never be reached due to undefined behaviour. + } +#endif TAccess::lock(); - reference result = this->front_implementation(); - TAccess::unlock(); - return result; } //************************************************************************* /// Peek a value at the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_spsc_isr_empty if the queue is empty. //************************************************************************* const_reference front() const { +#if ETL_CHECKING_EXTRA + TAccess::lock(); + if (!this->empty_from_isr()) + { + const_reference inner_result = this->front_implementation(); + TAccess::unlock(); + return inner_result; + } + else + { + TAccess::unlock(); + ETL_ASSERT_FAIL(ETL_ERROR(queue_spsc_isr_empty)); + // fall through to return something to satisfy the compiler, even + // though this should never be reached due to undefined behaviour. + } +#endif TAccess::lock(); - const_reference result = this->front_implementation(); - TAccess::unlock(); - return result; } @@ -716,7 +779,7 @@ namespace etl { TAccess::lock(); - size_type result = (this->current_size == 0); + bool result = (this->current_size == 0); TAccess::unlock(); @@ -730,7 +793,7 @@ namespace etl { TAccess::lock(); - size_type result = (this->current_size == this->MAX_SIZE); + bool result = (this->current_size == this->MAX_SIZE); TAccess::unlock(); diff --git a/include/etl/queue_spsc_locked.h b/include/etl/queue_spsc_locked.h index ee53c5c6..233644de 100644 --- a/include/etl/queue_spsc_locked.h +++ b/include/etl/queue_spsc_locked.h @@ -39,12 +39,41 @@ SOFTWARE. #include "function.h" #include "utility.h" #include "placement_new.h" +#include "mutex.h" #include #include namespace etl { + //*************************************************************************** + /// The base class for queue exceptions. + ///\ingroup queue + //*************************************************************************** + class queue_spsc_locked_exception : public exception + { + public: + + queue_spsc_locked_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //*************************************************************************** + /// The exception thrown when the queue is empty. + /// \ingroup queue + //*************************************************************************** + class queue_spsc_locked_empty : public queue_spsc_locked_exception + { + public: + + queue_spsc_locked_empty(string_type file_name_, numeric_type line_number_) + : queue_spsc_locked_exception(ETL_ERROR_TEXT("queue_spsc_locked:empty", ETL_QUEUE_SPSC_LOCKED_FILE_ID"A"), file_name_, line_number_) + { + } + }; + template class iqueue_spsc_locked_base { @@ -187,6 +216,7 @@ namespace etl ///\details Normally a reference to this type will be taken from a derived queue_spsc_locked. /// This queue supports concurrent access by one producer and one consumer. /// \tparam T The type of value that the queue_spsc_locked holds. + /// \note All types used must have a copy constructor. //*************************************************************************** template class iqueue_spsc_locked : public iqueue_spsc_locked_base @@ -466,29 +496,57 @@ namespace etl //************************************************************************* /// Peek a value from the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_spsc_locked_empty if the queue is empty. //************************************************************************* reference front() { +#if ETL_CHECKING_EXTRA + lock(); + if (!this->empty_from_unlocked()) + { + reference inner_result = front_implementation(); + unlock(); + return inner_result; + } + else + { + unlock(); + ETL_ASSERT_FAIL(ETL_ERROR(queue_spsc_locked_empty)); + // fall through to return something to satisfy the compiler, even + // though this should never be reached due to undefined behaviour. + } +#endif lock(); - reference result = front_implementation(); - unlock(); - return result; } //************************************************************************* /// Peek a value from the front of the queue. + /// If asserts or exceptions are enabled, throws an etl::queue_spsc_locked_empty if the queue is empty. //************************************************************************* const_reference front() const { +#if ETL_CHECKING_EXTRA + lock(); + if (!this->empty_from_unlocked()) + { + const_reference inner_result = front_implementation(); + unlock(); + return inner_result; + } + else + { + unlock(); + ETL_ASSERT_FAIL(ETL_ERROR(queue_spsc_locked_empty)); + // fall through to return something to satisfy the compiler, even + // though this should never be reached due to undefined behaviour. + } +#endif lock(); - const_reference result = front_implementation(); - unlock(); - return result; } @@ -857,6 +915,7 @@ namespace etl /// \tparam T The type this queue should support. /// \tparam SIZE The maximum capacity of the queue. /// \tparam MEMORY_MODEL The memory model for the queue. Determines the type of the internal counter variables. + /// \note T must have a copy constructor defined, as front() returns by value. //*************************************************************************** template class queue_spsc_locked : public etl::iqueue_spsc_locked diff --git a/include/etl/stack.h b/include/etl/stack.h index 2bcee404..6a8e75a6 100644 --- a/include/etl/stack.h +++ b/include/etl/stack.h @@ -97,6 +97,7 @@ namespace etl } }; + //*************************************************************************** ///\ingroup stack /// A fixed capacity stack written in the STL style. @@ -238,11 +239,13 @@ namespace etl public: //************************************************************************* - /// Gets a reference to the value at the top of the stack.
+ /// Gets a reference to the value at the top of the stack. + /// If asserts or exceptions are enabled, throws an etl::stack_empty if the stack is empty. /// \return A reference to the value at the top of the stack. //************************************************************************* reference top() { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(stack_empty)); return p_buffer[top_index]; } @@ -372,11 +375,13 @@ namespace etl #endif //************************************************************************* - /// Gets a const reference to the value at the top of the stack.
+ /// Gets a const reference to the value at the top of the stack. + /// If asserts or exceptions are enabled, throws an etl::stack_empty if the stack is empty. /// \return A const reference to the value at the top of the stack. //************************************************************************* const_reference top() const { + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(stack_empty)); return p_buffer[top_index]; } diff --git a/include/etl/string_view.h b/include/etl/string_view.h index 4e596594..b6abfcf7 100644 --- a/include/etl/string_view.h +++ b/include/etl/string_view.h @@ -96,6 +96,19 @@ namespace etl } }; + //*************************************************************************** + /// The exception thrown when the view is empty. + //*************************************************************************** + class string_view_empty : public string_view_exception + { + public: + + string_view_empty(string_type file_name_, numeric_type line_number_) + : string_view_exception(ETL_ERROR_TEXT("basic_string_view:empty", ETL_STRING_VIEW_FILE_ID"C"), file_name_, line_number_) + { + } + }; + //*************************************************************************** /// String view. //*************************************************************************** @@ -176,18 +189,30 @@ namespace etl //************************************************************************* /// Returns a const reference to the first element. + /// If asserts or exceptions are enabled, throws an etl::string_view_empty if the view is empty. //************************************************************************* ETL_CONSTEXPR const_reference front() const { +#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA + return !empty() ? *mbegin : throw(ETL_ERROR(string_view_empty)); +#else + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(string_view_empty)); return *mbegin; +#endif } //************************************************************************* /// Returns a const reference to the last element. + /// If asserts or exceptions are enabled, throws an etl::string_view_empty if the view is empty. //************************************************************************* ETL_CONSTEXPR const_reference back() const { +#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_EXTRA + return !empty() ? *(mend - 1) : throw(ETL_ERROR(string_view_empty)); +#else + ETL_ASSERT_CHECK_EXTRA(!empty(), ETL_ERROR(string_view_empty)); return *(mend - 1); +#endif } //************************************************************************* @@ -328,10 +353,16 @@ namespace etl //************************************************************************* /// Returns a const reference to the indexed value. + /// If asserts or exceptions are enabled, throws an etl::string_view_bounds if the index is out of bounds. //************************************************************************* - ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT + ETL_CONSTEXPR const_reference operator[](size_t i) const ETL_NOEXCEPT_EXPR(ETL_NOT_USING_EXCEPTIONS || ETL_NOT_CHECKING_INDEX_OPERATOR) { +#if ETL_USING_CPP11 && ETL_NOT_USING_CPP14 && ETL_USING_EXCEPTIONS && ETL_CHECKING_INDEX_OPERATOR + return i < size() ? mbegin[i] : throw(ETL_ERROR(string_view_bounds)); +#else + ETL_ASSERT_CHECK_INDEX_OPERATOR(i < size(), ETL_ERROR(string_view_bounds)); return mbegin[i]; +#endif } //************************************************************************* diff --git a/test/etl_profile.h b/test/etl_profile.h index 16d73d1b..e3439f70 100644 --- a/test/etl_profile.h +++ b/test/etl_profile.h @@ -36,6 +36,7 @@ SOFTWARE. #define ETL_VERBOSE_ERRORS #define ETL_CHECK_PUSH_POP #define ETL_CHECK_INDEX_OPERATOR +#define ETL_DEBUG #define ETL_CHECK_EXTRA #define ETL_ISTRING_REPAIR_ENABLE #define ETL_IVECTOR_REPAIR_ENABLE @@ -48,6 +49,7 @@ SOFTWARE. #define ETL_USE_STD_EXCEPTION #endif + #define ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK #define ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK diff --git a/test/test_array_view.cpp b/test/test_array_view.cpp index 0bb69eda..7162ce33 100644 --- a/test/test_array_view.cpp +++ b/test/test_array_view.cpp @@ -480,6 +480,20 @@ namespace } } +#ifdef ETL_CHECK_INDEX_OPERATOR + + //************************************************************************* + TEST(test_index_operator_bounds_check) + { + View view(etldata.begin(), etldata.end()); + CView cview(etldata.begin(), etldata.end()); + + CHECK_THROW(view[view.size()], etl::array_view_bounds); + CHECK_THROW(cview[cview.size()], etl::array_view_bounds); + } + +#endif + //************************************************************************* TEST(test_at) { @@ -732,6 +746,18 @@ namespace CHECK_EQUAL(hashdata, hashcview); } + TEST(test_bounds_check) + { + View view; + + CHECK_EQUAL(0U, view.size()); + CHECK_EQUAL(0U, view.max_size()); + CHECK(view.empty()); + + CHECK_THROW(view.front(), etl::array_view_empty); + CHECK_THROW(view.back(), etl::array_view_empty); + } + //************************************************************************* #include "etl/private/diagnostic_unused_function_push.h" diff --git a/test/test_array_wrapper.cpp b/test/test_array_wrapper.cpp index cfcb4131..6b469201 100644 --- a/test/test_array_wrapper.cpp +++ b/test/test_array_wrapper.cpp @@ -567,6 +567,21 @@ namespace CHECK_EQUAL(compare_hash, hash); } + +#ifdef ETL_CHECK_INDEX_OPERATOR + + TEST(test_bounds_check) + { + Data5 aw5; + CHECK_EQUAL(5 , aw5.SIZE); + CHECK_EQUAL(5U, aw5.size()); + + CHECK_THROW(aw5[6], etl::array_wrapper_bounds); + CHECK_THROW(aw5.at(6), etl::array_wrapper_bounds); + } + +#endif + } } diff --git a/test/test_deque.cpp b/test/test_deque.cpp index a1b0fa15..93b50a9f 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -459,6 +459,15 @@ namespace CHECK_EQUAL(N6, data.front()); } + //************************************************************************* + TEST(test_front_exception) + { + DataNDC data; + + CHECK(data.empty()); + CHECK_THROW(data.front(), etl::deque_empty); + } + //************************************************************************* TEST(test_front_const) { @@ -508,6 +517,15 @@ namespace CHECK_EQUAL(N6, data.back()); } + //************************************************************************* + TEST(test_back_exception) + { + DataNDC data; + + CHECK(data.empty()); + CHECK_THROW(data.back(), etl::deque_empty); + } + //************************************************************************* TEST(test_back_const) { diff --git a/test/test_forward_list.cpp b/test/test_forward_list.cpp index d7b44bf7..f84ebb01 100644 --- a/test/test_forward_list.cpp +++ b/test/test_forward_list.cpp @@ -757,6 +757,15 @@ namespace CHECK_EQUAL(ItemNDC("0"), data.front()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_front_const_exception) + { + const DataNDC data; + + CHECK(data.empty()); + CHECK_THROW(data.front(), etl::forward_list_empty); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_before_begin_const) { @@ -944,6 +953,15 @@ namespace CHECK_EQUAL(compare_data.front(), data.front()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_front_exception) + { + DataNDC data; + + CHECK(data.empty()); + CHECK_THROW(data.front(), etl::forward_list_empty); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_assignment) { diff --git a/test/test_intrusive_forward_list.cpp b/test/test_intrusive_forward_list.cpp index 4bb3d6f3..2aec53d7 100644 --- a/test/test_intrusive_forward_list.cpp +++ b/test/test_intrusive_forward_list.cpp @@ -936,6 +936,15 @@ namespace CHECK_EQUAL(sorted_data.front(), data0.front()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_front_exception) + { + DataNDC0 data0; + + CHECK(data0.empty()); + CHECK_THROW(data0.front(), etl::intrusive_forward_list_empty); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_unique) { diff --git a/test/test_intrusive_list.cpp b/test/test_intrusive_list.cpp index 41ea478f..308a8b2f 100644 --- a/test/test_intrusive_list.cpp +++ b/test/test_intrusive_list.cpp @@ -1092,6 +1092,32 @@ namespace CHECK_EQUAL(sorted_data.front(), data0.front()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_front_empty_exception) + { + DataNDC0 data0; + + CHECK_EQUAL(0U, data0.size()); + CHECK_THROW(data0.front(), etl::intrusive_list_empty); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_back) + { + DataNDC0 data0(sorted_data.begin(), sorted_data.end()); + + CHECK_EQUAL(sorted_data.back(), data0.back()); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_back_empty_exception) + { + DataNDC0 data0; + + CHECK_EQUAL(0U, data0.size()); + CHECK_THROW(data0.back(), etl::intrusive_list_empty); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_unique) { diff --git a/test/test_intrusive_queue.cpp b/test/test_intrusive_queue.cpp index 8ab16536..490a76f5 100644 --- a/test/test_intrusive_queue.cpp +++ b/test/test_intrusive_queue.cpp @@ -438,5 +438,43 @@ namespace queueD.pop(); CHECK_EQUAL(queueD.back(), queueDR.back()); } + + //************************************************************************* + TEST(test_front_empty_exception) + { + etl::intrusive_queue queueD; + + CHECK_EQUAL(0U, queueD.size()); + CHECK_THROW(queueD.front(), etl::intrusive_queue_empty); + } + + //************************************************************************* + TEST(test_back_empty_exception) + { + etl::intrusive_queue queueD; + + CHECK_EQUAL(0U, queueD.size()); + CHECK_THROW(queueD.back(), etl::intrusive_queue_empty); + } + + //************************************************************************* + TEST(test_front_const_empty_exception) + { + etl::intrusive_queue queueD; + const etl::intrusive_queue& queueDR = queueD; + + CHECK_EQUAL(0U, queueDR.size()); + CHECK_THROW(queueDR.front(), etl::intrusive_queue_empty); + } + + //************************************************************************* + TEST(test_back_const_empty_exception) + { + etl::intrusive_queue queueD; + const etl::intrusive_queue& queueDR = queueD; + + CHECK_EQUAL(0U, queueDR.size()); + CHECK_THROW(queueDR.back(), etl::intrusive_queue_empty); + } } } diff --git a/test/test_intrusive_stack.cpp b/test/test_intrusive_stack.cpp index ad55b635..2acad4b3 100644 --- a/test/test_intrusive_stack.cpp +++ b/test/test_intrusive_stack.cpp @@ -278,6 +278,25 @@ namespace CHECK_EQUAL(stackD.top(), stackDR.top()); } + //************************************************************************* + TEST(test_top_empty_exception) + { + etl::intrusive_stack stackD; + + CHECK_EQUAL(0U, stackD.size()); + CHECK_THROW(stackD.top(), etl::intrusive_stack_empty); + } + + //************************************************************************* + TEST(test_top_const_empty_exception) + { + etl::intrusive_stack stackD; + const etl::intrusive_stack& stackDR = stackD; + + CHECK_EQUAL(0U, stackDR.size()); + CHECK_THROW(stackDR.top(), etl::intrusive_stack_empty); + } + //************************************************************************* TEST(test_reverse1) { diff --git a/test/test_list.cpp b/test/test_list.cpp index 003fb853..adf7cf95 100644 --- a/test/test_list.cpp +++ b/test/test_list.cpp @@ -1171,6 +1171,15 @@ namespace CHECK_EQUAL(compare_data.front(), data.front()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_front_exception) + { + DataInt data; + + CHECK(data.empty()); + CHECK_THROW(data.front(), etl::list_empty); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_front_const) { @@ -1180,6 +1189,15 @@ namespace CHECK_EQUAL(compare_data.front(), data.front()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_front_const_exception) + { + const DataInt data; + + CHECK(data.empty()); + CHECK_THROW(data.front(), etl::list_empty); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_back) { @@ -1189,6 +1207,15 @@ namespace CHECK_EQUAL(compare_data.back(), data.back()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_back_exception) + { + DataInt data; + + CHECK(data.empty()); + CHECK_THROW(data.back(), etl::list_empty); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_back_const) { @@ -1198,6 +1225,15 @@ namespace CHECK_EQUAL(compare_data.back(), data.back()); } + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_back_const_exception) + { + const DataInt data; + + CHECK(data.empty()); + CHECK_THROW(data.back(), etl::list_empty); + } + //************************************************************************* TEST_FIXTURE(SetupFixture, test_assignment) { diff --git a/test/test_priority_queue.cpp b/test/test_priority_queue.cpp index 8d2b5067..608193c3 100644 --- a/test/test_priority_queue.cpp +++ b/test/test_priority_queue.cpp @@ -270,6 +270,15 @@ namespace CHECK_EQUAL(compare_priority_queue.top(), priority_queue.top()); } + //************************************************************************* + TEST(test_top_bounds_exception) + { + etl::priority_queue priority_queue; + + CHECK(priority_queue.empty()); + CHECK_THROW(priority_queue.top(), etl::priority_queue_empty); + } + //************************************************************************* TEST(test_top_const) { @@ -286,6 +295,16 @@ namespace CHECK_EQUAL(4, constQueue.top()); } + //************************************************************************* + TEST(test_top_const_bounds_exception) + { + etl::priority_queue priority_queue; + const etl::priority_queue& constQueue = priority_queue; + + CHECK(constQueue.empty()); + CHECK_THROW(constQueue.top(), etl::priority_queue_empty); + } + //************************************************************************* TEST(test_push) { diff --git a/test/test_queue.cpp b/test/test_queue.cpp index 1f2a04cf..6f82917e 100644 --- a/test/test_queue.cpp +++ b/test/test_queue.cpp @@ -251,6 +251,14 @@ namespace CHECK_EQUAL(3, queue.front()); } + //************************************************************************* + TEST(test_front_bounds_exception) + { + etl::queue queue; + CHECK(queue.empty()); + CHECK_THROW(queue.front(), etl::queue_empty); + } + //************************************************************************* TEST(test_front_const) { @@ -270,6 +278,16 @@ namespace CHECK_EQUAL(3, constQueue.front()); } + //************************************************************************* + TEST(test_front_const_bounds_exception) + { + etl::queue queue; + const etl::queue& constQueue = queue; + + CHECK(constQueue.empty()); + CHECK_THROW(constQueue.front(), etl::queue_empty); + } + //************************************************************************* TEST(test_back) { @@ -285,6 +303,14 @@ namespace CHECK_EQUAL(3, queue.back()); } + //************************************************************************* + TEST(test_back_bounds_exception) + { + etl::queue queue; + CHECK(queue.empty()); + CHECK_THROW(queue.back(), etl::queue_empty); + } + //************************************************************************* TEST(test_back_const) { @@ -301,6 +327,16 @@ namespace CHECK_EQUAL(3, constQueue.back()); } + //************************************************************************* + TEST(test_back_const_bounds_exception) + { + etl::queue queue; + const etl::queue& constQueue = queue; + + CHECK(constQueue.empty()); + CHECK_THROW(constQueue.back(), etl::queue_empty); + } + //************************************************************************* TEST(test_push) { diff --git a/test/test_queue_lockable.cpp b/test/test_queue_lockable.cpp index 893e5d2a..fa262911 100644 --- a/test/test_queue_lockable.cpp +++ b/test/test_queue_lockable.cpp @@ -547,6 +547,25 @@ namespace CHECK_EQUAL(0U, queue.size()); } + //************************************************************************* + TEST(test_front_bounds_exception) + { + QueueInt queue; + + CHECK_EQUAL(0U, queue.size()); + CHECK_THROW(queue.front(), etl::queue_lockable_empty); + } + + //************************************************************************* + TEST(test_front_const_bounds_exception) + { + QueueInt queue; + const QueueInt& constQueue = queue; + + CHECK_EQUAL(0U, constQueue.size()); + CHECK_THROW(constQueue.front(), etl::queue_lockable_empty); + } + //************************************************************************* TEST(test_multiple_emplace) { diff --git a/test/test_queue_mpmc_mutex.cpp b/test/test_queue_mpmc_mutex.cpp index 96c9c4e0..48ba334a 100644 --- a/test/test_queue_mpmc_mutex.cpp +++ b/test/test_queue_mpmc_mutex.cpp @@ -339,6 +339,25 @@ namespace CHECK_EQUAL(0U, queue.size()); } + //************************************************************************* + TEST(test_front_bounds_exception) + { + etl::queue_mpmc_mutex queue; + + CHECK_EQUAL(0U, queue.size()); + CHECK_THROW(queue.front(), etl::queue_mpmc_empty); + } + + //************************************************************************* + TEST(test_front_const_bounds_exception) + { + etl::queue_mpmc_mutex queue; + const etl::queue_mpmc_mutex& constQueue = queue; + + CHECK_EQUAL(0U, constQueue.size()); + CHECK_THROW(constQueue.front(), etl::queue_mpmc_empty); + } + //************************************************************************* TEST(test_clear) { diff --git a/test/test_queue_spsc_isr.cpp b/test/test_queue_spsc_isr.cpp index 3e26aeaf..9d3e8ba7 100644 --- a/test/test_queue_spsc_isr.cpp +++ b/test/test_queue_spsc_isr.cpp @@ -493,6 +493,29 @@ namespace CHECK_EQUAL(0U, queue.size()); } + //************************************************************************* + TEST(test_front_bounds_exception) + { + Access::clear(); + + etl::queue_spsc_isr queue; + + CHECK_EQUAL(0U, queue.size_from_isr()); + CHECK_THROW(queue.front(), etl::queue_spsc_isr_empty); + } + + //************************************************************************* + TEST(test_front_const_bounds_exception) + { + Access::clear(); + + etl::queue_spsc_isr queue; + const etl::queue_spsc_isr& constQueue = queue; + + CHECK_EQUAL(0U, constQueue.size_from_isr()); + CHECK_THROW(constQueue.front(), etl::queue_spsc_isr_empty); + } + //************************************************************************* TEST(test_multiple_emplace) { diff --git a/test/test_queue_spsc_locked.cpp b/test/test_queue_spsc_locked.cpp index be60cbab..b2762aa9 100644 --- a/test/test_queue_spsc_locked.cpp +++ b/test/test_queue_spsc_locked.cpp @@ -498,6 +498,29 @@ namespace CHECK_EQUAL(0U, queue.size()); } + //************************************************************************* + TEST(test_front_bounds_exception) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + + CHECK_EQUAL(0U, queue.size_from_unlocked()); + CHECK_THROW(queue.front(), etl::queue_spsc_locked_empty); + } + + //************************************************************************* + TEST(test_front_const_bounds_exception) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + const etl::queue_spsc_locked& constQueue = queue; + + CHECK_EQUAL(0U, constQueue.size_from_unlocked()); + CHECK_THROW(constQueue.front(), etl::queue_spsc_locked_empty); + } + //************************************************************************* TEST(test_multiple_emplace) { diff --git a/test/test_stack.cpp b/test/test_stack.cpp index cd05ddf2..a8a25525 100644 --- a/test/test_stack.cpp +++ b/test/test_stack.cpp @@ -398,6 +398,15 @@ namespace CHECK_EQUAL(1, stack.top()); } + //************************************************************************* + TEST(test_top_bounds_exception) + { + etl::stack stack; + + CHECK(stack.empty()); + CHECK_THROW(stack.top(), etl::stack_empty); + } + //************************************************************************* TEST(test_top_const) { @@ -417,6 +426,16 @@ namespace CHECK_EQUAL(1, constQueue.top()); } + //************************************************************************* + TEST(test_top_const_bounds_exception) + { + etl::stack stack; + const etl::stack& constStack = stack; + + CHECK(constStack.empty()); + CHECK_THROW(constStack.top(), etl::stack_empty); + } + //************************************************************************* TEST(test_multiple_push) { diff --git a/test/test_string_view.cpp b/test/test_string_view.cpp index a65b938b..faaeaa00 100644 --- a/test/test_string_view.cpp +++ b/test/test_string_view.cpp @@ -418,6 +418,34 @@ namespace CHECK_THROW(view.at(view.size()), etl::string_view_bounds); } +#ifdef ETL_CHECK_INDEX_OPERATOR + + //************************************************************************* + TEST(test_index_bounds_exception) + { + View view(text.c_str(), text.size()); + + CHECK_THROW(view[view.size()], etl::string_view_bounds); + } + +#endif + + //************************************************************************* + TEST(test_front_bounds_exception) + { + View view(text.c_str(), text.c_str()); + + CHECK_THROW(view.front(), etl::string_view_empty); + } + + //************************************************************************* + TEST(test_back_bounds_exception) + { + View view(text.c_str(), text.c_str()); + + CHECK_THROW(view.back(), etl::string_view_empty); + } + //************************************************************************* TEST(test_non_member_same) {