diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp index b46d38a66a..0ff0385364 100644 --- a/common/tool/tool_manager.cpp +++ b/common/tool/tool_manager.cpp @@ -290,7 +290,7 @@ bool TOOL_MANAGER::InvokeTool( const std::string& aToolName ) } -bool TOOL_MANAGER::doRunAction( const std::string& aActionName, bool aNow, const std::any& aParam, +bool TOOL_MANAGER::doRunAction( const std::string& aActionName, bool aNow, const ki::any& aParam, COMMIT* aCommit ) { TOOL_ACTION* action = m_actionMgr->FindAction( aActionName ); @@ -325,7 +325,7 @@ VECTOR2D TOOL_MANAGER::GetCursorPosition() const } -bool TOOL_MANAGER::doRunAction( const TOOL_ACTION& aAction, bool aNow, const std::any& aParam, +bool TOOL_MANAGER::doRunAction( const TOOL_ACTION& aAction, bool aNow, const ki::any& aParam, COMMIT* aCommit ) { if( m_shuttingDown ) diff --git a/include/ki_any.h b/include/ki_any.h new file mode 100644 index 0000000000..35a9645d9a --- /dev/null +++ b/include/ki_any.h @@ -0,0 +1,623 @@ +/* +* This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2024 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 <http://www.gnu.org/licenses/>. + */ + +// 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 +// <http://www.gnu.org/licenses/>. + + +/** 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 <initializer_list> +#include <new> +#include <typeinfo> +#include <utility> + +namespace ki +{ + +/* + * Disambiguation helpers + */ + +template <typename> +inline constexpr bool is_in_place_type_v = false; + +template <typename T> +inline constexpr bool is_in_place_type_v<std::in_place_type_t<T>> = true; + +/** + * @brief 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"; } +}; + +/** + * @brief 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 <typename T, bool Safe = std::is_nothrow_move_constructible_v<T>, + bool Fits = ( sizeof( T ) <= sizeof( Storage ) ) + && ( alignof( T ) <= alignof( Storage ) )> + using Use_Internal_Storage = std::integral_constant<bool, Safe && Fits>; + + template <typename T> + struct Manager_Internal; // uses small-object optimization + + template <typename T> + struct Manager_External; // creates contained object on the heap + + template <typename T> + using Manager = std::conditional_t<Use_Internal_Storage<T>::value, Manager_Internal<T>, + Manager_External<T>>; + + template <typename T, typename V = std::decay_t<T>> + using decay_if_not_any = std::enable_if_t<!std::is_same_v<V, any>, V>; + + /// @brief Emplace with an object created from @p args as the contained object + template <typename T, typename... Args, typename Mgr = Manager<T>> + void do_emplace( Args&&... args ) + { + reset(); + Mgr::do_create( m_storage, std::forward<Args>( args )... ); + m_manager = &Mgr::m_manage_fn; + } + + /// @brief Emplace with an object created from @p il and @p args as the contained object + template <typename T, typename U, typename... Args, typename Mgr = Manager<T>> + void do_emplace( std::initializer_list<U> il, Args&&... args ) + { + reset(); + Mgr::do_create( m_storage, il, std::forward<Args>( args )... ); + m_manager = &Mgr::m_manage_fn; + } + + template <typename Res, typename T, typename... Args> + using any_constructible = + std::enable_if<std::is_copy_constructible_v<T> && std::is_constructible_v<T, Args...>, + Res>; + + template <typename T, typename... Args> + using any_constructible_t = typename any_constructible<bool, T, Args...>::type; + + template <typename V, typename... Args> + using any_emplace_t = typename any_constructible<V&, V, Args...>::type; + +public: + /// @brief Default constructor, creates an empty object + constexpr any() noexcept : m_manager( nullptr ) {} + + /// @brief 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 ); + } + } + + /// @brief 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 ); + } + } + + /// @brief Construct with a copy of @p value as the contained object + template <typename T, typename V = decay_if_not_any<T>, typename Mgr = Manager<V>, + std::enable_if_t<std::is_copy_constructible_v<V> && !is_in_place_type_v<V>, bool> = + true> + + any( T&& value ) : m_manager( &Mgr::m_manage_fn ) + { + Mgr::do_create( m_storage, std::forward<T>( value ) ); + } + + /// @brief Construct with an object created from @p args as the contained object + template <typename T, typename... Args, typename V = std::decay_t<T>, typename Mgr = Manager<V>, + any_constructible_t<V, Args&&...> = false> + explicit any( std::in_place_type_t<T>, Args&&... args ) : m_manager( &Mgr::m_manage_fn ) + { + Mgr::do_create( m_storage, std::forward<Args>( args )... ); + } + + /// @brief Construct with an object created from @p il and @p args as the contained object + template <typename T, typename U, typename... Args, typename V = std::decay_t<T>, + typename Mgr = Manager<V>, + any_constructible_t<V, std::initializer_list<U>&, Args&&...> = false> + explicit any( std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args ) : + m_manager( &Mgr::m_manage_fn ) + { + Mgr::do_create( m_storage, il, std::forward<Args>( args )... ); + } + + /// @brief Destructor, calls @c reset() + ~any() { reset(); } + + /// @brief Copy the state of another object + any& operator=( const any& rhs ) + { + *this = any( rhs ); + return *this; + } + + /// @brief 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 <typename T> + std::enable_if_t<std::is_copy_constructible_v<decay_if_not_any<T>>, any&> operator=( T&& rhs ) + { + *this = any( std::forward<T>( rhs ) ); + return *this; + } + + /// Emplace with an object created from @p args as the contained object + template <typename T, typename... Args> + any_emplace_t<std::decay_t<T>, Args...> emplace( Args&&... args ) + { + using V = std::decay_t<T>; + do_emplace<V>( std::forward<Args>( args )... ); + return *any::Manager<V>::do_access( m_storage ); + } + + /// Emplace with an object created from @p il and @p args as the contained object + template <typename T, typename U, typename... Args> + any_emplace_t<std::decay_t<T>, std::initializer_list<U>&, Args&&...> + emplace( std::initializer_list<U> il, Args&&... args ) + { + using V = std::decay_t<T>; + do_emplace<V, U>( il, std::forward<Args>( args )... ); + return *any::Manager<V>::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 ); + } + } + + /// Reports 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 <typename T> + static constexpr bool is_valid_any_cast() + { + return std::is_reference_v<T> || std::is_copy_constructible_v<T>; + } + /// @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 <typename T> + friend void* any_caster( const any* any ); + /// @endcond + + // Manages in-place contained object + template <typename T> + struct Manager_Internal + { + static void m_manage_fn( Op which, const any* any, Arg* arg ); + + template <typename U> + static void do_create( Storage& storage, U&& value ) + { + void* addr = &storage.m_buffer; + ::new( addr ) T( std::forward<U>( value ) ); + } + + template <typename... Args> + static void do_create( Storage& storage, Args&&... args ) + { + void* addr = &storage.m_buffer; + ::new( addr ) T( std::forward<Args>( 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<T*>( const_cast<void*>( addr ) ); + } + }; + + // Manages externally (heap) contained object + template <typename T> + struct Manager_External + { + static void m_manage_fn( Op which, const any* any, Arg* arg ); + + template <typename U> + static void do_create( Storage& storage, U&& value ) + { + storage.m_ptr = new T( std::forward<U>( value ) ); + } + template <typename... Args> + static void do_create( Storage& storage, Args&&... args ) + { + storage.m_ptr = new T( std::forward<Args>( args )... ); + } + static T* do_access( const Storage& storage ) + { + // The contained object is in *storage.m_ptr + return static_cast<T*>( 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 <typename T, typename... Args> +std::enable_if_t<std::is_constructible_v<any, std::in_place_type_t<T>, Args...>, any> +make_any( Args&&... args ) +{ + return any( std::in_place_type<T>, std::forward<Args>( args )... ); +} + +/// Create an `any` holding a `T` constructed from `il` and `args...`. +template <typename T, typename U, typename... Args> +std::enable_if_t< + std::is_constructible_v<any, std::in_place_type_t<T>, std::initializer_list<U>&, Args...>, + any> +make_any( std::initializer_list<U> il, Args&&... args ) +{ + return any( std::in_place_type<T>, il, std::forward<Args>( args )... ); +} + +/** + * @brief 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 <code> + * any.type() != typeid(remove_reference_t<ValueType>) + * </code> + */ +template <typename ValueType> +ValueType any_cast( const any& any ) +{ + using U = std::remove_cvref_t<ValueType>; + + static_assert( any::is_valid_any_cast<ValueType>(), + "Template argument must be a reference or CopyConstructible type" ); + static_assert( std::is_constructible_v<ValueType, const U&>, + "Template argument must be constructible from a const value" ); + + auto p = any_cast<U>( &any ); + + if( p ) + return static_cast<ValueType>( *p ); + + throw bad_any_cast{}; +} + +/** + * @brief 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 <code> + * any.type() != typeid(remove_reference_t<ValueType>) + * </code> + * @{ + */ +template <typename ValueType> +ValueType any_cast( any& any ) +{ + using U = std::remove_cvref_t<ValueType>; + + static_assert( any::is_valid_any_cast<ValueType>(), + "Template argument must be a reference or CopyConstructible type" ); + static_assert( std::is_constructible_v<ValueType, U&>, + "Template argument must be constructible from an lvalue" ); + + auto p = any_cast<U>( &any ); + + if( p ) + return static_cast<ValueType>( *p ); + + throw bad_any_cast{}; +} + +template <typename ValueType> +ValueType any_cast( any&& any ) +{ + using U = std::remove_cvref_t<ValueType>; + + static_assert( any::is_valid_any_cast<ValueType>(), + "Template argument must be a reference or CopyConstructible type" ); + static_assert( std::is_constructible_v<ValueType, U>, + "Template argument must be constructible from an rvalue" ); + + auto p = any_cast<U>( &any ); + + if( p ) + return static_cast<ValueType>( std::move( *p ) ); + + throw bad_any_cast{}; +} + +/// @} + +/// @cond undocumented +template <typename T> +void* any_caster( const any* any ) +{ + // any_cast<T> returns non-null if any->type() == typeid(T) and + // typeid(T) ignores cv-qualifiers so remove them: + using U = std::remove_cv_t<T>; + + if constexpr( !std::is_same_v<std::decay_t<U>, U> ) + { + // The contained value has a decayed type, so if decay_t<U> 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<U> ) + { + // Only copy constructible types can be used for contained values + return nullptr; + } + else if( any->m_manager == &any::Manager<U>::m_manage_fn + || any->type().hash_code() == typeid( T ).hash_code() ) + { + return any::Manager<U>::do_access( any->m_storage ); + } + + return nullptr; +} +/// @endcond + +/** + * @brief 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 <code> + * any != nullptr && any.type() == typeid(ValueType) + * </code>, otherwise a null pointer. + * + * @{ + */ +template <typename ValueType> +const ValueType* any_cast( const any* any ) noexcept +{ + static_assert( !std::is_void_v<ValueType> ); + + // 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<ValueType> ) + { + if( any ) + return static_cast<ValueType*>( any_caster<ValueType>( any ) ); + } + + return nullptr; +} + +template <typename ValueType> +ValueType* any_cast( any* any ) noexcept +{ + static_assert( !std::is_void_v<ValueType> ); + + if constexpr( std::is_object_v<ValueType> ) + if( any ) + return static_cast<ValueType*>( any_caster<ValueType>( any ) ); + return nullptr; +} +/// @} + +template <typename T> +void any::Manager_Internal<T>::m_manage_fn( Op which, const any* any, Arg* arg ) +{ + // The contained object is in m_storage.m_buffer + auto ptr = reinterpret_cast<const T*>( &any->m_storage.m_buffer ); + switch( which ) + { + case Op_Access: arg->m_obj = const_cast<T*>( 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<T*>( ptr ) ) ); + ptr->~T(); + arg->m_any->m_manager = any->m_manager; + const_cast<ki::any*>( any )->m_manager = nullptr; + break; + } +} + +template <typename T> +void any::Manager_External<T>::m_manage_fn( Op which, const any* any, Arg* arg ) +{ + // The contained object is *m_storage.m_ptr + auto ptr = static_cast<const T*>( any->m_storage.m_ptr ); + switch( which ) + { + case Op_Access: arg->m_obj = const_cast<T*>( 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<ki::any*>( any )->m_manager = nullptr; + break; + } +} + +} // namespace ki + +#endif // INCLUDE_KI_ANY_H_ diff --git a/include/tool/tool_action.h b/include/tool/tool_action.h index 5c242d1e08..e709c559d4 100644 --- a/include/tool/tool_action.h +++ b/include/tool/tool_action.h @@ -28,12 +28,12 @@ #ifndef __TOOL_ACTION_H #define __TOOL_ACTION_H -#include <any> #include <cassert> #include <optional> #include <string> #include <string_view> +#include <ki_any.h> #include <wx/string.h> class TOOL_EVENT; @@ -251,7 +251,7 @@ protected: std::optional<TOOL_ACTION_GROUP> m_group; - std::any m_param; + ki::any m_param; }; /** @@ -377,9 +377,9 @@ public: try { - param = std::any_cast<T>( m_param ); + param = ki::any_cast<T>( m_param ); } - catch( const std::bad_any_cast& e ) + catch( const ki::bad_any_cast& e ) { wxASSERT_MSG( false, wxString::Format( "Requested parameter type %s from action with parameter type %s.", @@ -455,7 +455,7 @@ protected: std::optional<int> m_uiid; // ID to use when interacting with the UI (if empty, generate one) TOOL_ACTION_FLAGS m_flags; - std::any m_param; // Generic parameter + ki::any m_param; // Generic parameter }; #endif diff --git a/include/tool/tool_event.h b/include/tool/tool_event.h index 4ff93b9e05..4aedc6b05f 100644 --- a/include/tool/tool_event.h +++ b/include/tool/tool_event.h @@ -27,11 +27,12 @@ #ifndef __TOOL_EVENT_H #define __TOOL_EVENT_H -#include <any> #include <cstdio> #include <deque> #include <iterator> +#include <ki_any.h> + #include <math/vector2d.h> #include <optional> #include <atomic> @@ -466,9 +467,9 @@ public: try { - param = std::any_cast<T>( m_param ); + param = ki::any_cast<T>( m_param ); } - catch( const std::bad_any_cast& ) + catch( const ki::bad_any_cast& ) { wxCHECK_MSG( false, T(), wxString::Format( "Requested parameter type %s from event " "with parameter type %s.", @@ -492,9 +493,9 @@ public: try { - param = std::any_cast<T>( m_param ); + param = ki::any_cast<T>( m_param ); } - catch( const std::bad_any_cast& ) + catch( const ki::bad_any_cast& ) { wxCHECK_MSG( false, param, wxString::Format( "Requested parameter type %s from event " "with parameter type %s.", @@ -617,7 +618,7 @@ private: COMMIT* m_commit; ///< Generic parameter used for passing non-standard data. - std::any m_param; + ki::any m_param; ///< The first tool to receive the event TOOL_BASE* m_firstResponder; diff --git a/include/tool/tool_manager.h b/include/tool/tool_manager.h index b4745cc88c..a8939312c9 100644 --- a/include/tool/tool_manager.h +++ b/include/tool/tool_manager.h @@ -150,15 +150,15 @@ public: bool RunAction( const std::string& aActionName, T aParam ) { // Use a cast to ensure the proper type is stored inside the parameter - std::any a( static_cast<T>( aParam ) ); + ki::any a( static_cast<T>( aParam ) ); return doRunAction( aActionName, true, a, nullptr ); } bool RunAction( const std::string& aActionName ) { - // Default initialize the parameter argument to an empty std::any - std::any a; + // Default initialize the parameter argument to an empty ki_any + ki::any a; return doRunAction( aActionName, true, a, nullptr ); } @@ -178,7 +178,7 @@ public: bool RunAction( const TOOL_ACTION& aAction, T aParam ) { // Use a cast to ensure the proper type is stored inside the parameter - std::any a( static_cast<T>( aParam ) ); + ki::any a( static_cast<T>( aParam ) ); return doRunAction( aAction, true, a, nullptr ); } @@ -197,23 +197,23 @@ public: bool RunSynchronousAction( const TOOL_ACTION& aAction, COMMIT* aCommit, T aParam ) { // Use a cast to ensure the proper type is stored inside the parameter - std::any a( static_cast<T>( aParam ) ); + ki::any a( static_cast<T>( aParam ) ); return doRunAction( aAction, true, a, aCommit ); } bool RunSynchronousAction( const TOOL_ACTION& aAction, COMMIT* aCommit ) { - // Default initialize the parameter argument to an empty std::any - std::any a; + // Default initialize the parameter argument to an empty ki_any + ki::any a; return doRunAction( aAction, true, a, aCommit ); } bool RunAction( const TOOL_ACTION& aAction ) { - // Default initialize the parameter argument to an empty std::any - std::any a; + // Default initialize the parameter argument to an empty ki_any + ki::any a; return doRunAction( aAction, true, a, nullptr ); } @@ -235,15 +235,15 @@ public: bool PostAction( const std::string& aActionName, T aParam ) { // Use a cast to ensure the proper type is stored inside the parameter - std::any a( static_cast<T>( aParam ) ); + ki::any a( static_cast<T>( aParam ) ); return doRunAction( aActionName, false, a, nullptr ); } bool PostAction( const std::string& aActionName ) { - // Default initialize the parameter argument to an empty std::any - std::any a; + // Default initialize the parameter argument to an empty ki_any + ki::any a; return doRunAction( aActionName, false, a, nullptr ); } @@ -262,23 +262,23 @@ public: bool PostAction( const TOOL_ACTION& aAction, T aParam ) { // Use a cast to ensure the proper type is stored inside the parameter - std::any a( static_cast<T>( aParam ) ); + ki::any a( static_cast<T>( aParam ) ); return doRunAction( aAction, false, a, nullptr ); } void PostAction( const TOOL_ACTION& aAction ) { - // Default initialize the parameter argument to an empty std::any - std::any a; + // Default initialize the parameter argument to an empty ki_any + ki::any a; doRunAction( aAction, false, a, nullptr ); } bool PostAction( const TOOL_ACTION& aAction, COMMIT* aCommit ) { - // Default initialize the parameter argument to an empty std::any - std::any a; + // Default initialize the parameter argument to an empty ki_any + ki::any a; return doRunAction( aAction, false, a, aCommit ); } @@ -537,8 +537,10 @@ private: /** * Helper function to actually run an action. */ - bool doRunAction( const TOOL_ACTION& aAction, bool aNow, const std::any& aParam, COMMIT* aCommit ); - bool doRunAction( const std::string& aActionName, bool aNow, const std::any& aParam, COMMIT* aCommit ); + bool doRunAction( const TOOL_ACTION& aAction, bool aNow, const ki::any& aParam, + COMMIT* aCommit ); + bool doRunAction( const std::string& aActionName, bool aNow, const ki::any& aParam, + COMMIT* aCommit ); /** * Pass an event at first to the active tools, then to all others. diff --git a/qa/tests/common/CMakeLists.txt b/qa/tests/common/CMakeLists.txt index 5619b23c59..2c3cabf860 100644 --- a/qa/tests/common/CMakeLists.txt +++ b/qa/tests/common/CMakeLists.txt @@ -39,6 +39,7 @@ set( QA_COMMON_SRCS test_eda_text.cpp test_embedded_file_compress.cpp test_increment.cpp + test_ki_any.cpp test_lib_table.cpp test_markup_parser.cpp test_kicad_string.cpp diff --git a/qa/tests/common/test_ki_any.cpp b/qa/tests/common/test_ki_any.cpp new file mode 100644 index 0000000000..f0320f443d --- /dev/null +++ b/qa/tests/common/test_ki_any.cpp @@ -0,0 +1,840 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 1992-2024 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 <http://www.gnu.org/licenses/>. + */ + +// Tests taken from GCC: +// 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 +// <http://www.gnu.org/licenses/>. + +#include <boost/test/unit_test.hpp> + +#include <memory> +#include <set> +#include <utility> +#include <type_traits> + +#include <ki_any.h> + +using ki::any; +using ki::any_cast; + +////////////////////////////////////////// +// Exception hierarchy +////////////////////////////////////////// +/// +static_assert(std::is_base_of<std::bad_cast, ki::bad_any_cast>::value, + "ki::bad_any_cast must derive from std::bad_cast"); + +////////////////////////////////////////// +// Type requirements +////////////////////////////////////////// + +static_assert(std::is_assignable<any&, int>::value); +static_assert(!std::is_assignable<any&, std::unique_ptr<int>>::value); +static_assert(std::is_constructible<any, int>::value); +static_assert(!std::is_constructible<any, std::unique_ptr<int>>::value); +static_assert(!std::is_assignable<any&, const std::unique_ptr<int>&>::value); +static_assert(!std::is_constructible<any&, const std::unique_ptr<int>&>::value); +static_assert(!std::is_assignable<any&, std::unique_ptr<int>&>::value); +static_assert(!std::is_constructible<any&, std::unique_ptr<int>&>::value); + +struct NoDefaultCtor +{ + NoDefaultCtor() = delete; +}; + +static_assert(!std::is_constructible_v<any, + std::in_place_type_t<NoDefaultCtor>>); + +static_assert(!std::is_constructible_v<any, + std::in_place_type_t<NoDefaultCtor>&>); + +static_assert(!std::is_constructible_v<any, + std::in_place_type_t<NoDefaultCtor>&&>); + +static_assert(!std::is_constructible_v<any, + const std::in_place_type_t<NoDefaultCtor>&>); + +static_assert(!std::is_constructible_v<any, + const std::in_place_type_t<NoDefaultCtor>&&>); + +static_assert( std::is_copy_constructible_v<std::tuple<any>> ); + +struct A { + A(const A&) = default; + explicit A(any value); +}; +static_assert(std::is_copy_constructible_v<A>); + +BOOST_AUTO_TEST_SUITE( ki_any ) + +struct combined { + std::vector<int> v; + std::tuple<int, int> t; + template<class... Args> + combined(std::initializer_list<int> il, Args&&... args) + : v(il), t(std::forward<Args>(args)...) + { + } +}; + + +BOOST_AUTO_TEST_CASE( AnyCast_1 ) +{ + using std::string; + using std::strcmp; + + any x(5); // x holds int + BOOST_CHECK(any_cast<int>(x) == 5); // cast to value + any_cast<int&>(x) = 10; // cast to reference + BOOST_CHECK(any_cast<int>(x) == 10); + + x = "Meow"; // x holds const char* + BOOST_CHECK(strcmp(any_cast<const char*>(x), "Meow") == 0); + any_cast<const char*&>(x) = "Harry"; + BOOST_CHECK(strcmp(any_cast<const char*>(x), "Harry") == 0); + + x = string("Meow"); // x holds string + string s, s2("Jane"); + s = std::move(any_cast<string&>(x)); // move from any + BOOST_CHECK(s == "Meow"); + any_cast<string&>(x) = std::move(s2); // move to any + BOOST_CHECK(any_cast<const string&>(x) == "Jane"); + + string cat("Meow"); + const any y(cat); // const y holds string + BOOST_CHECK(any_cast<const string&>(y) == cat); +} + + +BOOST_AUTO_TEST_CASE( AnyCast_2 ) +{ + using ki::bad_any_cast; + any x(1); + auto p = any_cast<double>(&x); + BOOST_CHECK(p == nullptr); + + x = 1.0; + p = any_cast<double>(&x); + BOOST_CHECK(p != nullptr); + + x = any(); + p = any_cast<double>(&x); + BOOST_CHECK(p == nullptr); + + try { + any_cast<double>(x); + BOOST_CHECK(false); + } catch (const bad_any_cast&) { + } +} + +static int move_count = 0; + +BOOST_AUTO_TEST_CASE( AnyCast_3 ) +{ + struct MoveEnabled + { + MoveEnabled(MoveEnabled&&) + { + ++move_count; + } + MoveEnabled() = default; + MoveEnabled(const MoveEnabled&) = default; + }; + MoveEnabled m; + MoveEnabled m2 = any_cast<MoveEnabled>(any(m)); + BOOST_CHECK(move_count == 1); + MoveEnabled&& m3 = any_cast<MoveEnabled&&>(any(m)); + BOOST_CHECK(move_count == 1); +} + + +BOOST_AUTO_TEST_CASE( AnyCast_4 ) +{ + struct ExplicitCopy + { + ExplicitCopy() = default; + explicit ExplicitCopy(const ExplicitCopy&) = default; + }; + any x = ExplicitCopy(); + ExplicitCopy ec{any_cast<ExplicitCopy>(x)}; + ExplicitCopy ec2{any_cast<ExplicitCopy>(std::move(x))}; +} + + +BOOST_AUTO_TEST_CASE( AnyCast_5 ) +{ + struct noncopyable { + noncopyable(noncopyable const&) = delete; + }; + + any a; + auto p = any_cast<noncopyable>(&a); + BOOST_CHECK( p == nullptr ); +} + + +BOOST_AUTO_TEST_CASE( AnyCast_6 ) +{ + // The contained value of a std::any is always an object type, + // but any_cast does not forbid checking for function types. + + any a(1); + void (*p1)() = any_cast<void()>(&a); + BOOST_CHECK( p1 == nullptr ); + int (*p2)(int) = any_cast<int(int)>(&a); + BOOST_CHECK( p2 == nullptr ); + int (*p3)() = any_cast<int()>(&std::as_const(a)); + BOOST_CHECK( p3 == nullptr ); + + try { + any_cast<int(&)()>(a); + BOOST_CHECK( false ); + } catch (const ki::bad_any_cast&) { + } + + try { + any_cast<int(&)()>(std::move(a)); + BOOST_CHECK( false ); + } catch (const ki::bad_any_cast&) { + } + + try { + any_cast<int(&)()>(std::as_const(a)); + BOOST_CHECK( false ); + } catch (const ki::bad_any_cast&) { + } +} + + +BOOST_AUTO_TEST_CASE( AnyCast_7 ) +{ + int arr[3]; + any a(arr); + + BOOST_CHECK( a.type() == typeid(int*) ); // contained value is decayed + int (*p1)[3] = any_cast<int[3]>(&a); + BOOST_CHECK( a.type() != typeid(int[3]) ); // so any_cast should return nullptr + BOOST_CHECK( p1 == nullptr ); + int (*p2)[] = any_cast<int[]>(&a); + BOOST_CHECK( a.type() != typeid(int[]) ); // so any_cast should return nullptr + BOOST_CHECK( p2 == nullptr ); + const int (*p3)[] = any_cast<int[]>(&std::as_const(a)); + BOOST_CHECK( p3 == nullptr ); +} + + +struct LocationAware +{ + LocationAware() { } + ~LocationAware() { BOOST_CHECK(self == this); } + LocationAware(const LocationAware&) { } + LocationAware& operator=(const LocationAware&) { return *this; } + LocationAware(LocationAware&&) noexcept { } + LocationAware& operator=(LocationAware&&) noexcept { return *this; } + + void* const self = this; +}; +static_assert(std::is_nothrow_move_constructible<LocationAware>::value, ""); +static_assert(!std::is_trivially_copyable<LocationAware>::value, ""); + + +BOOST_AUTO_TEST_CASE( NonTrivialType_1 ) +{ + LocationAware l; + any a = l; +} + + +BOOST_AUTO_TEST_CASE( NonTrivialType_2 ) +{ + LocationAware l; + any a = l; + any b = a; + { + any tmp = std::move(a); + a = std::move(b); + b = std::move(tmp); + } +} + + +BOOST_AUTO_TEST_CASE( NonTrivialType_3 ) +{ + LocationAware l; + any a = l; + any b = a; + swap(a, b); +} + + +BOOST_AUTO_TEST_CASE( MakeAny ) +{ + const int i = 42; + auto o = ki::make_any<int>(i); + int& i2 = any_cast<int&>(o); + BOOST_CHECK( i2 == 42 ); + BOOST_CHECK( &i2 != &i ); + auto o2 = ki::make_any<std::tuple<int, int>>(1, 2); + std::tuple<int, int>& t = any_cast<std::tuple<int, int>&>(o2); + BOOST_CHECK( std::get<0>(t) == 1 && std::get<1>(t) == 2); + auto o3 = ki::make_any<std::vector<int>>({42, 666}); + std::vector<int>& v = any_cast<std::vector<int>&>(o3); + BOOST_CHECK(v[0] == 42 && v[1] == 666); + auto o4 = ki::make_any<combined>({42, 666}); + combined& c = any_cast<combined&>(o4); + BOOST_CHECK(c.v[0] == 42 && c.v[1] == 666 + && std::get<0>(c.t) == 0 && std::get<1>(c.t) == 0 ); + auto o5 = ki::make_any<combined>({1, 2}, 3, 4); + combined& c2 = any_cast<combined&>(o5); + BOOST_CHECK(c2.v[0] == 1 && c2.v[1] == 2 + && std::get<0>(c2.t) == 3 && std::get<1>(c2.t) == 4 ); +} + + +BOOST_AUTO_TEST_CASE( TypeObserver ) +{ + any x; + BOOST_CHECK( x.type() == typeid(void) ); + x = 1; + BOOST_CHECK( x.type() == typeid(int) ); + x = any(); + BOOST_CHECK( x.type() == typeid(void) ); +} + + +BOOST_AUTO_TEST_CASE( Swap ) +{ + any x(1); + any y; + swap(x, y); + BOOST_CHECK( !x.has_value() ); + BOOST_CHECK( y.has_value() ); +} + + +BOOST_AUTO_TEST_CASE( Modifiers ) +{ + any x(1); + any y; + x.swap(y); + BOOST_CHECK( !x.has_value() ); + BOOST_CHECK( y.has_value() ); + x.swap(y); + BOOST_CHECK( x.has_value() ); + BOOST_CHECK( !y.has_value() ); + + x.reset(); + BOOST_CHECK( !x.has_value() ); +} + + +BOOST_AUTO_TEST_CASE( Construction_InPlace_1 ) +{ + const int i = 42; + any o(std::in_place_type<int>, i); + int& i2 = any_cast<int&>(o); + BOOST_CHECK( i2 == 42 ); + BOOST_CHECK( &i2 != &i ); + any o2(std::in_place_type<std::tuple<int, int>>, 1, 2); + std::tuple<int, int>& t = any_cast<std::tuple<int, int>&>(o2); + BOOST_CHECK( std::get<0>(t) == 1 && std::get<1>(t) == 2); + any o3(std::in_place_type<std::vector<int>>, {42, 666}); + std::vector<int>& v = any_cast<std::vector<int>&>(o3); + BOOST_CHECK(v[0] == 42 && v[1] == 666); + any o4(std::in_place_type<combined>, {42, 666}); + combined& c = any_cast<combined&>(o4); + BOOST_CHECK(c.v[0] == 42 && c.v[1] == 666 + && std::get<0>(c.t) == 0 && std::get<1>(c.t) == 0 ); + any o5(std::in_place_type<combined>, {1, 2}, 3, 4); + combined& c2 = any_cast<combined&>(o5); + BOOST_CHECK(c2.v[0] == 1 && c2.v[1] == 2 + && std::get<0>(c2.t) == 3 && std::get<1>(c2.t) == 4 ); + any o6(std::in_place_type<int&>, i); + BOOST_CHECK(o6.type() == o.type()); + any o7(std::in_place_type<void()>, nullptr); + any o8(std::in_place_type<void(*)()>, nullptr); + BOOST_CHECK(o7.type() == o8.type()); + any o9(std::in_place_type<char(&)[42]>, nullptr); + any o10(std::in_place_type<char*>, nullptr); + BOOST_CHECK(o9.type() == o10.type()); +} + +bool moved = false; +bool copied = false; + +struct X +{ + X() = default; + X(const X&) { copied = true; } + X(X&&) { moved = true; } +}; + +struct X2 +{ + X2() = default; + X2(const X2&) { copied = true; } + X2(X2&&) noexcept { moved = true; } +}; + + +BOOST_AUTO_TEST_CASE( Construction_Basic_1 ) +{ + moved = false; + X x; + any a1(x); + BOOST_CHECK(moved == false); + any a2(std::move(x)); + BOOST_CHECK(moved == true); +} + + +BOOST_AUTO_TEST_CASE( Construction_Basic_2 ) +{ + moved = false; + X x; + any a1(x); + BOOST_CHECK(moved == false); + copied = false; + any a2(std::move(a1)); + BOOST_CHECK(copied == false); +} + + +BOOST_AUTO_TEST_CASE( Construction_Basic_3 ) +{ + moved = false; + X2 x; + any a1(x); + BOOST_CHECK(moved == false); + copied = false; + any a2(std::move(a1)); + BOOST_CHECK(copied == false); + BOOST_CHECK(moved == true); +} + + +BOOST_AUTO_TEST_CASE( Construction_Basic_4) +{ + any x; + BOOST_CHECK( !x.has_value() ); + + any y(x); + BOOST_CHECK( !x.has_value() ); + BOOST_CHECK( !y.has_value() ); + + any z(std::move(y)); + BOOST_CHECK( !y.has_value() ); + BOOST_CHECK( !z.has_value() ); +} + + +BOOST_AUTO_TEST_CASE( Construction_Basic_5) +{ + any x(1); + BOOST_CHECK( x.has_value() ); + + any y(x); + BOOST_CHECK( x.has_value() ); + BOOST_CHECK( y.has_value() ); + + any z(std::move(y)); + BOOST_CHECK( !y.has_value() ); + BOOST_CHECK( z.has_value() ); +} + + +BOOST_AUTO_TEST_CASE( Construction_InPlace_2 ) +{ + auto a = any(std::in_place_type<any>, 5); + BOOST_CHECK( any_cast<int>(any_cast<any>(a)) == 5 ); + + auto b = any(std::in_place_type<any>, {1}); + (void) any_cast<std::initializer_list<int>>(any_cast<any>(b)); +} + + +BOOST_AUTO_TEST_CASE( Construction_Pair ) +{ + any p = std::pair<any, any>(1, 1); + auto pt = any_cast<std::pair<any, any>>(p); + BOOST_CHECK( any_cast<int>(pt.first) == 1 ); + BOOST_CHECK( any_cast<int>(pt.second) == 1 ); + + any t = std::tuple<any>(1); + auto tt = any_cast<std::tuple<any>>(t); + BOOST_CHECK( any_cast<int>(std::get<0>(tt)) == 1 ); +} + + +// Alignment requiremnts of this type prevent it being stored in 'any' +struct alignas(2 * alignof(void*)) X3 { }; + +bool +stored_internally(void* obj, const ki::any& a) +{ + std::uintptr_t a_addr = reinterpret_cast<std::uintptr_t>(&a); + std::uintptr_t a_end = a_addr + sizeof(a); + std::uintptr_t obj_addr = reinterpret_cast<std::uintptr_t>(obj); + return (a_addr <= obj_addr) && (obj_addr < a_end); +} + + +BOOST_AUTO_TEST_CASE( Construction_Alignment ) +{ + any a = X3{}; + X3& x = any_cast<X3&>(a); + BOOST_CHECK( !stored_internally(&x, a) ); + + a = 'X'; + char& c = any_cast<char&>(a); + BOOST_CHECK( stored_internally(&c, a) ); +} + +struct wrapper +{ + wrapper() = default; + + wrapper(const any& t); + + wrapper(const wrapper& w); + + auto& operator=(const any& t); + + auto& operator=(const wrapper& w) + { + value = w.value; + return *this; + } + + any value; +}; + +BOOST_AUTO_TEST_CASE( Construction_Wrapper ) +{ + wrapper a, b; + a = b; +} + +// Following tests commented out until Apple Clang build version >= 16 +/* +struct aggressive_aggregate +{ + int a; + int b; +}; + +BOOST_AUTO_TEST_CASE( Construction_Aggregate_1 ) +{ + any x{std::in_place_type<aggressive_aggregate>, 1, 2}; + BOOST_CHECK(any_cast<aggressive_aggregate>(x).a == 1); + BOOST_CHECK(any_cast<aggressive_aggregate>(x).b == 2); + any y{std::in_place_type<aggressive_aggregate>, 1}; + BOOST_CHECK(any_cast<aggressive_aggregate>(y).a == 1); + BOOST_CHECK(any_cast<aggressive_aggregate>(y).b == 0); + any z{std::in_place_type<aggressive_aggregate>}; + BOOST_CHECK(any_cast<aggressive_aggregate>(z).a == 0); + BOOST_CHECK(any_cast<aggressive_aggregate>(z).b == 0); +} + +BOOST_AUTO_TEST_CASE( Construction_Aggregate_2 ) +{ + any x; + x.emplace<aggressive_aggregate>(1, 2); + BOOST_CHECK(any_cast<aggressive_aggregate>(x).a == 1); + BOOST_CHECK(any_cast<aggressive_aggregate>(x).b == 2); + x.emplace<aggressive_aggregate>(1); + BOOST_CHECK(any_cast<aggressive_aggregate>(x).a == 1); + BOOST_CHECK(any_cast<aggressive_aggregate>(x).b == 0); + x.emplace<aggressive_aggregate>(); + BOOST_CHECK(any_cast<aggressive_aggregate>(x).a == 0); + BOOST_CHECK(any_cast<aggressive_aggregate>(x).b == 0); +} + +*/ + +std::set<const void*> live_objects; + +struct A { + A() { live_objects.insert(this); } + ~A() { live_objects.erase(this); } + A(const A& a) { BOOST_CHECK(live_objects.count(&a)); live_objects.insert(this); } +}; + +BOOST_AUTO_TEST_CASE( Assign_1 ) +{ + any a; + a = a; + BOOST_CHECK( !a.has_value() ); + + a = A{}; + a = a; + BOOST_CHECK( a.has_value() ); + + a.reset(); + BOOST_CHECK( live_objects.empty() ); +} + + +BOOST_AUTO_TEST_CASE( Assign_2 ) +{ + struct X { + any a; + }; + + X x; + std::swap(x, x); // results in "self-move-assignment" of X::a + BOOST_CHECK( !x.a.has_value() ); + + x.a = A{}; + std::swap(x, x); // results in "self-move-assignment" of X::a + BOOST_CHECK( x.a.has_value() ); + + x.a.reset(); + BOOST_CHECK( live_objects.empty() ); +} + + +BOOST_AUTO_TEST_CASE( Assign_3 ) +{ + any a; + a.swap(a); + BOOST_CHECK( !a.has_value() ); + + a = A{}; + a.swap(a); + BOOST_CHECK( a.has_value() ); + + a.reset(); + BOOST_CHECK( live_objects.empty() ); +} + + +BOOST_AUTO_TEST_CASE( Assign_4 ) +{ + moved = false; + X x; + any a1; + a1 = x; + BOOST_CHECK(moved == false); + any a2; + copied = false; + a2 = std::move(x); + BOOST_CHECK(moved == true); + BOOST_CHECK(copied == false); +} + + +BOOST_AUTO_TEST_CASE( Assign_5 ) +{ + moved = false; + X x; + any a1; + a1 = x; + BOOST_CHECK(moved == false); + any a2; + copied = false; + a2 = std::move(a1); + BOOST_CHECK(moved == false); + BOOST_CHECK(copied == false); +} + + +BOOST_AUTO_TEST_CASE( Assign_6 ) +{ + moved = false; + X2 x; + any a1; + a1 = x; + BOOST_CHECK(copied && moved); + any a2; + moved = false; + copied = false; + a2 = std::move(a1); + BOOST_CHECK(moved == true); + BOOST_CHECK(copied == false); +} + +BOOST_AUTO_TEST_CASE( Assign_7 ) +{ + any x; + any y; + y = x; + BOOST_CHECK( !x.has_value() ); + BOOST_CHECK( !y.has_value() ); + + y = std::move(x); + BOOST_CHECK( !x.has_value() ); + BOOST_CHECK( !y.has_value() ); +} + +BOOST_AUTO_TEST_CASE( Assign_8 ) +{ + any x(1); + any y; + y = x; + BOOST_CHECK( x.has_value() ); + BOOST_CHECK( y.has_value() ); + + x = std::move(y); + BOOST_CHECK( x.has_value() ); + BOOST_CHECK( !y.has_value() ); + + x = y; + BOOST_CHECK( !x.has_value() ); + BOOST_CHECK( !y.has_value() ); +} + + +bool should_throw = false; +struct Bad +{ + Bad() = default; + Bad(const Bad&) {if (should_throw) throw 666;} +}; + +struct Bad2 +{ + Bad2() = default; + Bad2(const Bad2&) {if (should_throw) throw 666;} + Bad2(Bad2&&) noexcept {} +}; + +int del_count = 0; +struct Good +{ + Good() = default; + Good(const Good&) = default; + Good(Good&&) = default; + ~Good() {++del_count;} +}; + +BOOST_AUTO_TEST_CASE( Exceptions ) +{ + any a1 = Good(); + del_count = 0; + try { + Bad b; + any a2 = b; + should_throw = true; + a1 = a2; + } catch (...) { + auto x = any_cast<Good>(a1); + BOOST_CHECK( del_count == 0 ); + BOOST_CHECK( a1.has_value() ); + any_cast<Good>(a1); + } + any a3 = Good(); + del_count = 0; + try { + Bad2 b; + any a4 = b; + should_throw = true; + a3 = a4; + } catch (...) { + auto x = any_cast<Good>(a1); + BOOST_CHECK( del_count == 0 ); + BOOST_CHECK( a1.has_value() ); + any_cast<Good>(a1); + } +} + + +BOOST_AUTO_TEST_CASE( Emplace_1 ) +{ + const int i = 42; + any o; + o.emplace<int>(i); + int& i2 = any_cast<int&>(o); + BOOST_CHECK( i2 == 42 ); + BOOST_CHECK( &i2 != &i ); + any o2; + o2.emplace<std::tuple<int, int>>(1, 2); + std::tuple<int, int>& t = any_cast<std::tuple<int, int>&>(o2); + BOOST_CHECK( std::get<0>(t) == 1 && std::get<1>(t) == 2); + any o3; + o3.emplace<std::vector<int>>({42, 666}); + std::vector<int>& v = any_cast<std::vector<int>&>(o3); + BOOST_CHECK(v[0] == 42 && v[1] == 666); + any o4; + o4.emplace<combined>({42, 666}); + combined& c = any_cast<combined&>(o4); + BOOST_CHECK(c.v[0] == 42 && c.v[1] == 666 + && std::get<0>(c.t) == 0 && std::get<1>(c.t) == 0 ); + any o5; + o5.emplace<combined>({1, 2}, 3, 4); + combined& c2 = any_cast<combined&>(o5); + BOOST_CHECK(c2.v[0] == 1 && c2.v[1] == 2 + && std::get<0>(c2.t) == 3 && std::get<1>(c2.t) == 4 ); + any o6; + o6.emplace<const int&>(i); + BOOST_CHECK(o6.type() == o.type()); + any o7; + o7.emplace<void()>(nullptr); + any o8; + o8.emplace<void(*)()>(nullptr); + BOOST_CHECK(o7.type() == o8.type()); + any o9; + o9.emplace<char(&)[42]>(nullptr); + any o10; + o10.emplace<char*>(nullptr); + BOOST_CHECK(o9.type() == o10.type()); + any o11; + BOOST_CHECK(&o11.emplace<int>(42) == &any_cast<int&>(o11)); + BOOST_CHECK(&o11.emplace<std::vector<int>>({1,2,3}) == + &any_cast<std::vector<int>&>(o11)); +} + + + +struct X_e +{ + X_e() = default; + X_e(const X_e&) { copied = true; } + X_e(X_e&&) { moved = true; } +}; + +struct X2_e +{ + X2_e() = default; + X2_e(const X2_e&) { copied = true; } + X2_e(X2_e&&) noexcept { moved = true; } +}; + + +BOOST_AUTO_TEST_SUITE_END()