ArmNN
 22.11
Optional.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 #include "Exceptions.hpp"
8 
9 #include <cstring>
10 #include <type_traits>
11 
12 /// Optional is a drop in replacement for std::optional until we migrate
13 /// to c++-17. Only a subset of the optional features are implemented that
14 /// we intend to use in ArmNN.
15 
16 /// There are two distinct implementations here:
17 ///
18 /// 1, for normal constructable/destructable types and reference types
19 /// 2, for reference types
20 
21 /// The std::optional features we support are:
22 ///
23 /// - has_value() and operator bool() to tell if the optional has a value
24 /// - value() returns a reference to the held object
25 ///
26 
27 namespace armnn
28 {
29 
30 /// EmptyOptional is used to initialize the Optional class in case we want
31 /// to have default value for an Optional in a function declaration.
32 struct EmptyOptional {};
33 
34 /// Disambiguation tag that can be passed to the constructor to indicate that
35 /// the contained object should be constructed in-place
37 {
38  explicit ConstructInPlace() = default;
39 };
40 
41 #define CONSTRUCT_IN_PLACE armnn::ConstructInPlace{}
42 
43 /// OptionalBase is the common functionality between reference and non-reference
44 /// optional types.
46 {
47 public:
48  OptionalBase() noexcept
49  : m_HasValue{false}
50  {
51  }
52 
53  bool has_value() const noexcept
54  {
55  return m_HasValue;
56  }
57 
58  /// Conversion to bool, so can be used in if-statements and similar contexts expecting a bool.
59  /// Note this is explicit so that it doesn't get implicitly converted to a bool in unwanted cases,
60  /// for example "Optional<TypeA> == Optional<TypeB>" should not compile.
61  explicit operator bool() const noexcept
62  {
63  return has_value();
64  }
65 
66 protected:
67  OptionalBase(bool hasValue) noexcept
68  : m_HasValue{hasValue}
69  {
70  }
71 
72  bool m_HasValue;
73 };
74 
75 ///
76 /// The default implementation is the non-reference case. This
77 /// has an unsigned char array for storing the optional value which
78 /// is in-place constructed there.
79 ///
80 template <bool IsReference, typename T>
82 {
83 public:
84  using Base = OptionalBase;
85 
86  OptionalReferenceSwitch() noexcept : Base{} {}
88 
89  OptionalReferenceSwitch(const T& value)
90  : Base{}
91  {
92  Construct(value);
93  }
94 
95  template<class... Args>
97  : Base{}
98  {
99  Construct(CONSTRUCT_IN_PLACE, std::forward<Args>(args)...);
100  }
101 
103  : Base{}
104  {
105  *this = other;
106  }
107 
109  {
110  reset();
111  Construct(value);
112  return *this;
113  }
114 
116  {
117  reset();
118  if (other.has_value())
119  {
120  Construct(other.value());
121  }
122 
123  return *this;
124  }
125 
127  {
128  reset();
129  return *this;
130  }
131 
133  {
134  reset();
135  }
136 
137  void reset()
138  {
139  if (Base::has_value())
140  {
141  value().T::~T();
142  Base::m_HasValue = false;
143  }
144  }
145 
146  const T& value() const
147  {
148  if (!Base::has_value())
149  {
150  throw BadOptionalAccessException("Optional has no value");
151  }
152 
153  auto valuePtr = reinterpret_cast<const T*>(m_Storage);
154  return *valuePtr;
155  }
156 
157  T& value()
158  {
159  if (!Base::has_value())
160  {
161  throw BadOptionalAccessException("Optional has no value");
162  }
163 
164  auto valuePtr = reinterpret_cast<T*>(m_Storage);
165  return *valuePtr;
166  }
167 
168 private:
169  void Construct(const T& value)
170  {
171  new (m_Storage) T(value);
172  m_HasValue = true;
173  }
174 
175  template<class... Args>
176  void Construct(ConstructInPlace, Args&&... args)
177  {
178  new (m_Storage) T(std::forward<Args>(args)...);
179  m_HasValue = true;
180  }
181 
182  alignas(alignof(T)) unsigned char m_Storage[sizeof(T)];
183 };
184 
185 ///
186 /// This is the special case for reference types. This holds a pointer
187 /// to the referenced type. This doesn't own the referenced memory and
188 /// it never calls delete on the pointer.
189 ///
190 template <typename T>
191 class OptionalReferenceSwitch<true, T> : public OptionalBase
192 {
193 public:
195  using NonRefT = typename std::remove_reference<T>::type;
196 
197  OptionalReferenceSwitch() noexcept : Base{}, m_Storage{nullptr} {}
198  OptionalReferenceSwitch(EmptyOptional) noexcept : Base{}, m_Storage{nullptr} {}
199 
201  {
202  *this = other;
203  }
204 
206  : Base{true}
207  , m_Storage{&value}
208  {
209  }
210 
211  template<class... Args>
212  OptionalReferenceSwitch(ConstructInPlace, Args&&... args) = delete;
213 
215  {
216  m_Storage = &value;
217  Base::m_HasValue = true;
218  return *this;
219  }
220 
222  {
223  m_Storage = other.m_Storage;
224  Base::m_HasValue = other.has_value();
225  return *this;
226  }
227 
229  {
230  reset();
231  return *this;
232  }
233 
235  {
236  reset();
237  }
238 
239  void reset()
240  {
241  Base::m_HasValue = false;
242  m_Storage = nullptr;
243  }
244 
245  const T value() const
246  {
247  if (!Base::has_value())
248  {
249  throw BadOptionalAccessException("Optional has no value");
250  }
251 
252  return *m_Storage;
253  }
254 
255  T value()
256  {
257  if (!Base::has_value())
258  {
259  throw BadOptionalAccessException("Optional has no value");
260  }
261 
262  return *m_Storage;
263  }
264 
265 private:
266  NonRefT* m_Storage;
267 };
268 
269 template <typename T>
270 class Optional final : public OptionalReferenceSwitch<std::is_reference<T>::value, T>
271 {
272 public:
274 
275  Optional() noexcept : BaseSwitch{} {}
276  Optional(const T& value) : BaseSwitch{value} {}
277  Optional& operator=(const Optional& other) = default;
278  Optional(EmptyOptional empty) : BaseSwitch{empty} {}
279  Optional(const Optional& other) : BaseSwitch{other} {}
280  Optional(const BaseSwitch& other) : BaseSwitch{other} {}
281 
282  template<class... Args>
283  explicit Optional(ConstructInPlace, Args&&... args) :
284  BaseSwitch(CONSTRUCT_IN_PLACE, std::forward<Args>(args)...) {}
285 
286  /// Two optionals are considered equal if they are both empty or both contain values which
287  /// themselves are considered equal (via their own == operator).
288  bool operator==(const Optional<T>& rhs) const
289  {
290  if (!this->has_value() && !rhs.has_value())
291  {
292  return true;
293  }
294  if (this->has_value() && rhs.has_value() && this->value() == rhs.value())
295  {
296  return true;
297  }
298  return false;
299  }
300 };
301 
302 /// Utility template that constructs an object of type T in-place and wraps
303 /// it inside an Optional<T> object
304 template<typename T, class... Args>
305 Optional<T> MakeOptional(Args&&... args)
306 {
307  return Optional<T>(CONSTRUCT_IN_PLACE, std::forward<Args>(args)...);
308 }
309 
310 }
Optional(ConstructInPlace, Args &&... args)
Definition: Optional.hpp:283
OptionalReferenceSwitch & operator=(EmptyOptional)
Definition: Optional.hpp:126
Optional(const Optional &other)
Definition: Optional.hpp:279
Optional(const BaseSwitch &other)
Definition: Optional.hpp:280
OptionalBase(bool hasValue) noexcept
Definition: Optional.hpp:67
OptionalReferenceSwitch(EmptyOptional) noexcept
Definition: Optional.hpp:87
Disambiguation tag that can be passed to the constructor to indicate that the contained object should...
Definition: Optional.hpp:36
OptionalReferenceSwitch & operator=(const OptionalReferenceSwitch &other)
Definition: Optional.hpp:221
bool operator==(const Optional< T > &rhs) const
Two optionals are considered equal if they are both empty or both contain values which themselves are...
Definition: Optional.hpp:288
typename std::remove_reference< T >::type NonRefT
Definition: Optional.hpp:195
#define CONSTRUCT_IN_PLACE
Definition: Optional.hpp:41
OptionalReferenceSwitch & operator=(const T &value)
Definition: Optional.hpp:108
Optional() noexcept
Definition: Optional.hpp:275
Copyright (c) 2021 ARM Limited and Contributors.
OptionalReferenceSwitch(const OptionalReferenceSwitch &other)
Definition: Optional.hpp:102
OptionalReferenceSwitch & operator=(EmptyOptional)
Definition: Optional.hpp:228
OptionalReferenceSwitch(ConstructInPlace, Args &&... args)
Definition: Optional.hpp:96
OptionalReferenceSwitch(EmptyOptional) noexcept
Definition: Optional.hpp:198
OptionalReferenceSwitch(const OptionalReferenceSwitch &other)
Definition: Optional.hpp:200
OptionalBase() noexcept
Definition: Optional.hpp:48
bool has_value() const noexcept
Definition: Optional.hpp:53
The default implementation is the non-reference case.
Definition: Optional.hpp:81
Optional(EmptyOptional empty)
Definition: Optional.hpp:278
OptionalReferenceSwitch & operator=(const OptionalReferenceSwitch &other)
Definition: Optional.hpp:115
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
Optional(const T &value)
Definition: Optional.hpp:276
Optional< T > MakeOptional(Args &&... args)
Utility template that constructs an object of type T in-place and wraps it inside an Optional<T> obje...
Definition: Optional.hpp:305
OptionalReferenceSwitch(const T &value)
Definition: Optional.hpp:89
OptionalBase is the common functionality between reference and non-reference optional types...
Definition: Optional.hpp:45
OptionalReferenceSwitch & operator=(const T value)
Definition: Optional.hpp:214