Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #ifndef LINB_ANY_HPP
00017 #define LINB_ANY_HPP
00018 #pragma once
00019 #include <typeinfo>
00020 #include <type_traits>
00021 #include <stdexcept>
00022
00023 namespace linb
00024 {
00025 class bad_any_cast : public std::bad_cast
00026 {
00027 public:
00028 const char* what() const noexcept override
00029 {
00030 return "bad any cast";
00031 }
00032 };
00033
00034 class any final
00035 {
00036 public:
00038 any() : vtable(nullptr)
00039 {
00040 }
00041
00043 any(const any& rhs) : vtable(rhs.vtable)
00044 {
00045 if (!rhs.empty())
00046 {
00047 rhs.vtable->copy(rhs.storage, this->storage);
00048 }
00049 }
00050
00053 any(any&& rhs) noexcept : vtable(rhs.vtable)
00054 {
00055 if (!rhs.empty())
00056 {
00057 rhs.vtable->move(rhs.storage, this->storage);
00058 rhs.vtable = nullptr;
00059 }
00060 }
00061
00063 ~any()
00064 {
00065 this->clear();
00066 }
00067
00072 template <typename ValueType, typename = typename std::enable_if<!std::is_same<
00073 typename std::decay<ValueType>::type, any>::value>::type>
00074 any(ValueType&& value)
00075 {
00076 static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
00077 "T shall satisfy the CopyConstructible requirements.");
00078 this->construct(std::forward<ValueType>(value));
00079 }
00080
00082 any& operator=(const any& rhs)
00083 {
00084 any(rhs).swap(*this);
00085 return *this;
00086 }
00087
00092 any& operator=(any&& rhs) noexcept
00093 {
00094 any(std::move(rhs)).swap(*this);
00095 return *this;
00096 }
00097
00102 template <typename ValueType, typename = typename std::enable_if<!std::is_same<
00103 typename std::decay<ValueType>::type, any>::value>::type>
00104 any& operator=(ValueType&& value)
00105 {
00106 static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
00107 "T shall satisfy the CopyConstructible requirements.");
00108 any(std::forward<ValueType>(value)).swap(*this);
00109 return *this;
00110 }
00111
00113 void clear() noexcept
00114 {
00115 if (!empty())
00116 {
00117 this->vtable->destroy(storage);
00118 this->vtable = nullptr;
00119 }
00120 }
00121
00123 bool empty() const noexcept
00124 {
00125 return this->vtable == nullptr;
00126 }
00127
00129 const std::type_info& type() const noexcept
00130 {
00131 return empty() ? typeid(void) : this->vtable->type();
00132 }
00133
00135 void swap(any& rhs) noexcept
00136 {
00137 if (this->vtable != rhs.vtable)
00138 {
00139 any tmp(std::move(rhs));
00140
00141
00142 rhs.vtable = this->vtable;
00143 if (this->vtable != nullptr)
00144 {
00145 this->vtable->move(this->storage, rhs.storage);
00146
00147 }
00148
00149
00150 this->vtable = tmp.vtable;
00151 if (tmp.vtable != nullptr)
00152 {
00153 tmp.vtable->move(tmp.storage, this->storage);
00154 tmp.vtable = nullptr;
00155 }
00156 }
00157 else
00158 {
00159 if (this->vtable != nullptr)
00160 this->vtable->swap(this->storage, rhs.storage);
00161 }
00162 }
00163
00164 private:
00165 union storage_union
00166 {
00167 using stack_storage_t =
00168 typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
00169
00170 void* dynamic;
00171 stack_storage_t stack;
00172 };
00173
00175 struct vtable_type
00176 {
00177
00178
00179
00181 const std::type_info& (*type)() noexcept;
00182
00185 void (*destroy)(storage_union&) noexcept;
00186
00189 void (*copy)(const storage_union& src, storage_union& dest);
00190
00193 void (*move)(storage_union& src, storage_union& dest) noexcept;
00194
00196 void (*swap)(storage_union& lhs, storage_union& rhs) noexcept;
00197 };
00198
00200 template <typename T>
00201 struct vtable_dynamic
00202 {
00203 static const std::type_info& type() noexcept
00204 {
00205 return typeid(T);
00206 }
00207
00208 static void destroy(storage_union& storage) noexcept
00209 {
00210
00211 delete reinterpret_cast<T*>(storage.dynamic);
00212 }
00213
00214 static void copy(const storage_union& src, storage_union& dest)
00215 {
00216 dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
00217 }
00218
00219 static void move(storage_union& src, storage_union& dest) noexcept
00220 {
00221 dest.dynamic = src.dynamic;
00222 src.dynamic = nullptr;
00223 }
00224
00225 static void swap(storage_union& lhs, storage_union& rhs) noexcept
00226 {
00227
00228 std::swap(lhs.dynamic, rhs.dynamic);
00229 }
00230 };
00231
00233 template <typename T>
00234 struct vtable_stack
00235 {
00236 static const std::type_info& type() noexcept
00237 {
00238 return typeid(T);
00239 }
00240
00241 static void destroy(storage_union& storage) noexcept
00242 {
00243 reinterpret_cast<T*>(&storage.stack)->~T();
00244 }
00245
00246 static void copy(const storage_union& src, storage_union& dest)
00247 {
00248 new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
00249 }
00250
00251 static void move(storage_union& src, storage_union& dest) noexcept
00252 {
00253
00254
00255 new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
00256 destroy(src);
00257 }
00258
00259 static void swap(storage_union& lhs, storage_union& rhs) noexcept
00260 {
00261 storage_union tmp_storage;
00262 move(rhs, tmp_storage);
00263 move(lhs, rhs);
00264 move(tmp_storage, lhs);
00265 }
00266 };
00267
00269 template <typename T>
00270 struct requires_allocation
00271 : std::integral_constant<
00272 bool,
00273 !(std::is_nothrow_move_constructible<T>::value
00274 && sizeof(T) <= sizeof(storage_union::stack) &&
00275 std::alignment_of<T>::value <=
00276 std::alignment_of<storage_union::stack_storage_t>::value)>
00277 {
00278 };
00279
00281 template <typename T>
00282 static vtable_type* vtable_for_type()
00283 {
00284 using VTableType = typename std::conditional<requires_allocation<T>::value,
00285 vtable_dynamic<T>, vtable_stack<T>>::type;
00286 static vtable_type table = {
00287 VTableType::type, VTableType::destroy, VTableType::copy,
00288 VTableType::move, VTableType::swap,
00289 };
00290 return &table;
00291 }
00292
00293 protected:
00294 template <typename T>
00295 friend const T* any_cast(const any* operand) noexcept;
00296 template <typename T>
00297 friend T* any_cast(any* operand) noexcept;
00298
00300 bool is_typed(const std::type_info& t) const
00301 {
00302 return is_same(this->type(), t);
00303 }
00304
00311 static bool is_same(const std::type_info& a, const std::type_info& b)
00312 {
00313 #ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
00314 return &a == &b;
00315 #else
00316 return a == b;
00317 #endif
00318 }
00319
00321 template <typename T>
00322 const T* cast() const noexcept
00323 {
00324 return requires_allocation<typename std::decay<T>::type>::value ?
00325 reinterpret_cast<const T*>(storage.dynamic) :
00326 reinterpret_cast<const T*>(&storage.stack);
00327 }
00328
00330 template <typename T>
00331 T* cast() noexcept
00332 {
00333 return requires_allocation<typename std::decay<T>::type>::value ?
00334 reinterpret_cast<T*>(storage.dynamic) :
00335 reinterpret_cast<T*>(&storage.stack);
00336 }
00337
00338 private:
00339 storage_union storage;
00340 vtable_type* vtable;
00341
00342 template <typename ValueType, typename T>
00343 typename std::enable_if<requires_allocation<T>::value>::type do_construct(ValueType&& value)
00344 {
00345 storage.dynamic = new T(std::forward<ValueType>(value));
00346 }
00347
00348 template <typename ValueType, typename T>
00349 typename std::enable_if<!requires_allocation<T>::value>::type do_construct(ValueType&& value)
00350 {
00351 new (&storage.stack) T(std::forward<ValueType>(value));
00352 }
00353
00356 template <typename ValueType>
00357 void construct(ValueType&& value)
00358 {
00359 using T = typename std::decay<ValueType>::type;
00360
00361 this->vtable = vtable_for_type<T>();
00362
00363 do_construct<ValueType, T>(std::forward<ValueType>(value));
00364 }
00365 };
00366
00367 namespace detail
00368 {
00369 template <typename ValueType>
00370 inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p,
00371 std::true_type)
00372 {
00373 return std::move(*p);
00374 }
00375
00376 template <typename ValueType>
00377 inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p,
00378 std::false_type)
00379 {
00380 return *p;
00381 }
00382 }
00383
00385 template <typename ValueType>
00386 inline ValueType any_cast(const any& operand)
00387 {
00388 auto p =
00389 any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(
00390 &operand);
00391 if (p == nullptr)
00392 throw bad_any_cast();
00393 return *p;
00394 }
00395
00397 template <typename ValueType>
00398 inline ValueType any_cast(any& operand)
00399 {
00400 auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
00401 if (p == nullptr)
00402 throw bad_any_cast();
00403 return *p;
00404 }
00405
00415 template <typename ValueType>
00416 inline ValueType any_cast(any&& operand)
00417 {
00418 #ifdef ANY_IMPL_ANY_CAST_MOVEABLE
00419
00420 using can_move = std::integral_constant<bool, std::is_move_constructible<ValueType>::value &&
00421 !std::is_lvalue_reference<ValueType>::value>;
00422 #else
00423 using can_move = std::false_type;
00424 #endif
00425
00426 auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
00427 if (p == nullptr)
00428 throw bad_any_cast();
00429 return detail::any_cast_move_if_true<ValueType>(p, can_move());
00430 }
00431
00434 template <typename T>
00435 inline const T* any_cast(const any* operand) noexcept
00436 {
00437 if (operand == nullptr || !operand->is_typed(typeid(T)))
00438 return nullptr;
00439 else
00440 return operand->cast<T>();
00441 }
00442
00445 template <typename T>
00446 inline T* any_cast(any* operand) noexcept
00447 {
00448 if (operand == nullptr || !operand->is_typed(typeid(T)))
00449 return nullptr;
00450 else
00451 return operand->cast<T>();
00452 }
00453 }
00454
00455 namespace std
00456 {
00457 inline void swap(linb::any& lhs, linb::any& rhs) noexcept
00458 {
00459 lhs.swap(rhs);
00460 }
00461 }
00462
00463 #endif