any.hpp
Go to the documentation of this file.
00001 //
00002 // Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers.
00003 //
00004 // See also:
00005 //   + http://en.cppreference.com/w/cpp/any
00006 //   + http://en.cppreference.com/w/cpp/experimental/any
00007 //   + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
00008 //   + https://cplusplus.github.io/LWG/lwg-active.html#2509
00009 //
00010 //
00011 // Copyright (c) 2016 Denilson das Merc�s Amorim
00012 //
00013 // Distributed under the Boost Software License, Version 1.0. (See accompanying
00014 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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             // move from *this to rhs.
00144             rhs.vtable = this->vtable;
00145             if(this->vtable != nullptr)
00146             {
00147                 this->vtable->move(this->storage, rhs.storage);
00148                 //this->vtable = nullptr; -- uneeded, see below
00149             }
00150 
00151             // move from tmp (previously rhs) to *this.
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 // same types
00160         {
00161             if(this->vtable != nullptr)
00162                 this->vtable->swap(this->storage, rhs.storage);
00163         }
00164     }
00165 
00166 private: // Storage and Virtual Method Table
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;      // 2 words for e.g. shared_ptr
00174     };
00175 
00177     struct vtable_type
00178     {
00179         // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
00180         // such as destroy() and/or move().
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             //assert(reinterpret_cast<T*>(storage.dynamic));
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             // just exchage the storage pointers.
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             // one of the conditions for using vtable_stack is a nothrow move constructor,
00256             // so this move constructor will never throw a exception.
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      // N4562 �6.3/3 [any.class]
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; // on offset(0) so no padding for align
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     // https://cplusplus.github.io/LWG/lwg-active.html#2509
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


libnabo
Author(s): Stéphane Magnenat
autogenerated on Sun Feb 10 2019 03:52:20