diff --git a/src/kiwano/core/any.hpp b/src/kiwano/core/any.hpp index 3223fcd8..c8771e63 100644 --- a/src/kiwano/core/any.hpp +++ b/src/kiwano/core/any.hpp @@ -21,6 +21,7 @@ #pragma once #include #include +#include namespace kiwano { @@ -28,6 +29,18 @@ namespace kiwano inline namespace core { +class bad_any_cast : public std::exception +{ +public: + bad_any_cast() {} + + virtual const char* what() const override + { + return "bad and cast"; + } +}; + + class any { public: @@ -38,11 +51,11 @@ public: template < typename _Ty, typename _Decayed = typename std::decay<_Ty>::type, - typename = typename std::enable_if::value>::type + typename std::enable_if::value, int>::type = 0 > any(_Ty&& val) : storage_{} { - emplace<_Decayed>(std::forward<_Ty&&>(val)); + emplace<_Decayed>(std::forward<_Ty>(val)); } template < @@ -57,22 +70,7 @@ public: any(const any& rhs) : storage_{} { - if (rhs.has_value()) - { - typeinfo() = rhs.typeinfo(); - storage_.is_small_ = rhs.storage_.is_small_; - - if (rhs.has_small_type()) - { - small_rtti() = rhs.small_rtti(); - small_rtti().copy(small_data(), rhs.small_data()); - } - else - { - big_rtti() = rhs.big_rtti(); - big_data() = big_rtti().copy(rhs.big_data()); - } - } + copy_from(rhs); } any(any&& rhs) noexcept : storage_{} @@ -100,7 +98,10 @@ public: return typeinfo() != nullptr; } - template + template < + typename _Decayed, + typename... _Args + > void emplace(_Args&&... args) { reset(); @@ -119,6 +120,58 @@ public: tidy(); } + template + _Ty* cast_pointer() noexcept + { + return const_cast<_Ty*>(const_cast(this)->cast_pointer<_Ty>()); + } + + template + const _Ty* cast_pointer() const noexcept + { + static_assert(!std::is_void<_Ty>::value, "kiwano::any cannot contain void"); + + const type_info* const info = typeinfo(); + if (info && (*info == typeid(std::decay<_Ty>::type))) + { + if (has_small_type()) + { + return static_cast(small_data()); + } + else + { + return static_cast(big_data()); + } + } + return nullptr; + } + + template + _Ty cast() + { + using _Decayed = typename std::decay<_Ty>::type; + + const auto ptr = cast_pointer<_Decayed>(); + if (!ptr) + { + throw bad_any_cast{}; + } + return static_cast<_Ty>(*ptr); + } + + template + _Ty cast() const + { + using _Decayed = typename std::decay<_Ty>::type; + + const auto ptr = cast_pointer<_Decayed>(); + if (!ptr) + { + throw bad_any_cast{}; + } + return static_cast<_Ty>(*ptr); + } + any& operator=(const any& rhs) { *this = any(rhs); @@ -143,7 +196,10 @@ protected: return storage_.small_.info_; } - template + template < + typename _Decayed, + typename... _Args + > void store(std::true_type, _Args&&... args) { storage_.is_small_ = true; @@ -153,7 +209,10 @@ protected: ::new (small_data()) _Decayed(std::forward<_Args>(args)...); } - template + template < + typename _Decayed, + typename... _Args + > void store(std::false_type, _Args&&... args) { storage_.is_small_ = false; @@ -180,6 +239,26 @@ protected: } } + void copy_from(const any& rhs) + { + if (rhs.has_value()) + { + typeinfo() = rhs.typeinfo(); + storage_.is_small_ = rhs.storage_.is_small_; + + if (rhs.has_small_type()) + { + small_rtti() = rhs.small_rtti(); + small_rtti().copy(small_data(), rhs.small_data()); + } + else + { + big_rtti() = rhs.big_rtti(); + big_data() = big_rtti().copy(rhs.big_data()); + } + } + } + void move_from(any&& rhs) noexcept { if (rhs.has_value()) @@ -196,8 +275,8 @@ protected: { big_rtti() = rhs.big_rtti(); big_data() = rhs.big_data(); + rhs.typeinfo() = nullptr; } - rhs.typeinfo() = nullptr; } } @@ -227,10 +306,10 @@ protected: } protected: - static const auto ANY_SPACE_SIZE = 16U; + static const auto ANY_SMALL_SPACE_SIZE = 16U; template - struct decayed_is_small : public std::bool_constant + struct decayed_is_small : public std::bool_constant { }; @@ -274,7 +353,7 @@ protected: { using destroy_func = void(void*); using copy_func = void* (void*, const void*); - using move_func = void*(void*, const void*); + using move_func = void*(void*, void*); small_storage_rtti() { @@ -310,9 +389,9 @@ protected: } template - static void* move_impl(void* const target, const void* const ptr) noexcept + static void* move_impl(void* const target, void* const ptr) noexcept { - return ::new (static_cast<_Ty*>(target)) _Ty(std::move(*static_cast(ptr))); + return ::new (static_cast<_Ty*>(target)) _Ty(std::move(*static_cast<_Ty*>(ptr))); } destroy_func* destroy; @@ -346,7 +425,7 @@ protected: { const type_info* info_; small_storage_rtti rtti_; - char buffer_[ANY_SPACE_SIZE]; + char buffer_[ANY_SMALL_SPACE_SIZE]; }; struct big_storage @@ -369,6 +448,64 @@ protected: storage storage_; }; + +// +// any_cast functions +// + +template +_Ty* any_cast(any* const a) noexcept +{ + return a->cast_pointer<_Ty>(); } +template +const _Ty* any_cast(const any* const a) noexcept +{ + return a->cast_pointer<_Ty>(); +} + +template +_Ty any_cast(any& a) +{ + const auto ptr = any_cast::type>(&a); + if (!ptr) + { + throw bad_any_cast{}; + } + return static_cast<_Ty>(*ptr); +} + +template +const _Ty any_cast(const any& a) +{ + const auto ptr = any_cast::type>(&a); + if (!ptr) + { + throw bad_any_cast{}; + } + return static_cast<_Ty>(*ptr); +} + +template +_Ty any_cast(any&& a) +{ + _Ty* ptr = a.cast_pointer<_Ty>(); + if (!ptr) + { + throw bad_any_cast{}; + } + return static_cast<_Ty>(std::move(*ptr)); +} + +} // namespace core + +} // namespace kiwano + +namespace std +{ + inline void swap(kiwano::core::any& lhs, kiwano::core::any& rhs) noexcept + { + lhs.swap(rhs); + } }