PoDoFo 1.1.0
Loading...
Searching...
No Matches
nullable.h
1
7#ifndef AUX_NULLABLE_H
8#define AUX_NULLABLE_H
9#pragma once
10
11#include <cstddef>
12#include <stdexcept>
13#include <type_traits>
14
15namespace PoDoFo
16{
17 class bad_nullable_access : public std::runtime_error
18 {
19 public:
20 bad_nullable_access()
21 : std::runtime_error("nullable object doesn't have a value") { }
22 };
23
27 template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>>
29 {
30 public:
31 nullable()
32 : m_dummy{ }, m_hasValue(false) { }
33
34 nullable(T value)
35 : m_value(std::move(value)), m_hasValue(true) { }
36
37 nullable(std::nullptr_t)
38 : m_dummy{ }, m_hasValue(false) { }
39
40 nullable(const nullable& value)
41 : m_dummy{ }
42 {
43 if (value.m_hasValue)
44 {
45 new(&m_value)T(value.m_value);
46 m_hasValue = true;
47 }
48 else
49 {
50 m_hasValue = false;
51 }
52 }
53
54 ~nullable()
55 {
56 if (m_hasValue)
57 m_value.~T();
58 }
59
60 nullable& operator=(const nullable& value)
61 {
62 if (m_hasValue)
63 {
64 if (value.m_hasValue)
65 {
66 m_value = value.m_value;
67 }
68 else
69 {
70 m_value.~T();
71 m_hasValue = false;
72 }
73 }
74 else
75 {
76 if (value.m_hasValue)
77 {
78 new(&m_value)T(value.m_value);
79 m_hasValue = true;
80 }
81 }
82
83 return *this;
84 }
85
86 nullable& operator=(T value)
87 {
88 if (m_hasValue)
89 {
90 m_value = std::move(value);
91 }
92 else
93 {
94 new(&m_value)T(std::move(value));
95 m_hasValue = true;
96 }
97
98 return *this;
99 }
100
105 {
106 if (m_hasValue)
107 {
108 m_value = std::move(value);
109 }
110 else
111 {
112 new(&m_value)T(std::move(value));
113 m_hasValue = true;
114 }
115
116 return *this;
117 }
118
119 nullable& operator=(std::nullptr_t)
120 {
121 if (m_hasValue)
122 m_value.~T();
123
124 m_hasValue = false;
125 return *this;
126 }
127
128 operator nullable<const T&>() const
129 {
130 if (m_hasValue)
131 return nullable<const T&>(m_value);
132 else
133 return { };
134 }
135
136 const T& value() const
137 {
138 if (!m_hasValue)
139 throw bad_nullable_access();
140
141 return m_value;
142 }
143
144 bool has_value() const { return m_hasValue; }
145 const T* operator->() const { return &m_value; }
146 const T& operator*() const { return m_value; }
147 operator const T* () const
148 {
149 if (m_hasValue)
150 return &m_value;
151 else
152 return nullptr;
153 }
154
155 public:
156 template <typename T2>
157 friend bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs);
158
159 template <typename T2>
160 friend bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs);
161
162 template <typename T2>
163 friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
164
165 template <typename T2>
166 friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
167
168 template <typename T2>
169 friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
170
171 template <typename T2>
172 friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
173
174 template <typename T2>
175 friend bool operator==(const nullable<T2>& lhs, const T2& rhs);
176
177 template <typename T2>
178 friend bool operator==(const T2& lhs, const nullable<T2>& rhs);
179
180 template <typename T2>
181 friend bool operator!=(const nullable<T2>& lhs, const T2& rhs);
182
183 template <typename T2>
184 friend bool operator!=(const T2& lhs, const nullable<T2>& rhs);
185
186 template <typename T2>
187 friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t);
188
189 template <typename T2>
190 friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs);
191
192 template <typename T2>
193 friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t);
194
195 template <typename T2>
196 friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs);
197
198 private:
199 struct NonTrivialDummyType
200 {
201 constexpr NonTrivialDummyType() noexcept
202 {
203 // Avoid zero-initialization when objects are value-initialized
204 // Inspired from MS STL https://github.com/microsoft/STL/blob/8124540f8bce3faad76a6dddd050f9a69af4b87d/stl/inc/optional#L58
205 }
206 };
207
208 union
209 {
210 NonTrivialDummyType m_dummy;
211 T m_value;
212 };
213 bool m_hasValue;
214 };
215
216 // Template specialization for references
217 template <typename TRef>
218 class nullable<TRef, std::enable_if_t<std::is_reference_v<TRef>>> final
219 {
220 using T = std::remove_reference_t<TRef>;
221 public:
222 nullable()
223 : m_value{ } { }
224
225 nullable(T& value)
226 : m_value(&value) { }
227
228 nullable(T* value)
229 : m_value(value) { }
230
231 nullable(std::nullptr_t)
232 : m_value{ } { }
233
234 // Allow nullable<const T&>::nullable(const nullable<T&>&)
235 template <typename T2, typename = std::enable_if_t<
236 std::is_convertible_v<std::add_pointer_t<std::remove_reference_t<T2>>,
237 std::add_pointer_t<std::remove_reference_t<T>>>, int>>
238 nullable(const nullable<T2&>& value)
239 : m_value(reinterpret_cast<const nullable&>(value).m_value) { }
240
241 nullable(const nullable& value) = default;
242
243 nullable& operator=(const nullable& value) = default;
244
245 T& value()
246 {
247 if (m_value == nullptr)
248 throw bad_nullable_access();
249
250 return *m_value;
251 }
252
253 bool has_value() const { return m_value != nullptr; }
254
255 explicit operator T* () const { return m_value; }
256 T* operator->() const { return m_value; }
257 T& operator*() const { return *m_value; }
258
259 public:
260 template <typename T2>
261 friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
262
263 template <typename T2>
264 friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
265
266 template <typename T2>
267 friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
268
269 template <typename T2>
270 friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
271
272 template <typename T2>
273 friend bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
274
275 template <typename T2>
276 friend bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
277
278 template <typename T2>
279 friend bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
280
281 template <typename T2>
282 friend bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
283
284 template <typename T2>
285 friend bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
286
287 template <typename T2>
288 friend bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
289
290 template <typename T2>
291 friend std::enable_if_t<std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t);
292
293 template <typename T2>
294 friend std::enable_if_t<std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs);
295
296 template <typename T2>
297 friend std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t);
298
299 template <typename T2>
300 friend std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs);
301
302 template <typename T2>
303 friend bool operator==(const nullable<T2&>& lhs, const T2* rhs);
304
305 template <typename T2>
306 friend bool operator==(const T2* lhs, const nullable<T2&>& rhs);
307
308 template <typename T2>
309 friend bool operator!=(const nullable<T2&>& lhs, const T2* rhs);
310
311 template <typename T2>
312 friend bool operator!=(const T2* lhs, const nullable<T2&>& rhs);
313
314 private:
315 T* m_value;
316 };
317
318 template <typename T2>
319 bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs)
320 {
321 if (lhs.m_hasValue != rhs.m_hasValue)
322 return false;
323
324 if (lhs.m_hasValue)
325 return lhs.m_value == rhs.m_value;
326 else
327 return true;
328 }
329
330 template <typename T2>
331 bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs)
332 {
333 if (lhs.m_hasValue != rhs.m_hasValue)
334 return true;
335
336 if (lhs.m_hasValue)
337 return lhs.m_value != rhs.m_value;
338 else
339 return false;
340 }
341
342 template <typename T2>
343 bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
344 {
345 if (lhs.m_hasValue != rhs.has_value())
346 return false;
347
348 if (lhs.m_hasValue)
349 return lhs.m_value == *rhs.m_value;
350 else
351 return true;
352 }
353
354 template <typename T2>
355 bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
356 {
357 if (lhs.m_hasValue != rhs.has_value())
358 return true;
359
360 if (lhs.m_hasValue)
361 return lhs.m_value != *rhs.m_value;
362 else
363 return false;
364 }
365
366 template <typename T2>
367 bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
368 {
369 if (lhs.has_value() != rhs.m_hasValue)
370 return false;
371
372 if (lhs.has_value())
373 return *lhs.m_value == rhs.m_value;
374 else
375 return true;
376 }
377
378 template <typename T2>
379 bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
380 {
381 if (lhs.has_value() != rhs.m_hasValue)
382 return true;
383
384 if (lhs.has_value())
385 return *lhs.m_value != rhs.m_value;
386 else
387 return false;
388 }
389
390 template <typename T2>
391 bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
392 {
393 if (lhs.has_value() != rhs.has_value())
394 return false;
395
396 if (lhs.has_value())
397 return *lhs.m_value == *rhs.m_value;
398 else
399 return true;
400 }
401
402 template <typename T2>
403 bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
404 {
405 if (lhs.has_value() != rhs.has_value())
406 return true;
407
408 if (lhs.has_value())
409 return *lhs.m_value != *rhs.m_value;
410 else
411 return false;
412 }
413
414 template <typename T2>
415 bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
416 {
417 if (!lhs.has_value())
418 return false;
419
420 return *lhs.m_value == rhs;
421 }
422
423 template <typename T2>
424 bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
425 {
426 if (!lhs.has_value())
427 return true;
428
429 return *lhs.m_value != rhs;
430 }
431
432 template <typename T2>
433 bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
434 {
435 if (!rhs.has_value())
436 return false;
437
438 return lhs == *rhs.m_value;
439 }
440
441 template <typename T2>
442 bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
443 {
444 if (!rhs.has_value())
445 return true;
446
447 return lhs != *rhs.m_value;
448 }
449
450 template <typename T2>
451 bool operator==(const nullable<T2>& lhs, const T2& rhs)
452 {
453 if (!lhs.m_hasValue)
454 return false;
455
456 return lhs.m_value == rhs;
457 }
458
459 template <typename T2>
460 bool operator!=(const nullable<T2>& lhs, const T2& rhs)
461 {
462 if (!lhs.m_hasValue)
463 return true;
464
465 return lhs.m_value != rhs;
466 }
467
468 template <typename T2>
469 bool operator==(const T2& lhs, const nullable<T2>& rhs)
470 {
471 if (!rhs.m_hasValue)
472 return false;
473
474 return lhs == rhs.m_value;
475 }
476
477 template <typename T2>
478 bool operator!=(const T2& lhs, const nullable<T2>& rhs)
479 {
480 if (!rhs.m_hasValue)
481 return true;
482
483 return lhs != rhs.m_value;
484 }
485
486 template <typename T2>
487 std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t)
488 {
489 return !lhs.m_hasValue;
490 }
491
492 template <typename T2>
493 std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t)
494 {
495 return lhs.m_hasValue;
496 }
497
498 template <typename T2>
499 std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs)
500 {
501 return !rhs.m_hasValue;
502 }
503
504 template <typename T2>
505 std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs)
506 {
507 return rhs.m_hasValue;
508 }
509
510 template <typename T2>
511 std::enable_if_t<std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t)
512 {
513 return lhs.m_value == nullptr;
514 }
515
516 template <typename T2>
517 std::enable_if_t<std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs)
518 {
519 return rhs.m_value == nullptr;
520 }
521
522 template <typename T2>
523 std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t)
524 {
525 return lhs.m_value != nullptr;
526 }
527
528 template <typename T2>
529 std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs)
530 {
531 return rhs.m_value != nullptr;
532 }
533
534 template<typename T2>
535 bool operator==(const nullable<T2&>& lhs, const T2* rhs)
536 {
537 return lhs.m_value == rhs;
538 }
539
540 template<typename T2>
541 bool operator==(const T2* lhs, const nullable<T2&>& rhs)
542 {
543 return lhs == rhs.m_value;
544 }
545
546 template<typename T2>
547 bool operator!=(const nullable<T2&>& lhs, const T2* rhs)
548 {
549 return lhs.m_value != rhs;
550 }
551
552 template<typename T2>
553 bool operator!=(const T2* lhs, const nullable<T2&>& rhs)
554 {
555 return lhs != rhs.m_value;
556 }
557}
558
559#endif // AUX_NULLABLE_H
Convenient type for char array storage and/or buffer with std::string compatibility.
Definition basetypes.h:38
Alternative to std::optional that supports reference (but not pointer) types.
Definition nullable.h:29
nullable & operator*=(T value)
This is same as operator=(T value), but allows to avoid ambiguities or picking wrong overload.
Definition nullable.h:104
SPDX-FileCopyrightText: (C) 2022 Francesco Pretto ceztko@gmail.com SPDX-License-Identifier: LGPL-2....
Definition basetypes.h:16