Blender V4.3
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
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 inline bool is_allowed_v = std::is_copy_constructible_v<T>;
122
127 template<typename T>
128 static constexpr inline bool is_inline_v = std::is_nothrow_move_constructible_v<T> &&
129 sizeof(T) <= InlineBufferCapacity &&
130 alignof(T) <= Alignment;
131
136 template<typename T>
137 static constexpr inline bool is_same_any_v = std::is_same_v<std::decay_t<T>, Any>;
138
139 private:
140 template<typename T> const Info &get_info() const
141 {
142 using DecayT = std::decay_t<T>;
143 static_assert(is_allowed_v<DecayT>);
144 if constexpr (is_inline_v<DecayT>) {
145 return detail::template info_for_inline<RealExtraInfo, DecayT>;
146 }
147 else {
148 return detail::template info_for_unique_ptr<RealExtraInfo, DecayT>;
149 }
150 }
151
152 public:
153 Any() = default;
154
155 Any(const Any &other) : info_(other.info_)
156 {
157 if (info_ != nullptr) {
158 if (info_->copy_construct != nullptr) {
159 info_->copy_construct(&buffer_, &other.buffer_);
160 }
161 else {
162 std::copy_n(static_cast<const std::byte *>(other.buffer_.ptr()),
163 RealInlineBufferCapacity,
164 static_cast<std::byte *>(buffer_.ptr()));
165 }
166 }
167 }
168
173 Any(Any &&other) noexcept : info_(other.info_)
174 {
175 if (info_ != nullptr) {
176 if (info_->move_construct != nullptr) {
177 info_->move_construct(&buffer_, &other.buffer_);
178 }
179 else {
180 std::copy_n(static_cast<const std::byte *>(other.buffer_.ptr()),
181 RealInlineBufferCapacity,
182 static_cast<std::byte *>(buffer_.ptr()));
183 }
184 }
185 }
186
191 template<typename T, typename... Args> explicit Any(std::in_place_type_t<T>, Args &&...args)
192 {
193 this->emplace_on_empty<T>(std::forward<Args>(args)...);
194 }
195
199 template<typename T, BLI_ENABLE_IF((!is_same_any_v<T>))>
200 Any(T &&value) : Any(std::in_place_type<T>, std::forward<T>(value))
201 {
202 }
203
205 {
206 if (info_ != nullptr) {
207 if (info_->destruct != nullptr) {
208 info_->destruct(&buffer_);
209 }
210 }
211 }
212
216 Any &operator=(const Any &other)
217 {
218 if (this == &other) {
219 return *this;
220 }
221 this->~Any();
222 new (this) Any(other);
223 return *this;
224 }
225
227 template<typename T> Any &operator=(T &&other)
228 {
229 if constexpr (is_same_any_v<T>) {
230 if (this == &other) {
231 return *this;
232 }
233 }
234 this->~Any();
235 new (this) Any(std::forward<T>(other));
236 return *this;
237 }
238
240 void reset()
241 {
242 if (info_ != nullptr) {
243 if (info_->destruct != nullptr) {
244 info_->destruct(&buffer_);
245 }
246 }
247 info_ = nullptr;
248 }
249
250 operator bool() const
251 {
252 return this->has_value();
253 }
254
255 bool has_value() const
256 {
257 return info_ != nullptr;
258 }
259
260 template<typename T, typename... Args> std::decay_t<T> &emplace(Args &&...args)
261 {
262 this->~Any();
263 new (this) Any(std::in_place_type<T>, std::forward<Args>(args)...);
264 return this->get<T>();
265 }
266
267 template<typename T, typename... Args> std::decay_t<T> &emplace_on_empty(Args &&...args)
268 {
269 BLI_assert(!this->has_value());
270 using DecayT = std::decay_t<T>;
271 static_assert(is_allowed_v<DecayT>);
272 info_ = &this->template get_info<DecayT>();
273 if constexpr (is_inline_v<DecayT>) {
274 /* Construct the value directly in the inline buffer. */
275 DecayT *stored_value = new (&buffer_) DecayT(std::forward<Args>(args)...);
276 return *stored_value;
277 }
278 else {
279 /* Construct the value in a new allocation and store a #std::unique_ptr to it in the inline
280 * buffer. */
281 std::unique_ptr<DecayT> *stored_value = new (&buffer_)
282 std::unique_ptr<DecayT>(new DecayT(std::forward<Args>(args)...));
283 return **stored_value;
284 }
285 }
286
291 template<typename T> void *allocate()
292 {
293 this->reset();
294 return this->allocate_on_empty<T>();
295 }
296
301 template<typename T> void *allocate_on_empty()
302 {
303 BLI_assert(!this->has_value());
304 static_assert(is_allowed_v<T>);
305 info_ = &this->template get_info<T>();
306 if constexpr (is_inline_v<T>) {
307 return buffer_.ptr();
308 }
309 else {
310 /* Using raw allocation here. The caller is responsible for constructing the value. */
311 T *value = static_cast<T *>(::operator new(sizeof(T)));
312 new (&buffer_) std::unique_ptr<T>(value);
313 return value;
314 }
315 }
316
318 template<typename T> bool is() const
319 {
320 return info_ == &this->template get_info<T>();
321 }
322
324 void *get()
325 {
326 BLI_assert(info_ != nullptr);
327 if (info_->get != nullptr) {
328 return const_cast<void *>(info_->get(&buffer_));
329 }
330 return &buffer_;
331 }
332
334 const void *get() const
335 {
336 BLI_assert(info_ != nullptr);
337 if (info_->get != nullptr) {
338 return info_->get(&buffer_);
339 }
340 return &buffer_;
341 }
342
347 template<typename T> T &get()
348 {
349 /* Use const-cast to be able to reuse the const method above. */
350 return const_cast<T &>(const_cast<const Any *>(this)->get<T>());
351 }
352
357 template<typename T> const T &get() const
358 {
359 BLI_assert(this->is<T>());
360 const void *buffer;
361 /* Can avoid the `info_->get == nullptr` check because the result is known statically. */
362 if constexpr (is_inline_v<T>) {
363 buffer = &buffer_;
364 }
365 else {
366 BLI_assert(info_->get != nullptr);
367 buffer = info_->get(&buffer_);
368 }
369 return *static_cast<const T *>(buffer);
370 }
371
375 const RealExtraInfo &extra_info() const
376 {
377 BLI_assert(info_ != nullptr);
378 return info_->extra_info;
379 }
380};
381
382} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:50
static constexpr bool is_same_any_v
Definition BLI_any.hh:137
Any(std::in_place_type_t< T >, Args &&...args)
Definition BLI_any.hh:191
Any & operator=(T &&other)
Definition BLI_any.hh:227
std::decay_t< T > & emplace(Args &&...args)
Definition BLI_any.hh:260
Any(const Any &other)
Definition BLI_any.hh:155
void * allocate()
Definition BLI_any.hh:291
std::decay_t< T > & emplace_on_empty(Args &&...args)
Definition BLI_any.hh:267
const RealExtraInfo & extra_info() const
Definition BLI_any.hh:375
static constexpr bool is_inline_v
Definition BLI_any.hh:128
const void * get() const
Definition BLI_any.hh:334
void * allocate_on_empty()
Definition BLI_any.hh:301
bool has_value() const
Definition BLI_any.hh:255
void reset()
Definition BLI_any.hh:240
bool is() const
Definition BLI_any.hh:318
Any(Any &&other) noexcept
Definition BLI_any.hh:173
Any(T &&value)
Definition BLI_any.hh:200
const T & get() const
Definition BLI_any.hh:357
static constexpr bool is_allowed_v
Definition BLI_any.hh:121
void * get()
Definition BLI_any.hh:324
Any & operator=(const Any &other)
Definition BLI_any.hh:216
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(* destruct)(void *src)
Definition BLI_any.hh:34
void(* copy_construct)(void *dst, const void *src)
Definition BLI_any.hh:32
const void *(* get)(const void *src)
Definition BLI_any.hh:35
static constexpr NoExtraInfo get()
Definition BLI_any.hh:73