Skip to content

Instantly share code, notes, and snippets.

@ericniebler
Last active December 24, 2025 00:25
Show Gist options
  • Select an option

  • Save ericniebler/ab42506b8674f1f9633a004427e8a79c to your computer and use it in GitHub Desktop.

Select an option

Save ericniebler/ab42506b8674f1f9633a004427e8a79c to your computer and use it in GitHub Desktop.
another take on a type-erasure library
/*
* Copyright (c) 2025 NVIDIA Corporation
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cassert>
#define STRINGIZE(_ARG) #_ARG
// Define the pragma for the host compiler
#if defined(_MSC_VER)
# define PRAGMA(_ARG) __pragma(_ARG)
#else
# define PRAGMA(_ARG) _Pragma(STRINGIZE(_ARG))
#endif
#if defined(__NVCOMPILER)
# define DIAG_PUSH PRAGMA(diagnostic push)
# define DIAG_POP PRAGMA(diagnostic pop)
# define DIAG_SUPPRESS_NVHPC(_WARNING) PRAGMA(diag_suppress _WARNING)
#elif defined(__clang__)
# define DIAG_PUSH PRAGMA(clang diagnostic push)
# define DIAG_POP PRAGMA(clang diagnostic pop)
# define DIAG_SUPPRESS_CLANG(_WARNING) PRAGMA(clang diagnostic ignored _WARNING)
#elif defined(__GNUC__)
# define DIAG_PUSH PRAGMA(GCC diagnostic push)
# define DIAG_POP PRAGMA(GCC diagnostic pop)
# define DIAG_SUPPRESS_GCC(_WARNING) PRAGMA(GCC diagnostic ignored _WARNING)
#elif defined(_MSC_VER)
# define DIAG_PUSH PRAGMA(warning(push))
# define DIAG_POP PRAGMA(warning(pop))
# define DIAG_SUPPRESS_MSVC(_WARNING) PRAGMA(warning(disable : _WARNING))
#else
# define DIAG_PUSH
# define DIAG_POP
#endif
#if !defined(DIAG_SUPPRESS_CLANG)
# define DIAG_SUPPRESS_CLANG(_WARNING)
#endif
#if !defined(DIAG_SUPPRESS_GCC)
# define DIAG_SUPPRESS_GCC(_WARNING)
#endif
#if !defined(DIAG_SUPPRESS_NVHPC)
# define DIAG_SUPPRESS_NVHPC(_WARNING)
#endif
#if !defined(DIAG_SUPPRESS_MSVC)
# define DIAG_SUPPRESS_MSVC(_WARNING)
#endif
#if __cpp_rtti || _MSC_VER // MSVC has the typeid operator even with RTTI off
# include <typeinfo> // IWYU pragma: keep
# define HAS_TYPEID 1
#else
# define HAS_TYPEID 0
#endif
#if defined(__clang__)
# define ALWAYS_INLINE gnu::always_inline, gnu::artificial, gnu::nodebug
#elif defined(__GNUC__)
# define ALWAYS_INLINE gnu::always_inline, gnu::artificial
#else
# define ALWAYS_INLINE
#endif
#if defined(_MSC_VER)
# define EMPTY_BASES msvc::empty_bases
# define NO_UNIQUE_ADDRESS msvc::no_unique_address
#else
# define EMPTY_BASES
# define NO_UNIQUE_ADDRESS no_unique_address
#endif
#if defined(_MSC_VER)
# if _MSC_VER >= 19'35
# define PRETTY_FUNCTION __builtin_FUNCSIG()
# else
# define PRETTY_FUNCTION __FUNCSIG__
# endif
#else
# define PRETTY_FUNCTION __PRETTY_FUNCTION__
#endif
#define ASSERT(...) ((__VA_ARGS__) ? void() : assert(__VA_ARGS__))
#if __cpp_if_consteval
# define CONSTEVAL consteval
#else
# define CONSTEVAL (std::is_constant_evaluated())
#endif
#include <cstddef>
#include <compare>
#include <string_view>
//////////////////////////////////////////////////////////////////////////////////////////
// type_info and TYPEID
namespace any
{
#define TYPEID(...) ::any::typeid_of<__VA_ARGS__>
namespace _detail
{
//////////////////////////////////////////////////////////////////////////////////////////
// _pretty_name
template <class>
struct _xyzzy
{
struct _plugh
{
};
};
constexpr char _type_name_prefix[] = "_xyzzy<";
constexpr char _type_name_suffix[] = ">::_plugh";
// Get the type name from the function name by trimming the front and back.
[[nodiscard]]
constexpr std::string_view _find_pretty_name(std::string_view fun_name) noexcept
{
auto const beg_pos = fun_name.find(_type_name_prefix);
auto const end_pos = fun_name.rfind(_type_name_suffix);
auto const start = beg_pos + sizeof(_type_name_prefix) - 1;
auto const length = end_pos - start;
return fun_name.substr(start, length);
}
template <class T>
[[nodiscard]]
constexpr std::string_view _get_pretty_name_helper() noexcept
{
return _detail::_find_pretty_name(std::string_view{PRETTY_FUNCTION});
}
template <class T>
[[nodiscard]]
constexpr std::string_view _get_pretty_name() noexcept
{
return _detail::_get_pretty_name_helper<typename _xyzzy<T>::_plugh>();
}
template <class T>
inline constexpr std::string_view _pretty_name = _detail::_get_pretty_name<T>();
static_assert(_detail::_pretty_name<int> == "int");
} // namespace _detail
//////////////////////////////////////////////////////////////////////////////////////////
// type_info
struct type_info
{
type_info(type_info &&) = delete;
type_info &operator=(type_info &&) = delete;
constexpr explicit type_info(std::string_view name) noexcept
: name_(name)
{
}
constexpr std::string_view name() const noexcept
{
return name_;
}
auto operator==(type_info const &) const noexcept -> bool = default;
auto operator<=>(type_info const &) const noexcept -> std::strong_ordering = default;
private:
std::string_view name_;
};
template <class T>
inline constexpr type_info typeid_of{_detail::_pretty_name<T>};
template <class T>
inline constexpr type_info const &typeid_of<T const> = typeid_of<T>;
//////////////////////////////////////////////////////////////////////////////////////////
// type_index
struct type_index
{
constexpr type_index(type_info const &info) noexcept
: info_(&info)
{
}
constexpr std::string_view name() const noexcept
{
return info_->name();
}
constexpr bool operator==(type_index const &other) const noexcept
{
return *info_ == *other.info_;
}
constexpr std::strong_ordering operator<=>(type_index const &other) const noexcept
{
return *info_ <=> *other.info_;
}
type_info const *info_;
};
namespace _detail
{
DIAG_PUSH
DIAG_SUPPRESS_GCC("-Wnon-template-friend")
DIAG_SUPPRESS_NVHPC(probable_guiding_friend)
// The following two classes use the stateful metaprogramming trick to create a spooky
// association between a type_index object and the type it represents.
template <type_index Id>
struct _typeid_c
{
friend constexpr auto _typeid_lookup(_typeid_c<Id>) noexcept;
};
template <class T>
struct _typeid_of
{
using type = T;
static constexpr type_index id = type_index(typeid_of<T>);
friend constexpr auto _typeid_lookup(_typeid_c<id>) noexcept
{
return _typeid_of<T>();
}
};
DIAG_POP
} // namespace _detail
// For a given type, return a type_index object
template <class T>
inline constexpr type_index type_index_of = _detail::_typeid_of<T>::id;
// For a given type_index object, return the associated type
template <type_index Info>
using typeof_t = typename decltype(_typeid_lookup(_detail::_typeid_c<Info>()))::type;
} // namespace any
#include <cstdarg>
#include <cstdio>
#include <exception>
#include <new>
#include <utility>
namespace any
{
//////////////////////////////////////////////////////////////////////////////////////////
// start_lifetime_as
#if __cpp_lib_start_lifetime_as
using std::start_lifetime_as;
#else
template <class T>
[[ALWAYS_INLINE, nodiscard]]
inline T *start_lifetime_as(void *p) noexcept
{
return std::launder(static_cast<T *>(p));
}
template <class T>
[[ALWAYS_INLINE, nodiscard]]
inline T const *start_lifetime_as(void const *p) noexcept
{
return std::launder(static_cast<T const *>(p));
}
#endif
#if __cpp_lib_unreachable
using std::unreachable;
#else
[[noreturn]] inline void unreachable()
{
// Uses compiler specific extensions if possible.
// Even if no extension is used, undefined behavior is still raised by
// an empty function body and the noreturn attribute.
# if defined(_MSC_VER) && !defined(__clang__) // MSVC
__assume(false);
# else // GCC, Clang
__builtin_unreachable();
# endif
}
#endif
template <class Return = void>
[[noreturn]]
inline constexpr Return _die(char const *msg, ...) noexcept
{
if CONSTEVAL
{
::any::unreachable();
}
else
{
va_list args;
va_start(args, msg);
std::vfprintf(stderr, msg, args);
std::fflush(stderr);
va_end(args);
std::terminate();
}
}
template <class T, class U>
concept _decays_to = std::same_as<std::decay_t<T>, U>;
template <class T>
concept _decayed = _decays_to<T, T>;
template <class Fn, class... Args>
using _mcall = Fn::template call<Args...>;
template <bool>
struct _if_
{
template <class Then, class...>
using call = Then;
};
template <>
struct _if_<false>
{
template <class, class Else>
using call = Else;
};
template <bool Condition, class Then = void, class... Else>
using _if_t = _mcall<_if_<Condition>, Then, Else...>;
//////////////////////////////////////////////////////////////////////////////////////////
// _unconst
template <class T>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr T &_unconst(T const &t) noexcept
{
return const_cast<T &>(t);
}
//////////////////////////////////////////////////////////////////////////////////////////
// _const_if
template <bool MakeConst, class T>
using _const_if = _if_t<MakeConst, T const, T>;
//////////////////////////////////////////////////////////////////////////////////////////
// _as_const_if
template <bool MakeConst, class T>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto &_as_const_if(T &t) noexcept
{
return const_cast<_const_if<MakeConst, T> &>(t);
}
} // namespace any
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <exception>
#include <memory>
#include <span>
#include <type_traits>
#include <utility>
//////////////////////////////////////////////////////////////////////////////////////////
//! any: a library for ad hoc polymorphism with value semantics
//!
//! @par Terminology:
//!
//! - "root":
//!
//! A type satisfying the @c root concept that is used as the nucleus of a "model".
//! There are 5 root types:
//!
//! - @c _iroot: the abstract root
//! - @c _value_root: holds a concrete value
//! - @c _reference_root: holds a concrete reference
//! - @c _value_proxy_root: holds a type-erased value model
//! - @c _reference_proxy_root: holds a type-erased reference model
//!
//! Aside from @c _iroot, all root types inherit from @c iabstract<Interface>, where
//! @c Interface is the interface that the root type implements.
//!
//! The @c root concept is defined as:
//!
//! @code
//! template <class Root>
//! concept root = requires (Root& root)
//! {
//! any::value(root);
//! { any::reset(root); } -> std::same_as<void>;
//! { any::type(root) } -> std::same_as<const type_info &>;
//! { any::data(root) } -> std::same_as<void *>;
//! { any::empty(root) } -> std::same_as<bool>;
//! };
//! @endcode
//!
//! - "model":
//!
//! A polymorphic wrapper around a root that is constructed by recursively applying a
//! given interface and its base interfaces to the root type. For example, given an
//! interface @c Derived that extends @c Base, the value proxy model is a type derived
//! from @c Derived<Base<_value_proxy_root<Derived>>>. Model types implement their given
//! interfaces in terms of the root type. There are 5 model types:
//!
//! - @c iabstract: akin to an abstract base class for the
//! interface
//! - @c _value_model: implements the interface for a concrete value
//! - @c _reference_model: implements the interface for a concrete
//! reference
//! - @c _value_proxy_model: implements the interface over a type-erased
//! value model
//! - @c _reference_proxy_model: implements the interface over a type-erased
//! reference model
//!
//! - "proxy":
//!
//! A level of indirection that stores either a type-erased model in a small buffer or a
//! pointer to an object stored elsewhere. The @c _value_proxy_root and @c
//! _reference_proxy_root types model the @c root concept and contain an array of bytes
//! in which they stores either a polymorphic model in-situ or a (tagged) pointer to a
//! heap-allocated model. The @c _value_proxy_model and @c _reference_proxy_model types
//! implement the given interface in terms of the root type.
//!
//! @par Notes:
//!
//! - @c Interface<Base> inherits directly from @c
//! any::interface<Interface,Base>, which
//! inherits directly from @c Base.
//!
//! - Given an interface template @c Derived that extends @c Base, the type @c
//! iabstract<Derived> is derived from @c iabstract<Base>.
//!
//! - In the case of multiple interface extension, the inheritance is forced to be linear.
//! As a result, for an interface @c C that extends @c A and @c B (in that order), @c
//! iabstract<C> will have a linear inheritance hierarchy; it will be an alias for @c
//! C<B<A<_iroot>>>. The result is that @c iabstract<C> inherits from @c iabstract<A>
//! but not from @c iabstract<B>.
//!
//! - The "`_proxy_root`" types both implement an @c emplace function that accepts a
//! concrete value or reference, wraps it in the appropriate "`_model`" type, and stores
//! it either in-situ or on the heap depending on its size and whether it is nothrow
//! moveable.
//!
//! - The @c _root types (excluding @c _iroot) all inherit from @c iabstract<Interface>.
//! The @c _model types implement the interface in terms of the root type.
//!
//! - @c any<Derived> inherits from @c _value_proxy_model<Derived>, which in turn inherits
//! from @c Derived<Base<_value_proxy_root<Derived>>>, which in turn inherits from
//! @c Derived<Base<_iroot>> (aka @c iabstract<Derived> ).
//!
//! - @c any_const_ptr<Derived> inherits from @c _reference_proxy_model<Derived>, which in
//! turn inherits from @c Derived<Base<_reference_proxy_root<Derived>>>.
//!
//! - For every @c any<Interface> instantiation, there are 5 instantiations of
//! @c Interface:
//!
//! 1. @c Interface<...Bases...<_iroot>>>...>
//! 2. @c Interface<...Bases...<_value_root<Value,Interface>>...>
//! 3. @c Interface<...Bases...<_reference_root<Value,Interface>>...>
//! 4. @c Interface<...Bases...<_value_proxy_root<Interface>>...>
//! 5. @c Interface<...Bases...<_reference_proxy_root<Interface>>...>
namespace any
{
//////////////////////////////////////////////////////////////////////////////////////////
// forward declarations
// any types
template <template <class> class Interface>
struct any;
template <template <class> class Interface>
struct any_ptr;
template <template <class> class Interface>
struct any_const_ptr;
template <template <class> class... BaseInterfaces>
struct extends;
// semiregular interfaces
template <class Base>
struct imovable;
template <class Base>
struct icopyable;
template <class Base>
struct iequality_comparable;
template <class Base>
struct isemiregular;
struct _iroot;
template <template <class> class Interface>
using _bases_of = Interface<_iroot>::bases_type;
//////////////////////////////////////////////////////////////////////////////////////////
// interface_cast
template <template <class> class Interface, class Base>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr Interface<Base> &interface_cast(Interface<Base> &iface) noexcept
{
return iface;
}
template <template <class> class Interface, class Base>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr Interface<Base> const &interface_cast(Interface<Base> const &iface) noexcept
{
return iface;
}
//////////////////////////////////////////////////////////////////////////////////////////
// accessors
[[maybe_unused]] constexpr struct _value_t
{
template <class T>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto &operator()(T &t) const noexcept
{
return t._value();
}
} value{};
[[maybe_unused]] constexpr struct _empty_t
{
template <class T>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr bool operator()(T const &t) const noexcept
{
return t._empty();
}
} empty{};
[[maybe_unused]] constexpr struct _reset_t
{
template <class T>
[[ALWAYS_INLINE]]
inline constexpr void operator()(T &t) const noexcept
{
t._reset();
}
} reset{};
[[maybe_unused]] constexpr struct _type_t
{
template <class T>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr type_info const &operator()(T const &t) const noexcept
{
return t._type();
}
} type{};
[[maybe_unused]] constexpr struct _data
{
template <class T>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto *operator()(T &t) const noexcept
{
return t._data();
}
} data{};
[[maybe_unused]] constexpr struct caddressof_t
{
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto operator()(Interface<Base> const &iface) const noexcept
{
return any_const_ptr<Interface>(std::addressof(iface));
}
} caddressof{};
[[maybe_unused]] constexpr struct addressof_t : caddressof_t
{
using caddressof_t::operator();
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto operator()(Interface<Base> &iface) const noexcept
{
return any_ptr<Interface>(std::addressof(iface));
}
} addressof{};
// value_of_t
template <class T>
using value_of_t = std::decay_t<decltype(value(std::declval<T &>()))>;
//////////////////////////////////////////////////////////////////////////////////////////
// extension_of
template <class Interface, template <class> class BaseInterface>
concept extension_of =
requires(Interface const &iface) { ::any::interface_cast<BaseInterface>(iface); };
//////////////////////////////////////////////////////////////////////////////////////////
// _is_small: Model is Interface<T> for some concrete T
template <class Model>
[[nodiscard]]
constexpr bool _is_small(size_t buffer_size) noexcept
{
constexpr bool nothrow_movable =
!extension_of<Model, imovable> || std::is_nothrow_move_constructible_v<Model>;
return sizeof(Model) <= buffer_size && nothrow_movable;
}
//////////////////////////////////////////////////////////////////////////////////////////
// _tagged_ptr
struct _tagged_ptr
{
[[ALWAYS_INLINE]]
/*implicit*/ constexpr inline _tagged_ptr() noexcept
: data_(std::uintptr_t(1))
{
}
[[ALWAYS_INLINE]]
/*implicit*/ inline _tagged_ptr(void *ptr) noexcept
: data_(reinterpret_cast<std::uintptr_t>(ptr) | std::uintptr_t(1))
{
}
[[ALWAYS_INLINE, nodiscard]]
inline void *_get() const noexcept
{
ASSERT(!_is_vptr());
return reinterpret_cast<void *>(data_ & ~std::uintptr_t(1));
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr bool _is_vptr() const noexcept
{
return (data_ & 1) == 0;
}
[[nodiscard]]
constexpr bool operator==(std::nullptr_t) const noexcept
{
return data_ == 1;
}
std::uintptr_t data_;
};
//////////////////////////////////////////////////////////////////////////////////////////
// emplace_into
template <class Model, class... Args>
constexpr Model &emplace_into([[maybe_unused]] _iroot *&pointer,
[[maybe_unused]] std::span<unsigned char> buffer, Args &&...args)
{
static_assert(_decayed<Model>);
if CONSTEVAL
{
pointer = ::new Model(std::forward<Args>(args)...);
return *static_cast<Model *>(pointer);
}
else
{
if (::any::_is_small<Model>(buffer.size()))
{
return *std::construct_at(reinterpret_cast<Model *>(buffer.data()),
std::forward<Args>(args)...);
}
else
{
auto *const model = ::new Model(std::forward<Args>(args)...);
*::any::start_lifetime_as<_tagged_ptr>(buffer.data()) = _tagged_ptr(model);
return *model;
}
}
}
template <int = 0, class CvRefValue, class Value = std::decay_t<CvRefValue>>
constexpr Value &emplace_into(_iroot *&pointer, std::span<unsigned char> buffer, CvRefValue &&value)
{
return ::any::emplace_into<Value>(pointer, buffer, std::forward<CvRefValue>(value));
}
// template <class Root>
// concept root = requires (Root& root)
// {
// root._value();
// root._reset();
// { root._type() } -> std::same_as<const type_info &>;
// { root._data() } -> std::same_as<void *>;
// { root._empty() } -> std::same_as<bool>;
// };
//! @c iabstract must be an alias in order for @c iabstract<Derived> to be
//! derived from
//! @c iabstract<Base>. @c iabstract<Derived> is an alias for @c
//! Derived<Base<_iroot>>.
template <template <class> class Interface, class BaseInterfaces = _bases_of<Interface>>
using iabstract = Interface<_mcall<BaseInterfaces, _iroot>>;
// value
template <template <class> class Interface, class Value>
struct _value_root;
template <template <class> class Interface, class Value>
struct _value_model final : Interface<_mcall<_bases_of<Interface>, _value_root<Interface, Value>>>
{
using _base_t = Interface<_mcall<_bases_of<Interface>, _value_root<Interface, Value>>>;
using _base_t::_base_t;
// This is a virtual override if Interface extends imovable
//! @pre ::any::_is_small<_value_model>(buffer.size())
constexpr void _move_to(_iroot *&pointer, std::span<unsigned char> buffer) noexcept
{
static_assert(extension_of<iabstract<Interface>, imovable>);
ASSERT(::any::_is_small<_value_model>(buffer.size()));
::any::emplace_into(pointer, buffer, std::move(*this));
reset(*this);
}
// This is a virtual override if Interface extends icopyable
constexpr void _copy_to(_iroot *&pointer, std::span<unsigned char> buffer) const
{
static_assert(extension_of<iabstract<Interface>, icopyable>);
ASSERT(!empty(*this));
::any::emplace_into(pointer, buffer, *this);
}
};
// value proxy
template <template <class> class Interface>
struct _value_proxy_root;
template <template <class> class Interface>
struct _value_proxy_model : Interface<_mcall<_bases_of<Interface>, _value_proxy_root<Interface>>>
{
};
// reference
template <template <class> class Interface, class Value>
struct _reference_root;
template <template <class> class Interface, class Value>
struct _reference_model : Interface<_mcall<_bases_of<Interface>, _reference_root<Interface, Value>>>
{
using _base_t = Interface<_mcall<_bases_of<Interface>, _reference_root<Interface, Value>>>;
using _base_t::_base_t;
};
// reference proxy
template <template <class> class Interface>
struct _reference_proxy_root;
template <template <class> class Interface>
struct _reference_proxy_model
: Interface<_mcall<_bases_of<Interface>, _reference_proxy_root<Interface>>>
{
};
// interface
enum class _box_kind
{
_abstract,
_object,
_proxy
};
constexpr size_t default_buffer_size = 3 * sizeof(void *);
template <class Interface, _box_kind BoxKind>
concept _has_box_kind = std::remove_reference_t<Interface>::_box_kind == BoxKind;
// Without the check against _has_box_kind, this concept would always be
// satisfied when building an object model or a proxy model because of the
// abstract implementation of BaseInterface in the iabstract layer.
//
// any<Derived>
// : _value_proxy_model<Derived, V>
// : Derived<Base<_value_proxy_root<Derived, V>>> // box_kind == object
// ^^^^^^^ : Derived<Base<_iroot>> // box_kind ==
// abstract
// ^^^^^^^
template <class Interface, template <class> class BaseInterface>
concept _already_implements = requires(Interface const &iface) {
{ ::any::interface_cast<BaseInterface>(iface) } -> _has_box_kind<Interface::_box_kind>;
};
//////////////////////////////////////////////////////////////////////////////////////////
// extends
template <>
struct extends<>
{
template <class Base>
using call = Base;
};
template <template <class> class BaseInterface, template <class> class... BaseInterfaces>
struct extends<BaseInterface, BaseInterfaces...>
{
template <class Base, class BasesOfBase = _mcall<_bases_of<BaseInterface>, Base>>
using call = _mcall<
extends<BaseInterfaces...>,
// If Base already implements BaseInterface, do not re-apply it.
_if_t<_already_implements<Base, BaseInterface>, BasesOfBase, BaseInterface<BasesOfBase>>>;
};
constexpr char const *_pure_virt_msg = "internal error: pure virtual %s() called\n";
// If we are slicing into a buffer that is smaller than our own, then slicing
// may throw.
template <class Interface, class Base, size_t BufferSize>
concept _nothrow_slice = (!Base::_is_reference) && //
(Base::_box_kind != _box_kind::_abstract) && //
(Interface::buffer_size >= BufferSize);
//////////////////////////////////////////////////////////////////////////////////////////
//! interface
template <template <class> class Interface, class Base, class BaseInterfaces = extends<>,
size_t BufferSize = default_buffer_size>
struct interface : Base
{
using bases_type = BaseInterfaces;
using _interface_type = iabstract<Interface, BaseInterfaces>;
using Base::_bind;
using Base::_slice;
using Base::Base;
static constexpr size_t buffer_size =
BufferSize > Base::buffer_size ? BufferSize : Base::buffer_size;
static constexpr bool _nothrow_slice = ::any::_nothrow_slice<_interface_type, Base, buffer_size>;
constexpr ~interface() = default;
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto &_value() noexcept
{
if constexpr (Base::_box_kind == _box_kind::_abstract)
return ::any::_die<_interface_type &>(_pure_virt_msg, "value");
else
return Base::_value();
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto const &_value() const noexcept
{
if constexpr (Base::_box_kind == _box_kind::_abstract)
return ::any::_die<_interface_type const &>(_pure_virt_msg, "value");
else
return Base::_value();
}
//! @pre Base::_box_kind != _box_kind::_proxy || !empty(*this)
constexpr virtual void _slice(_value_proxy_root<Interface> &out) noexcept(_nothrow_slice)
{
if constexpr (Base::_box_kind == _box_kind::_abstract)
{
::any::_die(_pure_virt_msg, "slice");
}
else if constexpr (Base::_box_kind == _box_kind::_object)
{
out.emplace(std::move(value(*this))); // potentially throwing
}
else
{
value(*this)._slice(out);
reset(*this);
}
}
constexpr void _bind(_reference_proxy_root<Interface> &out) noexcept
{
return std::as_const(*this)._bind(out, false);
}
constexpr virtual void _bind(_reference_proxy_root<Interface> &out,
bool is_const = true) const noexcept
{
if constexpr (Base::_box_kind == _box_kind::_abstract)
{
::any::_die(_pure_virt_msg, "bind");
}
else if constexpr (Base::_box_kind == _box_kind::_object)
{
if (is_const)
out.rebind(value(*this));
else
out.rebind(value(::any::_unconst(*this)));
}
else
{
ASSERT(!empty(*this));
if (is_const)
value(*this)._bind(out, true);
else
value(::any::_unconst(*this))._bind(out, false);
}
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// _iroot
struct _iroot
{
static constexpr ::any::_box_kind _box_kind = ::any::_box_kind::_abstract;
static constexpr bool _is_reference = false;
static constexpr size_t buffer_size = sizeof(_tagged_ptr); // minimum size
using bases_type = extends<>;
// needed by MSVC for EBO to work for some reason:
constexpr virtual ~_iroot() = default;
[[nodiscard]]
constexpr virtual bool _empty() const noexcept
{
return ::any::_die<bool>(_pure_virt_msg, "empty");
}
constexpr virtual void _reset() noexcept
{
::any::_die(_pure_virt_msg, "reset");
}
[[nodiscard]]
constexpr virtual type_info const &_type() const noexcept
{
return ::any::_die<type_info const &>(_pure_virt_msg, "type");
}
[[nodiscard]]
constexpr virtual void *_data() const noexcept
{
return ::any::_die<void *>(_pure_virt_msg, "data");
}
void _slice() noexcept = delete;
void _bind() const noexcept = delete;
};
//////////////////////////////////////////////////////////////////////////////////////////
// _value_root
template <template <class> class Interface, class Value>
struct _value_root : iabstract<Interface>
{
using interface_type = iabstract<Interface>;
static constexpr ::any::_box_kind _box_kind = ::any::_box_kind::_object;
constexpr explicit _value_root(Value val) noexcept
: value(std::move(val))
{
}
[[nodiscard]]
constexpr Value &_value() noexcept
{
return value;
}
[[nodiscard]]
constexpr Value const &_value() const noexcept
{
return value;
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
return false;
}
constexpr void _reset() noexcept final override
{
if CONSTEVAL
{
delete this;
}
}
[[nodiscard]]
constexpr type_info const &_type() const noexcept final override
{
return TYPEID(Value);
}
[[nodiscard]]
constexpr void *_data() const noexcept final override
{
return const_cast<void *>(static_cast<void const *>(std::addressof(_value())));
}
Value value;
};
// A specialization of _value_root to take advantage of EBO (empty base
// optimization):
template <template <class> class Interface, class Value>
requires std::is_empty_v<Value> && (!std::is_final_v<Value>)
struct [[EMPTY_BASES]] _value_root<Interface, Value>
: iabstract<Interface>
, private Value
{
using interface_type = iabstract<Interface>;
static constexpr ::any::_box_kind _box_kind = ::any::_box_kind::_object;
constexpr explicit _value_root(Value val) noexcept
: Value(std::move(val))
{
}
[[nodiscard]]
constexpr Value &_value() noexcept
{
return *this;
}
[[nodiscard]]
constexpr Value const &_value() const noexcept
{
return *this;
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
return false;
}
constexpr void _reset() noexcept final override
{
if CONSTEVAL
{
delete this;
}
}
[[nodiscard]]
constexpr type_info const &_type() const noexcept final override
{
return TYPEID(Value);
}
[[nodiscard]]
constexpr void *_data() const noexcept final override
{
return const_cast<void *>(static_cast<void const *>(std::addressof(_value())));
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// _value_proxy_root
template <template <class> class Interface>
struct _value_proxy_root : iabstract<Interface>
{
using interface_type = iabstract<Interface>;
static constexpr ::any::_box_kind _box_kind = ::any::_box_kind::_proxy;
static constexpr bool _movable = extension_of<iabstract<Interface>, imovable>;
static constexpr bool _copyable = extension_of<iabstract<Interface>, icopyable>;
[[ALWAYS_INLINE]]
inline constexpr _value_proxy_root() noexcept
{
if CONSTEVAL
{
pointer = nullptr;
}
else
{
*::any::start_lifetime_as<_tagged_ptr>(buffer) = _tagged_ptr();
}
}
constexpr _value_proxy_root(_value_proxy_root &&other) noexcept
requires _movable
: _value_proxy_root()
{
swap(other);
}
constexpr _value_proxy_root(_value_proxy_root const &other)
requires _copyable
: _value_proxy_root()
{
if (!empty(other))
value(other)._copy_to(pointer, buffer);
}
constexpr ~_value_proxy_root()
{
_reset();
}
constexpr _value_proxy_root &operator=(_value_proxy_root &&other) noexcept
requires _movable
{
if (this != &other)
{
_reset();
swap(other);
}
return *this;
}
constexpr _value_proxy_root &operator=(_value_proxy_root const &other)
requires _copyable
{
if (this != &other)
_value_proxy_root(other).swap(*this);
return *this;
}
constexpr void swap(_value_proxy_root &other) noexcept
requires _movable
{
if CONSTEVAL
{
std::swap(pointer, other.pointer);
}
else
{
if (this == &other)
return;
auto &this_ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
auto &that_ptr = *::any::start_lifetime_as<_tagged_ptr>(other.buffer);
// This also covers the case where both this_ptr and that_ptr are null.
if (!this_ptr._is_vptr() && !that_ptr._is_vptr())
return std::swap(this_ptr, that_ptr);
if (this_ptr == nullptr)
return value(other)._move_to(pointer, buffer);
if (that_ptr == nullptr)
return value(*this)._move_to(other.pointer, other.buffer);
auto temp = std::move(*this);
value(other)._move_to(pointer, buffer);
value(temp)._move_to(other.pointer, other.buffer);
}
}
template <class Value, class... Args>
constexpr Value &_emplace(Args &&...args)
{
static_assert(_decayed<Value>, "Value must be an object type.");
using model_type = _value_model<Interface, Value>;
auto &model = ::any::emplace_into<model_type>(pointer, buffer, std::forward<Args>(args)...);
return model._value();
}
template <int = 0, class CvRefValue, class Value = std::decay_t<CvRefValue>>
constexpr Value &_emplace(CvRefValue &&value)
{
return _emplace<Value>(std::forward<CvRefValue>(value));
}
template <class Value, class... Args>
constexpr Value &emplace(Args &&...args)
{
_reset();
return _emplace<Value>(std::forward<Args>(args)...);
}
template <int = 0, class CvRefValue, class Value = std::decay_t<CvRefValue>>
constexpr Value &emplace(CvRefValue &&value)
{
_reset();
return _emplace<Value>(std::forward<CvRefValue>(value));
}
[[nodiscard]]
constexpr iabstract<Interface> &_value() noexcept
{
if CONSTEVAL
{
return *static_cast<iabstract<Interface> *>(pointer);
}
else
{
auto const ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
ASSERT(ptr != nullptr);
return *static_cast<iabstract<Interface> *>(ptr._is_vptr() ? buffer : ptr._get());
}
}
[[nodiscard]]
constexpr iabstract<Interface> const &_value() const noexcept
{
if CONSTEVAL
{
return *static_cast<iabstract<Interface> const *>(pointer);
}
else
{
auto const ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
ASSERT(ptr != nullptr);
return *static_cast<iabstract<Interface> const *>(ptr._is_vptr() ? buffer : ptr._get());
}
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
if CONSTEVAL
{
return pointer == nullptr;
}
else
{
return *::any::start_lifetime_as<_tagged_ptr>(buffer) == nullptr;
}
}
[[ALWAYS_INLINE]]
inline constexpr void _reset() noexcept final override
{
if CONSTEVAL
{
if (pointer != nullptr)
std::exchange(pointer, {})->_reset();
}
else
{
auto &ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
if (ptr == nullptr)
return;
else if (ptr._is_vptr())
std::destroy_at(std::addressof(_value()));
else
delete std::addressof(_value());
ptr = _tagged_ptr();
}
}
[[nodiscard]]
constexpr type_info const &_type() const noexcept final override
{
return _empty() ? TYPEID(void) : type(_value());
}
[[nodiscard]]
constexpr void *_data() const noexcept final override
{
return _empty() ? nullptr : data(_value());
}
[[nodiscard]]
constexpr bool _in_situ() const noexcept
{
if CONSTEVAL
{
return false;
}
else
{
return ::any::start_lifetime_as<_tagged_ptr>(buffer)->_is_vptr();
}
}
union
{
_iroot *pointer;
unsigned char buffer[iabstract<Interface>::buffer_size];
};
};
//////////////////////////////////////////////////////////////////////////////////////////
// _reference_root
template <template <class> class Interface, class CvValue>
struct _reference_root : iabstract<Interface>
{
static_assert(!extension_of<CvValue, Interface>,
"Value must be a concrete type, not an Interface type.");
using interface_type = iabstract<Interface>;
static constexpr ::any::_box_kind _box_kind = ::any::_box_kind::_object;
static constexpr bool _is_reference = true;
constexpr explicit _reference_root(CvValue &value) noexcept
: value_(std::addressof(value))
{
}
[[nodiscard]]
constexpr auto &_value() noexcept
{
// if constexpr (std::is_const_v<CvValue>)
// ::any::_die("attempt to obtain mutable reference from const reference\n");
return ::any::_unconst(*value_);
}
[[nodiscard]]
constexpr auto &_value() const noexcept
{
return *value_;
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
return false;
}
constexpr void _reset() noexcept final override
{
if CONSTEVAL
{
delete this;
}
}
[[nodiscard]]
constexpr type_info const &_type() const noexcept final override
{
return TYPEID(CvValue);
}
[[nodiscard]]
constexpr void *_data() const noexcept final override
{
return const_cast<void *>(static_cast<void const *>(value_));
}
private:
CvValue *value_;
};
//////////////////////////////////////////////////////////////////////////////////////////
// _reference_proxy_root
template <template <class> class Interface>
struct _reference_proxy_root : iabstract<Interface>
{
using interface_type = iabstract<Interface>;
static constexpr ::any::_box_kind _box_kind = ::any::_box_kind::_proxy;
static constexpr bool _is_reference = true;
constexpr _reference_proxy_root() noexcept
{
if CONSTEVAL
{
pointer = nullptr;
}
else
{
*::any::start_lifetime_as<_tagged_ptr>(buffer) = _tagged_ptr();
}
}
constexpr _reference_proxy_root(_reference_proxy_root &&__other) noexcept
: _reference_proxy_root()
{
swap(__other);
}
constexpr _reference_proxy_root(_reference_proxy_root const &__other) noexcept
{
if CONSTEVAL
{
value(__other)._bind(*this);
}
else
{
std::memcpy(buffer, __other.buffer, sizeof(buffer));
}
}
constexpr _reference_proxy_root &operator=(_reference_proxy_root &&__other) noexcept
{
if (this != &__other)
{
_reset();
swap(__other);
}
return *this;
}
constexpr _reference_proxy_root &operator=(_reference_proxy_root const &__other) noexcept
{
if (this != &__other)
{
_reset();
_reference_proxy_root(__other).swap(*this);
}
return *this;
}
constexpr ~_reference_proxy_root()
{
if CONSTEVAL
{
_reset();
}
}
constexpr void swap(_reference_proxy_root &other) noexcept
{
if (this != &other)
{
if CONSTEVAL
{
std::swap(pointer, other.pointer);
}
else
{
std::swap(buffer, other.buffer);
}
}
}
template <extension_of<Interface> CvDerived>
constexpr void rebind(CvDerived &other) noexcept
{
// val should be a reference to an value model
static_assert(!CvDerived::_is_reference, "CvDerived should not be a reference model");
if CONSTEVAL
{
other._bind(*this);
}
else
{
if (std::derived_from<CvDerived, iabstract<Interface>>)
{
//! Optimize for when Base derives from iabstract<Interface>. Store the
//! address of value(other) directly in out as a tagged ptr instead of
//! introducing an indirection.
//! @post _is_vptr() == false
auto &ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
ptr = static_cast<iabstract<Interface> *>(std::addressof(::any::_unconst(other)));
}
else
{
//! @post _is_vptr() == true
other._bind(*this);
}
}
}
template <class CvValue>
constexpr void rebind(CvValue &val) noexcept
{
using model_type = _reference_model<Interface, CvValue>;
if CONSTEVAL
{
pointer = ::new model_type(val);
}
else
{
if constexpr (std::derived_from<CvValue, iabstract<Interface>>)
{
static_assert(!CvValue::_is_reference, "CvValue should not be a reference model");
//! Optimize for when Base derives from iabstract<Interface>. Store the
//! address of value(other) directly in out as a tagged ptr instead of
//! introducing an indirection.
//! @post _is_vptr() == false
auto &ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
ptr = static_cast<iabstract<Interface> *>(std::addressof(::any::_unconst(val)));
}
else
{
//! @post _is_vptr() == true
::any::emplace_into<model_type>(pointer, buffer, val);
}
}
}
[[nodiscard]]
constexpr iabstract<Interface> &_value() noexcept
{
if CONSTEVAL
{
return *static_cast<iabstract<Interface> *>(pointer);
}
else
{
ASSERT(!_empty());
auto const ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
return *static_cast<iabstract<Interface> *>(ptr._is_vptr() ? buffer : ptr._get());
}
}
[[nodiscard]]
constexpr iabstract<Interface> const &_value() const noexcept
{
if CONSTEVAL
{
return *static_cast<iabstract<Interface> const *>(pointer);
}
else
{
ASSERT(!_empty());
auto const ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
return *static_cast<iabstract<Interface> const *>(ptr._is_vptr() ? buffer : ptr._get());
}
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
if CONSTEVAL
{
return pointer == nullptr;
}
else
{
return *::any::start_lifetime_as<_tagged_ptr>(buffer) == nullptr;
}
}
constexpr void _reset() noexcept final override
{
if CONSTEVAL
{
if (pointer != nullptr)
std::exchange(pointer, {})->_reset();
}
else
{
*::any::start_lifetime_as<_tagged_ptr>(buffer) = _tagged_ptr();
}
}
[[nodiscard]]
constexpr type_info const &_type() const noexcept final override
{
return _empty() ? TYPEID(void) : type(_value());
}
[[nodiscard]]
constexpr void *_data() const noexcept final override
{
return _empty() ? nullptr : data(_value());
}
[[nodiscard]]
constexpr bool _indirect() const noexcept
{
if CONSTEVAL
{
return true;
}
else
{
return ::any::start_lifetime_as<_tagged_ptr>(buffer)->_is_vptr();
}
}
private:
// storage for one vtable ptr and one pointer for the referant
union
{
_iroot *pointer;
mutable unsigned char buffer[2 * sizeof(void *)]{};
};
};
//////////////////////////////////////////////////////////////////////////////////////////
// bad_any_cast
struct bad_any_cast : std::exception
{
[[nodiscard]]
constexpr char const *what() const noexcept override
{
return "bad_any_cast";
}
};
#if __cpp_exceptions
[[noreturn]]
inline void _throw_bad_any_cast()
{
throw bad_any_cast();
}
#else
[[noreturn]]
inline constexpr void _throw_bad_any_cast() noexcept
{
::any::_die("bad_any_cast\n");
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////
// _polymorphic_downcast
template <class ResultPtr, class Interface>
[[nodiscard]]
constexpr auto *_polymorphic_downcast(Interface *from) noexcept
{
static_assert(std::is_pointer_v<ResultPtr>);
using root_t = _const_if<std::is_const_v<Interface>, std::remove_pointer_t<ResultPtr>>;
static_assert(std::derived_from<root_t, Interface>,
"_polymorphic_downcast requires From to be a base class of To");
#if __cpp_rtti
ASSERT(dynamic_cast<root_t *>(from) != nullptr);
#endif
return static_cast<root_t *>(from);
}
//////////////////////////////////////////////////////////////////////////////////////////
//! _any_static_cast
template <class Value>
struct _any_static_cast_t
{
template <template <class> class Interface, class Base>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto *operator()(Interface<Base> *proxy_ptr) const noexcept
{
return _cast<Interface>(proxy_ptr);
}
template <template <class> class Interface, class Base>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto *operator()(Interface<Base> const *proxy_ptr) const noexcept
{
return _cast<Interface>(proxy_ptr);
}
private:
static_assert(_decayed<Value>, "Value must be a decayed type.");
template <class CvModel>
[[ALWAYS_INLINE, nodiscard]]
inline static constexpr auto *_value_ptr(CvModel *model) noexcept
{
return model != nullptr ? std::addressof(value(*model)) : nullptr;
}
template <class CvProxy>
[[ALWAYS_INLINE, nodiscard]]
inline static constexpr bool _is_reference(CvProxy *proxy_ptr) noexcept
{
if constexpr (CvProxy::_is_reference)
return proxy_ptr->_indirect();
else
return false;
}
template <template <class> class Interface, class CvProxy>
[[nodiscard]]
static constexpr auto *_cast(CvProxy *proxy_ptr) noexcept
{
static_assert(CvProxy::_box_kind == _box_kind::_proxy, "CvProxy must be a proxy type.");
static_assert(!extension_of<Value, Interface>, "Cannot dynamic cast to an Interface type.");
constexpr bool is_const = std::is_const_v<CvProxy>;
using value_model = _const_if<is_const, _value_root<Interface, Value>>;
using referant_type = _const_if<is_const, Value>;
using reference_model = _const_if<is_const, _reference_root<Interface, referant_type>>;
// get the address of the model from the proxy:
auto *model_ptr = std::addressof(value(*proxy_ptr));
// If CvProxy is a reference proxy that stores the model indirectly, then model_ptr
// points to a reference_model. Otherwise, it points to a value_model.
return _is_reference(proxy_ptr)
? _value_ptr(::any::_polymorphic_downcast<reference_model *>(model_ptr))
: _value_ptr(::any::_polymorphic_downcast<value_model *>(model_ptr));
}
};
//////////////////////////////////////////////////////////////////////////////////////////
//! _any_static_cast
template <class Value>
struct _any_dynamic_cast_t
{
template <class CvProxy>
[[nodiscard]]
constexpr auto *operator()(CvProxy *proxy_ptr) const noexcept
{
return type(*proxy_ptr) == TYPEID(Value) ? _any_static_cast_t<Value>{}(proxy_ptr) : nullptr;
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// any_cast
template <class Value, template <class> class _Cast>
struct _any_cast_t
{
static_assert(_decayed<Value>);
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto *operator()(Interface<Base> *ptr) const noexcept
{
if constexpr (extension_of<Value, Interface>)
return ptr;
else if (ptr == nullptr || empty(*ptr))
return static_cast<Value *>(nullptr);
else
return _Cast<Value>{}(ptr);
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto *operator()(Interface<Base> const *ptr) const noexcept
{
if constexpr (extension_of<Value, Interface>)
return ptr;
else if (ptr == nullptr || empty(*ptr))
return static_cast<Value const *>(nullptr);
else
return _Cast<Value>{}(ptr);
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto &&operator()(Interface<Base> &&object) const
{
auto *ptr = (*this)(std::addressof(object));
if (ptr == nullptr)
_throw_bad_any_cast();
if constexpr (Base::_is_reference)
return *ptr;
else
return std::move(*ptr);
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto &operator()(Interface<Base> &object) const
{
auto *ptr = (*this)(std::addressof(object));
if (ptr == nullptr)
_throw_bad_any_cast();
return *ptr;
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto &operator()(Interface<Base> const &object) const
{
auto *ptr = (*this)(std::addressof(object));
if (ptr == nullptr)
_throw_bad_any_cast();
return *ptr;
}
template <template <class> class Interface>
[[nodiscard]]
constexpr auto *operator()(any_ptr<Interface> const &ptr) const
{
return (*this)(ptr.operator->());
}
template <template <class> class Interface>
[[nodiscard]]
constexpr auto *operator()(any_const_ptr<Interface> const &ptr) const
{
return (*this)(ptr.operator->());
}
};
template <class Value>
struct any_cast_t : _any_cast_t<Value, _any_dynamic_cast_t>
{
};
template <class Value>
constexpr any_cast_t<Value> any_cast{};
//////////////////////////////////////////////////////////////////////////////////////////
// any_static_cast
template <class Value>
struct any_static_cast_t : _any_cast_t<Value, _any_static_cast_t>
{
};
template <class Value>
constexpr any_static_cast_t<Value> any_static_cast{};
//////////////////////////////////////////////////////////////////////////////////////////
// imovable
template <class Base>
struct imovable : interface<imovable, Base>
{
using imovable::interface::interface;
constexpr virtual void _move_to(_iroot *&, std::span<unsigned char>) noexcept
{
::any::_die(_pure_virt_msg, "_move_to");
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// icopyable
template <class Base>
struct icopyable : interface<icopyable, Base, extends<imovable>>
{
using icopyable::interface::interface;
constexpr virtual void _copy_to(_iroot *&, std::span<unsigned char>) const
{
::any::_die(_pure_virt_msg, "_copy_to");
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// utils
template <class Value, template <class> class Interface>
concept _model_of = _decayed<Value> && !std::derived_from<Value, _iroot>;
//////////////////////////////////////////////////////////////////////////////////////////
// any
template <template <class> class Interface>
struct any final : _value_proxy_model<Interface>
{
private:
template <class Other>
static constexpr bool _as_large_as =
iabstract<Interface>::buffer_size >= Interface<Other>::buffer_size && !Other::_is_reference;
public:
any() = default;
// Construct from an object that implements the interface (and is not an any<>
// itself)
template <_model_of<Interface> Value>
constexpr any(Value value)
: any()
{
(*this)._emplace(std::move(value));
}
// Implicit derived-to-base conversion constructor
template <class Other>
requires extension_of<Interface<Other>, imovable>
constexpr any(Interface<Other> other) noexcept(_as_large_as<Other>)
{
(*this)._assign(std::move(other));
}
template <_model_of<Interface> Value>
constexpr any &operator=(Value value)
{
reset(*this);
(*this)._emplace(std::move(value));
return *this;
}
// Implicit derived-to-base conversion constructor
template <class Other>
requires extension_of<Interface<Other>, imovable>
constexpr any &operator=(Interface<Other> other) noexcept(_as_large_as<Other>)
{
// Guard against self-assignment when other is a reference to *this
if constexpr (Other::_is_reference)
if (data(other) == data(*this))
return *this;
reset(*this);
(*this)._assign(std::move(other));
return *this;
}
friend constexpr void swap(any &lhs, any &rhs) noexcept
requires any::_movable
{
lhs.swap(rhs);
}
private:
// Assigning from a type that extends Interface. Its buffer may be larger than
// ours, or it may be a reference type, so we can be only conditionally
// noexcept.
template <class Other>
requires extension_of<Interface<Other>, imovable>
constexpr void _assign(Interface<Other> &&other) noexcept(_as_large_as<Other>)
{
constexpr bool ptr_convertible = std::derived_from<Other, iabstract<Interface>>;
if (empty(other))
{
return;
}
else if constexpr (Other::_is_reference || !ptr_convertible)
{
return other._slice(*this);
}
else if (other._in_situ())
{
return other._slice(*this);
}
else if CONSTEVAL
{
(*this).pointer = std::exchange(other.pointer, nullptr);
}
else
{
auto &ptr = *::any::start_lifetime_as<_tagged_ptr>((*this).buffer);
ptr = *::any::start_lifetime_as<_tagged_ptr>(other.buffer);
}
}
static_assert(sizeof(iabstract<Interface>) == sizeof(void *)); // sanity check
};
//////////////////////////////////////////////////////////////////////////////////////////
// _any_ptr_base
template <template <class> class Interface>
struct _any_ptr_base
{
_any_ptr_base() = default;
constexpr _any_ptr_base(std::nullptr_t) noexcept
: ref_()
{
}
constexpr _any_ptr_base(_any_ptr_base const &other) noexcept
: ref_()
{
(*this)._proxy_assign(std::addressof(other.ref_));
}
template <template <class> class OtherInterface>
requires extension_of<iabstract<OtherInterface>, Interface>
constexpr _any_ptr_base(_any_ptr_base<OtherInterface> const &other) noexcept
: ref_()
{
(*this)._proxy_assign(std::addressof(other.ref_));
}
constexpr _any_ptr_base &operator=(_any_ptr_base const &other) noexcept
{
reset(ref_);
(*this)._proxy_assign(std::addressof(other.ref_));
return *this;
}
constexpr _any_ptr_base &operator=(std::nullptr_t) noexcept
{
reset(ref_);
return *this;
}
template <template <class> class OtherInterface>
requires extension_of<iabstract<OtherInterface>, Interface>
constexpr _any_ptr_base &operator=(_any_ptr_base<OtherInterface> const &other) noexcept
{
reset(ref_);
(*this)._proxy_assign(std::addressof(other.ref_));
return *this;
}
friend constexpr void swap(_any_ptr_base &lhs, _any_ptr_base &rhs) noexcept
{
lhs.ref_.swap(rhs.ref_);
}
[[nodiscard]]
constexpr bool operator==(_any_ptr_base const &other) const noexcept
{
return data(ref_) == data(other.ref_);
}
protected:
//! @param other A pointer to a value proxy model implementing Interface.
template <extension_of<Interface> CvDerived>
constexpr void _proxy_assign(CvDerived *other) noexcept
{
static_assert(CvDerived::_box_kind == _box_kind::_proxy);
constexpr bool is_const = std::is_const_v<CvDerived>;
if (other == nullptr || empty(*other))
return;
else if constexpr (!std::derived_from<CvDerived, iabstract<Interface>>)
value(*other)._bind(ref_);
// Optimize for when CvDerived derives from iabstract<Interface>. Store the address of
// value(other) directly in out as a tagged ptr instead of introducing an indirection.
else
ref_.rebind(::any::_as_const_if<is_const>(value(*other)));
}
//! @param other A pointer to a reference proxy model implementing Interface.
template <extension_of<Interface> CvDerived>
requires CvDerived::_is_reference
constexpr void _proxy_assign(CvDerived *other) noexcept
{
static_assert(CvDerived::_box_kind == _box_kind::_proxy);
using model_type = ::any::_reference_proxy_model<Interface>;
constexpr bool is_const = std::is_const_v<CvDerived>;
if (other == nullptr || empty(*other))
return;
// in the case where CvDerived is a base class of model_type, we can simply downcast
// and copy the model directly.
else if constexpr (std::derived_from<model_type, CvDerived>)
ref_ = *::any::_polymorphic_downcast<model_type const *>(other);
// Otherwise, we are assigning from a derived reference to a base reference, and the
// other reference is indirect (i.e., it holds a _reference_model in its buffer). We
// need to copy the referant model.
else if (other->_indirect())
value(*other)._bind(ref_);
else
ref_.rebind(::any::_as_const_if<is_const>(value(*other)));
}
template <class CvValue>
constexpr void _value_assign(CvValue *ptr) noexcept
{
reset(ref_);
if (ptr != nullptr)
ref_.rebind(*ptr);
}
static_assert(sizeof(iabstract<Interface>) == sizeof(void *)); // sanity check
template <template <class> class>
friend struct _any_ptr_base;
// the proxy model is mutable so that a const any_ptr can return non-const
// references from operator-> and operator*.
mutable _reference_proxy_model<Interface> ref_;
};
//////////////////////////////////////////////////////////////////////////////////////////
// any_ptr
template <template <class> class Interface>
struct any_ptr final : _any_ptr_base<Interface>
{
using _any_ptr_base<Interface>::_any_ptr_base;
using _any_ptr_base<Interface>::operator=;
// Disable const-to-mutable conversions:
template <template <class> class OtherInterface>
any_ptr(any_const_ptr<OtherInterface> const &) = delete;
template <template <class> class OtherInterface>
any_ptr &operator=(any_const_ptr<OtherInterface> const &) = delete;
template <_model_of<Interface> Value>
constexpr any_ptr(Value *ptr) noexcept
: _any_ptr_base<Interface>()
{
(*this)._value_assign(ptr);
}
template <class Base>
constexpr any_ptr(Interface<Base> *other) noexcept
: _any_ptr_base<Interface>()
{
(*this)._proxy_assign(other);
}
template <_model_of<Interface> Value>
constexpr any_ptr &operator=(Value *ptr) noexcept
{
reset((*this).ref_);
(*this)._value_assign(ptr);
return *this;
}
template <class Base>
constexpr any_ptr &operator=(Interface<Base> *other) noexcept
{
reset((*this).ref_);
(*this)._proxy_assign(other);
return *this;
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto *operator->() const noexcept
{
return std::addressof((*this).ref_);
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto &operator*() const noexcept
{
return (*this).ref_;
}
};
template <template <class> class Interface, class Base>
any_ptr(Interface<Base> *) -> any_ptr<Interface>;
//////////////////////////////////////////////////////////////////////////////////////////
// any_const_ptr
template <template <class> class Interface>
struct any_const_ptr final : _any_ptr_base<Interface>
{
using _any_ptr_base<Interface>::_any_ptr_base;
using _any_ptr_base<Interface>::operator=;
template <_model_of<Interface> Value>
constexpr any_const_ptr(Value const *ptr) noexcept
: _any_ptr_base<Interface>()
{
(*this)._value_assign(ptr);
}
template <class Base>
constexpr any_const_ptr(Interface<Base> const *other) noexcept
: _any_ptr_base<Interface>()
{
(*this)._proxy_assign(other);
}
template <_model_of<Interface> Value>
constexpr any_const_ptr &operator=(Value const *ptr) noexcept
{
reset((*this).ref_);
(*this)._value_assign(ptr);
return *this;
}
template <class Base>
constexpr any_const_ptr &operator=(Interface<Base> const *other) noexcept
{
reset((*this).ref_);
(*this)._proxy_assign(other);
return *this;
}
friend constexpr void swap(any_const_ptr &a, any_const_ptr &b) noexcept
{
a.ref_.swap(b.ref_);
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto const *operator->() const noexcept
{
return std::addressof((*this).ref_);
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto const &operator*() const noexcept
{
return (*this).ref_;
}
};
template <template <class> class Interface, class Base>
any_const_ptr(Interface<Base> const *) -> any_const_ptr<Interface>;
//////////////////////////////////////////////////////////////////////////////////////////
// iequality_comparable
template <class Base>
struct iequality_comparable : interface<iequality_comparable, Base>
{
using iequality_comparable::interface::interface;
template <class Other>
[[nodiscard]]
constexpr bool operator==(iequality_comparable<Other> const &other) const
{
return _equal_to(::any::caddressof(other));
}
private:
[[nodiscard]]
constexpr virtual bool _equal_to(any_const_ptr<iequality_comparable> other) const
{
auto const &type = ::any::type(*this);
if (type != ::any::type(*other))
return false;
if (type == TYPEID(void))
return true;
using value_type = value_of_t<iequality_comparable>;
return value(*this) == ::any::any_static_cast<value_type>(*other);
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// isemiregular
template <class Base>
struct isemiregular : interface<isemiregular, Base, extends<icopyable, iequality_comparable>>
{
using isemiregular::interface::interface;
};
} // namespace any
//////////////////////////////////////////////////////////////////////////////////////////
// smoke tests
#include <cstdio>
#include <concepts>
#include <type_traits>
template <class Base>
struct ifoo : any::interface<ifoo, Base>
{
using ifoo::interface::interface;
constexpr virtual void foo()
{
any::value(*this).foo();
}
constexpr virtual void cfoo() const
{
any::value(*this).cfoo();
}
};
template <class Base>
struct ibar : any::interface<ibar, Base, any::extends<ifoo, any::icopyable>>
{
using ibar::interface::interface;
constexpr virtual void bar()
{
any::value(*this).bar();
}
};
template <class Base>
struct ibaz : any::interface<ibaz, Base, any::extends<ibar>, 5 * sizeof(void *)>
{
using ibaz::interface::interface;
constexpr ~ibaz() = default;
constexpr virtual void baz()
{
any::value(*this).baz();
}
};
struct foobar
{
constexpr void foo()
{
if (!std::is_constant_evaluated())
std::printf("foo override, value = %d\n", value);
}
constexpr void cfoo() const
{
if (!std::is_constant_evaluated())
std::printf("cfoo override, value = %d\n", value);
}
constexpr void bar()
{
if (!std::is_constant_evaluated())
std::printf("bar override, value = %d\n", value);
}
constexpr void baz()
{
if (!std::is_constant_evaluated())
std::printf("baz override, value = %d\n", value);
}
bool operator==(const foobar &other) const noexcept = default;
int value = 42;
};
static_assert(
std::derived_from<any::iabstract<any::icopyable>, any::iabstract<any::imovable>>);
static_assert(std::derived_from<any::iabstract<ibar>, any::iabstract<ifoo>>);
static_assert(!std::derived_from<any::iabstract<ibar>, any::iabstract<any::icopyable>>);
static_assert(any::extension_of<any::iabstract<ibar>, any::icopyable>);
// Test the Diamond of Death inheritance problem:
template <class Base>
struct IFoo : any::interface<IFoo, Base, any::extends<any::icopyable>>
{
using IFoo::interface::interface;
constexpr virtual void foo()
{
any::value(*this).foo();
}
};
template <class Base>
struct IBar : any::interface<IBar, Base, any::extends<any::icopyable>>
{
using IBar::interface::interface;
constexpr virtual void bar()
{
any::value(*this).bar();
}
};
template <class Base>
struct IBaz : any::interface<IBaz, Base, any::extends<IFoo, IBar>> // inherits twice
// from icopyable
{
using IBaz::interface::interface;
constexpr virtual void baz()
{
any::value(*this).baz();
}
};
static_assert(std::derived_from<any::iabstract<IBaz>, any::iabstract<IFoo>>);
static_assert(std::derived_from<any::iabstract<IBaz>, any::iabstract<any::icopyable>>);
void test_deadly_diamond_of_death()
{
any::any<IBaz> m(foobar{});
m.foo();
m.bar();
m.baz();
}
static_assert(any::iabstract<ifoo>::buffer_size < any::iabstract<ibaz>::buffer_size);
// test constant evaluation works
consteval void test_consteval()
{
any::any<ibaz> m(foobar{});
[[maybe_unused]] auto x = any::any_static_cast<foobar>(m);
x = any::any_cast<foobar>(m);
m.foo();
[[maybe_unused]] auto n = m;
[[maybe_unused]] auto p = any::caddressof(m);
any::any<any::iequality_comparable> a = 42;
ASSERT(a == a);
any::any_ptr<ibaz> pifoo = any::addressof(m);
[[maybe_unused]] auto y = any::any_cast<foobar>(pifoo);
}
int main()
{
std::printf("%.*s\n", (int)TYPEID(foobar).name().size(), TYPEID(foobar).name().data());
std::printf("sizeof void*: %d\n", (int)sizeof(void *));
std::printf("sizeof interface: %d\n", (int)sizeof(any::iabstract<ibaz>));
test_consteval();
any::any<ibaz> m(foobar{});
ASSERT(m._in_situ());
ASSERT(any::type(m) == TYPEID(foobar));
m.foo();
m.bar();
m.baz();
any::any<ifoo> n = std::move(m);
n.foo();
auto ptr = any::caddressof(m);
any::_unconst(*ptr).foo();
// ptr->foo(); // does not compile because it is a const-correctness violation
ptr->cfoo();
const auto ptr2 = any::addressof(m);
ptr2->foo();
any::any_ptr<ifoo> pifoo = ptr2;
m = *ptr; // assignment from type-erased references is supported
any::any<any::isemiregular> a = 42;
any::any<any::isemiregular> b = 42;
any::any<any::isemiregular> c = 43;
ASSERT(a == b);
ASSERT(!(a != b));
ASSERT(!(a == c));
ASSERT(a != c);
any::reset(b);
ASSERT(!(a == b));
ASSERT(a != b);
ASSERT(!(b == a));
ASSERT(b != a);
any::any<any::iequality_comparable> x = a;
ASSERT(x == x);
ASSERT(x == a);
ASSERT(a == x);
a = 43;
ASSERT(x != a);
ASSERT(a != x);
any::reset(a);
ASSERT(b == a);
auto z = any::caddressof(c);
[[maybe_unused]] const int *p = &any::any_cast<int>(c);
[[maybe_unused]] const int *q = any::any_cast<int>(z);
ASSERT(any::any_cast<int>(z) == &any::any_cast<int>(c));
auto y = any::addressof(c);
int *r = any::any_cast<int>(std::move(y));
ASSERT(r == &any::any_cast<int>(c));
z = y; // assign non-const ptr to const ptr
z = &*y;
ASSERT(y == z);
test_deadly_diamond_of_death();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment