Last active
December 24, 2025 00:25
-
-
Save ericniebler/ab42506b8674f1f9633a004427e8a79c to your computer and use it in GitHub Desktop.
another take on a type-erasure library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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