465 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			465 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
| // Copyright (c) 2016-2018 Kiwano - Nomango
 | |
| //
 | |
| // Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| // of this software and associated documentation files (the "Software"), to deal
 | |
| // in the Software without restriction, including without limitation the rights
 | |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
| // copies of the Software, and to permit persons to whom the Software is
 | |
| // furnished to do so, subject to the following conditions:
 | |
| //
 | |
| // The above copyright notice and this permission notice shall be included in
 | |
| // all copies or substantial portions of the Software.
 | |
| //
 | |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
| // THE SOFTWARE.
 | |
| 
 | |
| #pragma once
 | |
| #include <typeinfo>
 | |
| #include <type_traits>
 | |
| #include <exception>
 | |
| 
 | |
| namespace kiwano
 | |
| {
 | |
| 
 | |
| /// \~chinese
 | |
| /// @brief 可储存单个任意对象的容器
 | |
| class Any
 | |
| {
 | |
| public:
 | |
|     Any()
 | |
|         : storage_{}
 | |
|     {
 | |
|     }
 | |
| 
 | |
|     template <typename _Ty, typename _Decayed = typename std::decay<_Ty>::type,
 | |
|               typename std::enable_if<std::is_copy_constructible<_Decayed>::value, int>::type = 0>
 | |
|     Any(_Ty&& val)
 | |
|         : storage_{}
 | |
|     {
 | |
|         Emplace<_Decayed>(std::forward<_Ty>(val));
 | |
|     }
 | |
| 
 | |
|     template <typename _Ty, typename... _Args>
 | |
|     Any(_Args&&... args)
 | |
|         : storage_{}
 | |
|     {
 | |
|         using _Decayed = typename std::decay<_Ty>::type;
 | |
| 
 | |
|         Clear();
 | |
|         EmplaceDecayed<_Decayed>(std::forward<_Args>(args)...);
 | |
|     }
 | |
| 
 | |
|     Any(const Any& rhs)
 | |
|         : storage_{}
 | |
|     {
 | |
|         CopyFrom(rhs);
 | |
|     }
 | |
| 
 | |
|     Any(Any&& rhs) noexcept
 | |
|         : storage_{}
 | |
|     {
 | |
|         MoveFrome(std::move(rhs));
 | |
|     }
 | |
| 
 | |
|     ~Any()
 | |
|     {
 | |
|         Clear();
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 获取含有对象类型
 | |
|     inline const type_info& GetType() const noexcept
 | |
|     {
 | |
|         const type_info* const info = GetTypeinfo();
 | |
|         if (info)
 | |
|         {
 | |
|             return *info;
 | |
|         }
 | |
|         return typeid(void);
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 是否含有对象
 | |
|     inline bool HasValue() const noexcept
 | |
|     {
 | |
|         return GetTypeinfo() != nullptr;
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 从参数构造对象
 | |
|     template <typename _Ty, typename... _Args>
 | |
|     void Emplace(_Args&&... args)
 | |
|     {
 | |
|         using _Decayed = typename std::decay<_Ty>::type;
 | |
| 
 | |
|         Clear();
 | |
|         EmplaceDecayed<_Decayed>(std::forward<_Args>(args)...);
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 交换容器
 | |
|     void Swap(Any& rhs) noexcept
 | |
|     {
 | |
|         Any old = std::move(rhs);
 | |
|         rhs     = std::move(*this);
 | |
|         *this   = std::move(old);
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 销毁所含对象
 | |
|     inline void Clear() noexcept
 | |
|     {
 | |
|         Tidy();
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 转换为指定类型的指针
 | |
|     template <typename _Ty>
 | |
|     _Ty* CastPtr() noexcept
 | |
|     {
 | |
|         return const_cast<_Ty*>(const_cast<const Any*>(this)->CastPtr<_Ty>());
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 转换为指定类型的指针
 | |
|     template <typename _Ty>
 | |
|     const _Ty* CastPtr() const noexcept
 | |
|     {
 | |
|         static_assert(!std::is_void<_Ty>::value, "oc::Any cannot contain void");
 | |
| 
 | |
|         const type_info* const info = GetTypeinfo();
 | |
|         if (info && (*info == typeid(std::decay<_Ty>::type)))
 | |
|         {
 | |
|             if (HasSmallType())
 | |
|             {
 | |
|                 return static_cast<const _Ty*>(GetSmallData());
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return static_cast<const _Ty*>(GetBigData());
 | |
|             }
 | |
|         }
 | |
|         return nullptr;
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 转换为指定类型
 | |
|     /// @throw std::bad_cast 转换失败时抛出
 | |
|     template <typename _Ty>
 | |
|     _Ty Cast()
 | |
|     {
 | |
|         using _Decayed = typename std::decay<_Ty>::type;
 | |
| 
 | |
|         const auto ptr = CastPtr<_Decayed>();
 | |
|         if (!ptr)
 | |
|         {
 | |
|             throw std::bad_cast();
 | |
|         }
 | |
|         return static_cast<_Ty>(*ptr);
 | |
|     }
 | |
| 
 | |
|     /// \~chinese
 | |
|     /// @brief 转换为指定类型
 | |
|     /// @throw std::bad_cast 转换失败时抛出
 | |
|     template <typename _Ty>
 | |
|     _Ty Cast() const
 | |
|     {
 | |
|         using _Decayed = typename std::decay<_Ty>::type;
 | |
| 
 | |
|         const auto ptr = CastPtr<_Decayed>();
 | |
|         if (!ptr)
 | |
|         {
 | |
|             throw std::bad_cast();
 | |
|         }
 | |
|         return static_cast<_Ty>(*ptr);
 | |
|     }
 | |
| 
 | |
|     Any& operator=(const Any& rhs)
 | |
|     {
 | |
|         *this = Any(rhs);
 | |
|         return (*this);
 | |
|     }
 | |
| 
 | |
|     Any& operator=(Any&& rhs) noexcept
 | |
|     {
 | |
|         Clear();
 | |
|         MoveFrome(std::move(rhs));
 | |
|         return (*this);
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     const type_info*& GetTypeinfo()
 | |
|     {
 | |
|         return storage_.small_.info_;
 | |
|     }
 | |
| 
 | |
|     const type_info* GetTypeinfo() const
 | |
|     {
 | |
|         return storage_.small_.info_;
 | |
|     }
 | |
| 
 | |
|     template <typename _Decayed, typename... _Args>
 | |
|     inline void EmplaceDecayed(_Args&&... args)
 | |
|     {
 | |
|         Store<_Decayed>(IsSmallSize<_Decayed>{}, std::forward<_Args>(args)...);
 | |
|     }
 | |
| 
 | |
|     template <typename _Decayed, typename... _Args>
 | |
|     void Store(std::true_type, _Args&&... args)
 | |
|     {
 | |
|         storage_.is_small_ = true;
 | |
|         GetTypeinfo()         = &typeid(_Decayed);
 | |
|         GetSmallRTTI()       = SmallStorageRTTI::make<_Decayed>();
 | |
| 
 | |
|         ::new (GetSmallData()) _Decayed(std::forward<_Args>(args)...);
 | |
|     }
 | |
| 
 | |
|     template <typename _Decayed, typename... _Args>
 | |
|     void Store(std::false_type, _Args&&... args)
 | |
|     {
 | |
|         storage_.is_small_ = false;
 | |
|         GetTypeinfo()         = &typeid(_Decayed);
 | |
|         GetBigRTTI()         = BigStorageRTTI::make<_Decayed>();
 | |
| 
 | |
|         GetBigData() = ::new _Decayed(std::forward<_Args>(args)...);
 | |
|     }
 | |
| 
 | |
|     void Tidy() noexcept
 | |
|     {
 | |
|         if (HasValue())
 | |
|         {
 | |
|             if (HasSmallType())
 | |
|             {
 | |
|                 GetSmallRTTI().destroy(GetSmallData());
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 GetBigRTTI().destroy(GetBigData());
 | |
|                 GetBigData() = nullptr;
 | |
|             }
 | |
|             GetTypeinfo() = nullptr;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void CopyFrom(const Any& rhs)
 | |
|     {
 | |
|         if (rhs.HasValue())
 | |
|         {
 | |
|             GetTypeinfo()         = rhs.GetTypeinfo();
 | |
|             storage_.is_small_ = rhs.storage_.is_small_;
 | |
| 
 | |
|             if (rhs.HasSmallType())
 | |
|             {
 | |
|                 GetSmallRTTI() = rhs.GetSmallRTTI();
 | |
|                 GetSmallRTTI().copy(GetSmallData(), rhs.GetSmallData());
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 GetBigRTTI() = rhs.GetBigRTTI();
 | |
|                 GetBigData() = GetBigRTTI().copy(rhs.GetBigData());
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void MoveFrome(Any&& rhs) noexcept
 | |
|     {
 | |
|         if (rhs.HasValue())
 | |
|         {
 | |
|             GetTypeinfo()         = rhs.GetTypeinfo();
 | |
|             storage_.is_small_ = rhs.storage_.is_small_;
 | |
| 
 | |
|             if (rhs.HasSmallType())
 | |
|             {
 | |
|                 GetSmallRTTI() = rhs.GetSmallRTTI();
 | |
|                 GetSmallRTTI().move(GetSmallData(), rhs.GetSmallData());
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 GetBigRTTI()     = rhs.GetBigRTTI();
 | |
|                 GetBigData()     = rhs.GetBigData();
 | |
|                 rhs.GetTypeinfo() = nullptr;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     inline void* GetSmallData()
 | |
|     {
 | |
|         return storage_.small_.buffer_;
 | |
|     }
 | |
| 
 | |
|     inline const void* GetSmallData() const
 | |
|     {
 | |
|         return storage_.small_.buffer_;
 | |
|     }
 | |
| 
 | |
|     inline void*& GetBigData()
 | |
|     {
 | |
|         return storage_.big_.ptr_;
 | |
|     }
 | |
| 
 | |
|     inline void* GetBigData() const
 | |
|     {
 | |
|         return storage_.big_.ptr_;
 | |
|     }
 | |
| 
 | |
|     inline bool HasSmallType() const
 | |
|     {
 | |
|         return storage_.is_small_;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     static const auto ANY_SMALL_SPACE_SIZE = 8U;
 | |
| 
 | |
|     template <typename _Ty>
 | |
|     struct IsSmallSize : public std::bool_constant<sizeof(_Ty) <= ANY_SMALL_SPACE_SIZE>
 | |
|     {
 | |
|     };
 | |
| 
 | |
|     struct BigStorageRTTI
 | |
|     {
 | |
|         using DestroyFunc = void(void*);
 | |
|         using CopyFunc    = void*(const void*);
 | |
| 
 | |
|         BigStorageRTTI()
 | |
|         {
 | |
|             destroy = nullptr;
 | |
|             copy    = nullptr;
 | |
|         }
 | |
| 
 | |
|         template <typename _Ty>
 | |
|         static inline BigStorageRTTI make()
 | |
|         {
 | |
|             BigStorageRTTI rtti;
 | |
|             rtti.destroy = &BigStorageRTTI::Destroy<_Ty>;
 | |
|             rtti.copy    = &BigStorageRTTI::Copy<_Ty>;
 | |
|             return rtti;
 | |
|         }
 | |
| 
 | |
|         template <typename _Ty>
 | |
|         static void Destroy(void* const ptr) noexcept
 | |
|         {
 | |
|             ::delete static_cast<_Ty*>(ptr);
 | |
|         }
 | |
| 
 | |
|         template <typename _Ty>
 | |
|         static void* Copy(const void* const ptr) noexcept
 | |
|         {
 | |
|             return ::new _Ty(*static_cast<const _Ty*>(ptr));
 | |
|         }
 | |
| 
 | |
|         DestroyFunc* destroy;
 | |
|         CopyFunc*    copy;
 | |
|     };
 | |
| 
 | |
|     struct SmallStorageRTTI
 | |
|     {
 | |
|         using DestroyFunc = void(void*);
 | |
|         using CopyFunc    = void*(void*, const void*);
 | |
|         using MoveFunc    = void*(void*, void*);
 | |
| 
 | |
|         SmallStorageRTTI()
 | |
|         {
 | |
|             destroy = nullptr;
 | |
|             copy    = nullptr;
 | |
|             move    = nullptr;
 | |
|         }
 | |
| 
 | |
|         template <typename _Ty>
 | |
|         static inline SmallStorageRTTI make()
 | |
|         {
 | |
|             SmallStorageRTTI rtti;
 | |
|             rtti.destroy = &SmallStorageRTTI::Destroy<_Ty>;
 | |
|             rtti.copy    = &SmallStorageRTTI::Copy<_Ty>;
 | |
|             rtti.move    = &SmallStorageRTTI::Move<_Ty>;
 | |
|             return rtti;
 | |
|         }
 | |
| 
 | |
|         template <typename _Ty>
 | |
|         static void Destroy(void* const ptr) noexcept
 | |
|         {
 | |
|             if (ptr)
 | |
|             {
 | |
|                 _Ty& obj = *(static_cast<_Ty* const>(ptr));
 | |
|                 obj.~_Ty();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         template <typename _Ty>
 | |
|         static void* Copy(void* const target, const void* const ptr) noexcept
 | |
|         {
 | |
|             return ::new (static_cast<_Ty*>(target)) _Ty(*static_cast<const _Ty*>(ptr));
 | |
|         }
 | |
| 
 | |
|         template <typename _Ty>
 | |
|         static void* Move(void* const target, void* const ptr) noexcept
 | |
|         {
 | |
|             return ::new (static_cast<_Ty*>(target)) _Ty(std::move(*static_cast<_Ty*>(ptr)));
 | |
|         }
 | |
| 
 | |
|         DestroyFunc* destroy;
 | |
|         CopyFunc*    copy;
 | |
|         MoveFunc*    move;
 | |
|     };
 | |
| 
 | |
| private:
 | |
|     inline SmallStorageRTTI& GetSmallRTTI()
 | |
|     {
 | |
|         return storage_.small_.rtti_;
 | |
|     }
 | |
| 
 | |
|     inline const SmallStorageRTTI& GetSmallRTTI() const
 | |
|     {
 | |
|         return storage_.small_.rtti_;
 | |
|     }
 | |
| 
 | |
|     inline BigStorageRTTI& GetBigRTTI()
 | |
|     {
 | |
|         return storage_.big_.rtti_;
 | |
|     }
 | |
| 
 | |
|     inline const BigStorageRTTI& GetBigRTTI() const
 | |
|     {
 | |
|         return storage_.big_.rtti_;
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     struct SmallStorage
 | |
|     {
 | |
|         const type_info* info_;
 | |
|         SmallStorageRTTI rtti_;
 | |
|         char             buffer_[ANY_SMALL_SPACE_SIZE];
 | |
|     };
 | |
| 
 | |
|     struct BigStorage
 | |
|     {
 | |
|         const type_info* info_;
 | |
|         BigStorageRTTI   rtti_;
 | |
|         void*            ptr_;
 | |
|     };
 | |
| 
 | |
|     struct Storage
 | |
|     {
 | |
|         bool is_small_;
 | |
|         union
 | |
|         {
 | |
|             SmallStorage small_;
 | |
|             BigStorage   big_;
 | |
|         };
 | |
| 
 | |
|         Storage()
 | |
|             : is_small_(false)
 | |
|             , small_()
 | |
|         {
 | |
|         }  // fix error C2280 for VisualStudio 2015
 | |
|     };
 | |
| 
 | |
|     Storage storage_;
 | |
| };
 | |
| 
 | |
| }  // namespace kiwano
 |