diff --git a/include/etl/array.h b/include/etl/array.h index c0cf8f98..e0854854 100644 --- a/include/etl/array.h +++ b/include/etl/array.h @@ -145,6 +145,8 @@ namespace etl ETL_CONSTEXPR14 reference operator[](size_t i) { + ETL_DEBUG_ASSERT(i < SIZE, ETL_ERROR(array_out_of_range)); + return _buffer[i]; } @@ -156,7 +158,14 @@ namespace etl ETL_NODISCARD ETL_CONSTEXPR const_reference operator[](size_t i) const { + //throwing from c++11 constexpr requires a special macro +#if ETL_USING_CPP11 && !ETL_USING_CPP14 && ETL_DEBUG_USING_EXCEPTIONS + ETL_DEBUG_ASSERT_OR_RETURN_VALUE_CPP11_CONSTEXPR(i < SIZE, ETL_ERROR(array_out_of_range), _buffer[i]); +#else + ETL_DEBUG_ASSERT(i < SIZE, ETL_ERROR(array_out_of_range)); + return _buffer[i]; +#endif } //************************************************************************* @@ -166,6 +175,8 @@ namespace etl ETL_CONSTEXPR14 reference front() { + ETL_STATIC_ASSERT(SIZE > 0, "Array is empty."); + return _buffer[0]; } @@ -175,6 +186,8 @@ namespace etl ETL_NODISCARD ETL_CONSTEXPR const_reference front() const { + ETL_STATIC_ASSERT(SIZE > 0, "Array is empty."); + return _buffer[0]; } @@ -185,6 +198,8 @@ namespace etl ETL_CONSTEXPR14 reference back() { + ETL_STATIC_ASSERT(SIZE > 0, "Array is empty."); + return _buffer[SIZE - 1]; } @@ -194,6 +209,8 @@ namespace etl ETL_NODISCARD ETL_CONSTEXPR const_reference back() const { + ETL_STATIC_ASSERT(SIZE > 0, "Array is empty."); + return _buffer[SIZE - 1]; } @@ -429,6 +446,8 @@ namespace etl //************************************************************************* inline iterator insert_at(size_t position, parameter_t value) { + ETL_DEBUG_ASSERT(position < SIZE, ETL_ERROR(array_out_of_range)); + return insert(begin() + position, value); } @@ -439,6 +458,8 @@ namespace etl //************************************************************************* iterator insert(const_iterator position, parameter_t value) { + ETL_DEBUG_ASSERT(cbegin() <= position && position < cend(), ETL_ERROR(array_out_of_range)); + iterator p = to_iterator(position); etl::move_backward(p, end() - 1, end()); @@ -456,6 +477,8 @@ namespace etl template inline iterator insert_at(size_t position, TIterator first, const TIterator last) { + ETL_DEBUG_ASSERT(position < SIZE, ETL_ERROR(array_out_of_range)); + return insert(begin() + position, first, last); } @@ -468,6 +491,8 @@ namespace etl template iterator insert(const_iterator position, TIterator first, const TIterator last) { + ETL_DEBUG_ASSERT(cbegin() <= position && position < cend(), ETL_ERROR(array_out_of_range)); + iterator p = to_iterator(position); iterator result(p); @@ -494,6 +519,8 @@ namespace etl //************************************************************************* inline iterator erase_at(size_t position) { + ETL_DEBUG_ASSERT(position < SIZE, ETL_ERROR(array_out_of_range)); + return erase(begin() + position); } @@ -504,6 +531,8 @@ namespace etl //************************************************************************* iterator erase(const_iterator position) { + ETL_DEBUG_ASSERT(cbegin() <= position && position < cend(), ETL_ERROR(array_out_of_range)); + iterator p = to_iterator(position); etl::move(p + 1, end(), p); @@ -518,6 +547,8 @@ namespace etl //************************************************************************* iterator erase_range(size_t first, size_t last) { + ETL_DEBUG_ASSERT(first <= last && last <= SIZE, ETL_ERROR(array_out_of_range)); + return erase(begin() + first, begin() + last); } @@ -529,6 +560,8 @@ namespace etl //************************************************************************* iterator erase(const_iterator first, const_iterator last) { + ETL_DEBUG_ASSERT(cbegin() <= first && first <= last && last <= cend(), ETL_ERROR(array_out_of_range)); + iterator p = to_iterator(first); etl::move(last, cend(), p); return p; @@ -541,6 +574,8 @@ namespace etl //************************************************************************* inline iterator erase_at(size_t position, parameter_t value) { + ETL_DEBUG_ASSERT(position < SIZE, ETL_ERROR(array_out_of_range)); + return erase(begin() + position, value); } @@ -551,6 +586,8 @@ namespace etl //************************************************************************* iterator erase(const_iterator position, parameter_t value) { + ETL_DEBUG_ASSERT(cbegin() <= position && position < cend(), ETL_ERROR(array_out_of_range)); + iterator p = to_iterator(position); etl::move(p + 1, end(), p); @@ -567,16 +604,21 @@ namespace etl //************************************************************************* iterator erase_range(size_t first, size_t last, parameter_t value) { + ETL_DEBUG_ASSERT(first <= last && last <= SIZE, ETL_ERROR(array_out_of_range)); + return erase(begin() + first, begin() + last, value); } //************************************************************************* /// Erases a range of values from the array. - ///\param position The iterator to the position to erase at. - ///\param value The value to use to overwrite the last elements in the array. + ///\param first The first item to erase. + ///\param last The one past the last item to erase. + ///\param value The value to use to overwrite the last elements in the array. //************************************************************************* iterator erase(const_iterator first, const_iterator last, parameter_t value) { + ETL_DEBUG_ASSERT(cbegin() <= first && first <= last && last <= cend(), ETL_ERROR(array_out_of_range)); + iterator p = to_iterator(first); p = etl::move(last, cend(), p); diff --git a/include/etl/error_handler.h b/include/etl/error_handler.h index 07237f89..75e71381 100644 --- a/include/etl/error_handler.h +++ b/include/etl/error_handler.h @@ -364,6 +364,62 @@ namespace etl #endif #endif +#if ETL_IS_DEBUG_BUILD + #if defined(ETL_DEBUG_USE_ASSERT_FUNCTION) + #define ETL_DEBUG_ASSERT(b, e) {if (!(b)) ETL_UNLIKELY {etl::private_error_handler::assert_handler<0>::assert_function_ptr((e));}} // If the condition fails, calls the assert function + #define ETL_DEBUG_ASSERT_OR_RETURN(b, e) {if (!(b)) ETL_UNLIKELY {etl::private_error_handler::assert_handler<0>::assert_function_ptr((e)); return;}} // If the condition fails, calls the assert function and return + #define ETL_DEBUG_ASSERT_OR_RETURN_VALUE(b, e, v) {if (!(b)) ETL_UNLIKELY {etl::private_error_handler::assert_handler<0>::assert_function_ptr((e)); return (v);}} // If the condition fails, calls the assert function and return a value + + #define ETL_DEBUG_ASSERT_FAIL(e) {etl::private_error_handler::assert_handler<0>::assert_function_ptr((e));} // Calls the assert function + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN(e) {etl::private_error_handler::assert_handler<0>::assert_function_ptr((e)); return;} // Calls the assert function and return + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN_VALUE(e, v) {etl::private_error_handler::assert_handler<0>::assert_function_ptr((e)); return (v);} // Calls the assert function and return a value + #elif ETL_DEBUG_USING_EXCEPTIONS + #if defined(ETL_DEBUG_LOG_ERRORS) + #define ETL_DEBUG_ASSERT(b, e) {if (!(b)) ETL_UNLIKELY {etl::error_handler::error((e)); throw((e));}} // If the condition fails, calls the error handler then throws an exception. + #define ETL_DEBUG_ASSERT_OR_RETURN_VALUE_CPP11_CONSTEXPR(b, e, v) if (!(b)) ETL_UNLIKELY {etl::error_handler::error((e));} return (b) ? (v) : throw(e) // throwing from c++11 constexpr requires ? operator + #define ETL_DEBUG_ASSERT_OR_RETURN(b, e) {if (!(b)) ETL_UNLIKELY {etl::error_handler::error((e)); throw((e)); return;}} // If the condition fails, calls the error handler then throws an exception. + #define ETL_DEBUG_ASSERT_OR_RETURN_VALUE(b, e, v) {if (!(b)) ETL_UNLIKELY {etl::error_handler::error((e)); throw((e)); return(v);}} // If the condition fails, calls the error handler then throws an exception. + + #define ETL_DEBUG_ASSERT_FAIL(e) {etl::error_handler::error((e)); throw((e));} // Calls the error handler then throws an exception. + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN(e) {etl::error_handler::error((e)); throw((e)); return;} // Calls the error handler then throws an exception. + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN_VALUE(e, v) {etl::error_handler::error((e)); throw((e)); return(v);} // Calls the error handler then throws an exception. + #else + #define ETL_DEBUG_ASSERT(b, e) {if (!(b)) ETL_UNLIKELY {throw((e));}} // If the condition fails, throws an exception. + #define ETL_DEBUG_ASSERT_OR_RETURN_VALUE_CPP11_CONSTEXPR(b, e, v) return (b) ? (v) : throw(e) // throwing from c++11 constexpr requires ? operator + #define ETL_DEBUG_ASSERT_OR_RETURN(b, e) {if (!(b)) ETL_UNLIKELY {throw((e));}} // If the condition fails, throws an exception. + #define ETL_DEBUG_ASSERT_OR_RETURN_VALUE(b, e, v) {if (!(b)) ETL_UNLIKELY {throw((e));}} // If the condition fails, throws an exception. + + #define ETL_DEBUG_ASSERT_FAIL(e) {throw((e));} // Throws an exception. + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN(e) {throw((e));} // Throws an exception. + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN_VALUE(e, v) {throw((e));} // Throws an exception. + #endif + #elif defined(ETL_DEBUG_LOG_ERRORS) + #define ETL_DEBUG_ASSERT(b, e) {if (!(b)) ETL_UNLIKELY {etl::error_handler::error((e));}} // If the condition fails, calls the error handler + #define ETL_DEBUG_ASSERT_OR_RETURN(b, e) {if (!(b)) ETL_UNLIKELY {etl::error_handler::error((e)); return;}} // If the condition fails, calls the error handler and return + #define ETL_DEBUG_ASSERT_OR_RETURN_VALUE(b, e, v) {if (!(b)) ETL_UNLIKELY {etl::error_handler::error((e)); return (v);}} // If the condition fails, calls the error handler and return a value + + #define ETL_DEBUG_ASSERT_FAIL(e) {etl::error_handler::error((e));} // Calls the error handler + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN(e) {etl::error_handler::error((e)); return;} // Calls the error handler and return + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN_VALUE(e, v) {etl::error_handler::error((e)); return (v);} // Calls the error handler and return a value + #else + #define ETL_DEBUG_ASSERT(b, e) assert((b)) // If the condition fails, asserts. + #define ETL_DEBUG_ASSERT_OR_RETURN(b, e) {if (!(b)) ETL_UNLIKELY {assert(false); return;}} // If the condition fails, asserts and return. + #define ETL_DEBUG_ASSERT_OR_RETURN_VALUE(b, e, v) {if (!(b)) ETL_UNLIKELY {assert(false); return(v);}} // If the condition fails, asserts and return a value. + + #define ETL_DEBUG_ASSERT_FAIL(e) assert(false) // Asserts. + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN(e) {assert(false); return;} // Asserts. + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN_VALUE(e, v) {assert(false); return(v);} // Asserts. + #endif +#else + #define ETL_DEBUG_ASSERT(b, e) // Does nothing. + #define ETL_DEBUG_ASSERT_OR_RETURN(b, e) {if (!(b)) ETL_UNLIKELY return;} // Returns. + #define ETL_DEBUG_ASSERT_OR_RETURN_VALUE(b, e, v) {if (!(b)) ETL_UNLIKELY return(v);} // Returns a value. + + #define ETL_DEBUG_ASSERT_FAIL(e) // Does nothing. + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN(e) {return;} // Returns. + #define ETL_DEBUG_ASSERT_FAIL_AND_RETURN_VALUE(e, v) {return(v);} // Returns a value. +#endif + #if defined(ETL_VERBOSE_ERRORS) #define ETL_ERROR(e) (e(__FILE__, __LINE__)) // Make an exception with the file name and line number. #define ETL_ERROR_WITH_VALUE(e, v) (e(__FILE__, __LINE__, (v))) // Make an exception with the file name, line number and value. diff --git a/include/etl/expected.h b/include/etl/expected.h index 49c3d82b..2e761348 100644 --- a/include/etl/expected.h +++ b/include/etl/expected.h @@ -675,9 +675,7 @@ namespace etl //******************************************* value_type* operator ->() { -#if ETL_IS_DEBUG_BUILD - ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); -#endif + ETL_DEBUG_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::addressof(etl::get(storage)); } @@ -687,9 +685,7 @@ namespace etl //******************************************* const value_type* operator ->() const { -#if ETL_IS_DEBUG_BUILD - ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); -#endif + ETL_DEBUG_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::addressof(etl::get(storage)); } @@ -699,9 +695,7 @@ namespace etl //******************************************* value_type& operator *() ETL_LVALUE_REF_QUALIFIER { -#if ETL_IS_DEBUG_BUILD - ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); -#endif + ETL_DEBUG_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::get(storage); } @@ -711,9 +705,7 @@ namespace etl //******************************************* const value_type& operator *() const ETL_LVALUE_REF_QUALIFIER { -#if ETL_IS_DEBUG_BUILD - ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); -#endif + ETL_DEBUG_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::get(storage); } @@ -724,9 +716,7 @@ namespace etl //******************************************* value_type&& operator *()&& { -#if ETL_IS_DEBUG_BUILD - ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); -#endif + ETL_DEBUG_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::move(etl::get(storage)); } @@ -736,9 +726,7 @@ namespace etl //******************************************* const value_type&& operator *() const&& { -#if ETL_IS_DEBUG_BUILD - ETL_ASSERT(has_value(), ETL_ERROR(expected_invalid)); -#endif + ETL_DEBUG_ASSERT(has_value(), ETL_ERROR(expected_invalid)); return etl::move(etl::get(storage)); } diff --git a/include/etl/platform.h b/include/etl/platform.h index 012a379b..af0b9a6d 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -251,6 +251,16 @@ SOFTWARE. #define ETL_NOT_USING_EXCEPTIONS 1 #endif +//************************************* +// Indicate if C++ exceptions are enabled for debug asserts. +#if ETL_IS_DEBUG_BUILD && defined(ETL_DEBUG_THROW_EXCEPTIONS) + #define ETL_DEBUG_USING_EXCEPTIONS 1 + #define ETL_DEBUG_NOT_USING_EXCEPTIONS 0 +#else + #define ETL_DEBUG_USING_EXCEPTIONS 0 + #define ETL_DEBUG_NOT_USING_EXCEPTIONS 1 +#endif + //************************************* // Indicate if nullptr is used. #if ETL_NO_NULLPTR_SUPPORT diff --git a/test/etl_profile.h b/test/etl_profile.h index d6475c6a..b396bfde 100644 --- a/test/etl_profile.h +++ b/test/etl_profile.h @@ -31,7 +31,9 @@ SOFTWARE. #ifndef ETL_PROFILE_H_INCLUDED #define ETL_PROFILE_H_INCLUDED +#define ETL_DEBUG #define ETL_THROW_EXCEPTIONS +#define ETL_DEBUG_THROW_EXCEPTIONS #define ETL_VERBOSE_ERRORS #define ETL_CHECK_PUSH_POP #define ETL_ISTRING_REPAIR_ENABLE diff --git a/test/test_array.cpp b/test/test_array.cpp index 4fd6a263..c779ab76 100644 --- a/test/test_array.cpp +++ b/test/test_array.cpp @@ -134,6 +134,9 @@ namespace { CHECK_EQUAL(data[i], compare_data[i]); } + + //ETL_DEBUG and ETL_THROW_EXCEPTIONS are defined + CHECK_THROW({ int d = data[data.size()]; (void)d; }, etl::array_out_of_range); } //************************************************************************* @@ -145,6 +148,9 @@ namespace { CHECK_EQUAL(data[i], compare_data[i]); } + + //ETL_DEBUG and ETL_THROW_EXCEPTIONS are defined + CHECK_THROW({ int d = data[data.size()]; (void)d; }, etl::array_out_of_range); } //************************************************************************* @@ -443,6 +449,10 @@ namespace CHECK_EQUAL(data[9], *result); isEqual = std::equal(data.begin(), data.end(), std::begin(check3)); CHECK(isEqual); + + // Insert out of range + //ETL_DEBUG and ETL_THROW_EXCEPTIONS are defined + CHECK_THROW({ result = data.insert_at(data.size(), 99); }, etl::array_out_of_range); } //************************************************************************* @@ -493,6 +503,10 @@ namespace CHECK_EQUAL(data[4], *result); isEqual = std::equal(data.begin(), data.end(), std::begin(check5)); CHECK(isEqual); + + // Insert out of range + //ETL_DEBUG and ETL_THROW_EXCEPTIONS are defined + CHECK_THROW({ result = data.insert_at(data.size(), &source2[0], &source2[13]); }, etl::array_out_of_range); } //************************************************************************* @@ -547,6 +561,10 @@ namespace CHECK_EQUAL(data[9], *result); isEqual = std::equal(data.begin(), data.end(), std::begin(check3b)); CHECK(isEqual); + + // Erase out of range + //ETL_DEBUG and ETL_THROW_EXCEPTIONS are defined + CHECK_THROW({ result = data.erase_at(data.size()); }, etl::array_out_of_range); } //************************************************************************* @@ -601,6 +619,13 @@ namespace CHECK_EQUAL(data[5], *result); isEqual = std::equal(data.begin(), data.end(), std::begin(check3b)); CHECK(isEqual); + + //ETL_DEBUG and ETL_THROW_EXCEPTIONS are defined + // first is greater than last + CHECK_THROW({ result = data.erase_range(6, 5, 99); }, etl::array_out_of_range); + + // Erase out of range + CHECK_THROW({ result = data.erase_range(5, data.size() + 1, 99); }, etl::array_out_of_range); } //*************************************************************************