diff --git a/include/etl/file_error_numbers.h b/include/etl/file_error_numbers.h index c816104e..7f841d28 100644 --- a/include/etl/file_error_numbers.h +++ b/include/etl/file_error_numbers.h @@ -103,5 +103,7 @@ SOFTWARE. #define ETL_EXPECTED_FILE_ID "70" #define ETL_ALIGNMENT_FILE_ID "71" #define ETL_BASE64_FILE_ID "72" -#define ETL_UNALIGNED_TYPE_FILE_ID "73" +#define ETL_SINGLETON_BASE_FILE_ID "73" + +#define ETL_UNALIGNED_TYPE_FILE_ID "74" #endif diff --git a/include/etl/singleton_base.h b/include/etl/singleton_base.h new file mode 100644 index 00000000..eb9653b4 --- /dev/null +++ b/include/etl/singleton_base.h @@ -0,0 +1,172 @@ +///\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 "nullptr.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 + { + 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: + + //*********************************************************************** + /// 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 == 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 = ETL_NULLPTR; + } + + private: + + static T* m_self; + }; + + //*********************************************************************** + /// No violation of one definition rule as this is a class template + //*********************************************************************** + template + T* singleton_base::m_self = ETL_NULLPTR; +} + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7441ae4c..60274598 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 d03606bf..88c0395a 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 28a69262..862433bb 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 131ae2a2..f1d1556d 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 f3fab390..61a18608 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 98acefa9..e051e3dd 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 new file mode 100644 index 00000000..fec07c59 --- /dev/null +++ b/test/test_singleton_base.cpp @@ -0,0 +1,104 @@ +/****************************************************************************** +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); + } + } +} + diff --git a/test/vs2022/etl.vcxproj b/test/vs2022/etl.vcxproj index a12b35ae..08415258 100644 --- a/test/vs2022/etl.vcxproj +++ b/test/vs2022/etl.vcxproj @@ -3190,6 +3190,7 @@ + @@ -7077,6 +7078,21 @@ true true + + true + true + true + true + true + true + true + true + true + true + true + true + true + true true @@ -8928,6 +8944,7 @@ true +