| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 |
- /***************************************************************************
- * Copyright (c) Sylvain Corlay and Johan Mabille and Wolf Vollprecht *
- * Copyright (c) QuantStack *
- * *
- * Distributed under the terms of the BSD 3-Clause License. *
- * *
- * The full license is in the file LICENSE, distributed with this software. *
- ****************************************************************************/
- #ifndef XTL_ANY_HPP
- #define XTL_ANY_HPP
- #include <exception>
- #include <stdexcept>
- #include <type_traits>
- #include <typeinfo>
- #include "xtl/xmeta_utils.hpp"
- namespace xtl
- {
- /**************************************
- * Implementation of C++17's std::any *
- **************************************/
- // Copyright (c) 2016 Denilson das Mercês Amorim
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- class bad_any_cast : public std::bad_cast
- {
- public:
- const char* what() const noexcept override
- {
- return "bad any cast";
- }
- };
- namespace detail {
- inline static void check_any_cast(const void* p) {
- if (p == nullptr) {
- #if defined(XTL_NO_EXCEPTIONS)
- std::fprintf(stderr, "bad_any_cast\n");
- std::terminate();
- #else
- throw bad_any_cast();
- #endif
- }
- }
- } // namespace detail
- class any final
- {
- public:
- /// Constructs an object of type any with an empty state.
- any()
- : vtable(nullptr)
- {
- }
- /// Constructs an object of type any with an equivalent state as other.
- any(const any& rhs)
- : vtable(rhs.vtable)
- {
- if (!rhs.empty())
- {
- rhs.vtable->copy(rhs.storage, this->storage);
- }
- }
- /// Constructs an object of type any with a state equivalent to the original state of other.
- /// rhs is left in a valid but otherwise unspecified state.
- any(any&& rhs) noexcept
- : vtable(rhs.vtable)
- {
- if (!rhs.empty())
- {
- rhs.vtable->move(rhs.storage, this->storage);
- rhs.vtable = nullptr;
- }
- }
- /// Same effect as this->clear().
- ~any()
- {
- this->clear();
- }
- /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward<ValueType>(value).
- ///
- /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed.
- /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed.
- template <typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
- any(ValueType&& value)
- {
- static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
- "T shall satisfy the CopyConstructible requirements.");
- this->construct(std::forward<ValueType>(value));
- }
- /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown.
- any& operator=(const any& rhs)
- {
- any(rhs).swap(*this);
- return *this;
- }
- /// Has the same effect as any(std::move(rhs)).swap(*this).
- ///
- /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid
- /// but otherwise unspecified state.
- any& operator=(any&& rhs) noexcept
- {
- any(std::move(rhs)).swap(*this);
- return *this;
- }
- /// Has the same effect as any(std::forward<ValueType>(value)).swap(*this). No effect if a exception is thrown.
- ///
- /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed.
- /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed.
- template <typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
- any& operator=(ValueType&& value)
- {
- static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
- "T shall satisfy the CopyConstructible requirements.");
- any(std::forward<ValueType>(value)).swap(*this);
- return *this;
- }
- /// If not empty, destroys the contained object.
- void clear() noexcept
- {
- if (!empty())
- {
- this->vtable->destroy(storage);
- this->vtable = nullptr;
- }
- }
- /// C++17 equivalent of clear
- void reset() noexcept
- {
- clear();
- }
- /// Returns true if *this has no contained object, otherwise false.
- bool empty() const noexcept
- {
- return this->vtable == nullptr;
- }
- /// C++17 equivalent of !empty()
- bool has_value() const noexcept
- {
- return !empty();
- }
- /// If *this has a contained object of type T, typeid(T); otherwise typeid(void).
- const std::type_info& type() const noexcept
- {
- return empty() ? typeid(void) : this->vtable->type();
- }
- /// Exchange the states of *this and rhs.
- void swap(any& rhs) noexcept
- {
- if (this->vtable != rhs.vtable)
- {
- any tmp(std::move(rhs));
- // move from *this to rhs.
- rhs.vtable = this->vtable;
- if (this->vtable != nullptr)
- {
- this->vtable->move(this->storage, rhs.storage);
- //this->vtable = nullptr; -- uneeded, see below
- }
- // move from tmp (previously rhs) to *this.
- this->vtable = tmp.vtable;
- if (tmp.vtable != nullptr)
- {
- tmp.vtable->move(tmp.storage, this->storage);
- tmp.vtable = nullptr;
- }
- }
- else // same types
- {
- if (this->vtable != nullptr)
- this->vtable->swap(this->storage, rhs.storage);
- }
- }
- private: // Storage and Virtual Method Table
- union storage_union {
- using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
- void* dynamic;
- stack_storage_t stack; // 2 words for e.g. shared_ptr
- };
- /// Base VTable specification.
- struct vtable_type
- {
- // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
- // such as destroy() and/or move().
- /// The type of the object this vtable is for.
- const std::type_info& (*type)() noexcept;
- /// Destroys the object in the union.
- /// The state of the union after this call is unspecified, caller must ensure not to use src anymore.
- void (*destroy)(storage_union&) noexcept;
- /// Copies the **inner** content of the src union into the yet unitialized dest union.
- /// As such, both inner objects will have the same state, but on separate memory locations.
- void (*copy)(const storage_union& src, storage_union& dest);
- /// Moves the storage from src to the yet unitialized dest union.
- /// The state of src after this call is unspecified, caller must ensure not to use src anymore.
- void (*move)(storage_union& src, storage_union& dest) noexcept;
- /// Exchanges the storage between lhs and rhs.
- void (*swap)(storage_union& lhs, storage_union& rhs) noexcept;
- };
- /// VTable for dynamically allocated storage.
- template <typename T>
- struct vtable_dynamic
- {
- static const std::type_info& type() noexcept
- {
- return typeid(T);
- }
- static void destroy(storage_union& storage) noexcept
- {
- //assert(reinterpret_cast<T*>(storage.dynamic));
- delete reinterpret_cast<T*>(storage.dynamic);
- }
- static void copy(const storage_union& src, storage_union& dest)
- {
- dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
- }
- static void move(storage_union& src, storage_union& dest) noexcept
- {
- dest.dynamic = src.dynamic;
- src.dynamic = nullptr;
- }
- static void swap(storage_union& lhs, storage_union& rhs) noexcept
- {
- // just exchage the storage pointers.
- std::swap(lhs.dynamic, rhs.dynamic);
- }
- };
- /// VTable for stack allocated storage.
- template <typename T>
- struct vtable_stack
- {
- static const std::type_info& type() noexcept
- {
- return typeid(T);
- }
- static void destroy(storage_union& storage) noexcept
- {
- reinterpret_cast<T*>(&storage.stack)->~T();
- }
- static void copy(const storage_union& src, storage_union& dest)
- {
- new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
- }
- static void move(storage_union& src, storage_union& dest) noexcept
- {
- // one of the conditions for using vtable_stack is a nothrow move constructor,
- // so this move constructor will never throw a exception.
- new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
- destroy(src);
- }
- static void swap(storage_union& lhs, storage_union& rhs) noexcept
- {
- storage_union tmp_storage;
- move(rhs, tmp_storage);
- move(lhs, rhs);
- move(tmp_storage, lhs);
- }
- };
- /// Whether the type T must be dynamically allocated or can be stored on the stack.
- template <typename T>
- struct requires_allocation : std::integral_constant<bool,
- !(std::is_nothrow_move_constructible<T>::value // N4562 �6.3/3 [any.class]
- && sizeof(T) <= sizeof(storage_union::stack) && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)>
- {
- };
- /// Returns the pointer to the vtable of the type T.
- template <typename T>
- static vtable_type* vtable_for_type()
- {
- using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type;
- static vtable_type table = {
- VTableType::type, VTableType::destroy,
- VTableType::copy, VTableType::move,
- VTableType::swap,
- };
- return &table;
- }
- protected:
- template <typename T>
- friend const T* any_cast(const any* operand) noexcept;
- template <typename T>
- friend T* any_cast(any* operand) noexcept;
- /// Same effect as is_same(this->type(), t);
- bool is_typed(const std::type_info& t) const
- {
- return is_same(this->type(), t);
- }
- /// Checks if two type infos are the same.
- ///
- /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the
- /// type infos, otherwise does an actual comparision. Checking addresses is
- /// only a valid approach when there's no interaction with outside sources
- /// (other shared libraries and such).
- static bool is_same(const std::type_info& a, const std::type_info& b)
- {
- #ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
- return &a == &b;
- #else
- return a == b;
- #endif
- }
- /// Casts (with no type_info checks) the storage pointer as const T*.
- template <typename T>
- const T* cast() const noexcept
- {
- return requires_allocation<typename std::decay<T>::type>::value ? reinterpret_cast<const T*>(storage.dynamic) : reinterpret_cast<const T*>(&storage.stack);
- }
- /// Casts (with no type_info checks) the storage pointer as T*.
- template <typename T>
- T* cast() noexcept
- {
- return requires_allocation<typename std::decay<T>::type>::value ? reinterpret_cast<T*>(storage.dynamic) : reinterpret_cast<T*>(&storage.stack);
- }
- private:
- storage_union storage; // on offset(0) so no padding for align
- vtable_type* vtable;
- /// Chooses between stack and dynamic allocation for the type decay_t<ValueType>,
- /// assigns the correct vtable, and constructs the object on our storage.
- template <typename ValueType>
- void construct(ValueType&& value)
- {
- using T = typename std::decay<ValueType>::type;
- this->vtable = vtable_for_type<T>();
- return xtl::mpl::static_if<requires_allocation<T>::value>([&](auto self)
- {
- self(*this).storage.dynamic = new T(std::forward<ValueType>(value));
- }, /*else*/ [&](auto self)
- {
- new (&self(*this).storage.stack) T(std::forward<ValueType>(value));
- });
- }
- };
- namespace detail
- {
- template <typename ValueType>
- inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::true_type)
- {
- return std::move(*p);
- }
- template <typename ValueType>
- inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::false_type)
- {
- return *p;
- }
- }
- /// Performs *any_cast<add_const_t<remove_reference_t<ValueType>>>(&operand), or throws bad_any_cast on failure.
- template <typename ValueType>
- inline ValueType any_cast(const any& operand)
- {
- auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand);
- detail::check_any_cast(p);
- return *p;
- }
- /// Performs *any_cast<remove_reference_t<ValueType>>(&operand), or throws bad_any_cast on failure.
- template <typename ValueType>
- inline ValueType any_cast(any& operand)
- {
- auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
- detail::check_any_cast(p);
- return *p;
- }
- ///
- /// If ANY_IMPL_ANYCAST_MOVEABLE is not defined, does as N4562 specifies:
- /// Performs *any_cast<remove_reference_t<ValueType>>(&operand), or throws bad_any_cast on failure.
- ///
- /// If ANY_IMPL_ANYCAST_MOVEABLE is defined, does as LWG Defect 2509 specifies:
- /// If ValueType is MoveConstructible and isn't a lvalue reference, performs
- /// std::move(*any_cast<remove_reference_t<ValueType>>(&operand)), otherwise
- /// *any_cast<remove_reference_t<ValueType>>(&operand). Throws bad_any_cast on failure.
- ///
- template <typename ValueType>
- inline ValueType any_cast(any&& operand)
- {
- #ifdef ANY_IMPL_ANY_CAST_MOVEABLE
- // https://cplusplus.github.io/LWG/lwg-active.html#2509
- using can_move = std::integral_constant<bool,
- std::is_move_constructible<ValueType>::value && !std::is_lvalue_reference<ValueType>::value>;
- #else
- using can_move = std::false_type;
- #endif
- auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
- detail::check_any_cast(p);
- return detail::any_cast_move_if_true<ValueType>(p, can_move());
- }
- /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object
- /// contained by operand, otherwise nullptr.
- template <typename T>
- inline const T* any_cast(const any* operand) noexcept
- {
- if (operand == nullptr || !operand->is_typed(typeid(T)))
- return nullptr;
- else
- return operand->cast<T>();
- }
- /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object
- /// contained by operand, otherwise nullptr.
- template <typename T>
- inline T* any_cast(any* operand) noexcept
- {
- if (operand == nullptr || !operand->is_typed(typeid(T)))
- return nullptr;
- else
- return operand->cast<T>();
- }
- }
- namespace std
- {
- inline void swap(xtl::any& lhs, xtl::any& rhs) noexcept
- {
- lhs.swap(rhs);
- }
- }
- #endif
|