/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once /** \file * \ingroup bli * * A #blender::Any is a type-safe container for single values of any copy constructible type. * It is similar to #std::any but provides the following two additional features: * - Adjustable inline buffer capacity and alignment. #std::any has a small inline buffer in most * implementations as well, but its size is not guaranteed. * - Can store additional user-defined type information without increasing the stack size of #Any. */ #include #include #include "BLI_memory_utils.hh" namespace blender { namespace detail { /** * Contains function pointers that manage the memory in an #Any. * Additional type specific #ExtraInfo can be embedded here as well. */ template struct AnyTypeInfo { void (*copy_construct)(void *dst, const void *src); void (*move_construct)(void *dst, void *src); void (*destruct)(void *src); const void *(*get)(const void *src); ExtraInfo extra_info; /** * Used when #T is stored directly in the inline buffer of the #Any. */ template static const AnyTypeInfo &get_for_inline() { static AnyTypeInfo funcs = {[](void *dst, const void *src) { new (dst) T(*(const T *)src); }, [](void *dst, void *src) { new (dst) T(std::move(*(T *)src)); }, [](void *src) { ((T *)src)->~T(); }, [](const void *src) { return src; }, ExtraInfo::template get()}; return funcs; } /** * Used when #T can't be stored directly in the inline buffer and is stored in a #std::unique_ptr * instead. In this scenario, the #std::unique_ptr is stored in the inline buffer. */ template static const AnyTypeInfo &get_for_unique_ptr() { using Ptr = std::unique_ptr; static AnyTypeInfo funcs = { [](void *dst, const void *src) { new (dst) Ptr(new T(**(const Ptr *)src)); }, [](void *dst, void *src) { new (dst) Ptr(new T(std::move(**(Ptr *)src))); }, [](void *src) { ((Ptr *)src)->~Ptr(); }, [](const void *src) -> const void * { return &**(const Ptr *)src; }, ExtraInfo::template get()}; return funcs; } /** * Used when the #Any does not contain any type currently. */ static const AnyTypeInfo &get_for_empty() { static AnyTypeInfo funcs = {[](void *UNUSED(dst), const void *UNUSED(src)) {}, [](void *UNUSED(dst), void *UNUSED(src)) {}, [](void *UNUSED(src)) {}, [](const void *UNUSED(src)) -> const void * { return nullptr; }, ExtraInfo{}}; return funcs; } }; /** * Dummy extra info that is used when no additional type information should be stored in the #Any. */ struct NoExtraInfo { template static NoExtraInfo get() { return {}; } }; } // namespace detail template< /** * Either void or a struct that contains data members for additional type information. * The struct has to have a static `ExtraInfo get()` method that initializes the struct * based on a type. */ typename ExtraInfo = void, /** * Size of the inline buffer. This allows types that are small enough to be stored directly * inside the #Any without an additional allocation. */ size_t InlineBufferCapacity = 8, /** * Required minimum alignment of the inline buffer. If this is smaller than the alignment * requirement of a used type, a separate allocation is necessary. */ size_t Alignment = 8> class Any { private: /* Makes it possible to use void in the template parameters. */ using RealExtraInfo = std::conditional_t, detail::NoExtraInfo, ExtraInfo>; using Info = detail::AnyTypeInfo; /** * Inline buffer that either contains nothing, the stored value directly, or a #std::unique_ptr * to the value. */ AlignedBuffer)), Alignment> buffer_{}; /** * Information about the type that is currently stored. */ const Info *info_ = &Info::get_for_empty(); public: /** Only copy constructible types can be stored in #Any. */ template static constexpr inline bool is_allowed_v = std::is_copy_constructible_v; /** * Checks if the type will be stored in the inline buffer or if it requires a separate * allocation. */ template static constexpr inline bool is_inline_v = std::is_nothrow_move_constructible_v && sizeof(T) <= InlineBufferCapacity && alignof(T) <= Alignment; /** * Checks if #T is the same type as this #Any, because in this case the behavior of e.g. the * assignment operator is different. */ template static constexpr inline bool is_same_any_v = std::is_same_v, Any>; private: template const Info &get_info() const { using DecayT = std::decay_t; static_assert(is_allowed_v); if constexpr (is_inline_v) { return Info::template get_for_inline(); } else { return Info::template get_for_unique_ptr(); } } public: Any() = default; Any(const Any &other) : info_(other.info_) { info_->copy_construct(&buffer_, &other.buffer_); } /** * \note The #other #Any will not be empty afterwards if it was not before. Just its value is in * a moved-from state. */ Any(Any &&other) noexcept : info_(other.info_) { info_->move_construct(&buffer_, &other.buffer_); } /** * Constructs a new #Any that contains the given type #T from #args. The #std::in_place_type_t is * used to disambiguate this and the copy/move constructors. */ template explicit Any(std::in_place_type_t, Args &&...args) { using DecayT = std::decay_t; static_assert(is_allowed_v); info_ = &this->template get_info(); if constexpr (is_inline_v) { /* Construct the value directly in the inline buffer. */ new (&buffer_) DecayT(std::forward(args)...); } else { /* Construct the value in a new allocation and store a #std::unique_ptr to it in the inline * buffer. */ new (&buffer_) std::unique_ptr(new DecayT(std::forward(args)...)); } } /** * Constructs a new #Any that contains the given value. */ template))> Any(T &&value) : Any(std::in_place_type, std::forward(value)) { } ~Any() { info_->destruct(&buffer_); } /** * \note: Only needed because the template below does not count as copy assignment operator. */ Any &operator=(const Any &other) { if (this == &other) { return *this; } this->~Any(); new (this) Any(other); return *this; } /** Assign any value to the #Any. */ template Any &operator=(T &&other) { if constexpr (is_same_any_v) { if (this == &other) { return *this; } } this->~Any(); new (this) Any(std::forward(other)); return *this; } /** Destruct any existing value to make it empty. */ void reset() { info_->destruct(&buffer_); info_ = &Info::get_for_empty(); } operator bool() const { return this->has_value(); } bool has_value() const { return info_ != &Info::get_for_empty(); } template std::decay_t &emplace(Args &&...args) { this->~Any(); new (this) Any(std::in_place_type, std::forward(args)...); return this->get(); } /** Return true when the value that is currently stored is a #T. */ template bool is() const { return info_ == &this->template get_info(); } /** Get a pointer to the stored value. */ void *get() { return const_cast(info_->get(&buffer_)); } /** Get a pointer to the stored value. */ const void *get() const { return info_->get(&buffer_); } /** * Get a reference to the stored value. This invokes undefined behavior when #T does not have the * correct type. */ template std::decay_t &get() { BLI_assert(this->is()); return *static_cast *>(this->get()); } /** * Get a reference to the stored value. This invokes undefined behavior when #T does not have the * correct type. */ template const std::decay_t &get() const { BLI_assert(this->is()); return *static_cast *>(this->get()); } /** * Get extra information that has been stored for the contained type. */ const RealExtraInfo &extra_info() const { return info_->extra_info; } }; } // namespace blender