Skip to content

Instantly share code, notes, and snippets.

@ericniebler
Last active November 21, 2025 21:40
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>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <concepts>
#include <exception>
#include <memory>
#include <new>
#include <span>
#include <type_traits>
#include <utility>
#if __cpp_rtti || _MSC_VER // MSVC has the typeid operator even with RTTI off
#include <typeinfo>
#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
#define ASSERT(...) ((__VA_ARGS__) ? void() : assert(__VA_ARGS__))
//////////////////////////////////////////////////////////////////////////////////////////
//! 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
{
//////////////////////////////////////////////////////////////////////////////////////////
// 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(const void *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
//////////////////////////////////////////////////////////////////////////////////////////
// type_info and TYPEID
#if HAS_TYPEID
#define TYPEID(...) typeid(__VA_ARGS__)
using type_info = std::type_info;
#else // ^^^ HAS_TYPEID / !HAS_TYPEID vvv
#define TYPEID(...) ::any::type_info_for<__VA_ARGS__>
template <class T>
constexpr char const *pretty_name() noexcept
{
return __PRETTY_FUNCTION__;
}
struct type_info
{
type_info(type_info &&) = delete;
type_info &operator=(type_info &&) = delete;
constexpr explicit type_info(char const *name) noexcept
: name_(name)
{
}
constexpr const char *name() const noexcept
{
return name_;
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr bool operator==(const type_info &other) const noexcept
{
return this == &other;
}
private:
char const *name_;
};
template <class T>
inline constexpr type_info type_info_for{::any::pretty_name<T>()};
template <class T>
constexpr const type_info &type_info_for<T const> = type_info_for<T>;
#endif // ^^^ !HAS_TYPEID
template <class...>
[[deprecated]]
void _print()
{
}
template <class Return = void>
[[noreturn]]
inline constexpr Return _die(char const *msg, ...) noexcept
{
if (std::is_constant_evaluated())
{
::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>
concept _decayed = std::same_as<std::decay_t<T>, T>;
template <class Fn, class... Args>
using _minvoke = typename Fn::template invoke<Args...>;
template <template <class> class Template>
struct mquote1
{
template <class Type>
using invoke = Template<Type>;
};
template <class Type>
using type_identity = Type;
using midentity = mquote1<type_identity>;
template <bool>
struct _if_
{
template <class Then, class...>
using invoke = Then;
};
template <>
struct _if_<false>
{
template <class, class Else>
using invoke = Else;
};
template <bool Condition, class Then = void, class... Else>
using _if_t = typename _if_<Condition>::template invoke<Then, Else...>;
//////////////////////////////////////////////////////////////////////////////////////////
// _unconst
template <class T>
[[ALWAYS_INLINE, nodiscard]]
inline constexpr T &_unconst(const T &t) noexcept
{
return const_cast<T &>(t);
}
//////////////////////////////////////////////////////////////////////////////////////////
// _const_if
template <bool MakeConst, class T>
using _const_if = _if_t<MakeConst, const T, T>;
//////////////////////////////////////////////////////////////////////////////////////////
// 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 const Interface<Base> &
interface_cast(const Interface<Base> &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()(const T &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 const type_info &operator()(const T &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()(const Interface<Base> &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(const Interface &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;
}
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 (std::is_constant_evaluated())
{
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<_minvoke<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<_minvoke<_bases_of<Interface>, _value_root<Interface, Value>>>
{
using _base_t =
Interface<_minvoke<_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<_minvoke<_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<_minvoke<_bases_of<Interface>, _reference_root<Interface, Value>>>
{
using _base_t =
Interface<_minvoke<_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<_minvoke<_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(const Interface &iface) {
{ ::any::interface_cast<BaseInterface>(iface) } -> _has_box_kind<Interface::_box_kind>;
};
//////////////////////////////////////////////////////////////////////////////////////////
// extends
template <>
struct extends<>
{
template <class Base>
using invoke = Base;
};
template <template <class> class BaseInterface, template <class> class... BaseInterfaces>
struct extends<BaseInterface, BaseInterfaces...>
{
template <class Base, class BasesOfBase = _minvoke<_bases_of<BaseInterface>, Base>>
using invoke = _minvoke<extends<BaseInterfaces...>,
// If Base already implements BaseInterface, do not re-apply it.
_if_t<_already_implements<Base, BaseInterface>, BasesOfBase,
BaseInterface<BasesOfBase>>>;
};
constexpr const char *_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
//! Specializations of @c interface are so that @c _slice and @c _bind are only
//! overridden when they need to be (i.e., in the case of multiple inheritance).
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 const auto &_value() const noexcept
{
if constexpr (Base::_box_kind == _box_kind::_abstract)
return ::any::_die<const _interface_type &>(_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 virtual void _bind(_reference_proxy_root<Interface> &out) 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)
{
out.rebind(value(*this));
}
else
{
ASSERT(!empty(*this));
value(*this)._bind(out);
}
}
};
//////////////////////////////////////////////////////////////////////////////////////////
// _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 const type_info &_type() const noexcept
{
return ::any::_die<const type_info &>(_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 const Value &_value() const noexcept
{
return value;
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
return false;
}
constexpr void _reset() noexcept final override
{
// no-op
}
[[nodiscard]]
constexpr const type_info &_type() const noexcept final override
{
return TYPEID(Value);
}
[[nodiscard]]
constexpr void *_data() const noexcept final override
{
return const_cast<void *>(static_cast<const void *>(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 const Value &_value() const noexcept
{
return *this;
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
return false;
}
constexpr void _reset() noexcept final override
{
// no-op
}
[[nodiscard]]
constexpr const type_info &_type() const noexcept final override
{
return TYPEID(Value);
}
[[nodiscard]]
constexpr void *_data() const noexcept final override
{
return const_cast<void *>(static_cast<const void *>(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 (std::is_constant_evaluated())
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 (std::is_constant_evaluated())
{
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 (std::is_constant_evaluated())
{
return *static_cast<iabstract<Interface> *>(pointer);
}
else
{
const auto ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
ASSERT(ptr != nullptr);
return *static_cast<iabstract<Interface> *>(ptr._is_vptr() ? buffer : ptr._get());
}
}
[[nodiscard]]
constexpr const iabstract<Interface> &_value() const noexcept
{
if (std::is_constant_evaluated())
{
return *static_cast<const iabstract<Interface> *>(pointer);
}
else
{
const auto ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
ASSERT(ptr != nullptr);
return *static_cast<const iabstract<Interface> *>(ptr._is_vptr() ? buffer
: ptr._get());
}
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
if (std::is_constant_evaluated())
{
return pointer == nullptr;
}
else
{
return *::any::start_lifetime_as<_tagged_ptr>(buffer) == nullptr;
}
}
[[ALWAYS_INLINE]]
inline constexpr void _reset() noexcept final override
{
if (std::is_constant_evaluated())
{
delete static_cast<iabstract<Interface> *>(std::exchange(pointer, {}));
}
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 const type_info &_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 (std::is_constant_evaluated())
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 Value>
struct _reference_root : iabstract<Interface>
{
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(Value &value) noexcept
: value_(std::addressof(value))
{
}
[[nodiscard]]
constexpr auto &_value() noexcept
{
if constexpr (std::is_const_v<Value>)
::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
{
// no-op
}
[[nodiscard]]
constexpr const type_info &_type() const noexcept final override
{
return TYPEID(Value);
}
[[nodiscard]]
constexpr void *_data() const noexcept final override
{
return const_cast<void *>(static_cast<const void *>(value_));
}
private:
Value *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 (std::is_constant_evaluated())
pointer = nullptr;
else
*::any::start_lifetime_as<_tagged_ptr>(buffer) = _tagged_ptr();
}
constexpr ~_reference_proxy_root()
{
if (std::is_constant_evaluated())
_reset();
}
constexpr void swap(_reference_proxy_root &other) noexcept
{
if (this != &other)
{
if (std::is_constant_evaluated())
std::swap(pointer, other.pointer);
else
std::swap(buffer, other.buffer);
}
}
template <class CvValue>
constexpr CvValue &rebind(CvValue &val) noexcept
{
using model_type = _reference_model<Interface, CvValue>;
if (std::is_constant_evaluated())
{
// TODO(ericniebler): try to avoid allocation here?
pointer = ::new model_type(val);
}
else if constexpr (std::derived_from<CvValue, 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(val)));
}
else
{
//! @post _is_vptr() == true
::any::emplace_into<model_type>(pointer, buffer, val);
}
return val;
}
[[nodiscard]]
constexpr iabstract<Interface> &_value() noexcept
{
if (std::is_constant_evaluated())
{
return *static_cast<iabstract<Interface> *>(pointer);
}
else
{
ASSERT(!_empty());
const auto ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
return *static_cast<iabstract<Interface> *>(ptr._is_vptr() ? buffer : ptr._get());
}
}
[[nodiscard]]
constexpr const iabstract<Interface> &_value() const noexcept
{
if (std::is_constant_evaluated())
{
return *static_cast<const iabstract<Interface> *>(pointer);
}
else
{
ASSERT(!_empty());
const auto ptr = *::any::start_lifetime_as<_tagged_ptr>(buffer);
return *static_cast<const iabstract<Interface> *>(ptr._is_vptr() ? buffer
: ptr._get());
}
}
[[nodiscard]]
constexpr bool _empty() const noexcept final override
{
if (std::is_constant_evaluated())
return pointer == nullptr;
else
return *::any::start_lifetime_as<_tagged_ptr>(buffer) == nullptr;
}
constexpr void _reset() noexcept final override
{
if (std::is_constant_evaluated())
delete static_cast<iabstract<Interface> *>(std::exchange(pointer, {}));
else
*::any::start_lifetime_as<_tagged_ptr>(buffer) = _tagged_ptr();
}
[[nodiscard]]
constexpr const type_info &_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 (std::is_constant_evaluated())
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 const char *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 Root, class Interface>
[[nodiscard]]
constexpr Root _polymorphic_downcast(Interface *from) noexcept
requires(std::is_pointer_v<Root>)
{
using root_type = _const_if<std::is_const_v<Interface>, std::remove_pointer_t<Root>>;
using value_type [[maybe_unused]] = std::decay_t<decltype(::any::value(*Root()))>;
static_assert(std::derived_from<root_type, Interface>,
"_polymorphic_downcast requires From to be a base class of To");
#if __cpp_rtti
ASSERT(dynamic_cast<root_type *>(from) != nullptr);
#else
ASSERT(type(*from) == TYPEID(value_type));
#endif
return static_cast<root_type *>(from);
}
//////////////////////////////////////////////////////////////////////////////////////////
// _value_ptr
template <class Root>
[[nodiscard]]
constexpr auto *_value_ptr(Root *root) noexcept
{
return root != nullptr ? std::addressof(value(*root)) : nullptr;
}
//////////////////////////////////////////////////////////////////////////////////////////
//! _any_dynamic_cast
//! @tparam Value The target type to cast to. Must be _decayed.
//! @tparam Interface The interface template the source any type implements.
//! @tparam From The possibly const-qualified Interface<Model> type.
template <class Value, template <class> class Interface, class From>
[[nodiscard]]
constexpr auto *_any_dynamic_cast(From *ptr) noexcept
{
auto *val = std::addressof(value(*ptr)); // get the address of the model from the proxy
if constexpr (extension_of<Value, Interface>)
{
return val;
}
else if constexpr (!From::_is_reference)
{
using value_model = _const_if<std::is_const_v<From>, _value_root<Interface, Value>>;
return ::any::_value_ptr(::any::_polymorphic_downcast<value_model *>(val));
}
else
{
using value_model = _const_if<std::is_const_v<From>, _value_root<Interface, Value>>;
using referant_type = _const_if<std::is_const_v<From>, Value>;
using reference_model =
_const_if<std::is_const_v<From>, _reference_root<Interface, referant_type>>;
return ptr->_indirect()
? ::any::_value_ptr(::any::_polymorphic_downcast<reference_model *>(val))
: ::any::_value_ptr(::any::_polymorphic_downcast<value_model *>(val));
}
}
//////////////////////////////////////////////////////////////////////////////////////////
// any_cast
template <class Value>
struct any_cast_t
{
static_assert(_decayed<Value>);
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto *operator()(Interface<Base> *ptr) const noexcept
{
return ptr != nullptr && !empty(*ptr)
? ::any::_any_dynamic_cast<Value, Interface>(ptr)
: nullptr;
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto *operator()(const Interface<Base> *ptr) const noexcept
{
return ptr != nullptr && !empty(*ptr)
? ::any::_any_dynamic_cast<Value, Interface>(ptr)
: nullptr;
}
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()(const Interface<Base> &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()(const any_ptr<Interface> &ptr) const
{
return (*this)(ptr.operator->());
}
template <template <class> class Interface>
[[nodiscard]]
constexpr auto *operator()(const any_const_ptr<Interface> &ptr) const
{
return (*this)(ptr.operator->());
}
};
template <class Value>
constexpr any_cast_t<Value> any_cast{};
//////////////////////////////////////////////////////////////////////////////////////////
// any_static_cast
template <class Value>
struct any_static_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);
#if __cpp_constexpr < 2023'06L // C++26 constexpr void* casts (https://wg21.link/P2738R1)
else if (std::is_constant_evaluated())
return ::any::_any_dynamic_cast<Value, Interface>(ptr);
#endif
else
return static_cast<Value *>(data(*ptr));
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto *operator()(const Interface<Base> *ptr) const noexcept
{
if constexpr (extension_of<Value, Interface>)
return ptr;
else if (ptr == nullptr || empty(*ptr))
return static_cast<const Value *>(nullptr);
#if __cpp_constexpr < 2023'06L // C++26 constexpr void* casts (https://wg21.link/P2738R1)
else if (std::is_constant_evaluated())
return ::any::_any_dynamic_cast<Value, Interface>(ptr);
#endif
else
return static_cast<const Value *>(data(*ptr));
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto &&operator()(Interface<Base> &&object) const noexcept
{
ASSERT(!empty(object));
if constexpr (Base::_is_reference)
return *(*this)(std::addressof(object));
else
return std::move(*(*this)(&object));
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto &operator()(Interface<Base> &object) const noexcept
{
ASSERT(!empty(object));
return *(*this)(std::addressof(object));
}
template <template <class> class Interface, class Base>
[[nodiscard]]
constexpr auto &operator()(const Interface<Base> &object) const noexcept
{
ASSERT(!empty(object));
return *(*this)(std::addressof(object));
}
template <template <class> class Interface>
[[nodiscard]]
constexpr auto *operator()(const any_ptr<Interface> &ptr) const noexcept
{
return (*this)(ptr.operator->());
}
template <template <class> class Interface>
[[nodiscard]]
constexpr auto *operator()(const any_const_ptr<Interface> &ptr) const noexcept
{
return (*this)(ptr.operator->());
}
};
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>
constexpr any(Interface<Other> other) noexcept(_as_large_as<Other>)
requires extension_of<Interface<Other>, imovable>
{
_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>
constexpr any &operator=(Interface<Other> other) noexcept(_as_large_as<Other>)
requires extension_of<Interface<Other>, imovable>
{
// Guard against self-assignment when other is a reference to *this
if constexpr (Other::_is_reference)
if (std::addressof(value(other)) == std::addressof(value(*this)))
return *this;
reset(*this);
_assign(std::move(other));
return *this;
}
friend constexpr void swap(any &a, any &b) noexcept
requires any::_movable
{
a.swap(b);
}
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>
constexpr void _assign(Interface<Other> &&other) noexcept(_as_large_as<Other>)
requires extension_of<Interface<Other>, imovable>
{
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 (std::is_constant_evaluated())
{
(*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
template <template <class> class Interface>
struct any_ptr final : any_const_ptr<Interface>
{
any_ptr() = default;
any_ptr(const any_ptr &other) noexcept = default;
any_ptr &operator=(const any_ptr &other) noexcept = default;
bool operator==(const any_ptr &other) const noexcept = default;
constexpr any_ptr(std::nullptr_t) noexcept
: any_const_ptr<Interface>()
{
}
template <_model_of<Interface> Value>
constexpr any_ptr(Value *ptr) noexcept
: any_const_ptr<Interface>()
{
(*this)._assign(ptr);
}
template <template <class> class OtherInterface>
requires extension_of<iabstract<OtherInterface>, Interface>
constexpr any_ptr(const any_ptr<OtherInterface> &other) noexcept
: any_const_ptr<Interface>()
{
(*this)._assign(other.operator->());
}
template <class Base>
constexpr any_ptr(Interface<Base> *other) noexcept
: any_const_ptr<Interface>()
{
(*this)._assign(other);
}
template <_model_of<Interface> Value>
constexpr any_ptr &operator=(Value *ptr) noexcept
{
reset((*this).base_);
(*this)._assign(ptr);
return *this;
}
template <class Base>
constexpr any_ptr &operator=(Interface<Base> *other) noexcept
{
reset((*this).base_);
(*this)._assign(other);
return *this;
}
template <template <class> class OtherInterface>
requires extension_of<iabstract<OtherInterface>, Interface>
constexpr any_ptr &operator=(const any_ptr<OtherInterface> &other) noexcept
{
reset((*this).base_);
(*this)._assign(other.operator->());
return *this;
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto *operator->() const noexcept
{
return &(*this).base_;
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr auto &operator*() const noexcept
{
return (*this).base_;
}
};
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
{
any_const_ptr() = default;
constexpr any_const_ptr(const any_const_ptr &other) noexcept
: base_()
{
_assign(std::addressof(other.base_));
}
constexpr any_const_ptr(std::nullptr_t) noexcept
: base_()
{
}
template <_model_of<Interface> Value>
constexpr any_const_ptr(const Value *ptr) noexcept
: base_()
{
_assign(ptr);
}
template <template <class> class OtherInterface>
requires extension_of<iabstract<OtherInterface>, Interface>
constexpr any_const_ptr(const any_ptr<OtherInterface> &other) noexcept
: base_()
{
_assign(other.operator->());
}
template <template <class> class OtherInterface>
requires extension_of<iabstract<OtherInterface>, Interface>
constexpr any_const_ptr(const any_const_ptr<OtherInterface> &other) noexcept
: base_()
{
_assign(other.operator->());
}
template <class Base>
constexpr any_const_ptr(const Interface<Base> *other) noexcept
: base_()
{
_assign(other);
}
constexpr any_const_ptr &operator=(const any_const_ptr &other) noexcept
{
reset(base_);
_assign(std::addressof(other.base_));
return *this;
}
constexpr any_const_ptr &operator=(std::nullptr_t) noexcept
{
reset(base_);
return *this;
}
template <_model_of<Interface> Value>
constexpr any_const_ptr &operator=(const Value *ptr) noexcept
{
reset(base_);
_assign(ptr);
return *this;
}
template <template <class> class OtherInterface>
requires extension_of<iabstract<OtherInterface>, Interface>
constexpr any_const_ptr &operator=(const any_ptr<OtherInterface> &other) noexcept
{
reset(base_);
_assign(other.operator->());
return *this;
}
template <template <class> class OtherInterface>
requires extension_of<iabstract<OtherInterface>, Interface>
constexpr any_const_ptr &operator=(const any_const_ptr<OtherInterface> &other) noexcept
{
reset(base_);
_assign(other.operator->());
return *this;
}
template <class Base>
constexpr any_const_ptr &operator=(const Interface<Base> *other) noexcept
{
reset(base_);
_assign(other);
return *this;
}
friend constexpr void swap(any_const_ptr &a, any_const_ptr &b) noexcept
{
a.base_.swap(b.base_);
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr const auto *operator->() const noexcept
{
return std::addressof(base_);
}
[[ALWAYS_INLINE, nodiscard]]
inline constexpr const auto &operator*() const noexcept
{
return base_;
}
constexpr bool operator==(const any_const_ptr &other) const noexcept
{
return data(base_) == data(other.base_);
}
private:
template <extension_of<Interface> CvDerived>
constexpr void _assign(CvDerived *other) noexcept
{
if (other != nullptr && !empty(*other))
{
// 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.
if constexpr (!std::derived_from<CvDerived, iabstract<Interface>>)
(*other)._bind(base_);
else if constexpr (std::is_const_v<CvDerived>)
base_.rebind(std::as_const(value(*other)));
else
base_.rebind(value(*other));
}
}
template <class CvValue>
constexpr void _assign(CvValue *ptr) noexcept
{
reset(base_);
if (ptr != nullptr)
base_.rebind(*ptr);
}
static_assert(sizeof(iabstract<Interface>) == sizeof(void *)); // sanity check
friend struct any_ptr<Interface>;
// 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> base_;
};
template <template <class> class Interface, class Base>
any_const_ptr(const Interface<Base> *) -> any_const_ptr<Interface>;
//////////////////////////////////////////////////////////////////////////////////////////
// iequality_comparable
template <class Base>
struct iequality_comparable : interface<iequality_comparable, Base>
{
using iequality_comparable::interface::interface;
template <class Other>
constexpr bool operator==(const iequality_comparable<Other> &other) const
{
return _equal_to(::any::caddressof(other));
}
private:
constexpr virtual bool _equal_to(any_const_ptr<iequality_comparable> other) const
{
const auto &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
///////////////////////////////////////////////////////////////////////////////////////////////////////
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()
{
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);
}
int main()
{
test();
std::printf("sizeof void*: %d\n", (int)sizeof(void *));
std::printf("sizeof interface: %d\n", (int)sizeof(any::iabstract<ibaz>));
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