diff --git a/include/etl/not_null.h b/include/etl/not_null.h index 941a3ec8..54a4f565 100644 --- a/include/etl/not_null.h +++ b/include/etl/not_null.h @@ -34,8 +34,8 @@ SOFTWARE. #include "platform.h" #include "error_handler.h" #include "exception.h" -#include "ETL_STATIC_ASSERT.h" -#include "unique_ptr.h" +#include "static_assert.h" +#include "memory.h" #include "type_traits.h" namespace etl @@ -61,128 +61,216 @@ namespace etl public: not_null_contains_null(string_type file_name_, numeric_type line_number_) - : delegate_exception(ETL_ERROR_TEXT("not_null:contains null", ETL_NOT_NULL_FILE_ID"A"), file_name_, line_number_) + : not_null_exception(ETL_ERROR_TEXT("not_null:contains null", ETL_NOT_NULL_FILE_ID"A"), file_name_, line_number_) { } }; + //*************************************************************************** + // not_null // Primary template + //*************************************************************************** template class not_null; + //*************************************************************************** // Specialisation for T* + // A container for pointers that are not allowed to be null. + //*************************************************************************** template class not_null { public: + //********************************* + /// Constructs a not_null from a pointer. + /// Asserts if the pointer is null. + //********************************* explicit not_null(T* ptr_) : ptr(ptr_) { - ETL_ASSERT(ptr != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); + ETL_ASSERT(ptr_ != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); } - not_null(const etl::not_null& other) + //********************************* + /// Copy constructor from a not_null pointer. + //********************************* + not_null(const etl::not_null& other) : ptr(other.get()) { } - not_null& operator =(const etl::not_null& other) + //********************************* + /// Assignment from a not_null. + //********************************* + not_null& operator =(const etl::not_null& rhs) { - ptr = other.get(); + ptr = rhs.get(); return *this; } - not_null& operator =(T* ptr_) + //********************************* + /// Assignment from a pointer. + /// Asserts if the pointer is null. + //********************************* + not_null& operator =(T* rhs) { - ETL_ASSERT(ptr_ != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); + ETL_ASSERT_OR_RETURN_VALUE(rhs != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); - ptr = ptr_; + ptr = rhs; return *this; } + //********************************* + /// Gets the underlying pointer. + //********************************* T* get() const { return ptr; } + //********************************* + /// Implicit conversion to T*. + //********************************* operator T*() const { return ptr; } - typename etl::remove_pointer::type& operator*() const - { - return *ptr; - } - - T operator->() const - { - return ptr; - } - - private: - - T* ptr; - }; - - // Partial specialisation for etl::unique_ptr - template - class not_null> - { - public: - - explicit not_null(etl::unique_ptr&& ptr_) - : ptr(etl::move(ptr_)) - { - ETL_ASSERT(ptr != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); - } - -#if ETL_USING_CPP11 - not_null& operator =(etl::unique_ptr&& ptr_) - { - ETL_ASSERT(ptr_ != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); - - ptr = etl::move(ptr_); - - return *this; - } -#endif - - T* get() const - { - return ptr.get(); - } - - operator T*() const - { - return ptr.get(); - } - + //********************************* + /// Dereference operator. + //********************************* T& operator*() const { return *ptr; } + //********************************* + /// Arrow operator. + //********************************* T* operator->() const - { - return ptr.get(); - } - - etl::unique_ptr& unique() - { - return ptr; - } - - const etl::unique_ptr& unique() const { return ptr; } private: + + /// The underlying pointer. + T* ptr; + }; + + //*************************************************************************** + // Partial specialisation for etl::unique_ptr + // A container for unique_ptr that are not allowed to be null. + //*************************************************************************** + template + class not_null> + { + private: + + // The unique_ptr type. + typedef etl::unique_ptr unique_ptr_type; + + public: + + //********************************* + /// Constructs a not_null from a unique_ptr. + /// Asserts if the unique_ptr is null. + //********************************* + explicit not_null(unique_ptr_type&& u_ptr_) + : u_ptr(etl::move(u_ptr_)) + { + ETL_ASSERT(u_ptr.get() != ETL_NULLPTR, ETL_ERROR(not_null_contains_null)); + } + +#if ETL_USING_CPP11 + //********************************* + /// Constructs a not_null from a unique_ptr. + //********************************* + not_null(etl::not_null&& other) + : u_ptr(etl::move(other.u_ptr)) + { + } + + //********************************* + /// Assign from a unique_ptr. + /// Asserts if the unique_ptr is null. + //********************************* + not_null& operator =(unique_ptr_type&& rhs) + { + ETL_ASSERT_OR_RETURN_VALUE(rhs.get() != ETL_NULLPTR, ETL_ERROR(not_null_contains_null), *this); + + u_ptr = etl::move(rhs); + + return *this; + } + + //********************************* + /// Assign from a not_null. + //********************************* + not_null& operator =(etl::not_null&& rhs) + { + u_ptr = etl::move(rhs.u_ptr); + + return *this; + } +#endif + + //********************************* + /// Gets the underlying unique_ptr. + //********************************* + T* get() const + { + return u_ptr.get(); + } + + //********************************* + /// Implicit conversion to T*. + //********************************* + operator T*() const + { + return u_ptr.get(); + } - etl::unique_ptr ptr; + //********************************* + /// Dereference operator. + //********************************* + T& operator*() const + { + return *u_ptr; + } + + //********************************* + /// Arrow operator. + //********************************* + T* operator->() const + { + return u_ptr.get(); + } + + //********************************* + /// Gets the underlying unique_ptr. + //********************************* + unique_ptr_type& unique() + { + return u_ptr; + } + + //********************************* + /// Gets the underlying unique_ptr. + //********************************* + const unique_ptr_type& unique() const + { + return u_ptr; + } + + private: + + /// The underlying unique_ptr. + unique_ptr_type u_ptr; }; } + +#endif diff --git a/test/test_not_null_pointer.cpp b/test/test_not_null_pointer.cpp new file mode 100644 index 00000000..fd9d1683 --- /dev/null +++ b/test/test_not_null_pointer.cpp @@ -0,0 +1,152 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2025 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" +#include "etl/not_null.h" + +namespace +{ + SUITE(test_not_null_pointer) + { + //************************************************************************* + TEST(test_construct_from_non_null_pointer) + { + int value = 42; + etl::not_null nn(&value); + CHECK_EQUAL(&value, nn.get()); + CHECK_EQUAL(42, *nn); + } + + //************************************************************************* + TEST(test_copy_construct) + { + int value = 123; + etl::not_null nn1(&value); + etl::not_null nn2(nn1); // Copy constructor + CHECK_EQUAL(&value, nn2.get()); + CHECK_EQUAL(123, *nn2); + } + + //************************************************************************* + TEST(test_assign_from_pointer) + { + int value1 = 123; + etl::not_null nn1(&value1); + + int value2 = 456; + + nn1 = &value2; + CHECK_EQUAL(&value2, nn1.get()); + CHECK_EQUAL(456, *nn1); + } + + //************************************************************************* + TEST(test_assign_from_not_null) + { + int value1 = 123; + etl::not_null nn1(&value1); + + int value2 = 456; + etl::not_null nn2(&value2); + + nn1 = nn2; + CHECK_EQUAL(&value2, nn2.get()); + CHECK_EQUAL(456, *nn2); + } + + //************************************************************************* + TEST(test_implicit_conversion) + { + struct S + { + int x; + }; + + S s{77}; + etl::not_null nn(&s); + + S* raw = nn; + CHECK_EQUAL(&s, raw); + } + + //************************************************************************* + TEST(test_arrow_operator) + { + struct S + { + int x; + + int get() const + { + return x; + } + }; + + S s{77}; + etl::not_null nn(&s); + + CHECK_EQUAL(s.x, nn->x); + CHECK_EQUAL(s.get(), nn->get()); + } + + //************************************************************************* + TEST(test_dereference_operator) + { + struct S + { + int x; + + int get() const + { + return x; + } + }; + + S s{77}; + etl::not_null nn(&s); + + CHECK_EQUAL(s.x, (*nn).x); + CHECK_EQUAL(s.get(), (*nn).get()); + } + + //************************************************************************* + TEST(test_construct_from_null_pointer_asserts) + { + CHECK_THROW(etl::not_null nn(nullptr), etl::not_null_contains_null); + } + + //************************************************************************* + TEST(test_assign_null_pointer_asserts) + { + int value = 1; + etl::not_null nn(&value); + + CHECK_THROW(nn = nullptr, etl::not_null_contains_null); + } + }; +} \ No newline at end of file diff --git a/test/test_not_null_unique_pointer.cpp b/test/test_not_null_unique_pointer.cpp new file mode 100644 index 00000000..8ad0f4b4 --- /dev/null +++ b/test/test_not_null_unique_pointer.cpp @@ -0,0 +1,174 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2025 John Wellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" +#include "etl/not_null.h" + +namespace +{ + struct S + { + int x; + + int get() const + { + return x; + } + }; + + SUITE(test_not_null_unique_pointer) + { + //************************************************************************* + TEST(test_construct_from_non_null_unique_ptr) + { + using up_t = etl::unique_ptr; + up_t up(new int{ 123 }); + etl::not_null nn(etl::move(up)); + + CHECK_EQUAL(123, *nn); + CHECK_EQUAL(123, *nn.get()); + } + + //************************************************************************* + TEST(test_unique) + { + using up_t = etl::unique_ptr; + up_t up1(new int{ 123 }); + etl::not_null nn(etl::move(up1)); + + up_t up2 = etl::move(nn.unique()); + + CHECK_EQUAL(123, *up2.get()); + } + + //************************************************************************* + TEST(test_unique_const) + { + using up_t = etl::unique_ptr; + up_t up1(new int{ 123 }); + const etl::not_null nn(etl::move(up1)); + + const up_t& up2 = nn.unique(); + + CHECK_EQUAL(123, *up2.get()); + } + + //************************************************************************* + TEST(test_move_construct) + { + using up_t = etl::unique_ptr; + up_t up(new int{ 123 }); + + etl::not_null nn1(etl::move(up)); + etl::not_null nn2(etl::move(nn1)); // Move constructor + + CHECK_TRUE(nn1.get() == nullptr); + CHECK_EQUAL(123, *nn2.get()); + CHECK_EQUAL(123, *nn2); + } + + //************************************************************************* + TEST(test_assign_from_unique_ptr) + { + using up_t = etl::unique_ptr; + up_t up1(new int{ 123 }); + etl::not_null nn1(etl::move(up1)); + + using up_t = etl::unique_ptr; + up_t up2(new int); + *up2 = 456; + + nn1 = etl::move(up2); + + CHECK_EQUAL(456, *nn1.get()); + } + + //************************************************************************* + TEST(test_assign_from_not_null) + { + using up_t = etl::unique_ptr; + up_t up1(new int{ 123 }); + etl::not_null nn1(etl::move(up1)); + + up_t up2(new int{ 456 }); + etl::not_null nn2(etl::move(up2)); + + nn1 = etl::move(nn2); + + CHECK_TRUE(nn2.get() == nullptr); + CHECK_EQUAL(456, *nn1.get()); + } + + //************************************************************************* + TEST(test_implicit_conversion) + { + using up_t = etl::unique_ptr; + up_t up1(new S{ 123 }); + etl::not_null nn1(etl::move(up1)); + + S s = *nn1; + + CHECK_EQUAL(123, s.x); + } + + //************************************************************************* + TEST(test_arrow_operator) + { + S s{ 123 }; + + using up_t = etl::unique_ptr; + up_t up1(new S{ 123 }); + etl::not_null nn1(etl::move(up1)); + + CHECK_EQUAL(s.x, nn1->x); + CHECK_EQUAL(s.get(), nn1->get()); + } + + //************************************************************************* + TEST(test_dereference_operator) + { + S s{ 123 }; + + using up_t = etl::unique_ptr; + up_t up1(new S{ 123 }); + etl::not_null nn1(etl::move(up1)); + + CHECK_EQUAL(s.x, (*nn1).x); + CHECK_EQUAL(s.get(), (*nn1).get()); + } + + //************************************************************************* + TEST(test_construct_from_null_pointer_asserts) + { + using up_t = etl::unique_ptr; + up_t up1(nullptr); + + CHECK_THROW(etl::not_null nn1(etl::move(up1)), etl::not_null_contains_null); + } + } +} \ No newline at end of file diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 4639af36..13d90f26 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -9472,6 +9472,8 @@ + + diff --git a/test/vs2022/etl.vcxproj.filters b/test/vs2022/etl.vcxproj.filters index 5bdf23fe..281ff201 100644 --- a/test/vs2022/etl.vcxproj.filters +++ b/test/vs2022/etl.vcxproj.filters @@ -3701,6 +3701,12 @@ Tests\Maths + + Tests\Misc + + + Tests\Misc +