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