mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-01-22 07:42:40 +00:00
628 lines
19 KiB
C++
628 lines
19 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <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/>.
|
|
|
|
|
|
/**
|
|
* @file ki_any.h
|
|
* @brief An implementation of std::any_cast, which uses type_info::hash_code to check validity of
|
|
* cast types.
|
|
*
|
|
* This is required as Clang compares types as being equivalent based on their type_info pointer
|
|
* locations. These are not guaranteed to be the same with identical types linked in multiple
|
|
* targets from shared libraries. The current Clang implementation of type_info::hash_code is
|
|
* based on the type names, which should be consistent across translation units.
|
|
*/
|
|
#ifndef INCLUDE_KI_ANY_H_
|
|
#define INCLUDE_KI_ANY_H_
|
|
|
|
#include <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;
|
|
|
|
/**
|
|
* Exception class thrown by a failed @c any_cast
|
|
*/
|
|
class bad_any_cast final : public std::bad_cast
|
|
{
|
|
public:
|
|
const char* what() const noexcept override { return "bad ki::any_cast"; }
|
|
};
|
|
|
|
/**
|
|
* A type-safe container of any type.
|
|
*
|
|
* An `any` object's state is either empty or it stores a contained object
|
|
* of CopyConstructible type.
|
|
*/
|
|
class any
|
|
{
|
|
// Holds either a pointer to a heap object or the contained object itself
|
|
union Storage
|
|
{
|
|
constexpr Storage() : m_ptr{ nullptr } {}
|
|
|
|
// Prevent trivial copies of this type as the buffer might hold a non-POD
|
|
Storage( const Storage& ) = delete;
|
|
Storage& operator=( const Storage& ) = delete;
|
|
|
|
void* m_ptr;
|
|
unsigned char m_buffer[sizeof( m_ptr )];
|
|
};
|
|
|
|
template <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>;
|
|
|
|
/// 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;
|
|
}
|
|
|
|
/// 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:
|
|
/// Default constructor, creates an empty object.
|
|
constexpr any() noexcept : m_manager( nullptr ) {}
|
|
|
|
/// Copy constructor, copies the state of @p other.
|
|
any( const any& other )
|
|
{
|
|
if( !other.has_value() )
|
|
{
|
|
m_manager = nullptr;
|
|
}
|
|
else
|
|
{
|
|
Arg arg;
|
|
arg.m_any = this;
|
|
other.m_manager( Op_Clone, &other, &arg );
|
|
}
|
|
}
|
|
|
|
/// Move constructor, transfer the state from @p other.
|
|
any( any&& other ) noexcept
|
|
{
|
|
if( !other.has_value() )
|
|
{
|
|
m_manager = nullptr;
|
|
}
|
|
else
|
|
{
|
|
Arg arg;
|
|
arg.m_any = this;
|
|
other.m_manager( Op_Xfer, &other, &arg );
|
|
}
|
|
}
|
|
|
|
/// Construct with a copy of @p value as the contained object.
|
|
template <typename 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 ) );
|
|
}
|
|
|
|
/// 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 )... );
|
|
}
|
|
|
|
/// 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 )... );
|
|
}
|
|
|
|
/// Destructor, calls @c reset().
|
|
~any() { reset(); }
|
|
|
|
/// Copy the state of another object.
|
|
any& operator=( const any& rhs )
|
|
{
|
|
*this = any( rhs );
|
|
return *this;
|
|
}
|
|
|
|
/// Move assignment operator.
|
|
any& operator=( any&& rhs ) noexcept
|
|
{
|
|
if( !rhs.has_value() )
|
|
{
|
|
reset();
|
|
}
|
|
else if( this != &rhs )
|
|
{
|
|
reset();
|
|
Arg arg;
|
|
arg.m_any = this;
|
|
rhs.m_manager( Op_Xfer, &rhs, &arg );
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/// Store a copy of @p rhs as the contained object.
|
|
template <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 );
|
|
}
|
|
}
|
|
|
|
/// Report whether there is a contained object or not.
|
|
bool has_value() const noexcept { return m_manager != nullptr; }
|
|
|
|
|
|
/// The @c typeid of the contained object, or @c typeid(void) if empty.
|
|
const std::type_info& type() const noexcept
|
|
{
|
|
if( !has_value() )
|
|
return typeid( void );
|
|
|
|
Arg arg;
|
|
m_manager( Op_Get_Type_Info, this, &arg );
|
|
return *arg.m_typeinfo;
|
|
}
|
|
|
|
/// @cond undocumented
|
|
template <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 )... );
|
|
}
|
|
|
|
/**
|
|
* 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{};
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
|
|
/**
|
|
* 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_
|