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()