mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-07 22:05:32 +00:00
Add ki::any and ki::any_cast for any_casts across translation units
ki::any is a standards-compliant implementation, based on the GCC standard library. However, it uses type_info::hash_code() to check the validitiy of a ki:any_cast, rather than comparing the type_info objects directly. This comparison, used in the standard implementations, is fragile across translation unit boundaries when built with Clang.
This commit is contained in:
parent
9636934631
commit
5ff21f9c2d
common/tool
include
qa/tests/common
@ -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 )
|
||||
|
623
include/ki_any.h
Normal file
623
include/ki_any.h
Normal file
@ -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_
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
840
qa/tests/common/test_ki_any.cpp
Normal file
840
qa/tests/common/test_ki_any.cpp
Normal file
@ -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()
|
Loading…
Reference in New Issue
Block a user