Blender V5.0
BLI_any.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
16
17#include <algorithm>
18#include <utility>
19
20#include "BLI_memory_utils.hh"
21
22namespace blender {
23
24namespace detail {
25
30template<typename ExtraInfo> struct AnyTypeInfo {
31 /* The pointers are allowed to be null, which means that the implementation is trivial. */
32 void (*copy_construct)(void *dst, const void *src);
33 void (*move_construct)(void *dst, void *src);
34 void (*destruct)(void *src);
35 const void *(*get)(const void *src);
36 ExtraInfo extra_info;
37};
38
42template<typename ExtraInfo, typename T>
45 nullptr :
46 +[](void *dst, const void *src) { new (dst) T(*static_cast<const T *>(src)); },
48 nullptr :
49 +[](void *dst, void *src) { new (dst) T(std::move(*static_cast<T *>(src))); },
51 nullptr :
52 +[](void *src) { std::destroy_at((static_cast<T *>(src))); },
53 nullptr,
54 ExtraInfo::template get<T>()};
55
60template<typename T> using Ptr = std::unique_ptr<T>;
61template<typename ExtraInfo, typename T>
63 [](void *dst, const void *src) { new (dst) Ptr<T>(new T(**(const Ptr<T> *)src)); },
64 [](void *dst, void *src) { new (dst) Ptr<T>(new T(std::move(**(Ptr<T> *)src))); },
65 [](void *src) { std::destroy_at((Ptr<T> *)src); },
66 [](const void *src) -> const void * { return &**(const Ptr<T> *)src; },
67 ExtraInfo::template get<T>()};
68
73 template<typename T> static constexpr NoExtraInfo get()
74 {
75 return {};
76 }
77};
78
79} // namespace detail
80
81template<
87 typename ExtraInfo = void,
92 size_t InlineBufferCapacity = 8,
97 size_t Alignment = 8>
98class Any {
99 private:
100 /* Makes it possible to use void in the template parameters. */
101 using RealExtraInfo =
102 std::conditional_t<std::is_void_v<ExtraInfo>, blender::detail::NoExtraInfo, ExtraInfo>;
104 static constexpr size_t RealInlineBufferCapacity = std::max(InlineBufferCapacity,
105 sizeof(std::unique_ptr<int>));
106
112
117 const Info *info_ = nullptr;
118
119 public:
121 template<typename T> static constexpr bool is_allowed_v = std::is_copy_constructible_v<T>;
122
127 template<typename T>
128 static constexpr bool is_inline_v = std::is_nothrow_move_constructible_v<T> &&
129 sizeof(T) <= InlineBufferCapacity && alignof(T) <= Alignment;
130
135 template<typename T> static constexpr bool is_same_any_v = std::is_same_v<std::decay_t<T>, Any>;
136
137 private:
138 template<typename T> const Info &get_info() const
139 {
140 using DecayT = std::decay_t<T>;
141 static_assert(is_allowed_v<DecayT>);
142 if constexpr (is_inline_v<DecayT>) {
143 return detail::template info_for_inline<RealExtraInfo, DecayT>;
144 }
145 else {
146 return detail::template info_for_unique_ptr<RealExtraInfo, DecayT>;
147 }
148 }
149
150 public:
151 Any() = default;
152
153 Any(const Any &other) : info_(other.info_)
154 {
155 if (info_ != nullptr) {
156 if (info_->copy_construct != nullptr) {
157 info_->copy_construct(&buffer_, &other.buffer_);
158 }
159 else {
160 std::copy_n(static_cast<const std::byte *>(other.buffer_.ptr()),
161 RealInlineBufferCapacity,
162 static_cast<std::byte *>(buffer_.ptr()));
163 }
164 }
165 }
166
171 Any(Any &&other) noexcept : info_(other.info_)
172 {
173 if (info_ != nullptr) {
174 if (info_->move_construct != nullptr) {
175 info_->move_construct(&buffer_, &other.buffer_);
176 }
177 else {
178 std::copy_n(static_cast<const std::byte *>(other.buffer_.ptr()),
179 RealInlineBufferCapacity,
180 static_cast<std::byte *>(buffer_.ptr()));
181 }
182 }
183 }
184
189 template<typename T, typename... Args> explicit Any(std::in_place_type_t<T>, Args &&...args)
190 {
191 this->emplace_on_empty<T>(std::forward<Args>(args)...);
192 }
193
197 template<typename T, BLI_ENABLE_IF((!is_same_any_v<T>))>
198 Any(T &&value) : Any(std::in_place_type<T>, std::forward<T>(value))
199 {
200 }
201
203 {
204 if (info_ != nullptr) {
205 if (info_->destruct != nullptr) {
206 info_->destruct(&buffer_);
207 }
208 }
209 }
210
214 Any &operator=(const Any &other)
215 {
216 if (this == &other) {
217 return *this;
218 }
219 this->~Any();
220 new (this) Any(other);
221 return *this;
222 }
223
225 template<typename T> Any &operator=(T &&other)
226 {
227 if constexpr (is_same_any_v<T>) {
228 if (this == &other) {
229 return *this;
230 }
231 }
232 this->~Any();
233 new (this) Any(std::forward<T>(other));
234 return *this;
235 }
236
238 void reset()
239 {
240 if (info_ != nullptr) {
241 if (info_->destruct != nullptr) {
242 info_->destruct(&buffer_);
243 }
244 }
245 info_ = nullptr;
246 }
247
248 operator bool() const
249 {
250 return this->has_value();
251 }
252
253 bool has_value() const
254 {
255 return info_ != nullptr;
256 }
257
258 template<typename T, typename... Args> std::decay_t<T> &emplace(Args &&...args)
259 {
260 this->~Any();
261 new (this) Any(std::in_place_type<T>, std::forward<Args>(args)...);
262 return this->get<T>();
263 }
264
265 template<typename T, typename... Args> std::decay_t<T> &emplace_on_empty(Args &&...args)
266 {
267 BLI_assert(!this->has_value());
268 using DecayT = std::decay_t<T>;
269 static_assert(is_allowed_v<DecayT>);
270 info_ = &this->template get_info<DecayT>();
271 if constexpr (is_inline_v<DecayT>) {
272 /* Construct the value directly in the inline buffer. */
273 DecayT *stored_value = new (&buffer_) DecayT(std::forward<Args>(args)...);
274 return *stored_value;
275 }
276 else {
277 /* Construct the value in a new allocation and store a #std::unique_ptr to it in the inline
278 * buffer. */
279 std::unique_ptr<DecayT> *stored_value = new (&buffer_)
280 std::unique_ptr<DecayT>(new DecayT(std::forward<Args>(args)...));
281 return **stored_value;
282 }
283 }
284
289 template<typename T> void *allocate()
290 {
291 this->reset();
292 return this->allocate_on_empty<T>();
293 }
294
299 template<typename T> void *allocate_on_empty()
300 {
301 BLI_assert(!this->has_value());
302 static_assert(is_allowed_v<T>);
303 info_ = &this->template get_info<T>();
304 if constexpr (is_inline_v<T>) {
305 return buffer_.ptr();
306 }
307 else {
308 /* Using raw allocation here. The caller is responsible for constructing the value. */
309 T *value = static_cast<T *>(::operator new(sizeof(T)));
310 new (&buffer_) std::unique_ptr<T>(value);
311 return value;
312 }
313 }
314
316 template<typename T> bool is() const
317 {
318 return info_ == &this->template get_info<T>();
319 }
320
322 void *get()
323 {
324 BLI_assert(info_ != nullptr);
325 if (info_->get != nullptr) {
326 return const_cast<void *>(info_->get(&buffer_));
327 }
328 return &buffer_;
329 }
330
332 const void *get() const
333 {
334 BLI_assert(info_ != nullptr);
335 if (info_->get != nullptr) {
336 return info_->get(&buffer_);
337 }
338 return &buffer_;
339 }
340
345 template<typename T> T &get()
346 {
347 /* Use const-cast to be able to reuse the const method above. */
348 return const_cast<T &>(const_cast<const Any *>(this)->get<T>());
349 }
350
355 template<typename T> const T &get() const
356 {
357 BLI_assert(this->is<T>());
358 const void *buffer;
359 /* Can avoid the `info_->get == nullptr` check because the result is known statically. */
360 if constexpr (is_inline_v<T>) {
361 buffer = &buffer_;
362 }
363 else {
364 BLI_assert(info_->get != nullptr);
365 buffer = info_->get(&buffer_);
366 }
367 return *static_cast<const T *>(buffer);
368 }
369
373 const RealExtraInfo &extra_info() const
374 {
375 BLI_assert(info_ != nullptr);
376 return info_->extra_info;
377 }
378};
379
380} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:46
Any(std::in_place_type_t< T >, Args &&...args)
Definition BLI_any.hh:189
Any & operator=(T &&other)
Definition BLI_any.hh:225
std::decay_t< T > & emplace(Args &&...args)
Definition BLI_any.hh:258
Any(const Any &other)
Definition BLI_any.hh:153
void * allocate()
Definition BLI_any.hh:289
std::decay_t< T > & emplace_on_empty(Args &&...args)
Definition BLI_any.hh:265
const RealExtraInfo & extra_info() const
Definition BLI_any.hh:373
const void * get() const
Definition BLI_any.hh:332
void * allocate_on_empty()
Definition BLI_any.hh:299
bool has_value() const
Definition BLI_any.hh:253
void reset()
Definition BLI_any.hh:238
bool is() const
Definition BLI_any.hh:316
Any(Any &&other) noexcept
Definition BLI_any.hh:171
Any(T &&value)
Definition BLI_any.hh:198
const T & get() const
Definition BLI_any.hh:355
void * get()
Definition BLI_any.hh:322
Any & operator=(const Any &other)
Definition BLI_any.hh:214
Any()=default
#define T
std::unique_ptr< T > Ptr
Definition BLI_any.hh:60
constexpr AnyTypeInfo< ExtraInfo > info_for_inline
Definition BLI_any.hh:43
constexpr AnyTypeInfo< ExtraInfo > info_for_unique_ptr
Definition BLI_any.hh:62
constexpr bool is_trivially_move_constructible_extended_v
constexpr bool is_trivially_destructible_extended_v
constexpr bool is_trivially_copy_constructible_extended_v
void(* move_construct)(void *dst, void *src)
Definition BLI_any.hh:33
void(* copy_construct)(void *dst, const void *src)
Definition BLI_any.hh:32
static constexpr NoExtraInfo get()
Definition BLI_any.hh:73