/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright The KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ // This code is a modified version of the GCC standard library implementation (see original // licence below): // Copyright (C) 2014-2024 Free Software Foundation, Inc. // // This file is part of the GNU ISO C++ Library. This library is free // software; you can redistribute it and/or modify it under the // terms of the GNU General Public License as published by the // Free Software Foundation; either version 3, or (at your option) // any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // Under Section 7 of GPL version 3, you are granted additional // permissions described in the GCC Runtime Library Exception, version // 3.1, as published by the Free Software Foundation. // // You should have received a copy of the GNU General Public License and // a copy of the GCC Runtime Library Exception along with this program; // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // . /** * @file ki_any.h * @brief An implementation of std::any_cast, which uses type_info::hash_code to check validity of * cast types. * * This is required as Clang compares types as being equivalent based on their type_info pointer * locations. These are not guaranteed to be the same with identical types linked in multiple * targets from shared libraries. The current Clang implementation of type_info::hash_code is * based on the type names, which should be consistent across translation units. */ #ifndef INCLUDE_KI_ANY_H_ #define INCLUDE_KI_ANY_H_ #include #include #include #include namespace ki { /* * Disambiguation helpers */ template inline constexpr bool is_in_place_type_v = false; template inline constexpr bool is_in_place_type_v> = true; /** * Exception class thrown by a failed @c any_cast */ class bad_any_cast final : public std::bad_cast { public: const char* what() const noexcept override { return "bad ki::any_cast"; } }; /** * A type-safe container of any type. * * An `any` object's state is either empty or it stores a contained object * of CopyConstructible type. */ class any { // Holds either a pointer to a heap object or the contained object itself union Storage { constexpr Storage() : m_ptr{ nullptr } {} // Prevent trivial copies of this type as the buffer might hold a non-POD Storage( const Storage& ) = delete; Storage& operator=( const Storage& ) = delete; void* m_ptr; unsigned char m_buffer[sizeof( m_ptr )]; }; template , bool Fits = ( sizeof( T ) <= sizeof( Storage ) ) && ( alignof( T ) <= alignof( Storage ) )> using Use_Internal_Storage = std::integral_constant; template struct Manager_Internal; // uses small-object optimization template struct Manager_External; // creates contained object on the heap template using Manager = std::conditional_t::value, Manager_Internal, Manager_External>; template > using decay_if_not_any = std::enable_if_t, V>; /// Emplace with an object created from @p args as the contained object. template > void do_emplace( Args&&... args ) { reset(); Mgr::do_create( m_storage, std::forward( args )... ); m_manager = &Mgr::m_manage_fn; } /// Emplace with an object created from @p il and @p args as the contained object. template > void do_emplace( std::initializer_list il, Args&&... args ) { reset(); Mgr::do_create( m_storage, il, std::forward( args )... ); m_manager = &Mgr::m_manage_fn; } template using any_constructible = std::enable_if && std::is_constructible_v, Res>; template using any_constructible_t = typename any_constructible::type; template using any_emplace_t = typename any_constructible::type; public: /// Default constructor, creates an empty object. constexpr any() noexcept : m_manager( nullptr ) {} /// Copy constructor, copies the state of @p other. any( const any& other ) { if( !other.has_value() ) { m_manager = nullptr; } else { Arg arg; arg.m_any = this; other.m_manager( Op_Clone, &other, &arg ); } } /// Move constructor, transfer the state from @p other. any( any&& other ) noexcept { if( !other.has_value() ) { m_manager = nullptr; } else { Arg arg; arg.m_any = this; other.m_manager( Op_Xfer, &other, &arg ); } } /// Construct with a copy of @p value as the contained object. template , typename Mgr = Manager, std::enable_if_t && !is_in_place_type_v, bool> = true> any( T&& value ) : m_manager( &Mgr::m_manage_fn ) { Mgr::do_create( m_storage, std::forward( value ) ); } /// Construct with an object created from @p args as the contained object. template , typename Mgr = Manager, any_constructible_t = false> explicit any( std::in_place_type_t, Args&&... args ) : m_manager( &Mgr::m_manage_fn ) { Mgr::do_create( m_storage, std::forward( args )... ); } /// Construct with an object created from @p il and @p args as the contained object. template , typename Mgr = Manager, any_constructible_t&, Args&&...> = false> explicit any( std::in_place_type_t, std::initializer_list il, Args&&... args ) : m_manager( &Mgr::m_manage_fn ) { Mgr::do_create( m_storage, il, std::forward( args )... ); } /// Destructor, calls @c reset(). ~any() { reset(); } /// Copy the state of another object. any& operator=( const any& rhs ) { *this = any( rhs ); return *this; } /// Move assignment operator. any& operator=( any&& rhs ) noexcept { if( !rhs.has_value() ) { reset(); } else if( this != &rhs ) { reset(); Arg arg; arg.m_any = this; rhs.m_manager( Op_Xfer, &rhs, &arg ); } return *this; } /// Store a copy of @p rhs as the contained object. template std::enable_if_t>, any&> operator=( T&& rhs ) { *this = any( std::forward( rhs ) ); return *this; } /// Emplace with an object created from @p args as the contained object. template any_emplace_t, Args...> emplace( Args&&... args ) { using V = std::decay_t; do_emplace( std::forward( args )... ); return *any::Manager::do_access( m_storage ); } /// Emplace with an object created from @p il and @p args as the contained object. template any_emplace_t, std::initializer_list&, Args&&...> emplace( std::initializer_list il, Args&&... args ) { using V = std::decay_t; do_emplace( il, std::forward( args )... ); return *any::Manager::do_access( m_storage ); } /// If not empty, destroys the contained object. void reset() noexcept { if( has_value() ) { m_manager( Op_Destroy, this, nullptr ); m_manager = nullptr; } } /// Exchange state with another object. void swap( any& rhs ) noexcept { if( !has_value() && !rhs.has_value() ) return; if( has_value() && rhs.has_value() ) { if( this == &rhs ) return; any tmp; Arg arg; arg.m_any = &tmp; rhs.m_manager( Op_Xfer, &rhs, &arg ); arg.m_any = &rhs; m_manager( Op_Xfer, this, &arg ); arg.m_any = this; tmp.m_manager( Op_Xfer, &tmp, &arg ); } else { any* empty = !has_value() ? this : &rhs; any* full = !has_value() ? &rhs : this; Arg arg; arg.m_any = empty; full->m_manager( Op_Xfer, full, &arg ); } } /// Report whether there is a contained object or not. bool has_value() const noexcept { return m_manager != nullptr; } /// The @c typeid of the contained object, or @c typeid(void) if empty. const std::type_info& type() const noexcept { if( !has_value() ) return typeid( void ); Arg arg; m_manager( Op_Get_Type_Info, this, &arg ); return *arg.m_typeinfo; } /// @cond undocumented template static constexpr bool is_valid_any_cast() { return std::is_reference_v || std::is_copy_constructible_v; } /// @endcond private: enum Op { Op_Access, Op_Get_Type_Info, Op_Clone, Op_Destroy, Op_Xfer }; union Arg { void* m_obj; const std::type_info* m_typeinfo; any* m_any; }; void ( *m_manager )( Op, const any*, Arg* ); Storage m_storage; /// @cond undocumented template friend void* any_caster( const any* any ); /// @endcond // Manages in-place contained object template struct Manager_Internal { static void m_manage_fn( Op which, const any* any, Arg* arg ); template static void do_create( Storage& storage, U&& value ) { void* addr = &storage.m_buffer; ::new( addr ) T( std::forward( value ) ); } template static void do_create( Storage& storage, Args&&... args ) { void* addr = &storage.m_buffer; ::new( addr ) T( std::forward( args )... ); } static T* do_access( const Storage& storage ) { // The contained object is in storage.m_buffer const void* addr = &storage.m_buffer; return static_cast( const_cast( addr ) ); } }; // Manages externally (heap) contained object template struct Manager_External { static void m_manage_fn( Op which, const any* any, Arg* arg ); template static void do_create( Storage& storage, U&& value ) { storage.m_ptr = new T( std::forward( value ) ); } template static void do_create( Storage& storage, Args&&... args ) { storage.m_ptr = new T( std::forward( args )... ); } static T* do_access( const Storage& storage ) { // The contained object is in *storage.m_ptr return static_cast( storage.m_ptr ); } }; }; /// Exchange the states of two @c any objects. inline void swap( any& x, any& y ) noexcept { x.swap( y ); } /// Create a `any` holding a `T` constructed from `args...`. template std::enable_if_t, Args...>, any> make_any( Args&&... args ) { return any( std::in_place_type, std::forward( args )... ); } /// Create an `any` holding a `T` constructed from `il` and `args...`. template std::enable_if_t< std::is_constructible_v, std::initializer_list&, Args...>, any> make_any( std::initializer_list il, Args&&... args ) { return any( std::in_place_type, il, std::forward( args )... ); } /** * Access the contained object. * * @tparam ValueType A const-reference or CopyConstructible type. * @param any The object to access. * @return The contained object. * @throw bad_any_cast If * any.type() != typeid(remove_reference_t) * */ template ValueType any_cast( const any& any ) { using U = std::remove_cvref_t; static_assert( any::is_valid_any_cast(), "Template argument must be a reference or CopyConstructible type" ); static_assert( std::is_constructible_v, "Template argument must be constructible from a const value" ); auto p = any_cast( &any ); if( p ) return static_cast( *p ); throw bad_any_cast{}; } /** * Access the contained object. * * @tparam ValueType A reference or CopyConstructible type. * @param any The object to access. * @return The contained object. * @throw bad_any_cast If * any.type() != typeid(remove_reference_t) * * @{ */ template ValueType any_cast( any& any ) { using U = std::remove_cvref_t; static_assert( any::is_valid_any_cast(), "Template argument must be a reference or CopyConstructible type" ); static_assert( std::is_constructible_v, "Template argument must be constructible from an lvalue" ); auto p = any_cast( &any ); if( p ) return static_cast( *p ); throw bad_any_cast{}; } template ValueType any_cast( any&& any ) { using U = std::remove_cvref_t; static_assert( any::is_valid_any_cast(), "Template argument must be a reference or CopyConstructible type" ); static_assert( std::is_constructible_v, "Template argument must be constructible from an rvalue" ); auto p = any_cast( &any ); if( p ) return static_cast( std::move( *p ) ); throw bad_any_cast{}; } /// @} /// @cond undocumented template void* any_caster( const any* any ) { // any_cast returns non-null if any->type() == typeid(T) and // typeid(T) ignores cv-qualifiers so remove them: using U = std::remove_cv_t; if constexpr( !std::is_same_v, U> ) { // The contained value has a decayed type, so if decay_t is not U, // then it's not possible to have a contained value of type U return nullptr; } else if constexpr( !std::is_copy_constructible_v ) { // Only copy constructible types can be used for contained values return nullptr; } else if( any->m_manager == &any::Manager::m_manage_fn || any->type().hash_code() == typeid( T ).hash_code() ) { return any::Manager::do_access( any->m_storage ); } return nullptr; } /// @endcond /** * Access the contained object. * * @tparam ValueType The type of the contained object. * @param any A pointer to the object to access. * @return The address of the contained object if * any != nullptr && any.type() == typeid(ValueType) * , otherwise a null pointer. * * @{ */ template const ValueType* any_cast( const any* any ) noexcept { static_assert( !std::is_void_v ); // As an optimization, don't bother instantiating any_caster for // function types, since std::any can only hold objects if constexpr( std::is_object_v ) { if( any ) return static_cast( any_caster( any ) ); } return nullptr; } template ValueType* any_cast( any* any ) noexcept { static_assert( !std::is_void_v ); if constexpr( std::is_object_v ) if( any ) return static_cast( any_caster( any ) ); return nullptr; } /// @} template void any::Manager_Internal::m_manage_fn( Op which, const any* any, Arg* arg ) { // The contained object is in m_storage.m_buffer auto ptr = reinterpret_cast( &any->m_storage.m_buffer ); switch( which ) { case Op_Access: arg->m_obj = const_cast( ptr ); break; case Op_Get_Type_Info: arg->m_typeinfo = &typeid( T ); break; case Op_Clone: ::new( &arg->m_any->m_storage.m_buffer ) T( *ptr ); arg->m_any->m_manager = any->m_manager; break; case Op_Destroy: ptr->~T(); break; case Op_Xfer: ::new( &arg->m_any->m_storage.m_buffer ) T( std::move( *const_cast( ptr ) ) ); ptr->~T(); arg->m_any->m_manager = any->m_manager; const_cast( any )->m_manager = nullptr; break; } } template void any::Manager_External::m_manage_fn( Op which, const any* any, Arg* arg ) { // The contained object is *m_storage.m_ptr auto ptr = static_cast( any->m_storage.m_ptr ); switch( which ) { case Op_Access: arg->m_obj = const_cast( ptr ); break; case Op_Get_Type_Info: arg->m_typeinfo = &typeid( T ); break; case Op_Clone: arg->m_any->m_storage.m_ptr = new T( *ptr ); arg->m_any->m_manager = any->m_manager; break; case Op_Destroy: delete ptr; break; case Op_Xfer: arg->m_any->m_storage.m_ptr = any->m_storage.m_ptr; arg->m_any->m_manager = any->m_manager; const_cast( any )->m_manager = nullptr; break; } } } // namespace ki #endif // INCLUDE_KI_ANY_H_