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
00026 class bad_any_cast : public std::bad_cast
00027 {
00028 public:
00029 const char* what() const noexcept override
00030 {
00031 return "bad any cast";
00032 }
00033 };
00034
00035 class any final
00036 {
00037 public:
00039 any() :
00040 vtable(nullptr)
00041 {
00042 }
00043
00045 any(const any& rhs) :
00046 vtable(rhs.vtable)
00047 {
00048 if(!rhs.empty())
00049 {
00050 rhs.vtable->copy(rhs.storage, this->storage);
00051 }
00052 }
00053
00056 any(any&& rhs) noexcept :
00057 vtable(rhs.vtable)
00058 {
00059 if(!rhs.empty())
00060 {
00061 rhs.vtable->move(rhs.storage, this->storage);
00062 rhs.vtable = nullptr;
00063 }
00064 }
00065
00067 ~any()
00068 {
00069 this->clear();
00070 }
00071
00076 template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
00077 any(ValueType&& value)
00078 {
00079 static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
00080 "T shall satisfy the CopyConstructible requirements.");
00081 this->construct(std::forward<ValueType>(value));
00082 }
00083
00085 any& operator=(const any& rhs)
00086 {
00087 any(rhs).swap(*this);
00088 return *this;
00089 }
00090
00095 any& operator=(any&& rhs) noexcept
00096 {
00097 any(std::move(rhs)).swap(*this);
00098 return *this;
00099 }
00100
00105 template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
00106 any& operator=(ValueType&& value)
00107 {
00108 static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
00109 "T shall satisfy the CopyConstructible requirements.");
00110 any(std::forward<ValueType>(value)).swap(*this);
00111 return *this;
00112 }
00113
00115 void clear() noexcept
00116 {
00117 if(!empty())
00118 {
00119 this->vtable->destroy(storage);
00120 this->vtable = nullptr;
00121 }
00122 }
00123
00125 bool empty() const noexcept
00126 {
00127 return this->vtable == nullptr;
00128 }
00129
00131 const std::type_info& type() const noexcept
00132 {
00133 return empty()? typeid(void) : this->vtable->type();
00134 }
00135
00137 void swap(any& rhs) noexcept
00138 {
00139 if(this->vtable != rhs.vtable)
00140 {
00141 any tmp(std::move(rhs));
00142
00143
00144 rhs.vtable = this->vtable;
00145 if(this->vtable != nullptr)
00146 {
00147 this->vtable->move(this->storage, rhs.storage);
00148
00149 }
00150
00151
00152 this->vtable = tmp.vtable;
00153 if(tmp.vtable != nullptr)
00154 {
00155 tmp.vtable->move(tmp.storage, this->storage);
00156 tmp.vtable = nullptr;
00157 }
00158 }
00159 else
00160 {
00161 if(this->vtable != nullptr)
00162 this->vtable->swap(this->storage, rhs.storage);
00163 }
00164 }
00165
00166 private:
00167
00168 union storage_union
00169 {
00170 using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
00171
00172 void* dynamic;
00173 stack_storage_t stack;
00174 };
00175
00177 struct vtable_type
00178 {
00179
00180
00181
00183 const std::type_info& (*type)() noexcept;
00184
00187 void(*destroy)(storage_union&) noexcept;
00188
00191 void(*copy)(const storage_union& src, storage_union& dest);
00192
00195 void(*move)(storage_union& src, storage_union& dest) noexcept;
00196
00198 void(*swap)(storage_union& lhs, storage_union& rhs) noexcept;
00199 };
00200
00202 template<typename T>
00203 struct vtable_dynamic
00204 {
00205 static const std::type_info& type() noexcept
00206 {
00207 return typeid(T);
00208 }
00209
00210 static void destroy(storage_union& storage) noexcept
00211 {
00212
00213 delete reinterpret_cast<T*>(storage.dynamic);
00214 }
00215
00216 static void copy(const storage_union& src, storage_union& dest)
00217 {
00218 dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
00219 }
00220
00221 static void move(storage_union& src, storage_union& dest) noexcept
00222 {
00223 dest.dynamic = src.dynamic;
00224 src.dynamic = nullptr;
00225 }
00226
00227 static void swap(storage_union& lhs, storage_union& rhs) noexcept
00228 {
00229
00230 std::swap(lhs.dynamic, rhs.dynamic);
00231 }
00232 };
00233
00235 template<typename T>
00236 struct vtable_stack
00237 {
00238 static const std::type_info& type() noexcept
00239 {
00240 return typeid(T);
00241 }
00242
00243 static void destroy(storage_union& storage) noexcept
00244 {
00245 reinterpret_cast<T*>(&storage.stack)->~T();
00246 }
00247
00248 static void copy(const storage_union& src, storage_union& dest)
00249 {
00250 new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
00251 }
00252
00253 static void move(storage_union& src, storage_union& dest) noexcept
00254 {
00255
00256
00257 new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
00258 destroy(src);
00259 }
00260
00261 static void swap(storage_union& lhs, storage_union& rhs) noexcept
00262 {
00263 std::swap(reinterpret_cast<T&>(lhs.stack), reinterpret_cast<T&>(rhs.stack));
00264 }
00265 };
00266
00268 template<typename T>
00269 struct requires_allocation :
00270 std::integral_constant<bool,
00271 !(std::is_nothrow_move_constructible<T>::value
00272 && sizeof(T) <= sizeof(storage_union::stack)
00273 && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)>
00274 {};
00275
00277 template<typename T>
00278 static vtable_type* vtable_for_type()
00279 {
00280 using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type;
00281 static vtable_type table = {
00282 VTableType::type, VTableType::destroy,
00283 VTableType::copy, VTableType::move,
00284 VTableType::swap,
00285 };
00286 return &table;
00287 }
00288
00289 protected:
00290 template<typename T>
00291 friend const T* any_cast(const any* operand) noexcept;
00292 template<typename T>
00293 friend T* any_cast(any* operand) noexcept;
00294
00296 bool is_typed(const std::type_info& t) const
00297 {
00298 return is_same(this->type(), t);
00299 }
00300
00307 static bool is_same(const std::type_info& a, const std::type_info& b)
00308 {
00309 #ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
00310 return &a == &b;
00311 #else
00312 return a == b;
00313 #endif
00314 }
00315
00317 template<typename T>
00318 const T* cast() const noexcept
00319 {
00320 return requires_allocation<typename std::decay<T>::type>::value?
00321 reinterpret_cast<const T*>(storage.dynamic) :
00322 reinterpret_cast<const T*>(&storage.stack);
00323 }
00324
00326 template<typename T>
00327 T* cast() noexcept
00328 {
00329 return requires_allocation<typename std::decay<T>::type>::value?
00330 reinterpret_cast<T*>(storage.dynamic) :
00331 reinterpret_cast<T*>(&storage.stack);
00332 }
00333
00334 private:
00335 storage_union storage;
00336 vtable_type* vtable;
00337
00338 template<typename ValueType, typename T>
00339 typename std::enable_if<requires_allocation<T>::value>::type
00340 do_construct(ValueType&& value)
00341 {
00342 storage.dynamic = new T(std::forward<ValueType>(value));
00343 }
00344
00345 template<typename ValueType, typename T>
00346 typename std::enable_if<!requires_allocation<T>::value>::type
00347 do_construct(ValueType&& value)
00348 {
00349 new (&storage.stack) T(std::forward<ValueType>(value));
00350 }
00351
00354 template<typename ValueType>
00355 void construct(ValueType&& value)
00356 {
00357 using T = typename std::decay<ValueType>::type;
00358
00359 this->vtable = vtable_for_type<T>();
00360
00361 do_construct<ValueType,T>(std::forward<ValueType>(value));
00362 }
00363 };
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, std::true_type)
00371 {
00372 return std::move(*p);
00373 }
00374
00375 template<typename ValueType>
00376 inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::false_type)
00377 {
00378 return *p;
00379 }
00380 }
00381
00383 template<typename ValueType>
00384 inline ValueType any_cast(const any& operand)
00385 {
00386 auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand);
00387 if(p == nullptr) throw bad_any_cast();
00388 return *p;
00389 }
00390
00392 template<typename ValueType>
00393 inline ValueType any_cast(any& operand)
00394 {
00395 auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
00396 if(p == nullptr) throw bad_any_cast();
00397 return *p;
00398 }
00399
00409 template<typename ValueType>
00410 inline ValueType any_cast(any&& operand)
00411 {
00412 #ifdef ANY_IMPL_ANY_CAST_MOVEABLE
00413
00414 using can_move = std::integral_constant<bool,
00415 std::is_move_constructible<ValueType>::value
00416 && !std::is_lvalue_reference<ValueType>::value>;
00417 #else
00418 using can_move = std::false_type;
00419 #endif
00420
00421 auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
00422 if(p == nullptr) throw bad_any_cast();
00423 return detail::any_cast_move_if_true<ValueType>(p, can_move());
00424 }
00425
00428 template<typename T>
00429 inline const T* any_cast(const any* operand) noexcept
00430 {
00431 if(operand == nullptr || !operand->is_typed(typeid(T)))
00432 return nullptr;
00433 else
00434 return operand->cast<T>();
00435 }
00436
00439 template<typename T>
00440 inline T* any_cast(any* operand) noexcept
00441 {
00442 if(operand == nullptr || !operand->is_typed(typeid(T)))
00443 return nullptr;
00444 else
00445 return operand->cast<T>();
00446 }
00447
00448 }
00449
00450 namespace std
00451 {
00452 inline void swap(linb::any& lhs, linb::any& rhs) noexcept
00453 {
00454 lhs.swap(rhs);
00455 }
00456 }
00457
00458 #endif