From 5e3b029af0c4145f43d31d2b5e2bef4e9dfee5ec Mon Sep 17 00:00:00 2001 From: rolandreichweinbmw Date: Thu, 26 Dec 2024 14:27:47 +0100 Subject: [PATCH 1/2] Add singleton_base with test (#1002) --- include/etl/file_error_numbers.h | 1 + include/etl/singleton_base.h | 162 +++++++++++++++++++++++++++++++ test/test_singleton_base.cpp | 105 ++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 include/etl/singleton_base.h create mode 100644 test/test_singleton_base.cpp diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index a866ce41..25f02d27 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -103,5 +103,6 @@ SOFTWARE. #define ETL_EXPECTED_FILE_ID "70" #define ETL_ALIGNMENT_FILE_ID "71" #define ETL_BASE64_FILE_ID "72" +#define ETL_SINGLETON_BASE_FILE_ID "73" #endif diff --git a/include/etl/singleton_base.h b/include/etl/singleton_base.h new file mode 100644 index 00000000..6b68aafb --- /dev/null +++ b/include/etl/singleton_base.h @@ -0,0 +1,162 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2019 John Wellbelove +Copyright(c) 2024 BMW AG + +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. +******************************************************************************/ + +#ifndef ETL_SINGLETON_BASE_INCLUDED +#define ETL_SINGLETON_BASE_INCLUDED + +///\defgroup singleton_base singleton_base +/// Templated version of the singleton pattern, implemented as base class +/// for singletons. +///\ingroup etl + +#include "platform.h" +#include "error_handler.h" +#include "file_error_numbers.h" + +namespace etl +{ + //************************************************************************* + /// Base singleton error exception. + //************************************************************************* + class singleton_base_exception : public etl::exception + { + public: + + singleton_base_exception(string_type reason_, string_type file_name_, numeric_type line_number_) + : exception(reason_, file_name_, line_number_) + { + } + }; + + //************************************************************************* + /// Singleton not created error exception. + //************************************************************************* + class singleton_base_not_created : public etl::singleton_base_exception + { + public: + + singleton_base_not_created(string_type file_name_, numeric_type line_number_) + : singleton_base_exception(ETL_ERROR_TEXT("singleton_base:not created", ETL_SINGLETON_BASE_FILE_ID"A"), file_name_, line_number_) + { + } + }; + + //************************************************************************* + /// Singleton instance already exists. + //************************************************************************* + class singleton_base_already_created : public etl::singleton_base_exception + { + public: + + singleton_base_already_created(string_type file_name_, numeric_type line_number_) + : singleton_base_exception(ETL_ERROR_TEXT("singleton_base:already created", ETL_SINGLETON_BASE_FILE_ID"A"), file_name_, line_number_) + { + } + }; + + //*********************************************************************** + /// Base class for singletons. + /// \tparam T Any type that wants to expose the instance() interface. + /// + /// This class is designed to work as a generic base class for any class that wants to + /// provide a singleton interface. It'll also work for classes that do not have a + /// default constructor. + /// + /// Usage example: + /// + /// class Origin + /// : singleton + /// { + /// public: + /// Origin(int x, int y) + /// : singleton(*this) + /// {} + /// + /// int getX() const; + /// } theOrigin(0, 0); + /// + /// int x = Origin::instance().getX(); + /// + /// + /// Note: + /// + /// It is important that a call to instance() will not create the instance of the class. It needs + /// to be created by the user before calling instance(). This way, the user has better control + /// over the instance lifetime instead of e.g. lazy initialization. + //*********************************************************************** + template + class singleton_base + { + protected: + + //*********************************************************************** + /// Constructs the instance of singleton. + /// theInstance Reference to T, which will be returned when instance() is called. + //*********************************************************************** + explicit singleton_base(T& theInstance) + { + ETL_ASSERT(m_self == nullptr, ETL_ERROR(etl::singleton_base_already_created)); + m_self = &theInstance; + } + + //*********************************************************************** + /// Removes the internal reference to the instance passed in the constructor. + //*********************************************************************** + ~singleton_base() { m_self = nullptr; } + + public: + + //*********************************************************************** + // Returns a reference to the instance. + //*********************************************************************** + static T& instance() + { + ETL_ASSERT(m_self != nullptr, ETL_ERROR(etl::singleton_base_not_created)); + return *m_self; + } + + //*********************************************************************** + /// Returns whether an instance has been attached to singleton or not. + //*********************************************************************** + static bool is_valid() { return (m_self != nullptr); } + + private: + static T* m_self; + }; + + //*********************************************************************** + /// No violation of one definition rule as this is a class template + //*********************************************************************** + template + T* singleton_base::m_self = nullptr; +} + +#endif diff --git a/test/test_singleton_base.cpp b/test/test_singleton_base.cpp new file mode 100644 index 00000000..40cfa543 --- /dev/null +++ b/test/test_singleton_base.cpp @@ -0,0 +1,105 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2022 John Wellbelove +Copyright(c) 2024 BMW AG + +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/singleton_base.h" + +#include +#include + +class Test_Singleton: public ::etl::singleton_base +{ +public: + + Test_Singleton(int i_, std::string text_) + : ::etl::singleton_base(*this) + , i(i_) + , text(text_) + { + } + + void Increment() + { + ++i; + text += "*"; + } + + int i; + std::string text; +}; + +namespace +{ + SUITE(test_singleton) + { + //************************************************************************* + TEST(test1) + { + + CHECK(!Test_Singleton::is_valid()); + CHECK_THROW(Test_Singleton::instance(), etl::singleton_base_not_created); + + { + Test_Singleton singleton(1, "Start:"); + CHECK(Test_Singleton::is_valid()); + + Test_Singleton& ts = Test_Singleton::instance(); + + CHECK_EQUAL(1, ts.i); + CHECK_EQUAL("Start:", ts.text); + + ts.Increment(); + + CHECK_EQUAL(2, ts.i); + CHECK_EQUAL("Start:*", ts.text); + + Test_Singleton* pts = &Test_Singleton::instance(); + + CHECK_EQUAL(2, ts.i); + CHECK_EQUAL(2, pts->i); + CHECK_EQUAL("Start:*", ts.text); + CHECK_EQUAL("Start:*", pts->text); + + pts->Increment(); + + CHECK_EQUAL(3, ts.i); + CHECK_EQUAL(3, pts->i); + CHECK_EQUAL("Start:**", ts.text); + CHECK_EQUAL("Start:**", pts->text); + + CHECK_THROW(Test_Singleton(2, "XYZ"), etl::singleton_base_already_created); + } + + CHECK(!Test_Singleton::is_valid()); + CHECK_THROW(Test_Singleton::instance(), etl::singleton_base_not_created); + } + } +} + From c1a1d24712e09247b6bcaead1d9ae662e45ce821 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 26 Dec 2024 15:50:32 +0000 Subject: [PATCH 2/2] Updates to singleton_base --- .gitignore | 1 + include/etl/singleton_base.h | 50 ++++++++++++++---------- test/CMakeLists.txt | 1 + test/syntax_check/c++03/CMakeLists.txt | 1 + test/syntax_check/c++11/CMakeLists.txt | 1 + test/syntax_check/c++14/CMakeLists.txt | 1 + test/syntax_check/c++17/CMakeLists.txt | 1 + test/syntax_check/c++20/CMakeLists.txt | 1 + test/syntax_check/singleton_base.h.t.cpp | 29 ++++++++++++++ test/test_singleton_base.cpp | 3 +- test/vs2022/etl.vcxproj | 17 ++++++++ 11 files changed, 84 insertions(+), 22 deletions(-) create mode 100644 test/syntax_check/singleton_base.h.t.cpp diff --git a/.gitignore b/.gitignore index 8f313ddb..6d624695 100644 --- a/.gitignore +++ b/.gitignore @@ -388,3 +388,4 @@ support/time remaining test.xlsx test/vs2022/Debug MSVC C++20 - Force C++03 test/vs2022/Release MSVC C++20 - No STL - Optimised -O2 - Sanitiser test/test_file_list.txt +test/etl_error_handler/assert_function/build-make diff --git a/include/etl/singleton_base.h b/include/etl/singleton_base.h index 6b68aafb..eb9653b4 100644 --- a/include/etl/singleton_base.h +++ b/include/etl/singleton_base.h @@ -39,6 +39,7 @@ SOFTWARE. #include "platform.h" #include "error_handler.h" +#include "nullptr.h" #include "file_error_numbers.h" namespace etl @@ -93,7 +94,7 @@ namespace etl /// Usage example: /// /// class Origin - /// : singleton + /// : singleton /// { /// public: /// Origin(int x, int y) @@ -112,9 +113,29 @@ namespace etl /// to be created by the user before calling instance(). This way, the user has better control /// over the instance lifetime instead of e.g. lazy initialization. //*********************************************************************** - template + template class singleton_base { + public: + + //*********************************************************************** + // Returns a reference to the instance. + //*********************************************************************** + static T& instance() + { + ETL_ASSERT(m_self != ETL_NULLPTR, ETL_ERROR(etl::singleton_base_not_created)); + + return *m_self; + } + + //*********************************************************************** + /// Returns whether an instance has been attached to singleton or not. + //*********************************************************************** + static bool is_valid() + { + return (m_self != ETL_NULLPTR); + } + protected: //*********************************************************************** @@ -123,32 +144,21 @@ namespace etl //*********************************************************************** explicit singleton_base(T& theInstance) { - ETL_ASSERT(m_self == nullptr, ETL_ERROR(etl::singleton_base_already_created)); + ETL_ASSERT(m_self == ETL_NULLPTR, ETL_ERROR(etl::singleton_base_already_created)); + m_self = &theInstance; } //*********************************************************************** /// Removes the internal reference to the instance passed in the constructor. //*********************************************************************** - ~singleton_base() { m_self = nullptr; } - - public: - - //*********************************************************************** - // Returns a reference to the instance. - //*********************************************************************** - static T& instance() - { - ETL_ASSERT(m_self != nullptr, ETL_ERROR(etl::singleton_base_not_created)); - return *m_self; + ~singleton_base() + { + m_self = ETL_NULLPTR; } - //*********************************************************************** - /// Returns whether an instance has been attached to singleton or not. - //*********************************************************************** - static bool is_valid() { return (m_self != nullptr); } - private: + static T* m_self; }; @@ -156,7 +166,7 @@ namespace etl /// No violation of one definition rule as this is a class template //*********************************************************************** template - T* singleton_base::m_self = nullptr; + T* singleton_base::m_self = ETL_NULLPTR; } #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ce9e2a66..90b9fbb4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -233,6 +233,7 @@ add_executable(etl_tests test_set.cpp test_shared_message.cpp test_singleton.cpp + test_singleton_base.cpp test_smallest.cpp test_span_dynamic_extent.cpp test_span_fixed_extent.cpp diff --git a/test/syntax_check/c++03/CMakeLists.txt b/test/syntax_check/c++03/CMakeLists.txt index fad0c6eb..651b23e6 100644 --- a/test/syntax_check/c++03/CMakeLists.txt +++ b/test/syntax_check/c++03/CMakeLists.txt @@ -260,6 +260,7 @@ target_sources(t98 PRIVATE etl_profile.h ../set.h.t.cpp ../shared_message.h.t.cpp ../singleton.h.t.cpp + ../singleton_base.h.t.cpp ../smallest.h.t.cpp ../span.h.t.cpp ../sqrt.h.t.cpp diff --git a/test/syntax_check/c++11/CMakeLists.txt b/test/syntax_check/c++11/CMakeLists.txt index f426cecc..68c48d23 100644 --- a/test/syntax_check/c++11/CMakeLists.txt +++ b/test/syntax_check/c++11/CMakeLists.txt @@ -260,6 +260,7 @@ target_sources(t11 PRIVATE etl_profile.h ../set.h.t.cpp ../shared_message.h.t.cpp ../singleton.h.t.cpp + ../singleton_base.h.t.cpp ../smallest.h.t.cpp ../span.h.t.cpp ../sqrt.h.t.cpp diff --git a/test/syntax_check/c++14/CMakeLists.txt b/test/syntax_check/c++14/CMakeLists.txt index b7bc68b9..5d50945f 100644 --- a/test/syntax_check/c++14/CMakeLists.txt +++ b/test/syntax_check/c++14/CMakeLists.txt @@ -260,6 +260,7 @@ target_sources(t14 PRIVATE etl_profile.h ../set.h.t.cpp ../shared_message.h.t.cpp ../singleton.h.t.cpp + ../singleton_base.h.t.cpp ../smallest.h.t.cpp ../span.h.t.cpp ../sqrt.h.t.cpp diff --git a/test/syntax_check/c++17/CMakeLists.txt b/test/syntax_check/c++17/CMakeLists.txt index 5d8761ab..be787908 100644 --- a/test/syntax_check/c++17/CMakeLists.txt +++ b/test/syntax_check/c++17/CMakeLists.txt @@ -260,6 +260,7 @@ target_sources(t17 PRIVATE etl_profile.h ../set.h.t.cpp ../shared_message.h.t.cpp ../singleton.h.t.cpp + ../singleton_base.h.t.cpp ../smallest.h.t.cpp ../span.h.t.cpp ../sqrt.h.t.cpp diff --git a/test/syntax_check/c++20/CMakeLists.txt b/test/syntax_check/c++20/CMakeLists.txt index f38c48ad..71a5bb16 100644 --- a/test/syntax_check/c++20/CMakeLists.txt +++ b/test/syntax_check/c++20/CMakeLists.txt @@ -260,6 +260,7 @@ target_sources(t20 PRIVATE etl_profile.h ../set.h.t.cpp ../shared_message.h.t.cpp ../singleton.h.t.cpp + ../singleton_base.h.t.cpp ../smallest.h.t.cpp ../span.h.t.cpp ../sqrt.h.t.cpp diff --git a/test/syntax_check/singleton_base.h.t.cpp b/test/syntax_check/singleton_base.h.t.cpp new file mode 100644 index 00000000..723ffcf9 --- /dev/null +++ b/test/syntax_check/singleton_base.h.t.cpp @@ -0,0 +1,29 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2024 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 diff --git a/test/test_singleton_base.cpp b/test/test_singleton_base.cpp index 40cfa543..fec07c59 100644 --- a/test/test_singleton_base.cpp +++ b/test/test_singleton_base.cpp @@ -34,7 +34,7 @@ SOFTWARE. #include #include -class Test_Singleton: public ::etl::singleton_base +class Test_Singleton: public etl::singleton_base { public: @@ -62,7 +62,6 @@ namespace //************************************************************************* TEST(test1) { - CHECK(!Test_Singleton::is_valid()); CHECK_THROW(Test_Singleton::instance(), etl::singleton_base_not_created); diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index 7506cfa1..7809c438 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3190,6 +3190,7 @@ + @@ -7076,6 +7077,21 @@ true true + + true + true + true + true + true + true + true + true + true + true + true + true + true + true true @@ -8912,6 +8928,7 @@ true +