// // Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. // // See also: // + http://en.cppreference.com/w/cpp/any // + http://en.cppreference.com/w/cpp/experimental/any // + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any // + https://cplusplus.github.io/LWG/lwg-active.html#2509 // // // 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) // #ifndef LINB_ANY_HPP #define LINB_ANY_HPP #pragma once #include #include #include #include #include #if defined(PARTICLE) #if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && !defined(ANY_IMPL_EXCEPTIONS) # define ANY_IMPL_NO_EXCEPTIONS # endif #else // you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS, // but you must ensure not to cast badly when passing an `any' object to any_cast(any) #endif #if defined(PARTICLE) #if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && !defined(ANY_IMPL_RTTI) # define ANY_IMPL_NO_RTTI # endif #else // you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI, // in order to disable functions working with the typeid of a type #endif namespace linb { template struct in_place_type_t { constexpr explicit in_place_type_t() noexcept = default; }; #if defined (__cpp_variable_templates) || defined(_MSC_VER) template constexpr in_place_type_t in_place_type{}; #endif class bad_any_cast : public std::bad_cast { public: const char* what() const noexcept override { return "bad any cast"; } }; class any final { public: /// Constructs an object of type any with an empty state. any() noexcept : 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(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::type, any>::value>::type> any(ValueType&& value) { static_assert(std::is_copy_constructible::type>::value, "T shall satisfy the CopyConstructible requirements."); this->construct(std::forward(value)); } template explicit any(in_place_type_t, Args&&... args) { this->emplace_construct(std::forward(args)...); } template explicit any(in_place_type_t, std::initializer_list il, Args&&... args) { this->emplace_construct(il, std::forward(args)...); } /// 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 { std::move(rhs).swap(*this); return *this; } /// Has the same effect as any(std::forward(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::type, any>::value>::type> any& operator=(ValueType&& value) { static_assert(std::is_copy_constructible::type>::value, "T shall satisfy the CopyConstructible requirements."); any(std::forward(value)).swap(*this); return *this; } /// If not empty, destroys the contained object. void clear() noexcept { if(!empty()) { this->vtable->destroy(storage); this->vtable = nullptr; } } /// Returns true if *this has no contained object, otherwise false. bool empty() const noexcept { return this->vtable == nullptr; } #ifndef ANY_IMPL_NO_RTTI /// 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(); } #endif /// 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; -- unneeded, 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::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 responsible for doing .vtable = nullptr after destructful operations // such as destroy() and/or move(). #ifndef ANY_IMPL_NO_RTTI /// The type of the object this vtable is for. const std::type_info& (*type)() noexcept; #endif /// 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 struct vtable_dynamic { #ifndef ANY_IMPL_NO_RTTI static const std::type_info& type() noexcept { return typeid(T); } #endif static void destroy(storage_union& storage) noexcept { //assert(reinterpret_cast(storage.dynamic)); delete reinterpret_cast(storage.dynamic); } static void copy(const storage_union& src, storage_union& dest) { dest.dynamic = new T(*reinterpret_cast(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 exchange the storage pointers. std::swap(lhs.dynamic, rhs.dynamic); } }; /// VTable for stack allocated storage. template struct vtable_stack { #ifndef ANY_IMPL_NO_RTTI static const std::type_info& type() noexcept { return typeid(T); } #endif static void destroy(storage_union& storage) noexcept { reinterpret_cast(&storage.stack)->~T(); } static void copy(const storage_union& src, storage_union& dest) { new (&dest.stack) T(reinterpret_cast(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(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 struct requires_allocation : std::integral_constant::value // N4562 §6.3/3 [any.class] && sizeof(T) <= sizeof(storage_union::stack) && std::alignment_of::value <= std::alignment_of::value)> {}; /// Returns the pointer to the vtable of the type T. template static vtable_type* vtable_for_type() { using VTableType = typename std::conditional::value, vtable_dynamic, vtable_stack>::type; static vtable_type table = { #ifndef ANY_IMPL_NO_RTTI VTableType::type, #endif VTableType::destroy, VTableType::copy, VTableType::move, VTableType::swap, }; return &table; } protected: template friend const T* any_cast(const any* operand) noexcept; template friend T* any_cast(any* operand) noexcept; #ifndef ANY_IMPL_NO_RTTI /// Same effect as is_same(this->type(), t); bool is_typed(const std::type_info& t) const { return is_same(this->type(), t); } #endif #ifndef ANY_IMPL_NO_RTTI /// 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 } #endif /// Casts (with no type_info checks) the storage pointer as const T*. template const T* cast() const noexcept { return requires_allocation::type>::value? reinterpret_cast(storage.dynamic) : reinterpret_cast(&storage.stack); } /// Casts (with no type_info checks) the storage pointer as T*. template T* cast() noexcept { return requires_allocation::type>::value? reinterpret_cast(storage.dynamic) : reinterpret_cast(&storage.stack); } private: storage_union storage; // on offset(0) so no padding for align vtable_type* vtable; template typename std::enable_if::value>::type do_emplace(Args&&... args) { storage.dynamic = new T(std::forward(args)...); } template typename std::enable_if::value>::type do_emplace(Args&&... args) { new (&storage.stack) T(std::forward(args)...); } template void emplace_construct(Args&&... args) { using T = typename std::decay::type; this->vtable = vtable_for_type(); do_emplace(std::forward(args)...); } template typename std::enable_if::value>::type do_construct(ValueType&& value) { storage.dynamic = new T(std::forward(value)); } template typename std::enable_if::value>::type do_construct(ValueType&& value) { new (&storage.stack) T(std::forward(value)); } /// Chooses between stack and dynamic allocation for the type decay_t, /// assigns the correct vtable, and constructs the object on our storage. template void construct(ValueType&& value) { using T = typename std::decay::type; this->vtable = vtable_for_type(); do_construct(std::forward(value)); } }; namespace detail { template inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::true_type) { return std::move(*p); } template inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::false_type) { return *p; } } /// Performs *any_cast>>(&operand), or throws bad_any_cast on failure. template inline ValueType any_cast(const any& operand) { auto p = any_cast::type>::type>(&operand); #ifndef ANY_IMPL_NO_EXCEPTIONS if(p == nullptr) throw bad_any_cast(); #endif return *p; } /// Performs *any_cast>(&operand), or throws bad_any_cast on failure. template inline ValueType any_cast(any& operand) { auto p = any_cast::type>(&operand); #ifndef ANY_IMPL_NO_EXCEPTIONS if(p == nullptr) throw bad_any_cast(); #endif return *p; } /// /// If ValueType is MoveConstructible and isn't a lvalue reference, performs /// std::move(*any_cast>(&operand)), otherwise /// *any_cast>(&operand). Throws bad_any_cast on failure. /// template inline ValueType any_cast(any&& operand) { using can_move = std::integral_constant::value && !std::is_lvalue_reference::value>; auto p = any_cast::type>(&operand); #ifndef ANY_IMPL_NO_EXCEPTIONS if(p == nullptr) throw bad_any_cast(); #endif return detail::any_cast_move_if_true(p, can_move()); } /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object /// contained by operand, otherwise nullptr. template inline const ValueType* any_cast(const any* operand) noexcept { using T = typename std::decay::type; #ifndef ANY_IMPL_NO_RTTI if (operand && operand->is_typed(typeid(T))) #else if (operand && operand->vtable == any::vtable_for_type()) #endif return operand->cast(); else return nullptr; } /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object /// contained by operand, otherwise nullptr. template inline ValueType* any_cast(any* operand) noexcept { using T = typename std::decay::type; #ifndef ANY_IMPL_NO_RTTI if (operand && operand->is_typed(typeid(T))) #else if (operand && operand->vtable == any::vtable_for_type()) #endif return operand->cast(); else return nullptr; } inline void swap(any& lhs, any& rhs) noexcept { lhs.swap(rhs); } template any make_any(Args&&... args) { return any(in_place_type_t{}, std::forward(args)...); } template any make_any(std::initializer_list il, Args&&... args) { return any(in_place_type_t{}, il, std::forward(args)...); } } #endif