]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2018 Adam C. Emerson <aemerson@redhat.com> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #ifndef INCLUDE_STATIC_ANY | |
16 | #define INCLUDE_STATIC_ANY | |
17 | ||
18 | #include <any> | |
19 | #include <cstddef> | |
20 | #include <initializer_list> | |
21 | #include <memory> | |
22 | #include <typeinfo> | |
23 | #include <type_traits> | |
24 | ||
25 | #include <boost/smart_ptr/shared_ptr.hpp> | |
26 | #include <boost/smart_ptr/make_shared.hpp> | |
27 | ||
28 | namespace ceph { | |
29 | ||
30 | namespace _any { | |
31 | ||
32 | // Shared Functionality | |
33 | // -------------------- | |
34 | // | |
35 | // Common implementation details. Most functionality is here. We | |
36 | // assume that destructors do not throw. Some of them might and | |
37 | // they'll invoke terminate and that's fine. | |
38 | // | |
39 | // We are using the Curiously Recurring Template Pattern! We require | |
40 | // that all classes inheriting from us provide: | |
41 | // | |
42 | // - `static constexpr size_t capacity`: Maximum capacity. No object | |
43 | // larger than this may be | |
44 | // stored. `dynamic` for dynamic. | |
45 | // - `void* ptr() const noexcept`: returns a pointer to storage. | |
46 | // (`alloc_storage` must have been called. | |
47 | // `free_storage` must not have been called | |
48 | // since.) | |
49 | // - `void* alloc_storage(const std::size_t)`: allocate storage | |
50 | // - `void free_storage() noexcept`: free storage. Must be idempotent. | |
51 | // | |
52 | // We provide most of the public interface, as well as the operator function, | |
53 | // cast_helper, and the type() call. | |
54 | ||
55 | // Set `capacity` to this value to indicate that there is no fixed | |
56 | // capacity. | |
57 | // | |
58 | inline constexpr std::size_t dynamic = ~0; | |
59 | ||
60 | // Driver Function | |
61 | // --------------- | |
62 | // | |
63 | // The usual type-erasure control function trick. This one is simpler | |
64 | // than usual since we punt on moving and copying. We could dispense | |
65 | // with this and just store a deleter and a pointer to a typeinfo, but | |
66 | // that would be twice the space. | |
67 | // | |
68 | // Moved out here so the type of `func_t` isn't dependent on the | |
69 | // enclosing class. | |
70 | // | |
71 | enum class op { type, destroy }; | |
72 | template<typename T> | |
73 | inline void op_func(const op o, void* p) noexcept { | |
74 | static const std::type_info& type = typeid(T); | |
75 | switch (o) { | |
76 | case op::type: | |
77 | *(reinterpret_cast<const std::type_info**>(p)) = &type; | |
78 | break; | |
79 | case op::destroy: | |
80 | reinterpret_cast<T*>(p)->~T(); | |
81 | break; | |
82 | } | |
83 | } | |
84 | using func_t = void (*)(const op, void* p) noexcept; | |
85 | ||
86 | // The base class | |
87 | // -------------- | |
88 | // | |
89 | // The `storage_t` parameter gives the type of the value that manages | |
90 | // storage and allocation. We use it to create a protected data member | |
91 | // (named `storage`). This allows us to sidestep the problem in | |
92 | // initialization order where, where exposed constructors were using | |
93 | // trying to allocate or free storage *before* the data members of the | |
94 | // derived class were initialized. | |
95 | // | |
96 | // Making storage_t a member type of the derived class won't work, due | |
97 | // to C++'s rules for nested types being *horrible*. Just downright | |
98 | // *horrible*. | |
99 | // | |
100 | template<typename D, typename storage_t> | |
101 | class base { | |
102 | // Make definitions from our superclass visible | |
103 | // -------------------------------------------- | |
104 | // | |
105 | // And check that they fit the requirements. At least those that are | |
106 | // statically checkable. | |
107 | // | |
108 | static constexpr std::size_t capacity = D::capacity; | |
109 | ||
110 | void* ptr() const noexcept { | |
111 | static_assert( | |
112 | noexcept(static_cast<const D*>(this)->ptr()) && | |
113 | std::is_same_v<decltype(static_cast<const D*>(this)->ptr()), void*>, | |
114 | "‘void* ptr() const noexcept’ missing from superclass"); | |
115 | return static_cast<const D*>(this)->ptr(); | |
116 | } | |
117 | ||
118 | void* alloc_storage(const std::size_t z) { | |
119 | static_assert( | |
120 | std::is_same_v<decltype(static_cast<D*>(this)->alloc_storage(z)), void*>, | |
121 | "‘void* alloc_storage(const size_t)’ missing from superclass."); | |
122 | return static_cast<D*>(this)->alloc_storage(z); | |
123 | } | |
124 | ||
125 | void free_storage() noexcept { | |
126 | static_assert( | |
127 | noexcept(static_cast<D*>(this)->free_storage()) && | |
128 | std::is_void_v<decltype(static_cast<D*>(this)->free_storage())>, | |
129 | "‘void free_storage() noexcept’ missing from superclass."); | |
130 | static_cast<D*>(this)->free_storage(); | |
131 | } | |
132 | ||
133 | ||
134 | // Pile O' Templates | |
135 | // ----------------- | |
136 | // | |
137 | // These are just verbose and better typed once than twice. They're | |
138 | // used for SFINAE and declaring noexcept. | |
139 | // | |
140 | template<class T> | |
141 | struct is_in_place_type_helper : std::false_type {}; | |
142 | template<class T> | |
143 | struct is_in_place_type_helper<std::in_place_type_t<T>> : std::true_type {}; | |
144 | ||
145 | template<class T> | |
146 | static constexpr bool is_in_place_type_v = | |
147 | is_in_place_type_helper<std::decay_t<T>>::value; | |
148 | ||
149 | // SFINAE condition for value initialized | |
150 | // constructors/assigners. This is analogous to the standard's | |
151 | // requirement that this overload only participate in overload | |
152 | // resolution if std::decay_t<T> is not the same type as the | |
153 | // any-type, nor a specialization of std::in_place_type_t | |
154 | // | |
155 | template<typename T> | |
156 | using value_condition_t = std::enable_if_t< | |
157 | !std::is_same_v<std::decay_t<T>, D> && | |
158 | !is_in_place_type_v<std::decay_t<T>>>; | |
159 | ||
160 | // This `noexcept` condition for value construction lets | |
161 | // `immobile_any`'s value constructor/assigner be noexcept, so long | |
162 | // as the type's copy or move constructor cooperates. | |
163 | // | |
164 | template<typename T> | |
165 | static constexpr bool value_noexcept_v = | |
166 | std::is_nothrow_constructible_v<std::decay_t<T>, T> && capacity != dynamic; | |
167 | ||
168 | // SFINAE condition for in-place constructors/assigners | |
169 | // | |
170 | template<typename T, typename... Args> | |
171 | using in_place_condition_t = std::enable_if_t<std::is_constructible_v< | |
172 | std::decay_t<T>, Args...>>; | |
173 | ||
174 | // Analogous to the above. Give noexcept to immobile_any::emplace | |
175 | // when possible. | |
176 | // | |
177 | template<typename T, typename... Args> | |
178 | static constexpr bool in_place_noexcept_v = | |
179 | std::is_nothrow_constructible_v<std::decay_t<T>, Args...> && | |
180 | capacity != dynamic; | |
181 | ||
182 | private: | |
183 | ||
184 | // Functionality! | |
185 | // -------------- | |
186 | ||
187 | // The driver function for the currently stored object. Whether this | |
188 | // is null is the canonical way to know whether an instance has a | |
189 | // value. | |
190 | // | |
191 | func_t func = nullptr; | |
192 | ||
193 | // Construct an object within ourselves. As you can see we give the | |
194 | // weak exception safety guarantee. | |
195 | // | |
196 | template<typename T, typename ...Args> | |
197 | std::decay_t<T>& construct(Args&& ...args) { | |
198 | using Td = std::decay_t<T>; | |
199 | static_assert(capacity == dynamic || sizeof(Td) <= capacity, | |
200 | "Supplied type is too large for this specialization."); | |
201 | try { | |
202 | func = &op_func<Td>; | |
203 | return *new (reinterpret_cast<Td*>(alloc_storage(sizeof(Td)))) | |
204 | Td(std::forward<Args>(args)...); | |
205 | } catch (...) { | |
206 | reset(); | |
207 | throw; | |
208 | } | |
209 | } | |
210 | ||
211 | protected: | |
212 | ||
213 | // We hold the storage, even if the superclass class manipulates it, | |
214 | // so that its default initialization comes soon enough for us to | |
215 | // use it in our constructors. | |
216 | // | |
217 | storage_t storage; | |
218 | ||
219 | public: | |
220 | ||
221 | base() noexcept = default; | |
222 | ~base() noexcept { | |
223 | reset(); | |
224 | } | |
225 | ||
226 | protected: | |
227 | // Since some of our derived classes /can/ be copied or moved. | |
228 | // | |
229 | base(const base& rhs) noexcept : func(rhs.func) { | |
230 | if constexpr (std::is_copy_assignable_v<storage_t>) { | |
231 | storage = rhs.storage; | |
232 | } | |
233 | } | |
234 | base& operator =(const base& rhs) noexcept { | |
235 | reset(); | |
236 | func = rhs.func; | |
237 | if constexpr (std::is_copy_assignable_v<storage_t>) { | |
238 | storage = rhs.storage; | |
239 | } | |
240 | return *this; | |
241 | } | |
242 | ||
243 | base(base&& rhs) noexcept : func(std::move(rhs.func)) { | |
244 | if constexpr (std::is_move_assignable_v<storage_t>) { | |
245 | storage = std::move(rhs.storage); | |
246 | } | |
247 | rhs.func = nullptr; | |
248 | } | |
249 | base& operator =(base&& rhs) noexcept { | |
250 | reset(); | |
251 | func = rhs.func; | |
252 | if constexpr (std::is_move_assignable_v<storage_t>) { | |
253 | storage = std::move(rhs.storage); | |
254 | } | |
255 | rhs.func = nullptr; | |
256 | return *this; | |
257 | } | |
258 | ||
259 | public: | |
260 | ||
261 | // Value construct/assign | |
262 | // ---------------------- | |
263 | // | |
264 | template<typename T, | |
265 | typename = value_condition_t<T>> | |
266 | base(T&& t) noexcept(value_noexcept_v<T>) { | |
267 | construct<T>(std::forward<T>(t)); | |
268 | } | |
269 | ||
270 | // On exception, *this is set to empty. | |
271 | // | |
272 | template<typename T, | |
273 | typename = value_condition_t<T>> | |
274 | base& operator =(T&& t) noexcept(value_noexcept_v<T>) { | |
275 | reset(); | |
276 | construct<T>(std::forward<T>(t)); | |
277 | return *this; | |
278 | } | |
279 | ||
280 | // In-place construct/assign | |
281 | // ------------------------- | |
282 | // | |
283 | // I really hate the way the C++ standard library treats references | |
284 | // as if they were stepchildren in a Charles Dickens novel. I am | |
285 | // quite upset that std::optional lacks a specialization for | |
286 | // references. There's no legitimate reason for it. The whole | |
287 | // 're-seat or refuse' debate is simply a canard. The optional is | |
288 | // effectively a container, so of course it can be emptied or | |
289 | // reassigned. No, pointers are not an acceptable substitute. A | |
290 | // pointer gives an address in memory which may be null and which | |
291 | // may represent an object or may a location in which an object is | |
292 | // to be created. An optional reference, on the other hand, is a | |
293 | // reference to an initialized, live object or /empty/. This is an | |
294 | // obvious difference that should be communicable to any programmer | |
295 | // reading the code through the type system. | |
296 | // | |
297 | // `std::any`, even in the case of in-place construction, | |
298 | // only stores the decayed type. I suspect this was to get around | |
299 | // the question of whether, for a std::any holding a T&, | |
300 | // std::any_cast<T> should return a copy or throw | |
301 | // std::bad_any_cast. | |
302 | // | |
303 | // I think the appropriate response in that case would be to make a | |
304 | // copy if the type supports it and fail otherwise. Once a concrete | |
305 | // type is known the problem solves itself. | |
306 | // | |
307 | // If one were inclined, one could easily load the driver function | |
308 | // with a heavy subset of the type traits (those that depend only on | |
309 | // the type in question) and simply /ask/ whether it's a reference. | |
310 | // | |
311 | // At the moment, I'm maintaining compatibility with the standard | |
312 | // library except for copy/move semantics. | |
313 | // | |
314 | template<typename T, | |
315 | typename... Args, | |
316 | typename = in_place_condition_t<T, Args...>> | |
317 | base(std::in_place_type_t<T>, | |
318 | Args&& ...args) noexcept(in_place_noexcept_v<T, Args...>) { | |
319 | construct<T>(std::forward<Args>(args)...); | |
320 | } | |
321 | ||
322 | // On exception, *this is set to empty. | |
323 | // | |
324 | template<typename T, | |
325 | typename... Args, | |
326 | typename = in_place_condition_t<T>> | |
327 | std::decay_t<T>& emplace(Args&& ...args) noexcept(in_place_noexcept_v< | |
328 | T, Args...>) { | |
329 | reset(); | |
330 | return construct<T>(std::forward<Args>(args)...); | |
331 | } | |
332 | ||
333 | template<typename T, | |
334 | typename U, | |
335 | typename... Args, | |
336 | typename = in_place_condition_t<T, std::initializer_list<U>, | |
337 | Args...>> | |
338 | base(std::in_place_type_t<T>, | |
339 | std::initializer_list<U> i, | |
340 | Args&& ...args) noexcept(in_place_noexcept_v<T, std::initializer_list<U>, | |
341 | Args...>) { | |
342 | construct<T>(i, std::forward<Args>(args)...); | |
343 | } | |
344 | ||
345 | // On exception, *this is set to empty. | |
346 | // | |
347 | template<typename T, | |
348 | typename U, | |
349 | typename... Args, | |
350 | typename = in_place_condition_t<T, std::initializer_list<U>, | |
351 | Args...>> | |
352 | std::decay_t<T>& emplace(std::initializer_list<U> i, | |
353 | Args&& ...args) noexcept(in_place_noexcept_v<T, | |
354 | std::initializer_list<U>, | |
355 | Args...>) { | |
356 | reset(); | |
357 | return construct<T>(i,std::forward<Args>(args)...); | |
358 | } | |
359 | ||
360 | // Empty ourselves, using the subclass to free any storage. | |
361 | // | |
362 | void reset() noexcept { | |
363 | if (has_value()) { | |
364 | func(op::destroy, ptr()); | |
365 | func = nullptr; | |
366 | } | |
367 | free_storage(); | |
368 | } | |
369 | ||
370 | template<typename U = storage_t, | |
371 | typename = std::enable_if<std::is_swappable_v<storage_t>>> | |
372 | void swap(base& rhs) { | |
373 | using std::swap; | |
374 | swap(func, rhs.func); | |
375 | swap(storage, rhs.storage); | |
376 | } | |
377 | ||
378 | // All other functions should use this function to test emptiness | |
379 | // rather than examining `func` directly. | |
380 | // | |
381 | bool has_value() const noexcept { | |
382 | return !!func; | |
383 | } | |
384 | ||
385 | // Returns the type of the value stored, if any. | |
386 | // | |
387 | const std::type_info& type() const noexcept { | |
388 | if (has_value()) { | |
389 | const std::type_info* t; | |
390 | func(op::type, reinterpret_cast<void*>(&t)); | |
391 | return *t; | |
392 | } else { | |
393 | return typeid(void); | |
394 | } | |
395 | } | |
396 | ||
397 | template<typename T, typename U, typename V> | |
398 | friend inline void* cast_helper(const base<U, V>& b) noexcept; | |
399 | }; | |
400 | ||
401 | // Function used by all `any_cast` functions | |
402 | // | |
403 | // Returns a void* to the contents if they exist and match the | |
404 | // requested type, otherwise `nullptr`. | |
405 | // | |
406 | template<typename T, typename U, typename V> | |
407 | inline void* cast_helper(const base<U, V>& b) noexcept { | |
408 | if (b.func && ((&op_func<T> == b.func) || | |
409 | (b.type() == typeid(T)))) { | |
410 | return b.ptr(); | |
411 | } else { | |
412 | return nullptr; | |
413 | } | |
414 | } | |
415 | } | |
416 | ||
417 | // `any_cast` | |
418 | // ========== | |
419 | // | |
420 | // Just the usual gamut of `any_cast` overloads. These get a bit | |
421 | // repetitive and it would be nice to think of a way to collapse them | |
422 | // down a bit. | |
423 | // | |
424 | ||
425 | // The pointer pair! | |
426 | // | |
427 | template<typename T, typename U, typename V> | |
428 | inline T* any_cast(_any::base<U, V>* a) noexcept { | |
429 | if (a) { | |
430 | return static_cast<T*>(_any::cast_helper<std::decay_t<T>>(*a)); | |
431 | } | |
432 | return nullptr; | |
433 | } | |
434 | ||
435 | template<typename T, typename U, typename V> | |
436 | inline const T* any_cast(const _any::base<U, V>* a) noexcept { | |
437 | if (a) { | |
438 | return static_cast<T*>(_any::cast_helper<std::decay_t<T>>(*a)); | |
439 | } | |
440 | return nullptr; | |
441 | } | |
442 | ||
443 | // While we disallow copying the immobile any itself, we can allow | |
444 | // anything with an extracted value that the type supports. | |
445 | // | |
446 | template<typename T, typename U, typename V> | |
447 | inline T any_cast(_any::base<U, V>& a) { | |
448 | static_assert(std::is_reference_v<T> || | |
449 | std::is_copy_constructible_v<T>, | |
450 | "The supplied type must be either a reference or " | |
451 | "copy constructible."); | |
452 | auto p = any_cast<std::decay_t<T>>(&a); | |
453 | if (p) { | |
454 | return static_cast<T>(*p); | |
455 | } | |
456 | throw std::bad_any_cast(); | |
457 | } | |
458 | ||
459 | template<typename T, typename U, typename V> | |
460 | inline T any_cast(const _any::base<U, V>& a) { | |
461 | static_assert(std::is_reference_v<T> || | |
462 | std::is_copy_constructible_v<T>, | |
463 | "The supplied type must be either a reference or " | |
464 | "copy constructible."); | |
465 | auto p = any_cast<std::decay_t<T>>(&a); | |
466 | if (p) { | |
467 | return static_cast<T>(*p); | |
468 | } | |
469 | throw std::bad_any_cast(); | |
470 | } | |
471 | ||
472 | template<typename T, typename U, typename V> | |
473 | inline std::enable_if_t<(std::is_move_constructible_v<T> || | |
474 | std::is_copy_constructible_v<T>) && | |
475 | !std::is_rvalue_reference_v<T>, T> | |
476 | any_cast(_any::base<U, V>&& a) { | |
477 | auto p = any_cast<std::decay_t<T>>(&a); | |
478 | if (p) { | |
479 | return std::move((*p)); | |
480 | } | |
481 | throw std::bad_any_cast(); | |
482 | } | |
483 | ||
484 | template<typename T, typename U, typename V> | |
485 | inline std::enable_if_t<std::is_rvalue_reference_v<T>, T> | |
486 | any_cast(_any::base<U, V>&& a) { | |
487 | auto p = any_cast<std::decay_t<T>>(&a); | |
488 | if (p) { | |
489 | return static_cast<T>(*p); | |
490 | } | |
491 | throw std::bad_any_cast(); | |
492 | } | |
493 | ||
494 | // `immobile_any` | |
495 | // ============== | |
496 | // | |
497 | // Sometimes, uncopyable objects exist and I want to do things with | |
498 | // them. The C++ standard library is really quite keen on insisting | |
499 | // things be copyable before it deigns to work. I find this annoying. | |
500 | // | |
501 | // Also, the allocator, while useful, is really not considerate of | |
502 | // other people's time. Every time we go to visit it, it takes us | |
503 | // quite an awfully long time to get away again. As such, I've been | |
504 | // trying to avoid its company whenever it is convenient and seemly. | |
505 | // | |
506 | // We accept any type that will fit in the declared capacity. You may | |
507 | // store types with throwing destructors, but terminate will be | |
508 | // invoked when they throw. | |
509 | // | |
510 | template<std::size_t S> | |
511 | class immobile_any : public _any::base<immobile_any<S>, | |
512 | std::aligned_storage_t<S>> { | |
513 | using base = _any::base<immobile_any<S>, std::aligned_storage_t<S>>; | |
514 | friend base; | |
515 | ||
516 | using _any::base<immobile_any<S>, std::aligned_storage_t<S>>::storage; | |
517 | ||
518 | // Superclass requirements! | |
519 | // ------------------------ | |
520 | // | |
521 | // Simple as anything. We have a buffer of fixed size and return the | |
522 | // pointer to it when asked. | |
523 | // | |
524 | static constexpr std::size_t capacity = S; | |
525 | void* ptr() const noexcept { | |
526 | return const_cast<void*>(static_cast<const void*>(&storage)); | |
527 | } | |
528 | void* alloc_storage(std::size_t) noexcept { | |
529 | return ptr(); | |
530 | } | |
531 | void free_storage() noexcept {} | |
532 | ||
533 | static_assert(capacity != _any::dynamic, | |
534 | "That is not a valid size for an immobile_any."); | |
535 | ||
536 | public: | |
537 | ||
538 | immobile_any() noexcept = default; | |
539 | ||
540 | immobile_any(const immobile_any&) = delete; | |
541 | immobile_any& operator =(const immobile_any&) = delete; | |
542 | immobile_any(immobile_any&&) = delete; | |
543 | immobile_any& operator =(immobile_any&&) = delete; | |
544 | ||
545 | using base::base; | |
546 | using base::operator =; | |
547 | ||
548 | void swap(immobile_any&) = delete; | |
549 | }; | |
550 | ||
551 | template<typename T, std::size_t S, typename... Args> | |
552 | inline immobile_any<S> make_immobile_any(Args&& ...args) { | |
553 | return immobile_any<S>(std::in_place_type<T>, std::forward<Args>(args)...); | |
554 | } | |
555 | ||
556 | template<typename T, std::size_t S, typename U, typename... Args> | |
557 | inline immobile_any<S> make_immobile_any(std::initializer_list<U> i, Args&& ...args) { | |
558 | return immobile_any<S>(std::in_place_type<T>, i, std::forward<Args>(args)...); | |
559 | } | |
560 | ||
561 | // `unique_any` | |
562 | // ============ | |
563 | // | |
564 | // Oh dear. Now we're getting back into allocation. You don't think | |
565 | // the allocator noticed all those mean things we said about it, do | |
566 | // you? | |
567 | // | |
568 | // Well. Okay, allocator. Sometimes when it's the middle of the night | |
569 | // and you're writing template code you say things you don't exactly | |
570 | // mean. If it weren't for you, we wouldn't have any memory to run all | |
571 | // our programs in at all. Really, I'm just being considerate of | |
572 | // *your* needs, trying to avoid having to run to you every time we | |
573 | // instantiate a type, making a few that can be self-sufficient…uh… | |
574 | // | |
575 | // **Anyway**, this is movable but not copyable, as you should expect | |
576 | // from anything with ‘unique’ in the name. | |
577 | // | |
578 | class unique_any : public _any::base<unique_any, std::unique_ptr<std::byte[]>> { | |
579 | using base = _any::base<unique_any, std::unique_ptr<std::byte[]>>; | |
580 | friend base; | |
581 | ||
582 | using base::storage; | |
583 | ||
584 | // Superclass requirements | |
585 | // ----------------------- | |
586 | // | |
587 | // Our storage is a single chunk of RAM owned by a | |
588 | // `std::unique_ptr`. | |
589 | // | |
590 | static constexpr std::size_t capacity = _any::dynamic; | |
591 | void* ptr() const noexcept { | |
592 | return static_cast<void*>(storage.get()); | |
593 | return nullptr; | |
594 | } | |
595 | ||
596 | void* alloc_storage(const std::size_t z) { | |
597 | storage.reset(new std::byte[z]); | |
598 | return ptr(); | |
599 | } | |
600 | ||
601 | void free_storage() noexcept { | |
602 | storage.reset(); | |
603 | } | |
604 | ||
605 | public: | |
606 | ||
607 | unique_any() noexcept = default; | |
608 | ~unique_any() noexcept = default; | |
609 | ||
610 | unique_any(const unique_any&) = delete; | |
611 | unique_any& operator =(const unique_any&) = delete; | |
612 | ||
613 | // We can rely on the behavior of `unique_ptr` and the base class to | |
614 | // give us a default move constructor that does the right thing. | |
615 | // | |
616 | unique_any(unique_any&& rhs) noexcept = default; | |
617 | unique_any& operator =(unique_any&& rhs) = default; | |
618 | ||
619 | using base::base; | |
620 | using base::operator =; | |
621 | }; | |
622 | ||
623 | inline void swap(unique_any& lhs, unique_any& rhs) noexcept { | |
624 | lhs.swap(rhs); | |
625 | } | |
626 | ||
627 | template<typename T, typename... Args> | |
628 | inline unique_any make_unique_any(Args&& ...args) { | |
629 | return unique_any(std::in_place_type<T>, std::forward<Args>(args)...); | |
630 | } | |
631 | ||
632 | template<typename T, typename U, typename... Args> | |
633 | inline unique_any make_unique_any(std::initializer_list<U> i, Args&& ...args) { | |
634 | return unique_any(std::in_place_type<T>, i, std::forward<Args>(args)...); | |
635 | } | |
636 | ||
637 | // `shared_any` | |
638 | // ============ | |
639 | // | |
640 | // Once more with feeling! | |
641 | // | |
642 | // This is both copyable *and* movable. In case you need that sort of | |
643 | // thing. It seemed a reasonable completion. | |
644 | // | |
645 | class shared_any : public _any::base<shared_any, boost::shared_ptr<std::byte[]>> { | |
646 | using base = _any::base<shared_any, boost::shared_ptr<std::byte[]>>; | |
647 | friend base; | |
648 | ||
649 | using base::storage; | |
650 | ||
651 | // Superclass requirements | |
652 | // ----------------------- | |
653 | // | |
654 | // Our storage is a single chunk of RAM allocated from the | |
655 | // heap. This time it's owned by a `boost::shared_ptr` so we can use | |
656 | // `boost::make_shared_noinit`. (This lets us get the optimization | |
657 | // that allocates array and control block in one without wasting | |
658 | // time on `memset`.) | |
659 | // | |
660 | static constexpr std::size_t capacity = _any::dynamic; | |
661 | void* ptr() const noexcept { | |
662 | return static_cast<void*>(storage.get()); | |
663 | } | |
664 | ||
665 | void* alloc_storage(std::size_t n) { | |
666 | storage = boost::make_shared_noinit<std::byte[]>(n); | |
667 | return ptr(); | |
668 | } | |
669 | ||
670 | void free_storage() noexcept { | |
671 | storage.reset(); | |
672 | } | |
673 | ||
674 | public: | |
675 | ||
676 | shared_any() noexcept = default; | |
677 | ~shared_any() noexcept = default; | |
678 | ||
679 | shared_any(const shared_any& rhs) noexcept = default; | |
680 | shared_any& operator =(const shared_any&) noexcept = default; | |
681 | ||
682 | shared_any(shared_any&& rhs) noexcept = default; | |
683 | shared_any& operator =(shared_any&& rhs) noexcept = default; | |
684 | ||
685 | using base::base; | |
686 | using base::operator =; | |
687 | }; | |
688 | ||
689 | inline void swap(shared_any& lhs, shared_any& rhs) noexcept { | |
690 | lhs.swap(rhs); | |
691 | } | |
692 | ||
693 | template<typename T, typename... Args> | |
694 | inline shared_any make_shared_any(Args&& ...args) { | |
695 | return shared_any(std::in_place_type<T>, std::forward<Args>(args)...); | |
696 | } | |
697 | ||
698 | template<typename T, typename U, typename... Args> | |
699 | inline shared_any make_shared_any(std::initializer_list<U> i, Args&& ...args) { | |
700 | return shared_any(std::in_place_type<T>, i, std::forward<Args>(args)...); | |
701 | } | |
702 | } | |
703 | ||
704 | #endif // INCLUDE_STATIC_ANY |