Skip to content

Commit 5e3b029

Browse files
Add singleton_base with test (#1002)
1 parent 99d7537 commit 5e3b029

File tree

3 files changed

+268
-0
lines changed

3 files changed

+268
-0
lines changed

include/etl/file_error_numbers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,5 +103,6 @@ SOFTWARE.
103103
#define ETL_EXPECTED_FILE_ID "70"
104104
#define ETL_ALIGNMENT_FILE_ID "71"
105105
#define ETL_BASE64_FILE_ID "72"
106+
#define ETL_SINGLETON_BASE_FILE_ID "73"
106107

107108
#endif

include/etl/singleton_base.h

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
///\file
2+
3+
/******************************************************************************
4+
The MIT License(MIT)
5+
6+
Embedded Template Library.
7+
https://github.com/ETLCPP/etl
8+
https://www.etlcpp.com
9+
10+
Copyright(c) 2019 John Wellbelove
11+
Copyright(c) 2024 BMW AG
12+
13+
Permission is hereby granted, free of charge, to any person obtaining a copy
14+
of this software and associated documentation files(the "Software"), to deal
15+
in the Software without restriction, including without limitation the rights
16+
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
17+
copies of the Software, and to permit persons to whom the Software is
18+
furnished to do so, subject to the following conditions :
19+
20+
The above copyright notice and this permission notice shall be included in all
21+
copies or substantial portions of the Software.
22+
23+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
26+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
SOFTWARE.
30+
******************************************************************************/
31+
32+
#ifndef ETL_SINGLETON_BASE_INCLUDED
33+
#define ETL_SINGLETON_BASE_INCLUDED
34+
35+
///\defgroup singleton_base singleton_base
36+
/// Templated version of the singleton pattern, implemented as base class
37+
/// for singletons.
38+
///\ingroup etl
39+
40+
#include "platform.h"
41+
#include "error_handler.h"
42+
#include "file_error_numbers.h"
43+
44+
namespace etl
45+
{
46+
//*************************************************************************
47+
/// Base singleton error exception.
48+
//*************************************************************************
49+
class singleton_base_exception : public etl::exception
50+
{
51+
public:
52+
53+
singleton_base_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
54+
: exception(reason_, file_name_, line_number_)
55+
{
56+
}
57+
};
58+
59+
//*************************************************************************
60+
/// Singleton not created error exception.
61+
//*************************************************************************
62+
class singleton_base_not_created : public etl::singleton_base_exception
63+
{
64+
public:
65+
66+
singleton_base_not_created(string_type file_name_, numeric_type line_number_)
67+
: singleton_base_exception(ETL_ERROR_TEXT("singleton_base:not created", ETL_SINGLETON_BASE_FILE_ID"A"), file_name_, line_number_)
68+
{
69+
}
70+
};
71+
72+
//*************************************************************************
73+
/// Singleton instance already exists.
74+
//*************************************************************************
75+
class singleton_base_already_created : public etl::singleton_base_exception
76+
{
77+
public:
78+
79+
singleton_base_already_created(string_type file_name_, numeric_type line_number_)
80+
: singleton_base_exception(ETL_ERROR_TEXT("singleton_base:already created", ETL_SINGLETON_BASE_FILE_ID"A"), file_name_, line_number_)
81+
{
82+
}
83+
};
84+
85+
//***********************************************************************
86+
/// Base class for singletons.
87+
/// \tparam T Any type that wants to expose the instance() interface.
88+
///
89+
/// This class is designed to work as a generic base class for any class that wants to
90+
/// provide a singleton interface. It'll also work for classes that do not have a
91+
/// default constructor.
92+
///
93+
/// Usage example:
94+
///
95+
/// class Origin
96+
/// : singleton<Origin>
97+
/// {
98+
/// public:
99+
/// Origin(int x, int y)
100+
/// : singleton<Origin>(*this)
101+
/// {}
102+
///
103+
/// int getX() const;
104+
/// } theOrigin(0, 0);
105+
///
106+
/// int x = Origin::instance().getX();
107+
///
108+
///
109+
/// Note:
110+
///
111+
/// It is important that a call to instance() will not create the instance of the class. It needs
112+
/// to be created by the user before calling instance(). This way, the user has better control
113+
/// over the instance lifetime instead of e.g. lazy initialization.
114+
//***********************************************************************
115+
template<class T>
116+
class singleton_base
117+
{
118+
protected:
119+
120+
//***********************************************************************
121+
/// Constructs the instance of singleton.
122+
/// theInstance Reference to T, which will be returned when instance() is called.
123+
//***********************************************************************
124+
explicit singleton_base(T& theInstance)
125+
{
126+
ETL_ASSERT(m_self == nullptr, ETL_ERROR(etl::singleton_base_already_created));
127+
m_self = &theInstance;
128+
}
129+
130+
//***********************************************************************
131+
/// Removes the internal reference to the instance passed in the constructor.
132+
//***********************************************************************
133+
~singleton_base() { m_self = nullptr; }
134+
135+
public:
136+
137+
//***********************************************************************
138+
// Returns a reference to the instance.
139+
//***********************************************************************
140+
static T& instance()
141+
{
142+
ETL_ASSERT(m_self != nullptr, ETL_ERROR(etl::singleton_base_not_created));
143+
return *m_self;
144+
}
145+
146+
//***********************************************************************
147+
/// Returns whether an instance has been attached to singleton<T> or not.
148+
//***********************************************************************
149+
static bool is_valid() { return (m_self != nullptr); }
150+
151+
private:
152+
static T* m_self;
153+
};
154+
155+
//***********************************************************************
156+
/// No violation of one definition rule as this is a class template
157+
//***********************************************************************
158+
template<class T>
159+
T* singleton_base<T>::m_self = nullptr;
160+
}
161+
162+
#endif

test/test_singleton_base.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/******************************************************************************
2+
The MIT License(MIT)
3+
4+
Embedded Template Library.
5+
https://github.com/ETLCPP/etl
6+
https://www.etlcpp.com
7+
8+
Copyright(c) 2022 John Wellbelove
9+
Copyright(c) 2024 BMW AG
10+
11+
Permission is hereby granted, free of charge, to any person obtaining a copy
12+
of this software and associated documentation files(the "Software"), to deal
13+
in the Software without restriction, including without limitation the rights
14+
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
15+
copies of the Software, and to permit persons to whom the Software is
16+
furnished to do so, subject to the following conditions :
17+
18+
The above copyright notice and this permission notice shall be included in all
19+
copies or substantial portions of the Software.
20+
21+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
24+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27+
SOFTWARE.
28+
******************************************************************************/
29+
30+
#include "unit_test_framework.h"
31+
32+
#include "etl/singleton_base.h"
33+
34+
#include <string>
35+
#include <type_traits>
36+
37+
class Test_Singleton: public ::etl::singleton_base<Test_Singleton>
38+
{
39+
public:
40+
41+
Test_Singleton(int i_, std::string text_)
42+
: ::etl::singleton_base<Test_Singleton>(*this)
43+
, i(i_)
44+
, text(text_)
45+
{
46+
}
47+
48+
void Increment()
49+
{
50+
++i;
51+
text += "*";
52+
}
53+
54+
int i;
55+
std::string text;
56+
};
57+
58+
namespace
59+
{
60+
SUITE(test_singleton)
61+
{
62+
//*************************************************************************
63+
TEST(test1)
64+
{
65+
66+
CHECK(!Test_Singleton::is_valid());
67+
CHECK_THROW(Test_Singleton::instance(), etl::singleton_base_not_created);
68+
69+
{
70+
Test_Singleton singleton(1, "Start:");
71+
CHECK(Test_Singleton::is_valid());
72+
73+
Test_Singleton& ts = Test_Singleton::instance();
74+
75+
CHECK_EQUAL(1, ts.i);
76+
CHECK_EQUAL("Start:", ts.text);
77+
78+
ts.Increment();
79+
80+
CHECK_EQUAL(2, ts.i);
81+
CHECK_EQUAL("Start:*", ts.text);
82+
83+
Test_Singleton* pts = &Test_Singleton::instance();
84+
85+
CHECK_EQUAL(2, ts.i);
86+
CHECK_EQUAL(2, pts->i);
87+
CHECK_EQUAL("Start:*", ts.text);
88+
CHECK_EQUAL("Start:*", pts->text);
89+
90+
pts->Increment();
91+
92+
CHECK_EQUAL(3, ts.i);
93+
CHECK_EQUAL(3, pts->i);
94+
CHECK_EQUAL("Start:**", ts.text);
95+
CHECK_EQUAL("Start:**", pts->text);
96+
97+
CHECK_THROW(Test_Singleton(2, "XYZ"), etl::singleton_base_already_created);
98+
}
99+
100+
CHECK(!Test_Singleton::is_valid());
101+
CHECK_THROW(Test_Singleton::instance(), etl::singleton_base_not_created);
102+
}
103+
}
104+
}
105+

0 commit comments

Comments
 (0)