#ifndef CORE_ANY_HPP #define CORE_ANY_HPP #include #include #include #include #include #include #include #ifndef CORE_NO_EXCEPTIONS #include #endif /* CORE_NO_EXCEPTIONS */ namespace core { inline namespace v2 { namespace impl { using data_type = add_pointer_t; template struct is_small final : meta::all_t< sizeof(decay_t) <= sizeof(data_type), alignof(decay_t) <= alignof(data_type), ::std::is_nothrow_copy_constructible>::value > { }; template <> struct is_small final : ::std::true_type { }; template ::value> struct dispatch; template <> struct dispatch { dispatch () noexcept = default; virtual ~dispatch () noexcept = default; virtual void clone (data_type const&, data_type&) const { } virtual void move (data_type&, data_type&) const noexcept { } virtual void destroy (data_type&) const noexcept { } virtual type_info const& type () const noexcept { return type_of(); } }; template struct dispatch final : dispatch<> { using value_type = T; using const_pointer = add_pointer_t>; using pointer = add_pointer_t; using allocator_type = ::std::allocator; using allocator_traits = ::std::allocator_traits; virtual void clone (data_type const& src, data_type& dst) const final { allocator_type alloc { }; auto val = reinterpret_cast>(&src); auto ptr = reinterpret_cast(&dst); allocator_traits::construct(alloc, ptr, *val); } virtual void move (data_type& src, data_type& dst) const noexcept final { allocator_type alloc { }; auto val = reinterpret_cast(&src); auto ptr = reinterpret_cast(&dst); allocator_traits::construct(alloc, ptr, ::core::move(*val)); } virtual void destroy (data_type& src) const noexcept final { allocator_type alloc { }; auto ptr = reinterpret_cast(&src); allocator_traits::destroy(alloc, ptr); } virtual type_info const& type () const noexcept final { return type_of(); } }; template struct dispatch final : dispatch<> { using value_type = T; using pointer = add_pointer_t; using allocator_type = ::std::allocator; using allocator_traits = ::std::allocator_traits; virtual void clone (data_type const& src, data_type& dst) const final { allocator_type alloc { }; auto const& value = *static_cast>(src); auto ptr = allocator_traits::allocate(alloc, 1); auto scope = make_scope_guard([&alloc, ptr] { allocator_traits::deallocate(alloc, ptr, 1); }); allocator_traits::construct(alloc, ptr, value); scope.dismiss(); dst = ptr; } virtual void move (data_type& src, data_type& dst) const noexcept final { allocator_type alloc { }; auto& value = *static_cast(src); auto ptr = allocator_traits::allocate(alloc, 1); auto scope = make_scope_guard([&alloc, ptr] { allocator_traits::deallocate(alloc, ptr, 1); }); allocator_traits::construct(alloc, ptr, ::core::move(value)); scope.dismiss(); dst = ptr; } virtual void destroy (data_type& src) const noexcept final { allocator_type alloc { }; auto ptr = static_cast(src); allocator_traits::destroy(alloc, ptr); allocator_traits::deallocate(alloc, ptr, 1); } virtual type_info const& type () const noexcept final { return type_of(); } }; template dispatch<> const* lookup () noexcept { static dispatch instance; return ::std::addressof(instance); } template <> inline dispatch<> const* lookup () noexcept { static dispatch<> instance; return ::std::addressof(instance); } } /* namespace impl */ #ifndef CORE_NO_EXCEPTIONS class bad_any_cast final : public ::std::bad_cast { public: virtual char const* what () const noexcept override { return "bad any cast"; } }; [[noreturn]] inline void throw_bad_any_cast () { throw bad_any_cast { }; } #else /* CORE_NO_EXCEPTIONS */ [[noreturn]] inline void throw_bad_any_cast () { ::std::abort(); } #endif /* CORE_NO_EXCEPTIONS */ struct any final { template friend T const* any_cast (any const*) noexcept; template friend T* any_cast (any*) noexcept; any (any const& that) : table { that.table }, data { nullptr } { this->table->clone(that.data, this->data); } any (any&& that) noexcept : table { that.table }, data { nullptr } { this->table->move(that.data, this->data); } any () noexcept : table { impl::lookup() }, data { nullptr } { } template < class T, class=enable_if_t>::value> > any (T&& value) : any { ::std::forward(value), impl::is_small { } } { } ~any () noexcept { this->clear(); } any& operator = (any const& that) { any { that }.swap(*this); return *this; } any& operator = (any&& that) noexcept { any { ::std::move(that) }.swap(*this); return *this; } template < class T, class=enable_if_t>::value> > any& operator = (T&& value) { any { ::std::forward(value), impl::is_small { } }.swap(*this); return *this; } void swap (any& that) noexcept { using ::std::swap; swap(this->table, that.table); swap(this->data, that.data); } void clear () noexcept { this->table->destroy(this->data); this->table = impl::lookup(); } type_info const& type () const noexcept { return this->table->type(); } bool empty () const noexcept { return this->table == impl::lookup(); } private: impl::dispatch<> const* table; impl::data_type data; template any (T&& value, ::std::true_type&&) : table { impl::lookup>() }, data { nullptr } { using value_type = decay_t; using allocator_type = ::std::allocator; using allocator_traits = ::std::allocator_traits; allocator_type alloc { }; auto pointer = reinterpret_cast(::std::addressof(this->data)); allocator_traits::construct(alloc, pointer, ::core::forward(value)); } template any (T&& value, ::std::false_type&&) : table { impl::lookup>() }, data { nullptr } { using value_type = decay_t; using allocator_type = ::std::allocator; using allocator_traits = ::std::allocator_traits; allocator_type alloc { }; auto pointer = allocator_traits::allocate(alloc, 1); allocator_traits::construct(alloc, pointer, ::core::forward(value)); this->data = pointer; } template T const* cast (::std::true_type&&) const { return reinterpret_cast(::std::addressof(this->data)); } template T* cast (::std::true_type&&) { return reinterpret_cast(::std::addressof(this->data)); } template T const* cast (::std::false_type&&) const { return static_cast(this->data); } template T* cast (::std::false_type&&) { return static_cast(this->data); } }; template T const* any_cast (any const* operand) noexcept { return operand and operand->type() == type_of() ? operand->cast(impl::is_small { }) : nullptr; } template T* any_cast (any* operand) noexcept { return operand and operand->type() == type_of() ? operand->cast(impl::is_small { }) : nullptr; } template < class T, class=meta::when< meta::any< ::std::is_reference::value, ::std::is_copy_constructible::value >() > > T any_cast (any const& operand) { using type = remove_reference_t; auto pointer = any_cast>(::std::addressof(operand)); if (not pointer) { throw_bad_any_cast(); } return *pointer; } template < class T, class=meta::when< meta::any< ::std::is_reference::value, ::std::is_copy_constructible::value >() > > T any_cast (any&& operand) { using type = remove_reference_t; auto pointer = any_cast(::std::addressof(operand)); if (not pointer) { throw_bad_any_cast(); } return *pointer; } template < class T, class=meta::when< meta::any< ::std::is_reference::value, ::std::is_copy_constructible::value >() > > T any_cast (any& operand) { using type = remove_reference_t; auto pointer = any_cast(::std::addressof(operand)); if (not pointer) { throw_bad_any_cast(); } return *pointer; } inline void swap (any& lhs, any& rhs) noexcept { lhs.swap(rhs); } }} /* namespace core::v2 */ #endif /* CORE_ANY_HPP */