PoDoFo  1.0.0-dev
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 
15 namespace 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>>>
28  class nullable final
29  {
30  public:
31  nullable()
32  : m_value{ }, 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_value{ }, m_hasValue(false) { }
39 
40  nullable(const nullable& value) = default;
41 
42  nullable& operator=(const nullable& value) = default;
43 
44  nullable& operator=(T value)
45  {
46  m_hasValue = true;
47  m_value = std::move(value);
48  return *this;
49  }
50 
51  nullable& operator=(std::nullptr_t)
52  {
53  m_hasValue = false;
54  m_value = { };
55  return *this;
56  }
57 
58  const T& value() const
59  {
60  if (!m_hasValue)
61  throw bad_nullable_access();
62 
63  return m_value;
64  }
65 
66  T& value()
67  {
68  if (!m_hasValue)
69  throw bad_nullable_access();
70 
71  return m_value;
72  }
73 
74  bool has_value() const { return m_hasValue; }
75  const T* operator->() const { return &m_value; }
76  T* operator->() { return &m_value; }
77  const T& operator*() const { return m_value; }
78  T& operator*() { return m_value; }
79 
80  public:
81  template <typename T2>
82  friend bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs);
83 
84  template <typename T2>
85  friend bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs);
86 
87  template <typename T2>
88  friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
89 
90  template <typename T2>
91  friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
92 
93  template <typename T2>
94  friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
95 
96  template <typename T2>
97  friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
98 
99  template <typename T2>
100  friend bool operator==(const nullable<T2>& lhs, const T2& rhs);
101 
102  template <typename T2>
103  friend bool operator==(const T2& lhs, const nullable<T2>& rhs);
104 
105  template <typename T2>
106  friend bool operator==(const nullable<T2>& lhs, std::nullptr_t);
107 
108  template <typename T2>
109  friend bool operator!=(const nullable<T2>& lhs, const T2& rhs);
110 
111  template <typename T2>
112  friend bool operator!=(const T2& lhs, const nullable<T2>& rhs);
113 
114  template <typename T2>
115  friend bool operator==(std::nullptr_t, const nullable<T2>& rhs);
116 
117  template <typename T2>
118  friend bool operator!=(const nullable<T2>& lhs, std::nullptr_t);
119 
120  template <typename T2>
121  friend bool operator!=(std::nullptr_t, const nullable<T2>& rhs);
122 
123  private:
124  T m_value;
125  bool m_hasValue;
126  };
127 
128  // Template specialization for references
129  template <typename T>
130  class nullable<T&> final
131  {
132  public:
133  nullable()
134  : m_value{ }, m_hasValue(false){ }
135 
136  nullable(T& value)
137  : m_value(&value), m_hasValue(true) { }
138 
139  nullable(std::nullptr_t)
140  : m_value{ }, m_hasValue(false) { }
141 
142  // Allow nullable<const T&>::nullable(const nullable<T&>&)
143  template <typename T2, std::enable_if_t<std::is_convertible_v<std::add_pointer_t<std::remove_reference_t<T2>>,
144  std::add_pointer_t<std::remove_reference_t<T>>>, int> = 0>
145  nullable(const nullable<T2&>& value)
146  : m_value(reinterpret_cast<const nullable&>(value).m_value), m_hasValue(reinterpret_cast<const nullable&>(value).m_hasValue) { }
147 
148  nullable(const nullable& value) = default;
149 
150  nullable& operator=(const nullable& value) = default;
151 
152  const T& value() const
153  {
154  if (!m_hasValue)
155  throw bad_nullable_access();
156 
157  return *m_value;
158  }
159 
160  T& value()
161  {
162  if (!m_hasValue)
163  throw bad_nullable_access();
164 
165  return *m_value;
166  }
167 
168  bool has_value() const { return m_hasValue; }
169  const T* operator->() const { return m_value; }
170  T* operator->() { return m_value; }
171  const T& operator*() const { return *m_value; }
172  T& operator*() { return *m_value; }
173 
174  public:
175  template <typename T2>
176  friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
177 
178  template <typename T2>
179  friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);
180 
181  template <typename T2>
182  friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
183 
184  template <typename T2>
185  friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);
186 
187  template <typename T2>
188  friend bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
189 
190  template <typename T2>
191  friend bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs);
192 
193  template <typename T2>
194  friend bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
195 
196  template <typename T2>
197  friend bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);
198 
199  template <typename T2>
200  friend bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
201 
202  template <typename T2>
203  friend bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);
204 
205  template <typename T2>
206  friend bool operator==(const nullable<T2>& lhs, std::nullptr_t);
207 
208  template <typename T2>
209  friend bool operator==(std::nullptr_t, const nullable<T2>& rhs);
210 
211  template <typename T2>
212  friend bool operator!=(const nullable<T2>& lhs, std::nullptr_t);
213 
214  template <typename T2>
215  friend bool operator!=(std::nullptr_t, const nullable<T2>& rhs);
216 
217  private:
218  T* m_value;
219  bool m_hasValue;
220  };
221 
222  template <typename T2>
223  bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs)
224  {
225  if (lhs.m_hasValue != rhs.m_hasValue)
226  return false;
227 
228  if (lhs.m_hasValue)
229  return lhs.m_value == rhs.m_value;
230  else
231  return true;
232  }
233 
234  template <typename T2>
235  bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs)
236  {
237  if (lhs.m_hasValue != rhs.m_hasValue)
238  return true;
239 
240  if (lhs.m_hasValue)
241  return lhs.m_value != rhs.m_value;
242  else
243  return false;
244  }
245 
246  template <typename T2>
247  bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
248  {
249  if (lhs.m_hasValue != rhs.m_hasValue)
250  return false;
251 
252  if (lhs.m_hasValue)
253  return lhs.m_value == *rhs.m_value;
254  else
255  return true;
256  }
257 
258  template <typename T2>
259  bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
260  {
261  if (lhs.m_hasValue != rhs.m_hasValue)
262  return true;
263 
264  if (lhs.m_hasValue)
265  return lhs.m_value != *rhs.m_value;
266  else
267  return false;
268  }
269 
270  template <typename T2>
271  bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
272  {
273  if (lhs.m_hasValue != rhs.m_hasValue)
274  return false;
275 
276  if (lhs.m_hasValue)
277  return *lhs.m_value == rhs.m_value;
278  else
279  return true;
280  }
281 
282  template <typename T2>
283  bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
284  {
285  if (lhs.m_hasValue != rhs.m_hasValue)
286  return true;
287 
288  if (lhs.m_hasValue)
289  return *lhs.m_value != rhs.m_value;
290  else
291  return false;
292  }
293 
294  template <typename T2>
295  bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
296  {
297  if (lhs.m_hasValue != rhs.m_hasValue)
298  return false;
299 
300  if (lhs.m_hasValue)
301  return *lhs.m_value == *rhs.m_value;
302  else
303  return true;
304  }
305 
306  template <typename T2>
307  bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
308  {
309  if (lhs.m_hasValue != rhs.m_hasValue)
310  return true;
311 
312  if (lhs.m_hasValue)
313  return *lhs.m_value != *rhs.m_value;
314  else
315  return false;
316  }
317 
318  template <typename T2>
319  bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
320  {
321  if (!lhs.m_hasValue)
322  return false;
323 
324  return *lhs.m_value == rhs;
325  }
326 
327  template <typename T2>
328  bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
329  {
330  if (!lhs.m_hasValue)
331  return true;
332 
333  return *lhs.m_value != rhs;
334  }
335 
336  template <typename T2>
337  bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
338  {
339  if (!rhs.m_hasValue)
340  return false;
341 
342  return lhs == *rhs.m_value;
343  }
344 
345  template <typename T2>
346  bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
347  {
348  if (!rhs.m_hasValue)
349  return true;
350 
351  return lhs != *rhs.m_value;
352  }
353 
354  template <typename T2>
355  bool operator==(const nullable<T2>& lhs, const T2& rhs)
356  {
357  if (!lhs.m_hasValue)
358  return false;
359 
360  return lhs.m_value == rhs;
361  }
362 
363  template <typename T2>
364  bool operator!=(const nullable<T2>& lhs, const T2& rhs)
365  {
366  if (!lhs.m_hasValue)
367  return true;
368 
369  return lhs.m_value != rhs;
370  }
371 
372  template <typename T2>
373  bool operator==(const T2& lhs, const nullable<T2>& rhs)
374  {
375  if (!rhs.m_hasValue)
376  return false;
377 
378  return lhs == rhs.m_value;
379  }
380 
381  template <typename T2>
382  bool operator!=(const T2& lhs, const nullable<T2>& rhs)
383  {
384  if (!rhs.m_hasValue)
385  return true;
386 
387  return lhs != rhs.m_value;
388  }
389 
390  template <typename T2>
391  bool operator==(const nullable<T2>& lhs, std::nullptr_t)
392  {
393  return !lhs.m_hasValue;
394  }
395 
396  template <typename T2>
397  bool operator!=(const nullable<T2>& lhs, std::nullptr_t)
398  {
399  return lhs.m_hasValue;
400  }
401 
402  template <typename T2>
403  bool operator==(std::nullptr_t, const nullable<T2>& rhs)
404  {
405  return !rhs.m_hasValue;
406  }
407 
408  template <typename T2>
409  bool operator!=(std::nullptr_t, const nullable<T2>& rhs)
410  {
411  return rhs.m_hasValue;
412  }
413 }
414 
415 #endif // AUX_NULLABLE_H
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