ArmNN  NotReleased
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 
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>
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(EmptyOptional empty) : BaseSwitch{empty} {}
278  Optional(const Optional& other) : BaseSwitch{other} {}
279  Optional(const BaseSwitch& other) : BaseSwitch{other} {}
280 
281  template<class... Args>
282  explicit Optional(ConstructInPlace, Args&&... args) :
283  BaseSwitch(CONSTRUCT_IN_PLACE, std::forward<Args>(args)...) {}
284 
287  bool operator==(const Optional<T>& rhs) const
288  {
289  if (!this->has_value() && !rhs.has_value())
290  {
291  return true;
292  }
293  if (this->has_value() && rhs.has_value() && this->value() == rhs.value())
294  {
295  return true;
296  }
297  return false;
298  }
299 };
300 
301 // Utility template that constructs an object of type T in-place and wraps
302 // it inside an Optional<T> object
303 template<typename T, class... Args>
304 Optional<T> MakeOptional(Args&&... args)
305 {
306  return Optional<T>(CONSTRUCT_IN_PLACE, std::forward<Args>(args)...);
307 }
308 
309 }
OptionalBase(bool hasValue) noexcept
Definition: Optional.hpp:67
OptionalBase() noexcept
Definition: Optional.hpp:48
Optional(ConstructInPlace, Args &&... args)
Definition: Optional.hpp:282
#define CONSTRUCT_IN_PLACE
Definition: Optional.hpp:41
OptionalReferenceSwitch & operator=(const T value)
Definition: Optional.hpp:214
OptionalReferenceSwitch(const T &value)
Definition: Optional.hpp:89
OptionalReferenceSwitch(const OptionalReferenceSwitch &other)
Definition: Optional.hpp:200
OptionalReferenceSwitch(ConstructInPlace, Args &&... args)
Definition: Optional.hpp:96
DataLayout::NCHW DataLayout::NCHW DataLayout::NHWC DataLayout::NHWC true
OptionalReferenceSwitch & operator=(EmptyOptional)
Definition: Optional.hpp:126
OptionalReferenceSwitch & operator=(EmptyOptional)
Definition: Optional.hpp:228
OptionalReferenceSwitch(EmptyOptional) noexcept
Definition: Optional.hpp:87
OptionalReferenceSwitch(EmptyOptional) noexcept
Definition: Optional.hpp:198
Optional(EmptyOptional empty)
Definition: Optional.hpp:277
Optional() noexcept
Definition: Optional.hpp:275
Optional(const Optional &other)
Definition: Optional.hpp:278
typename std::remove_reference< T >::type NonRefT
Definition: Optional.hpp:195
Optional(const BaseSwitch &other)
Definition: Optional.hpp:279
bool operator==(const Optional< T > &rhs) const
Definition: Optional.hpp:287
Optional< T > MakeOptional(Args &&... args)
Definition: Optional.hpp:304
OptionalReferenceSwitch(const OptionalReferenceSwitch &other)
Definition: Optional.hpp:102
OptionalReferenceSwitch & operator=(const T &value)
Definition: Optional.hpp:108
Optional(const T &value)
Definition: Optional.hpp:276
OptionalReferenceSwitch & operator=(const OptionalReferenceSwitch &other)
Definition: Optional.hpp:221
OptionalReferenceSwitch & operator=(const OptionalReferenceSwitch &other)
Definition: Optional.hpp:115
bool has_value() const noexcept
Definition: Optional.hpp:53