PoDoFo 1.0.0-dev
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
101 nullable& operator=(std::nullptr_t)
102 {
103 if (m_hasValue)
104 m_value.~T();
105
106 m_hasValue = false;
107 return *this;
108 }
109
110 operator nullable<const T&>() const
111 {
112 if (m_hasValue)
113 return nullable<const T&>(m_value);
114 else
115 return { };
116 }
117
118 const T& value() const
119 {
120 if (!m_hasValue)
121 throw bad_nullable_access();
122
123 return m_value;
124 }
125
126 bool has_value() const { return m_hasValue; }
127 const T* operator->() const { return &m_value; }
128 const T& operator*() const { return m_value; }
129 operator const T* () const
130 {
131 if (m_hasValue)
132 return &m_value;
133 else
134 return nullptr;
135 }
136
137 public:
138 template <typename T2>
139 friend bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs);
140
141 template <typename T2>
142 friend bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs);
143
144 template <typename T2>
145 friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
146
147 template <typename T2>
148 friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
149
150 template <typename T2>
151 friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
152
153 template <typename T2>
154 friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
155
156 template <typename T2>
157 friend bool operator==(const nullable<T2>& lhs, const T2& rhs);
158
159 template <typename T2>
160 friend bool operator==(const T2& lhs, const nullable<T2>& rhs);
161
162 template <typename T2>
163 friend bool operator!=(const nullable<T2>& lhs, const T2& rhs);
164
165 template <typename T2>
166 friend bool operator!=(const T2& lhs, const nullable<T2>& rhs);
167
168 template <typename T2>
169 friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t);
170
171 template <typename T2>
172 friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs);
173
174 template <typename T2>
175 friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t);
176
177 template <typename T2>
178 friend std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs);
179
180 private:
181 struct NonTrivialDummyType
182 {
183 constexpr NonTrivialDummyType() noexcept
184 {
185 // Avoid zero-initialization when objects are value-initialized
186 // Inspired from MS STL https://github.com/microsoft/STL/blob/8124540f8bce3faad76a6dddd050f9a69af4b87d/stl/inc/optional#L58
187 }
188 };
189
190 union
191 {
192 NonTrivialDummyType m_dummy;
193 T m_value;
194 };
195 bool m_hasValue;
196 };
197
198 // Template specialization for references
199 template <typename TRef>
200 class nullable<TRef, std::enable_if_t<std::is_reference_v<TRef>>> final
201 {
202 using T = std::remove_reference_t<TRef>;
203 public:
204 nullable()
205 : m_value{ } { }
206
207 nullable(T& value)
208 : m_value(&value) { }
209
210 nullable(T* value)
211 : m_value(value) { }
212
213 nullable(std::nullptr_t)
214 : m_value{ } { }
215
216 // Allow nullable<const T&>::nullable(const nullable<T&>&)
217 template <typename T2, typename = std::enable_if_t<
218 std::is_convertible_v<std::add_pointer_t<std::remove_reference_t<T2>>,
219 std::add_pointer_t<std::remove_reference_t<T>>>, int>>
220 nullable(const nullable<T2&>& value)
221 : m_value(reinterpret_cast<const nullable&>(value).m_value) { }
222
223 nullable(const nullable& value) = default;
224
225 nullable& operator=(const nullable& value) = default;
226
227 T& value()
228 {
229 if (m_value == nullptr)
230 throw bad_nullable_access();
231
232 return *m_value;
233 }
234
235 bool has_value() const { return m_value != nullptr; }
236
237 explicit operator T* () const { return m_value; }
238 T* operator->() const { return m_value; }
239 T& operator*() const { return *m_value; }
240
241 public:
242 template <typename T2>
243 friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
244
245 template <typename T2>
246 friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
247
248 template <typename T2>
249 friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
250
251 template <typename T2>
252 friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
253
254 template <typename T2>
255 friend bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
256
257 template <typename T2>
258 friend bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
259
260 template <typename T2>
261 friend bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
262
263 template <typename T2>
264 friend bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
265
266 template <typename T2>
267 friend bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
268
269 template <typename T2>
270 friend bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
271
272 template <typename T2>
273 friend std::enable_if_t<std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t);
274
275 template <typename T2>
276 friend std::enable_if_t<std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs);
277
278 template <typename T2>
279 friend std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t);
280
281 template <typename T2>
282 friend std::enable_if_t<std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs);
283
284 template <typename T2>
285 friend bool operator==(const nullable<T2&>& lhs, const T2* rhs);
286
287 template <typename T2>
288 friend bool operator==(const T2* lhs, const nullable<T2&>& rhs);
289
290 template <typename T2>
291 friend bool operator!=(const nullable<T2&>& lhs, const T2* rhs);
292
293 template <typename T2>
294 friend bool operator!=(const T2* lhs, const nullable<T2&>& rhs);
295
296 private:
297 T* m_value;
298 };
299
300 template <typename T2>
301 bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs)
302 {
303 if (lhs.m_hasValue != rhs.m_hasValue)
304 return false;
305
306 if (lhs.m_hasValue)
307 return lhs.m_value == rhs.m_value;
308 else
309 return true;
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 true;
317
318 if (lhs.m_hasValue)
319 return lhs.m_value != rhs.m_value;
320 else
321 return false;
322 }
323
324 template <typename T2>
325 bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
326 {
327 if (lhs.m_hasValue != rhs.has_value())
328 return false;
329
330 if (lhs.m_hasValue)
331 return lhs.m_value == *rhs.m_value;
332 else
333 return true;
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 true;
341
342 if (lhs.m_hasValue)
343 return lhs.m_value != *rhs.m_value;
344 else
345 return false;
346 }
347
348 template <typename T2>
349 bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
350 {
351 if (lhs.has_value() != rhs.m_hasValue)
352 return false;
353
354 if (lhs.has_value())
355 return *lhs.m_value == rhs.m_value;
356 else
357 return true;
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 true;
365
366 if (lhs.has_value())
367 return *lhs.m_value != rhs.m_value;
368 else
369 return false;
370 }
371
372 template <typename T2>
373 bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
374 {
375 if (lhs.has_value() != rhs.has_value())
376 return false;
377
378 if (lhs.has_value())
379 return *lhs.m_value == *rhs.m_value;
380 else
381 return true;
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 true;
389
390 if (lhs.has_value())
391 return *lhs.m_value != *rhs.m_value;
392 else
393 return false;
394 }
395
396 template <typename T2>
397 bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
398 {
399 if (!lhs.has_value())
400 return false;
401
402 return *lhs.m_value == rhs;
403 }
404
405 template <typename T2>
406 bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
407 {
408 if (!lhs.has_value())
409 return true;
410
411 return *lhs.m_value != rhs;
412 }
413
414 template <typename T2>
415 bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
416 {
417 if (!rhs.has_value())
418 return false;
419
420 return lhs == *rhs.m_value;
421 }
422
423 template <typename T2>
424 bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
425 {
426 if (!rhs.has_value())
427 return true;
428
429 return lhs != *rhs.m_value;
430 }
431
432 template <typename T2>
433 bool operator==(const nullable<T2>& lhs, const T2& rhs)
434 {
435 if (!lhs.m_hasValue)
436 return false;
437
438 return lhs.m_value == rhs;
439 }
440
441 template <typename T2>
442 bool operator!=(const nullable<T2>& lhs, const T2& rhs)
443 {
444 if (!lhs.m_hasValue)
445 return true;
446
447 return lhs.m_value != rhs;
448 }
449
450 template <typename T2>
451 bool operator==(const T2& lhs, const nullable<T2>& rhs)
452 {
453 if (!rhs.m_hasValue)
454 return false;
455
456 return lhs == rhs.m_value;
457 }
458
459 template <typename T2>
460 bool operator!=(const T2& lhs, const nullable<T2>& rhs)
461 {
462 if (!rhs.m_hasValue)
463 return true;
464
465 return lhs != rhs.m_value;
466 }
467
468 template <typename T2>
469 std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(const nullable<T2>& lhs, std::nullptr_t)
470 {
471 return !lhs.m_hasValue;
472 }
473
474 template <typename T2>
475 std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(const nullable<T2>& lhs, std::nullptr_t)
476 {
477 return lhs.m_hasValue;
478 }
479
480 template <typename T2>
481 std::enable_if_t<!std::is_reference_v<T2>, bool> operator==(std::nullptr_t, const nullable<T2>& rhs)
482 {
483 return !rhs.m_hasValue;
484 }
485
486 template <typename T2>
487 std::enable_if_t<!std::is_reference_v<T2>, bool> operator!=(std::nullptr_t, const nullable<T2>& rhs)
488 {
489 return rhs.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_value == nullptr;
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_value == nullptr;
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 bool operator==(const nullable<T2&>& lhs, const T2* rhs)
518 {
519 return lhs.m_value == rhs;
520 }
521
522 template<typename T2>
523 bool operator==(const T2* lhs, const nullable<T2&>& rhs)
524 {
525 return lhs == rhs.m_value;
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
541#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
SPDX-FileCopyrightText: (C) 2022 Francesco Pretto ceztko@gmail.com SPDX-License-Identifier: LGPL-2....
Definition basetypes.h:16