Shaka Player Embedded
variant.h
Go to the documentation of this file.
1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef SHAKA_EMBEDDED_VARIANT_H_
16 #define SHAKA_EMBEDDED_VARIANT_H_
17 
18 #include <assert.h>
19 #include <stddef.h>
20 
21 #include <cstdint>
22 #include <limits>
23 #include <type_traits>
24 #include <utility>
25 
26 #include "macros.h"
27 
28 namespace shaka {
29 
30 // Note that we shouldn't use the C++17 type even if we are compiling with that
31 // version of C++. This is because Shaka Embedded is compiled using C++11 and
32 // will use this type. So the app should always use this type for our API.
33 // Otherwise using different types will cause subtle bugs.
34 
35 template <typename... Choices>
36 class variant;
37 
38 namespace impl {
39 
40 #define SHAKA_MAX(a, b) ((a) > (b) ? (a) : (b))
41 
42 template <typename... Types>
43 struct get_max_size;
44 template <>
45 struct get_max_size<> : std::integral_constant<size_t, 0> {};
46 template <typename T, typename... Rest>
47 struct get_max_size<T, Rest...>
48  : std::integral_constant<size_t, SHAKA_MAX(sizeof(T),
49  get_max_size<Rest...>::value)> {
50 };
51 
52 #undef SHAKA_MAX
53 
54 
56 template <size_t I, typename... Choices>
57 struct get_type_at;
58 template <size_t I, typename T, typename... Choices>
59 struct get_type_at<I, T, Choices...> final {
60  static_assert(I > 0, "Bad specialization chosen.");
61  using type = typename get_type_at<I - 1, Choices...>::type;
62 };
63 template <typename T, typename... Choices>
64 struct get_type_at<0, T, Choices...> final {
65  using type = T;
66 };
67 
68 template <size_t I, typename... Choices>
69 using get_type_at_t = typename get_type_at<I, Choices...>::type;
70 
71 template <size_t I, typename... Choices>
73  typename std::add_lvalue_reference<typename std::add_const<
74  typename get_type_at<I, Choices...>::type>::type>::type;
75 
77 template <typename T, typename... Choices>
79 template <typename T, typename... Choices>
80 struct get_type_index<T, T, Choices...> {
81  static constexpr const size_t index = 0;
82 };
83 template <typename T, typename A, typename... Choices>
84 struct get_type_index<T, A, Choices...> {
85  static_assert(!std::is_same<T, A>::value, "Bad specialization chosen");
86  static constexpr const size_t index =
87  get_type_index<T, Choices...>::index + 1;
88 };
89 
94 template <typename Arg, typename T, typename... Choices>
96  private:
97  // We can't use a ternary conditional to terminate the recursion, so we have
98  // to use a specialization like this.
99  template <bool CanConstruct, typename Dummy = void>
100  struct Helper { // CanConstruct = true;
101  static constexpr const size_t index = 0;
102  };
103  template <typename Dummy>
104  struct Helper<false, Dummy> {
105  static constexpr const size_t index =
106  get_construct_index<Arg, Choices...>::index + 1;
107  };
108 
109  public:
110  static constexpr const size_t index =
111  Helper<std::is_constructible<T, Arg&&>::value>::index;
112 };
113 
119 template <typename T, typename... Rest>
120 class union_ final {
121  public:
122  union_() {}
123  ~union_() {}
124 
126 
127  void copy(const union_& other, size_t i) {
128  if (i == 0) {
129  new (&value_) T(other.value_);
130  } else {
131  new (&rest_) union_<Rest...>;
132  rest_.copy(other.rest_, i - 1);
133  }
134  }
135 
136  void move(union_&& other, size_t i) {
137  if (i == 0) {
138  new (&value_) T(std::move(other.value_));
139  } else {
140  new (&rest_) union_<Rest...>;
141  rest_.move(std::move(other.rest_), i - 1);
142  }
143  }
144 
145  template <size_t I>
146  get_const_reference_at_t<I, T, Rest...> get() const {
147  return GetHelper<I, void*>::get(this);
148  }
149 
150  template <size_t I>
151  get_type_at_t<I, T, Rest...>& get() {
152  return GetHelper<I, void*>::get(this);
153  }
154 
155  // Note this works for copy- and move-constructors too.
156  template <size_t I, typename... U>
157  void emplace(U&&... args) {
158  EmplaceHelper<I, U...>::emplace(this, std::forward<U>(args)...);
159  }
160 
161  void reset(size_t i) {
162  if (i == 0) {
163  value_.~T();
164  } else {
165  rest_.reset(i - 1);
166  rest_.~union_<Rest...>();
167  }
168  }
169 
170  bool equals(const union_& other, size_t i) const {
171  if (i == 0)
172  return value_ == other.value_;
173  else
174  return rest_.equals(other.rest_, i - 1);
175  }
176 
177  private:
178  template <size_t I, typename... U>
179  struct EmplaceHelper {
180  static void emplace(union_* that, U&&... args) {
181  new (&that->rest_) union_<Rest...>;
182  that->rest_.template emplace<I - 1>(std::forward<U>(args)...);
183  }
184  };
185  template <typename... U>
186  struct EmplaceHelper<0, U...> {
187  static void emplace(union_* that, U&&... args) {
188  new (&that->value_) T(std::forward<U>(args)...);
189  }
190  };
191 
192  template <size_t I, typename Dummy = void>
193  struct GetHelper {
194  static get_const_reference_at_t<I, T, Rest...> get(const union_* that) {
195  return that->rest_.template get<I - 1>();
196  }
197 
198  static get_type_at_t<I, T, Rest...>& get(union_* that) {
199  return that->rest_.template get<I - 1>();
200  }
201  };
202  template <typename Dummy>
203  struct GetHelper<0, Dummy> {
204  static const T& get(const union_* that) {
205  return that->value_;
206  }
207 
208  static T& get(union_* that) {
209  return that->value_;
210  }
211  };
212 
213  union {
215  union_<Rest...> rest_;
216  };
217 };
218 
219 template <typename T>
220 class union_<T> final {
221  public:
222  union_() {}
223  ~union_() {}
224 
226 
227  void copy(const union_& other, size_t i) {
228  assert(i == 0);
229  new (&value_) T(other.value_);
230  }
231 
232  void move(union_&& other, size_t i) {
233  assert(i == 0);
234  new (&value_) T(std::move(other.value_));
235  }
236 
237  template <size_t I>
238  const T& get() const {
239  static_assert(I == 0, "Index out of range");
240  return value_;
241  }
242 
243  template <size_t I>
244  T& get() {
245  static_assert(I == 0, "Index out of range");
246  return value_;
247  }
248 
249  // Note this works for copy- and move-constructors too.
250  template <size_t I, typename... U>
251  void emplace(U&&... args) {
252  static_assert(I == 0, "Index out of range");
253  new (&value_) T(std::forward<U>(args)...);
254  }
255 
256  void reset(size_t i) {
257  assert(i == 0);
258  value_.~T();
259  }
260 
261  bool equals(const union_& other, size_t i) const {
262  assert(i == 0);
263  return value_ == other.value_;
264  }
265 
266  private:
267  union {
269  uint8_t dummy_;
270  };
271 };
272 
273 template <typename T>
274 struct is_variant : std::false_type {};
275 template <typename... Choices>
276 struct is_variant<variant<Choices...>> : std::true_type {};
277 
278 } // namespace impl
279 
280 template <size_t I, typename Variant>
282 template <size_t I, typename... Choices>
283 struct variant_alternative<I, variant<Choices...>> {
284  using type = typename impl::get_type_at<I, Choices...>::type;
285 };
286 
287 template <size_t I, typename Variant>
289 
290 struct monostate {};
291 
300 template <typename... Types>
301 class variant {
302  public:
303  variant() : index_(0) {
304  union_.template emplace<0>();
305  }
306 
307  template <typename U, typename = typename std::enable_if<!impl::is_variant<
308  typename std::decay<U>::type>::value>::type>
309  variant(U&& value) {
310  constexpr const size_t I = impl::get_construct_index<U, Types...>::index;
311  union_.template emplace<I>(std::forward<U>(value));
312  index_ = I;
313  }
314 
315  variant(const variant& other) : index_(other.index_) {
316  union_.copy(other.union_, other.index_);
317  }
318 
319  variant(variant&& other) : index_(other.index_) {
320  union_.move(std::move(other.union_), other.index_);
321  }
322 
324  union_.reset(index_);
325  }
326 
327  variant& operator=(const variant& other) {
328  union_.reset(index_);
329  union_.copy(other.union_, other.index_);
330  index_ = other.index_;
331  return *this;
332  }
334  union_.reset(index_);
335  union_.move(std::move(other.union_), other.index_);
336  index_ = other.index_;
337  return *this;
338  }
339 
340  size_t index() const {
341  return index_;
342  }
343 
344  template <typename T, typename... Args>
345  T& emplace(Args&&... args) {
346  constexpr const size_t I = impl::get_type_index<T, Types...>::index;
347  union_.reset(index_);
348  union_.template emplace<I>(std::forward<Args>(args)...);
349  index_ = I;
350  return union_.template get<I>();
351  }
352 
353  template <size_t I, typename... Args>
355  union_.reset(index_);
356  union_.template emplace<I>(std::forward<Args>(args)...);
357  index_ = I;
358  return union_.template get<I>();
359  }
360 
361  private:
362  template <typename...>
363  friend class variant;
364  template <typename... Choices>
365  friend bool operator==(const variant<Choices...>&,
366  const variant<Choices...>&);
367 
368  template <size_t I, typename... Choices>
369  friend impl::get_const_reference_at_t<I, Choices...> get(
370  const variant<Choices...>&);
371  template <size_t I, typename... Choices>
372  friend variant_alternative_t<I, variant<Choices...>>& get(
373  variant<Choices...>&);
374  template <typename T, typename... Choices>
375  friend const T& get(const variant<Choices...>&);
376  template <typename T, typename... Choices>
377  friend T& get(variant<Choices...>&);
378  template <typename T, typename... Choices>
379  friend T&& get(variant<Choices...>&&);
380 
381  template <size_t I, typename... Choices>
382  friend typename std::add_const<
383  variant_alternative_t<I, variant<Choices...>>>::type*
384  get_if(const variant<Choices...>&);
385  template <size_t I, typename... Choices>
386  friend variant_alternative_t<I, variant<Choices...>>* get_if(
388  template <typename T, typename... Choices>
389  friend const T* get_if(const variant<Choices...>&);
390  template <typename T, typename... Choices>
391  friend T* get_if(variant<Choices...>&);
392 
393  // This should be a C++ union, so the size should be the same as the largest
394  // type.
395  static_assert(sizeof(impl::union_<Types...>) ==
397  "Union too big");
398 
399  // Use a smaller integer type to avoid taking up more space to store the
400  // index. The index is only as large as the number of types.
401  using size_type = typename std::conditional<
402  sizeof...(Types) <= std::numeric_limits<uint8_t>::max(), uint8_t,
403  typename std::conditional<sizeof...(Types) <=
404  std::numeric_limits<uint16_t>::max(),
405  uint16_t, size_t>::type>::type;
406 
407 
408  bool equals(const variant& other) const {
409  return index_ == other.index_ && union_.equals(other.union_, index_);
410  }
411 
412  template <size_t I>
413  impl::get_const_reference_at_t<I, Types...> get() const {
414  assert(I == index_);
415  return union_.template get<I>();
416  }
417 
418  template <size_t I>
420  assert(I == index_);
421  return union_.template get<I>();
422  }
423 
424  template <size_t I>
426  assert(I == index_);
427  return std::move(union_.template get<I>());
428  }
429 
430  impl::union_<Types...> union_;
431  size_type index_;
432 };
433 
434 
435 template <typename... Choices>
437  const variant<Choices...>& rhs) {
438  return lhs.equals(rhs);
439 }
440 
441 template <typename... Choices>
443  const variant<Choices...>& rhs) {
444  return !(lhs == rhs);
445 }
446 
447 
448 template <typename T, typename... Choices>
450  constexpr const size_t I = impl::get_type_index<T, Choices...>::index;
451  return variant.index() == I;
452 }
453 
454 
455 template <size_t I, typename... Choices>
457  const variant<Choices...>& variant) {
458  return variant.template get<I>();
459 }
460 
461 template <size_t I, typename... Choices>
462 variant_alternative_t<I, variant<Choices...>>& get(
463  variant<Choices...>& variant) {
464  return variant.template get<I>();
465 }
466 
467 template <typename T, typename... Choices>
468 const T& get(const variant<Choices...>& variant) {
469  constexpr const size_t I = impl::get_type_index<T, Choices...>::index;
470  return variant.template get<I>();
471 }
472 
473 template <typename T, typename... Choices>
474 T& get(variant<Choices...>& variant) {
475  constexpr const size_t I = impl::get_type_index<T, Choices...>::index;
476  return variant.template get<I>();
477 }
478 
479 template <typename T, typename... Choices>
480 T&& get(variant<Choices...>&& variant) {
481  constexpr const size_t I = impl::get_type_index<T, Choices...>::index;
482  return std::move(variant.template get<I>());
483 }
484 
485 
486 template <size_t I, typename... Choices>
487 typename std::add_const<variant_alternative_t<I, variant<Choices...>>>::type*
489  if (variant.index() == I)
490  return &variant.template get<I>();
491  else
492  return nullptr;
493 }
494 
495 template <size_t I, typename... Choices>
498  if (variant.index() == I)
499  return &variant.template get<I>();
500  else
501  return nullptr;
502 }
503 
504 template <typename T, typename... Choices>
506  constexpr const size_t I = impl::get_type_index<T, Choices...>::index;
507  if (variant.index() == I)
508  return &variant.template get<I>();
509  else
510  return nullptr;
511 }
512 
513 template <typename T, typename... Choices>
515  constexpr const size_t I = impl::get_type_index<T, Choices...>::index;
516  if (variant.index() == I)
517  return &variant.template get<I>();
518  else
519  return nullptr;
520 }
521 
522 } // namespace shaka
523 
524 #endif // SHAKA_EMBEDDED_VARIANT_H_
void reset(size_t i)
Definition: variant.h:161
variant(variant &&other)
Definition: variant.h:319
bool equals(const union_ &other, size_t i) const
Definition: variant.h:261
void move(union_ &&other, size_t i)
Definition: variant.h:136
bool operator==(const optional< A > &lhs, const optional< B > &rhs)
Definition: optional.h:207
T && get(variant< Choices... > &&variant)
Definition: variant.h:480
bool operator!=(const optional< A > &lhs, const optional< B > &rhs)
Definition: optional.h:214
std::add_const< variant_alternative_t< I, variant< Choices... > > >::type * get_if(const variant< Choices... > &variant)
Definition: variant.h:488
#define SHAKA_NON_COPYABLE_OR_MOVABLE_TYPE(Type)
Definition: macros.h:51
void copy(const union_ &other, size_t i)
Definition: variant.h:227
T & emplace(Args &&... args)
Definition: variant.h:345
variant & operator=(variant &&other)
Definition: variant.h:333
bool equals(const union_ &other, size_t i) const
Definition: variant.h:170
variant(U &&value)
Definition: variant.h:309
ExceptionCode type
union_< Rest... > rest_
Definition: variant.h:215
variant & operator=(const variant &other)
Definition: variant.h:327
void move(union_ &&other, size_t i)
Definition: variant.h:232
typename get_type_at< I - 1, Choices... >::type type
Definition: variant.h:61
void reset(size_t i)
Definition: variant.h:256
void emplace(U &&... args)
Definition: variant.h:157
variant_alternative_t< I, variant > & emplace(Args &&... args)
Definition: variant.h:354
variant(const variant &other)
Definition: variant.h:315
size_t index() const
Definition: variant.h:340
typename std::add_lvalue_reference< typename std::add_const< typename get_type_at< I, Choices... >::type >::type >::type get_const_reference_at_t
Definition: variant.h:74
typename impl::get_type_at< I, Choices... >::type type
Definition: variant.h:284
typename get_type_at< I, Choices... >::type get_type_at_t
Definition: variant.h:69
void copy(const union_ &other, size_t i)
Definition: variant.h:127
void emplace(U &&... args)
Definition: variant.h:251
friend class variant
Definition: variant.h:363
bool holds_alternative(const variant< Choices... > &variant)
Definition: variant.h:449
typename variant_alternative< I, Variant >::type variant_alternative_t
Definition: variant.h:288