1 | // |
2 | // Nullable.h |
3 | // |
4 | // Library: Foundation |
5 | // Package: Core |
6 | // Module: Nullable |
7 | // |
8 | // Definition of the Nullable template class. |
9 | // |
10 | // Copyright (c) 2010, Applied Informatics Software Engineering GmbH. |
11 | // and Contributors. |
12 | // |
13 | // SPDX-License-Identifier: BSL-1.0 |
14 | // |
15 | |
16 | |
17 | #ifndef Foundation_Nullable_INCLUDED |
18 | #define Foundation_Nullable_INCLUDED |
19 | |
20 | |
21 | #include "Poco/Foundation.h" |
22 | #include "Poco/MemoryPool.h" |
23 | #include "Poco/Exception.h" |
24 | #include <algorithm> |
25 | #include <iostream> |
26 | |
27 | |
28 | namespace Poco { |
29 | |
30 | |
31 | enum NullType |
32 | { |
33 | NULL_GENERIC = 0 |
34 | }; |
35 | |
36 | |
37 | template <typename C> |
38 | class Nullable |
39 | /// Nullable is a simple wrapper class for value types |
40 | /// that allows objects or native type variables |
41 | /// to have "null" value. |
42 | /// |
43 | /// The class is useful for passing parameters to functions |
44 | /// when parameters are optional and no default values |
45 | /// should be used or when a non-assigned state is needed, |
46 | /// such as in e.g. fetching null values from database. |
47 | /// |
48 | /// A Nullable can be default constructed. In this case, |
49 | /// the Nullable will have a Null value and isNull() will |
50 | /// return true. Calling value() (without default value) on |
51 | /// a Null object will throw a NullValueException. |
52 | /// |
53 | /// A Nullable can also be constructed from a value. |
54 | /// It is possible to assign a value to a Nullable, and |
55 | /// to reset a Nullable to contain a Null value by calling |
56 | /// clear(). |
57 | { |
58 | public: |
59 | Nullable(): _isNull(true) |
60 | /// Creates an empty Nullable. |
61 | { |
62 | construct(); |
63 | } |
64 | |
65 | Nullable(const NullType&): _isNull(true) |
66 | /// Creates an empty Nullable. |
67 | { |
68 | construct(); |
69 | } |
70 | |
71 | Nullable(const C& val): _isNull(true) |
72 | /// Creates a Nullable with the given value. |
73 | { |
74 | construct(&val); |
75 | } |
76 | |
77 | Nullable(const Nullable& other): _isNull(true) |
78 | /// Creates a Nullable by copying another one. |
79 | { |
80 | construct(other); |
81 | } |
82 | |
83 | ~Nullable() |
84 | /// Destroys the Nullable. |
85 | { |
86 | destruct(); |
87 | } |
88 | |
89 | Nullable& assign(const C& val) |
90 | /// Assigns a value to the Nullable. |
91 | { |
92 | construct(&val); |
93 | return *this; |
94 | } |
95 | |
96 | Nullable& assign(const Nullable& other) |
97 | /// Assigns another Nullable. |
98 | { |
99 | if (&other != this) construct(other); |
100 | return *this; |
101 | } |
102 | |
103 | Nullable& assign(NullType) |
104 | /// Sets value to null. |
105 | { |
106 | destruct(); |
107 | return *this; |
108 | } |
109 | |
110 | Nullable& operator = (const C& value) |
111 | /// Assigns a value to the Nullable. |
112 | { |
113 | return assign(value); |
114 | } |
115 | |
116 | Nullable& operator = (const Nullable& other) |
117 | /// Assigns another Nullable. |
118 | { |
119 | return assign(other); |
120 | } |
121 | |
122 | Nullable& operator = (NullType) |
123 | /// Assigns another Nullable. |
124 | { |
125 | destruct(); |
126 | return *this; |
127 | } |
128 | |
129 | bool operator == (const Nullable<C>& other) const |
130 | /// Compares two Nullables for equality |
131 | { |
132 | return (_isNull && other._isNull) || |
133 | (!_isNull && !other._isNull && value() == other.value()); |
134 | } |
135 | |
136 | bool operator == (const C& val) const |
137 | /// Compares Nullable with value for equality |
138 | { |
139 | return (!_isNull && value() == val); |
140 | } |
141 | |
142 | bool operator == (const NullType&) const |
143 | /// Compares Nullable with NullData for equality |
144 | { |
145 | return _isNull; |
146 | } |
147 | |
148 | bool operator != (const C& val) const |
149 | /// Compares Nullable with value for non equality |
150 | { |
151 | return !(*this == val); |
152 | } |
153 | |
154 | bool operator != (const Nullable<C>& other) const |
155 | /// Compares two Nullables for non equality |
156 | { |
157 | return !(*this == other); |
158 | } |
159 | |
160 | bool operator != (const NullType&) const |
161 | /// Compares with NullData for non equality |
162 | { |
163 | return !_isNull; |
164 | } |
165 | |
166 | bool operator < (const Nullable<C>& other) const |
167 | /// Compares two Nullable objects. Return true if this object's |
168 | /// value is smaller than the other object's value. |
169 | /// Null value is smaller than a non-null value. |
170 | { |
171 | if (_isNull && other._isNull) return false; |
172 | |
173 | if (!_isNull && !other._isNull) |
174 | return (value() < other.value()); |
175 | |
176 | if (_isNull && !other._isNull) return true; |
177 | |
178 | return false; |
179 | } |
180 | |
181 | bool operator <= (const Nullable<C>& other) const |
182 | /// Compares two Nullable objects. Return true if this object's |
183 | /// value is smaller or equal than the other object's value. |
184 | /// Null value is smaller than a non-null value. |
185 | { |
186 | if (_isNull && other._isNull) return true; |
187 | |
188 | if (!_isNull && !other._isNull) |
189 | return ((value() < other.value()) || (value() == other.value())); |
190 | |
191 | if (_isNull && !other._isNull) return true; |
192 | |
193 | return false; |
194 | } |
195 | |
196 | bool operator > (const Nullable<C>& other) const |
197 | /// Compares two Nullable objects. Return true if this object's |
198 | /// value is greater than the other object's value. |
199 | /// A non-null value is greater than a null value. |
200 | { |
201 | return !(*this == other) && !(*this < other); |
202 | } |
203 | |
204 | bool operator >= (const Nullable<C>& other) const |
205 | /// Compares two Nullable objects. Return true if this object's |
206 | /// value is greater or equal than the other object's value. |
207 | /// A non-null value is greater than a null value. |
208 | { |
209 | return (*this > other) || (*this == other); |
210 | } |
211 | |
212 | C& value() |
213 | /// Returns the Nullable's value. |
214 | /// |
215 | /// Throws a NullValueException if the Nullable is empty. |
216 | { |
217 | if (!_isNull) |
218 | { |
219 | C* ptr = reinterpret_cast<C*>(_value.buffer); |
220 | return *ptr; |
221 | } |
222 | else throw NullValueException(); |
223 | } |
224 | |
225 | const C& value() const |
226 | /// Returns the Nullable's value. |
227 | /// |
228 | /// Throws a NullValueException if the Nullable is empty. |
229 | { |
230 | if (!_isNull) |
231 | { |
232 | const C* ptr = reinterpret_cast<const C*>(_value.buffer); |
233 | return *ptr; |
234 | } |
235 | else throw NullValueException(); |
236 | } |
237 | |
238 | const C& value(const C& deflt) const |
239 | /// Returns the Nullable's value, or the |
240 | /// given default value if the Nullable is empty. |
241 | { |
242 | if (_isNull) return deflt; |
243 | const C* pC = reinterpret_cast<const C*>(_value.buffer); |
244 | return *pC; |
245 | } |
246 | |
247 | operator C& () |
248 | /// Get reference to the value |
249 | { |
250 | return value(); |
251 | } |
252 | |
253 | operator const C& () const |
254 | /// Get const reference to the value |
255 | { |
256 | return value(); |
257 | } |
258 | |
259 | operator NullType& () |
260 | /// Get reference to the value |
261 | { |
262 | return _null; |
263 | } |
264 | |
265 | bool isNull() const |
266 | /// Returns true if the Nullable is empty. |
267 | { |
268 | return _isNull; |
269 | } |
270 | |
271 | void clear() |
272 | /// Clears the Nullable. |
273 | { |
274 | destruct(); |
275 | } |
276 | |
277 | private: |
278 | void construct(const Nullable& val) |
279 | { |
280 | if (!val._isNull) construct(&val.value()); |
281 | else destruct(); |
282 | } |
283 | |
284 | void construct(const C* pVal = 0) |
285 | { |
286 | destruct(); |
287 | if (pVal) |
288 | { |
289 | new(_value.buffer) C(*pVal); |
290 | _isNull = false; |
291 | } |
292 | } |
293 | |
294 | void destruct(bool /*init*/ = true) |
295 | { |
296 | if (!_isNull) |
297 | { |
298 | value().~C(); |
299 | _isNull = true; |
300 | } |
301 | } |
302 | |
303 | typedef typename std::aligned_storage<sizeof(C)>::type AlignerType; |
304 | union |
305 | { |
306 | char buffer[sizeof(C)]; |
307 | private: |
308 | AlignerType aligner; |
309 | } _value; |
310 | bool _isNull; |
311 | NullType _null; |
312 | }; |
313 | |
314 | |
315 | template <typename C> |
316 | inline void swap(Nullable<C>& n1, Nullable<C>& n2) |
317 | { |
318 | n1.swap(n2); |
319 | } |
320 | |
321 | |
322 | template <typename C> |
323 | std::ostream& operator<<(std::ostream& out, const Nullable<C>& obj) |
324 | { |
325 | if (!obj.isNull()) out << obj.value(); |
326 | return out; |
327 | } |
328 | |
329 | |
330 | template <typename C> |
331 | bool operator == (const NullType&, const Nullable<C>& n) |
332 | /// Returns true if this Nullable is null. |
333 | { |
334 | return n.isNull(); |
335 | } |
336 | |
337 | |
338 | template <typename C> |
339 | bool operator != (const C& c, const Nullable<C>& n) |
340 | /// Compares Nullable with value for non equality |
341 | { |
342 | return !(n == c); |
343 | } |
344 | |
345 | |
346 | template <typename C> |
347 | bool operator == (const C& c, const Nullable<C>& n) |
348 | /// Compares Nullable with NullData for equality |
349 | { |
350 | return (n == c); |
351 | } |
352 | |
353 | |
354 | template <typename C> |
355 | bool operator != (const NullType&, const Nullable<C>& n) |
356 | /// Returns true if this Nullable is not null. |
357 | { |
358 | return !n.isNull(); |
359 | } |
360 | |
361 | |
362 | } // namespace Poco |
363 | |
364 | |
365 | #endif // Foundation_Nullable_INCLUDED |
366 | |