uvcc
libuv C++ bindings
utility.hpp
1 
2 #ifndef UVCC_UTILITY__HPP
3 #define UVCC_UTILITY__HPP
4 
5 #include "uvcc/debug.hpp"
6 
7 #include <cstddef> // nullptr_t
8 #include <type_traits> // is_void is_convertible enable_if_t decay common_type aligned_storage
9 #include <atomic> // atomic memory_order_* atomic_flag ATOMIC_FLAG_INIT
10 #include <utility> // forward() move()
11 #include <memory> // addressof()
12 #include <stdexcept> // runtime_error
13 #include <typeinfo> // type_info
14 
15 
16 namespace uv
17 {
18 /*! \defgroup doxy_group__utility Utility structures and definitions
19  \brief The utility definitions to be used throughout the library. */
20 //! \{
21 
22 
23 /*! \brief The analogue of the `std::default_delete`.
24  \details It also provides the static member function `void Delete(void*)`
25  holding the proper delete operator for the type `_T_`. The client code then
26  can store a pointer to this function being alike a pointer to the virtual
27  delete operator for implementing the run-time data polymorphism.
28  */
29 template< typename _T_ > struct default_delete
30 {
31  using value_type = typename std::decay< _T_ >::type;
32 
33  constexpr default_delete() noexcept = default;
34 
35  // we can delete an object through the polymorphic pointer to its base class
36  template<
37  typename _U_,
38  typename = std::enable_if_t< std::is_convertible< _U_*, _T_* >::value >
39  > default_delete(const default_delete< _U_ >&) noexcept {}
40 
41  void operator ()(value_type *_) const { Delete(_); }
42 
43  // not for use for polymorphic deleting without the operator () above
44  static void Delete(void *_)
45  {
46  static_assert(!std::is_void< value_type >::value, "void type");
47  static_assert(sizeof(value_type) > 0, "incomplete type");
48  delete static_cast< value_type* >(_);
49  //uvcc_debug_function_return("object [0x%08tX]", (ptrdiff_t)_);
50  }
51 };
52 
53 
54 /*! \brief The analogue of the `uv::default_delete` but for the type destructor only.
55  \details Provides the static member function `void Destroy(void*)` holding
56  the proper destructor call for the type `_T_`. The client code then can store
57  a pointer to this function being alike a pointer to the virtual destructor
58  for implementing the run-time data polymorphism.
59  */
60 template< typename _T_ > struct default_destroy
61 {
62  using value_type = typename std::decay< _T_ >::type;
63 
64  constexpr default_destroy() noexcept = default;
65 
66  // we can destroy an object through the polymorphic pointer to its base class
67  template<
68  typename _U_,
69  typename = std::enable_if_t< std::is_convertible< _U_*, _T_* >::value >
70  > default_destroy(const default_destroy< _U_ >&) noexcept {}
71 
72  void operator ()(value_type *_) const { Destroy(_); }
73 
74  // not for use for polymorphic destroying without the operator () above
75  static void Destroy(void *_)
76  {
77  static_assert(!std::is_void< value_type >::value, "void type");
78  static_assert(sizeof(value_type) > 0, "incomplete type");
79  static_cast< value_type* >(_)->~value_type();
80  //uvcc_debug_function_return("object [0x%08tX]", (ptrdiff_t)_);
81  }
82 };
83 
84 
85 
86 /*! \defgroup doxy_group__variadic Dealing with type lists and parameter packs */
87 //! \{
88 
89 /*! \brief Checks if a type `_T_` belongs to a type list `_Ts_`.
90  \details Provides the constexpr `value` that is equal to the index of the given type `_T_`
91  in the type list `_Ts_` starting from **1** up to `sizeof...(_Ts_)` or **0** otherwise. */
92 template< typename _T_, typename... _Ts_ > struct is_one_of;
93 //! \cond
94 template< typename _T_ > struct is_one_of< _T_ >
95 {
96  constexpr static const std::size_t value = 0;
97 };
98 template< typename _T_, typename... _Ts_ > struct is_one_of< _T_, _T_, _Ts_... >
99 {
100  constexpr static const std::size_t value = 1;
101 };
102 template< typename _T_, typename _U_, typename... _Ts_ > struct is_one_of< _T_, _U_, _Ts_... >
103 {
104 private:
105  constexpr static const std::size_t value_ = is_one_of< _T_, _Ts_... >::value;
106 public:
107  constexpr static const std::size_t value = value_ ? value_+1 : 0;
108 };
109 //! \endcond
110 
111 /*! \brief Checks if a type `_T_` is convertible to one of the types from the type list `_Ts_`.
112  \details Provides the constexpr `value` that is equal to the index of the type from the type
113  list `_Ts_` which the given type `_T_` can be converted to by using implicit conversion.
114  The index starts from **1** up to `sizeof...(_Ts_)` or is **0** otherwise. */
115 template< typename _T_, typename... _Ts_ > struct is_convertible_to_one_of;
116 //! \cond
117 template< typename _T_ > struct is_convertible_to_one_of< _T_ >
118 {
119  constexpr static const std::size_t value = 0;
120 };
121 template< typename _T_, typename _U_, typename... _Ts_ > struct is_convertible_to_one_of< _T_, _U_, _Ts_... >
122 {
123 private:
124  constexpr static const std::size_t value_ = is_convertible_to_one_of< _T_, _Ts_... >::value;
125 public:
126  constexpr static const std::size_t value = std::is_convertible< _T_, _U_ >::value ? 1 : (value_ ? value_+1 : 0);
127 };
128 //! \endcond
129 
130 /*! \brief Provides a typedef member `type` equal to the type from the type list `_Ts_` at index `_index_`.
131  The index should start from **1** up to `sizeof...(_Ts_)`. */
132 template< std::size_t _index_, typename... _Ts_ > struct type_at;
133 //! \cond
134 template< typename... _Ts_ > struct type_at< 0, _Ts_... > {}; // index in type lists starts from 1 rather than from 0
135 template< std::size_t _index_ > struct type_at< _index_ > {}; // index is behind the length of the type list
136 template< typename _T_, typename... _Ts_ > struct type_at< 1, _T_, _Ts_...> { using type = _T_; };
137 template< std::size_t _index_, typename _T_, typename... _Ts_ > struct type_at< _index_, _T_, _Ts_...>
138 { using type = typename type_at< _index_-1, _Ts_... >::type; };
139 //! \endcond
140 
141 
142 //! \cond
143 template< typename _T_ >
144 constexpr
145 inline
146 auto greatest(_T_&& _v) -> decltype(_v) // return _T_&& or _T_&
147 {
148  return std::forward< _T_ >(_v);
149 }
150 //! \endcond
151 /*! \brief Intended to be used instead of `constexpr T max(std::initializer_list<T>)`...
152  \details ...if the latter is not defined of being `constexpr` in the current STL version and therefore
153  cannot be employed at compile-time. Does not require the arguments to be of the same type and using
154  the `std::initializer_list` curly braces when there are more than two arguments.
155 
156  While promoting to a common resulting type a temporary local value can be implicitly created by the compiler,
157  therefore the function is not able to safely return a reference type result, or else the
158  `'warning: returning reference to temporary'` is generated in some use cases. */
159 template< typename _T_, typename... _Ts_ >
160 constexpr
161 inline
162 auto greatest(_T_&& _v, _Ts_&&... _vs) -> std::common_type_t< decltype(_v), decltype(_vs)... > // thouhg it is anyway a decayed type
163 {
164  return _v < greatest(std::forward< _Ts_ >(_vs)...) ? greatest(std::forward< _Ts_ >(_vs)...) : _v;
165 }
166 
167 //! \cond
168 template< typename _T_ >
169 constexpr
170 inline
171 auto lowest(_T_&& _v) -> decltype(_v) // return _T_&& or _T_&
172 {
173  return std::forward< _T_ >(_v);
174 }
175 //! \endcond
176 /*! \brief The counterpart of `greatest()` */
177 template< typename _T_, typename... _Ts_ >
178 constexpr
179 inline
180 auto lowest(_T_&& _v, _Ts_&&... _vs) -> std::common_type_t< decltype(_v), decltype(_vs)... > // though it is anyway a decayed type
181 {
182  return lowest(std::forward< _Ts_ >(_vs)...) < _v ? lowest(std::forward< _Ts_ >(_vs)...) : _v;
183 }
184 
185 
186 //! \cond
187 template< typename _T_ >
188 constexpr
189 auto sum(_T_&& _v) -> decltype(_v) // return _T_&& or _T_&
190 {
191  return std::forward< _T_ >(_v);
192 }
193 //! \endcond
194 /*! \brief Primarily intended for summation of values from parameter packs if fold expressions are not supported. */
195 template< typename _T_, typename... _Ts_ >
196 constexpr
197 auto sum(_T_&& _v, _Ts_&&... _vs) -> std::common_type_t< _T_, _Ts_... >
198 {
199  return _v + sum(std::forward< _Ts_ >(_vs)...);
200 }
201 
202 // \}
203 
204 
205 
206 /*! \brief A reference counter with atomic increment/decrement.
207  \details The default constructor creates a new `ref_count` object with the count value = **1**.
208 
209  Atomic operations on the `ref_count` object provide the following memory ordering semantics:
210  Member function | Memory ordering
211  :----------------|:---------------:
212  `get_value()` | acquire
213  `set_value()` | release
214  `inc()` | relaxed
215  `dec()` | release
216 
217  Thus the client code can use `get_value()` function to check the current number of the variables
218  referencing a counted object and be sure to be _synchronized-with_ the last `dec()` operation
219  (i.e. to see all the results of non-atomic memory changes _happened-before_ the last `dec()`
220  operation which should normally occurs when one of the variable of the counted object is destroyed
221  on going out of its scope). Since only `inc()`/`dec()` operations are supposed to be normally used
222  for changing the count value, it is considered to be an unusual case where using of `set_value()`
223  function can be necessary.
224 
225  `inc()` throws `std::runtime_error` if the current value to be incremented is **0** as this
226  circumstance is considered as a variable of the counted object is being constructed/copied
227  from a reference just becoming a dangling one.
228 */
230 {
231 public: /*types*/
232  using type = long;
233 
234 private: /*data*/
235  std::atomic< type > count;
236 
237 public: /*constructors*/
238  ~ref_count() = default;
239 
240  ref_count() noexcept : count(1) {}
241 
242  ref_count(const ref_count&) = delete;
243  ref_count& operator =(const ref_count&) = delete;
244 
245  ref_count(ref_count&&) = delete;
246  ref_count& operator =(ref_count&&) = delete;
247 
248 public: /*interface*/
249  type get_value() const noexcept { return count.load(std::memory_order_acquire); }
250  void set_value(type _count) noexcept { count.store(_count, std::memory_order_release); }
251 
252  type inc()
253  {
254  auto c = count.load(std::memory_order_relaxed);
255  do
256  if (c == 0) // perhaps constructing/copying from a reference just becoming a dangling one
257  throw std::runtime_error(__PRETTY_FUNCTION__);
258  while (!count.compare_exchange_weak(c, c+1, std::memory_order_relaxed, std::memory_order_relaxed));
259  return c+1;
260  }
261 
262  type dec() noexcept
263  {
264  auto c = count.fetch_sub(1, std::memory_order_release);
265  return c-1;
266  }
267 };
268 
269 
270 
271 /*! \brief The type of the `adopt_ref` constant. */
272 struct adopt_ref_t { constexpr adopt_ref_t() = default; };
273 /*! \brief The tag to be used to prevent `ref_guard` constructor from increasing reference count of the protected object. */
274 constexpr const adopt_ref_t adopt_ref;
275 
276 /*! \brief A scoped reference counting guard.
277  \details Similar to `std::lock_guard` but it is for reference counting.
278  The target object should provide `ref()` and `unref()` public member functions. */
279 template< class _T_ >
281 {
282 public: /*types*/
283  using target_type = _T_;
284 
285 private: /*data*/
286  target_type &t;
287 
288 public: /*constructors*/
289  ~ref_guard() { t.unref(); }
290 
291  explicit ref_guard(target_type &_t) : t(_t) { t.ref(); }
292  explicit ref_guard(target_type &_t, const adopt_ref_t) : t(_t) {} /*!< \brief The constructor to be used with `uv::adopt_ref` tag. */
293 
294  ref_guard(const ref_guard&) = delete;
295  ref_guard& operator =(const ref_guard&) = delete;
296 
297  ref_guard(ref_guard&&) = delete;
298  ref_guard& operator =(ref_guard&&) = delete;
299 };
300 
301 
302 
303 /*! \brief A simple spinlock mutex built around `std::atomic_flag`. */
304 class spinlock
305 {
306 private: /*data*/
307  std::atomic_flag flag;
308 
309 public: /*constructors*/
310  ~spinlock() = default;
311  spinlock() : flag(ATOMIC_FLAG_INIT) {}
312 
313  spinlock(const spinlock&) = delete;
314  spinlock& operator =(const spinlock&) = delete;
315 
316  spinlock(spinlock&&) = delete;
317  spinlock& operator =(spinlock&&) = delete;
318 
319 public: /*interface*/
320  void lock(std::memory_order _o = std::memory_order_acquire) noexcept
321  {
322  while (flag.test_and_set(_o));
323  }
324 
325  void unlock(std::memory_order _o = std::memory_order_release) noexcept
326  {
327  flag.clear(_o);
328  }
329 };
330 
331 
332 
333 /*! \brief A wrapper around `std::aligned_storage< _LEN_, _ALIGN_ >::type` that simplifies
334  initializing the provided storage space, getting from it, setting it to, and automatic destroying it from
335  objects of any type fitting to the given size and alignment requirements.
336  \note All the member functions creating a new value in the storage from their arguments
337  use the _curly brace initialization_. */
338 template< std::size_t _LEN_, std::size_t _ALIGN_ >
340 {
341 private: /*data*/
342  const std::type_info *type_tag = nullptr;
343  void (*Destroy)(void*) = nullptr;
344  typename std::aligned_storage< _LEN_, _ALIGN_ >::type storage;
345 
346 public: /*constructors*/
347  ~aligned_storage() { destroy(); }
348  aligned_storage() = default; /*!< \brief Create an uninitialized storage. */
349 
350  aligned_storage(const aligned_storage&) = delete;
351  aligned_storage& operator =(const aligned_storage&) = delete;
352 
353  aligned_storage(aligned_storage&&) = delete;
354  aligned_storage& operator =(aligned_storage&&) = delete;
355 
356  /*! \brief Create a storage space with a copy-initialized value from the specified one. */
357  template< typename _T_ > aligned_storage(const _T_ &_value)
358  {
359  using type = typename std::decay< _T_ >::type;
360  static_assert(sizeof(type) <= _LEN_, "insufficient storage size");
361  static_assert(alignof(type) <= _ALIGN_, "not adjusted storage alignment");
362 
363  new(static_cast< void* >(&storage)) type{ _value };
364  Destroy = default_destroy< type >::Destroy;
365  type_tag = &typeid(type);
366  }
367  /*! \brief Create a storage space with a move-initialized value from the specified value. */
368  template< typename _T_ > aligned_storage(_T_ &&_value)
369  {
370  using type = typename std::decay< _T_ >::type;
371  static_assert(sizeof(type) <= _LEN_, "insufficient storage size");
372  static_assert(alignof(type) <= _ALIGN_, "not adjusted storage alignment");
373 
374  new(static_cast< void* >(&storage)) type{ std::move(_value) };
375  Destroy = default_destroy< type >::Destroy;
376  type_tag = &typeid(type);
377  }
378 
379 private: /*functions*/
380  void destroy() noexcept
381  {
382  if (Destroy) Destroy(&storage);
383  Destroy = nullptr;
384  type_tag = nullptr;
385  }
386 
387 public: /*interface*/
388  /*! \name Functions to reinitialize the storage space:
389  \note The previously stored value is destroyed. */
390  //! \{
391  /*! \brief Reinitialize the storage space with a default value of the specified type. */
392  template< typename _T_ > void reset()
393  {
394  using type = typename std::decay< _T_ >::type;
395  static_assert(sizeof(type) <= _LEN_, "insufficient storage size");
396  static_assert(alignof(type) <= _ALIGN_, "not adjusted storage alignment");
397 
398  destroy();
399 
400  new(static_cast< void* >(&storage)) type{};
401  Destroy = default_destroy< type >::Destroy;
402  type_tag = &typeid(type);
403  }
404  /*! \brief Ditto but the value is created from the arguments forwarded to the type constructor. */
405  template< typename _T_, typename... _Args_ > void reset(_Args_&&... _args)
406  {
407  using type = typename std::decay< _T_ >::type;
408  static_assert(sizeof(type) <= _LEN_, "insufficient storage size");
409  static_assert(alignof(type) <= _ALIGN_, "not adjusted storage alignment");
410 
411  destroy();
412 
413  new(static_cast< void* >(&storage)) type{ std::forward< _Args_ >(_args)... };
414  Destroy = default_destroy< type >::Destroy;
415  type_tag = &typeid(type);
416  }
417  /*! \brief Ditto but the value is copy-created from the specified argument. */
418  template< typename _T_ > void reset(const _T_ &_value)
419  {
420  using type = typename std::decay< _T_ >::type;
421  static_assert(sizeof(type) <= _LEN_, "insufficient storage size");
422  static_assert(alignof(type) <= _ALIGN_, "not adjusted storage alignment");
423 
424  if (reinterpret_cast< type* >(&storage) == std::addressof(static_cast< const type& >(_value))) return; // cast _T_& to type&
425 
426  destroy();
427 
428  new(static_cast< void* >(&storage)) type{ _value };
429  Destroy = default_destroy< type >::Destroy;
430  type_tag = &typeid(type);
431  }
432  /*! \brief Ditto but the value is move-created from the specified argument. */
433  template< typename _T_ > void reset(_T_ &&_value)
434  {
435  using type = typename std::decay< _T_ >::type;
436  static_assert(sizeof(type) <= _LEN_, "insufficient storage size");
437  static_assert(alignof(type) <= _ALIGN_, "not adjusted storage alignment");
438 
439  if (reinterpret_cast< type* >(&storage) == std::addressof(static_cast< type& >(_value))) return; // cast _T_& to type&
440 
441  destroy();
442 
443  new(static_cast< void* >(&storage)) type{ std::move(_value) };
444  Destroy = default_destroy< type >::Destroy;
445  type_tag = &typeid(type);
446  }
447  //! \}
448 
449  /*! \name Functions to get the value that this storage space is holding: */
450  //! \{
451  template< typename _T_ >
452  const typename std::decay< _T_ >::type& get() const noexcept
453  { return *reinterpret_cast< const typename std::decay< _T_ >::type* >(&storage); }
454  template< typename _T_ >
455  typename std::decay< _T_ >::type& get() noexcept
456  { return *reinterpret_cast< typename std::decay< _T_ >::type* >(&storage); }
457  //! \}
458 
459  /*! \brief The type tag of the stored value.
460  \details It's just a pointer referring to the static global constant object returned by the
461  `typeid()` operator for the type of the value currently stored in this variable.
462  `nullptr` is returned if the storage space is not initialized and is not holding any value. */
463  const std::type_info* tag() const noexcept { return type_tag; }
464 
465 public: /*conversion operators*/
466  explicit operator bool() const noexcept { return (tag() != nullptr); } /*!< \brief Equivalent to `(tag() != nullptr)`. */
467 };
468 
469 
470 
471 /*! \brief A wrapper providing the feature of being a _standard layout type_ for the given type `_T_`.
472  \note All the member functions creating a new value in the storage from their arguments
473  use the _curly brace initialization_. */
474 template< typename _T_ >
476 {
477 public: /*types*/
478  using value_type = typename std::decay< _T_ >::type;
479  using storage_type = typename std::aligned_storage< sizeof(value_type), alignof(value_type) >::type;
480 
481 private: /*data*/
482  storage_type storage;
483 
484 public: /*constructors*/
485  ~type_storage() { value().~value_type(); }
486  type_storage() { new(static_cast< void* >(&storage)) value_type{}; }
487 
488  type_storage(const type_storage&) = delete;
489  type_storage& operator =(const type_storage&) = delete;
490 
491  type_storage(type_storage&&) = delete;
492  type_storage& operator =(type_storage&&) = delete;
493 
494  template< typename... _Args_ > type_storage(_Args_&&... _args)
495  {
496  new(static_cast< void* >(&storage)) value_type{ std::forward< _Args_ >(_args)... };
497  }
498  type_storage(const value_type &_value)
499  {
500  new(static_cast< void* >(&storage)) value_type{ _value };
501  }
502  type_storage(value_type &&_value)
503  {
504  new(static_cast< void* >(&storage)) value_type{ std::move(_value) };
505  }
506 
507 public: /*interface*/
508  const value_type& value() const noexcept { return *reinterpret_cast< const value_type* >(&storage); }
509  value_type& value() noexcept { return *reinterpret_cast< value_type* >(&storage); }
510 };
511 
512 
513 
514 /*! \brief A mimic of STL's `std::aligned_union` missed in gcc 4.9.2. */
515 template< typename... _Ts_ >
516 using aligned_union = std::aligned_storage< greatest(sizeof(_Ts_)...), greatest(alignof(_Ts_)...) >;
517 
518 
519 /*! \brief A tagged union that provide a storage space being a _standard layout type_
520  suited for all its type variants specified in the type list `_Ts_`.
521  \details Only values from the specified set of types `_Ts_` are created and stored in the union
522  even though the values of any types that are implicitly convertible to one of the types
523  from `_Ts_` list are acceptable for copy- or move-initialization `reset()` functions.
524  \note All the member functions creating a new value in the union from their arguments
525  use the _curly brace initialization_. */
526 template< typename... _Ts_ >
528 {
529 public: /*types*/
530  using storage_type = typename aligned_union< _Ts_... >::type;
531 
532 private: /*data*/
533  const std::type_info *type_tag = nullptr;
534  void (*Destroy)(void*) = nullptr;
535  storage_type storage;
536 
537 public: /*constructors*/
538  ~union_storage() { destroy(); }
539  union_storage() = default; /*!< \brief Create an uninitialized union storage. */
540 
541  union_storage(const union_storage&) = delete;
542  union_storage& operator =(const union_storage&) = delete;
543 
544  union_storage(union_storage&&) = delete;
545  union_storage& operator =(union_storage&&) = delete;
546 
547  /*! \brief Create a union with a copy-initialized value from the specified one. */
548  template< typename _T_, typename = std::enable_if_t< is_convertible_to_one_of< _T_, _Ts_... >::value > >
549  union_storage(const _T_ &_value)
550  {
551  constexpr const std::size_t tag = is_convertible_to_one_of< _T_, _Ts_... >::value;
552  using type = typename std::decay< typename type_at< tag, _Ts_... >::type >::type;
553 
554  new(static_cast< void* >(&storage)) type{ _value };
555  Destroy = default_destroy< type >::Destroy;
556  type_tag = &typeid(type);
557  }
558  /*! \brief Create a union with a move-initialized value from the specified value. */
559  template< typename _T_, typename = std::enable_if_t< is_convertible_to_one_of< _T_, _Ts_... >::value > >
560  union_storage(_T_ &&_value)
561  {
562  constexpr const std::size_t tag = is_convertible_to_one_of< _T_, _Ts_... >::value;
563  using type = typename std::decay< typename type_at< tag, _Ts_... >::type >::type;
564 
565  new(static_cast< void* >(&storage)) type{ std::move(_value) };
566  Destroy = default_destroy< type >::Destroy;
567  type_tag = &typeid(type);
568  }
569 
570 private: /*functions*/
571  void destroy() noexcept
572  {
573  if (Destroy) Destroy(&storage);
574  Destroy = nullptr;
575  type_tag = nullptr;
576  }
577 
578 public: /*interface*/
579  /*! \name Functions to reinitialize the union storage:
580  \note The previously stored value is destroyed. */
581  //! \{
582  /*! \brief Reinitialize the union storage with a default value of the one of the type from `_Ts_` list
583  that the specified type `_T_` is convertible to. */
584  template< typename _T_ >
586  {
587  constexpr const std::size_t tag = is_convertible_to_one_of< _T_, _Ts_... >::value;
588  using type = typename std::decay< typename type_at< tag, _Ts_... >::type >::type;
589 
590  destroy();
591 
592  new(static_cast< void* >(&storage)) type{};
594  type_tag = &typeid(type);
595  }
596  /*! \brief Ditto but the value is created from the arguments forwarded to the type constructor. */
597  template< typename _T_, typename... _Args_ >
599  {
600  constexpr const std::size_t tag = is_convertible_to_one_of< _T_, _Ts_... >::value;
601  using type = typename std::decay< typename type_at< tag, _Ts_... >::type >::type;
602 
603  destroy();
604 
605  new(static_cast< void* >(&storage)) type{ std::forward< _Args_ >(_args)... };
607  type_tag = &typeid(type);
608  }
609  /*! \brief Ditto but the value is copy-created from the specified argument. */
610  template< typename _T_ >
612  {
613  constexpr const std::size_t tag = is_convertible_to_one_of< _T_, _Ts_... >::value;
614  using type = typename std::decay< typename type_at< tag, _Ts_... >::type >::type;
615 
616  if (reinterpret_cast< type* >(&storage) == std::addressof(static_cast< const type& >(_value))) return; // cast _T_& to type&
617 
618  destroy();
619 
620  new(static_cast< void* >(&storage)) type{ _value };
622  type_tag = &typeid(type);
623  }
624  /*! \brief Ditto but the value is move-created from the specified argument. */
625  template< typename _T_ >
627  {
628  constexpr const std::size_t tag = is_convertible_to_one_of< _T_, _Ts_... >::value;
629  using type = typename std::decay< typename type_at< tag, _Ts_... >::type >::type;
630 
631  if (reinterpret_cast< type* >(&storage) == std::addressof(static_cast< type& >(_value))) return; // cast _T_& to type&
632 
633  destroy();
634 
635  new(static_cast< void* >(&storage)) type{ std::move(_value) };
637  type_tag = &typeid(type);
638  }
639  //! \}
640 
641  /*! \name Functions to get the value stored in the union: */
642  //! \{
643  template< typename _T_, typename = std::enable_if_t< is_one_of< _T_, _Ts_... >::value > >
644  const typename std::decay< _T_ >::type& get() const noexcept
645  { return *reinterpret_cast< const typename std::decay< _T_ >::type* >(&storage); }
646  template< typename _T_, typename = std::enable_if_t< is_one_of< _T_, _Ts_... >::value > >
647  typename std::decay< _T_ >::type& get() noexcept
648  { return *reinterpret_cast< typename std::decay< _T_ >::type* >(&storage); }
649  //! \}
650 
651  /*! \brief The type tag of the stored value.
652  \details It's just a pointer referring to the static global constant object returned by the
653  `typeid()` operator for the type of the value currently stored in this `union_storage` variable.
654  `nullptr` is returned if the union is not initialized and is not storing any value. */
655  const std::type_info* tag() const noexcept { return type_tag; }
656 
657 public: /*conversion operators*/
658  explicit operator bool() const noexcept { return (tag() != nullptr); } /*!< \brief Equivalent to `(tag() != nullptr)`. */
659 };
660 
661 
662 
663 /*! \brief The analogue of `std::unique_ptr` that managed object type is not defined at compile time and can be varied. */
664 class any_ptr
665 {
666 private: /*data*/
667  const std::type_info *type_tag = nullptr;
668  void (*Delete)(void*) = nullptr;
669  void *ptr = nullptr;
670 
671 public: /*constructors*/
672  ~any_ptr() { destroy(); }
673  constexpr any_ptr() = default;
674 
675  any_ptr(const any_ptr&) = delete;
676  any_ptr& operator =(const any_ptr&) = delete;
677 
678  any_ptr(any_ptr &&_that) noexcept : type_tag(_that.type_tag), Delete(_that.Delete), ptr(_that.ptr) { _that.release(); }
679  any_ptr& operator =(any_ptr &&_that)
680  {
681  if (ptr != _that.ptr)
682  {
683  destroy();
684  new (this) any_ptr(std::move(_that));
685  }
686  return *this;
687  }
688 
689  constexpr any_ptr(std::nullptr_t) noexcept {}
690  any_ptr& operator =(std::nullptr_t) { destroy(); return *this; }
691 
692  template< typename _T_ > explicit any_ptr(_T_* &&_ptr) noexcept
693  {
694  ptr = _ptr;
695  Delete = default_delete< _T_ >::Delete;
696  type_tag = &typeid(_T_);
697  _ptr = nullptr;
698  }
699 
700 private: /*functions*/
701  void destroy()
702  {
703  if (Delete) Delete(ptr);
704  Delete = nullptr;
705  type_tag = nullptr;
706  }
707 
708 public: /*intreface*/
709  void* release() noexcept
710  {
711  void *p = ptr;
712  ptr = nullptr; Delete = nullptr; type_tag = nullptr;
713  return p;
714  }
715 
716  void reset(std::nullptr_t _ptr = nullptr) { destroy(); }
717  template< typename _T_ > void reset(_T_* &&_ptr)
718  {
719  if (ptr == _ptr) return;
720  destroy();
721  new (this) any_ptr(std::move(_ptr));
722  }
723 
724  template< typename _T_ = void >
725  const typename std::decay< _T_ >::type* get() const noexcept
726  { return static_cast< const typename std::decay< _T_ >::type* >(ptr); }
727  template< typename _T_ = void >
728  typename std::decay< _T_ >::type* get() noexcept
729  { return static_cast< typename std::decay< _T_ >::type* >(ptr); }
730 
731  const std::type_info* tag() const noexcept { return type_tag; }
732 
733 public: /*conversion operators*/
734  explicit operator bool() const noexcept { return (ptr != nullptr); } /*!< \brief Equivalent to `(get() != nullptr)`. */
735 };
736 
737 
738 //! \}
739 }
740 
741 
742 #endif
Namespace for all uvcc definitions.
Definition: buffer.hpp:17
aligned_storage(_T_ &&_value)
Create a storage space with a move-initialized value from the specified value.
Definition: utility.hpp:368
constexpr const adopt_ref_t adopt_ref
The tag to be used to prevent ref_guard constructor from increasing reference count of the protected ...
Definition: utility.hpp:274
A wrapper providing the feature of being a standard layout type for the given type _T_...
Definition: utility.hpp:475
void reset(_Args_ &&... _args)
Ditto but the value is created from the arguments forwarded to the type constructor.
Definition: utility.hpp:405
A simple spinlock mutex built around std::atomic_flag.
Definition: utility.hpp:304
The analogue of the uv::default_delete but for the type destructor only.
Definition: utility.hpp:60
union_storage()=default
Create an uninitialized union storage.
ref_guard(target_type &_t, const adopt_ref_t)
The constructor to be used with uv::adopt_ref tag.
Definition: utility.hpp:292
constexpr auto sum(_T_ &&_v, _Ts_ &&... _vs) -> std::common_type_t< _T_, _Ts_... >
Primarily intended for summation of values from parameter packs if fold expressions are not supported...
Definition: utility.hpp:197
union_storage(_T_ &&_value)
Create a union with a move-initialized value from the specified value.
Definition: utility.hpp:560
The analogue of the std::default_delete.
Definition: utility.hpp:29
The analogue of std::unique_ptr that managed object type is not defined at compile time and can be va...
Definition: utility.hpp:664
constexpr auto greatest(_T_ &&_v, _Ts_ &&... _vs) -> std::common_type_t< decltype(_v), decltype(_vs)... >
Intended to be used instead of constexpr T max(std::initializer_list<T>)...
Definition: utility.hpp:162
aligned_storage()=default
Create an uninitialized storage.
void reset()
Reinitialize the storage space with a default value of the specified type.
Definition: utility.hpp:392
operator bool() const noexcept
Equivalent to (tag() != nullptr).
Definition: utility.hpp:466
operator bool() const noexcept
Equivalent to (tag() != nullptr).
Definition: utility.hpp:658
A scoped reference counting guard.
Definition: utility.hpp:280
A reference counter with atomic increment/decrement.
Definition: utility.hpp:229
aligned_storage(const _T_ &_value)
Create a storage space with a copy-initialized value from the specified one.
Definition: utility.hpp:357
operator bool() const noexcept
Equivalent to (get() != nullptr).
Definition: utility.hpp:734
union_storage(const _T_ &_value)
Create a union with a copy-initialized value from the specified one.
Definition: utility.hpp:549
void reset(const _T_ &_value)
Ditto but the value is copy-created from the specified argument.
Definition: utility.hpp:418
void reset(_T_ &&_value)
Ditto but the value is move-created from the specified argument.
Definition: utility.hpp:433
A wrapper around std::aligned_storage< _LEN_, _ALIGN_ >::type that simplifies initializing the provid...
Definition: utility.hpp:339
const std::type_info * tag() const noexcept
The type tag of the stored value.
Definition: utility.hpp:463
constexpr auto lowest(_T_ &&_v, _Ts_ &&... _vs) -> std::common_type_t< decltype(_v), decltype(_vs)... >
The counterpart of greatest()
Definition: utility.hpp:180