WPILibC++ 2025.1.1
Loading...
Searching...
No Matches
allocator_storage.hpp
Go to the documentation of this file.
1// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
2// SPDX-License-Identifier: Zlib
3
4#ifndef WPI_MEMORY_ALLOCATOR_STORAGE_HPP_INCLUDED
5#define WPI_MEMORY_ALLOCATOR_STORAGE_HPP_INCLUDED
6
7/// \file
8/// Class template \ref wpi::memory::allocator_storage, some policies and resulting typedefs.
9
10#include <new>
11#include <type_traits>
12
13#include "detail/utility.hpp"
14#include "config.hpp"
15#include "allocator_traits.hpp"
16#include "threading.hpp"
17
18namespace wpi
19{
20 namespace memory
21 {
22 namespace detail
23 {
24 template <class Alloc>
25 void* try_allocate_node(std::true_type, Alloc& alloc, std::size_t size,
26 std::size_t alignment) noexcept
27 {
29 alignment);
30 }
31
32 template <class Alloc>
33 void* try_allocate_array(std::true_type, Alloc& alloc, std::size_t count,
34 std::size_t size, std::size_t alignment) noexcept
35 {
37 alignment);
38 }
39
40 template <class Alloc>
41 bool try_deallocate_node(std::true_type, Alloc& alloc, void* ptr, std::size_t size,
42 std::size_t alignment) noexcept
43 {
45 alignment);
46 }
47
48 template <class Alloc>
49 bool try_deallocate_array(std::true_type, Alloc& alloc, void* ptr, std::size_t count,
50 std::size_t size, std::size_t alignment) noexcept
51 {
53 size, alignment);
54 }
55
56 template <class Alloc>
57 void* try_allocate_node(std::false_type, Alloc&, std::size_t, std::size_t) noexcept
58 {
59 WPI_MEMORY_UNREACHABLE("Allocator is not compositioning");
60 return nullptr;
61 }
62
63 template <class Alloc>
64 void* try_allocate_array(std::false_type, Alloc&, std::size_t, std::size_t,
65 std::size_t) noexcept
66 {
67 WPI_MEMORY_UNREACHABLE("Allocator is not compositioning");
68 return nullptr;
69 }
70
71 template <class Alloc>
72 bool try_deallocate_node(std::false_type, Alloc&, void*, std::size_t,
73 std::size_t) noexcept
74 {
75 WPI_MEMORY_UNREACHABLE("Allocator is not compositioning");
76 return false;
77 }
78
79 template <class Alloc>
80 bool try_deallocate_array(std::false_type, Alloc&, void*, std::size_t, std::size_t,
81 std::size_t) noexcept
82 {
83 WPI_MEMORY_UNREACHABLE("Allocator is not compositioning");
84 return false;
85 }
86 } // namespace detail
87
88 /// A RawAllocator that stores another allocator.
89 /// The StoragePolicy defines the allocator type being stored and how it is stored.
90 /// The \c Mutex controls synchronization of the access.
91 /// \ingroup memory_storage
92 template <class StoragePolicy, class Mutex>
94 : WPI_EBO(StoragePolicy,
95 detail::mutex_storage<
96 detail::mutex_for<typename StoragePolicy::allocator_type, Mutex>>)
97 {
99 using composable_traits =
104
105 public:
106 using allocator_type = typename StoragePolicy::allocator_type;
107 using storage_policy = StoragePolicy;
108 using mutex = Mutex;
110
111 /// \effects Creates it by default-constructing the \c StoragePolicy.
112 /// \requires The \c StoragePolicy must be default-constructible.
113 /// \notes The default constructor may create an invalid allocator storage not associated with any allocator.
114 /// If that is the case, it must not be used.
115 allocator_storage() = default;
116
117 /// \effects Creates it by passing it an allocator.
118 /// The allocator will be forwarded to the \c StoragePolicy, it decides whether it will be moved, its address stored or something else.
119 /// \requires The expression <tt>new storage_policy(std::forward<Alloc>(alloc))</tt> must be well-formed,
120 /// otherwise this constructor does not participate in overload resolution.
121 template <
122 class Alloc,
123 // MSVC seems to ignore access rights in SFINAE below
124 // use this to prevent this constructor being chosen instead of move for types inheriting from it
126 (!std::is_base_of<allocator_storage, typename std::decay<Alloc>::type>::value))>
127 allocator_storage(Alloc&& alloc,
128 WPI_SFINAE(new storage_policy(std::declval<Alloc>())))
129 : storage_policy(detail::forward<Alloc>(alloc))
130 {
131 }
132
133 /// \effects Creates it by passing it another \c allocator_storage with a different \c StoragePolicy but the same \c Mutex type.
134 /// Initializes it with the result of \c other.get_allocator().
135 /// \requires The expression <tt>new storage_policy(other.get_allocator())</tt> must be well-formed,
136 /// otherwise this constructor does not participate in overload resolution.
137 template <class OtherPolicy>
145
146 /// @{
147 /// \effects Moves the \c allocator_storage object.
148 /// A moved-out \c allocator_storage object must still store a valid allocator object.
156
158 {
159 storage_policy:: operator=(detail::move(other));
160 detail::mutex_storage<detail::mutex_for<typename StoragePolicy::allocator_type,
161 Mutex>>::operator=(detail::move(other));
162 return *this;
163 }
164 /// @}
165
166 /// @{
167 /// \effects Copies the \c allocator_storage object.
168 /// \requires The \c StoragePolicy must be copyable.
171 /// @}
172
173 /// @{
174 /// \effects Calls the function on the stored allocator.
175 /// The \c Mutex will be locked during the operation.
176 void* allocate_node(std::size_t size, std::size_t alignment)
177 {
178 std::lock_guard<actual_mutex> lock(*this);
179 auto&& alloc = get_allocator();
180 return traits::allocate_node(alloc, size, alignment);
181 }
182
183 void* allocate_array(std::size_t count, std::size_t size, std::size_t alignment)
184 {
185 std::lock_guard<actual_mutex> lock(*this);
186 auto&& alloc = get_allocator();
187 return traits::allocate_array(alloc, count, size, alignment);
188 }
189
190 void deallocate_node(void* ptr, std::size_t size, std::size_t alignment) noexcept
191 {
192 std::lock_guard<actual_mutex> lock(*this);
193 auto&& alloc = get_allocator();
194 traits::deallocate_node(alloc, ptr, size, alignment);
195 }
196
197 void deallocate_array(void* ptr, std::size_t count, std::size_t size,
198 std::size_t alignment) noexcept
199 {
200 std::lock_guard<actual_mutex> lock(*this);
201 auto&& alloc = get_allocator();
202 traits::deallocate_array(alloc, ptr, count, size, alignment);
203 }
204
205 std::size_t max_node_size() const
206 {
207 std::lock_guard<actual_mutex> lock(*this);
208 auto&& alloc = get_allocator();
209 return traits::max_node_size(alloc);
210 }
211
212 std::size_t max_array_size() const
213 {
214 std::lock_guard<actual_mutex> lock(*this);
215 auto&& alloc = get_allocator();
216 return traits::max_array_size(alloc);
217 }
218
219 std::size_t max_alignment() const
220 {
221 std::lock_guard<actual_mutex> lock(*this);
222 auto&& alloc = get_allocator();
223 return traits::max_alignment(alloc);
224 }
225 /// @}
226
227 /// @{
228 /// \effects Calls the function on the stored composable allocator.
229 /// The \c Mutex will be locked during the operation.
230 /// \requires The allocator must be composable,
231 /// i.e. \ref is_composable() must return `true`.
232 /// \note This check is done at compile-time where possible,
233 /// and at runtime in the case of type-erased storage.
234 WPI_ENABLE_IF(composable::value)
235 void* try_allocate_node(std::size_t size, std::size_t alignment) noexcept
236 {
238 std::lock_guard<actual_mutex> lock(*this);
239 auto&& alloc = get_allocator();
240 return composable_traits::try_allocate_node(alloc, size, alignment);
241 }
242
243 WPI_ENABLE_IF(composable::value)
244 void* try_allocate_array(std::size_t count, std::size_t size,
245 std::size_t alignment) noexcept
246 {
248 std::lock_guard<actual_mutex> lock(*this);
249 auto&& alloc = get_allocator();
250 return composable_traits::try_allocate_array(alloc, count, size, alignment);
251 }
252
253 WPI_ENABLE_IF(composable::value)
254 bool try_deallocate_node(void* ptr, std::size_t size, std::size_t alignment) noexcept
255 {
257 std::lock_guard<actual_mutex> lock(*this);
258 auto&& alloc = get_allocator();
259 return composable_traits::try_deallocate_node(alloc, ptr, size, alignment);
260 }
261
262 WPI_ENABLE_IF(composable::value)
263 bool try_deallocate_array(void* ptr, std::size_t count, std::size_t size,
264 std::size_t alignment) noexcept
265 {
267 std::lock_guard<actual_mutex> lock(*this);
268 auto&& alloc = get_allocator();
269 return composable_traits::try_deallocate_array(alloc, ptr, count, size, alignment);
270 }
271 /// @}
272
273 /// @{
274 /// \effects Forwards to the \c StoragePolicy.
275 /// \returns Returns a reference to the stored allocator.
276 /// \note This does not lock the \c Mutex.
277 auto get_allocator() noexcept
278 -> decltype(std::declval<storage_policy>().get_allocator())
279 {
280 return storage_policy::get_allocator();
281 }
282
283 auto get_allocator() const noexcept
284 -> decltype(std::declval<const storage_policy>().get_allocator())
285 {
286 return storage_policy::get_allocator();
287 }
288 /// @}
289
290 /// @{
291 /// \returns A proxy object that acts like a pointer to the stored allocator.
292 /// It cannot be reassigned to point to another allocator object and only moving is supported, which is destructive.
293 /// As long as the proxy object lives and is not moved from, the \c Mutex will be kept locked.
294 auto lock() noexcept -> WPI_IMPL_DEFINED(decltype(detail::lock_allocator(
295 std::declval<storage_policy>().get_allocator(), std::declval<actual_mutex&>())))
296 {
297 return detail::lock_allocator(get_allocator(), static_cast<actual_mutex&>(*this));
298 }
299
300 auto lock() const noexcept -> WPI_IMPL_DEFINED(decltype(detail::lock_allocator(
301 std::declval<const storage_policy>().get_allocator(),
302 std::declval<actual_mutex&>())))
303 {
304 return detail::lock_allocator(get_allocator(), static_cast<actual_mutex&>(*this));
305 }
306 /// @}.
307
308 /// \returns Whether or not the stored allocator is composable,
309 /// that is you can use the compositioning functions.
310 /// \note Due to type-erased allocators,
311 /// this function can not be `constexpr`.
312 bool is_composable() const noexcept
313 {
314 return StoragePolicy::is_composable();
315 }
316 };
317
318 /// Tag type that enables type-erasure in \ref reference_storage.
319 /// It can be used everywhere a \ref allocator_reference is used internally.
320 /// \ingroup memory_storage
322 {
323 };
324
325 /// A StoragePolicy that stores the allocator directly.
326 /// It embeds the allocator inside it, i.e. moving the storage policy will move the allocator.
327 /// \ingroup memory_storage
328 template <class RawAllocator>
329 class direct_storage : WPI_EBO(allocator_traits<RawAllocator>::allocator_type)
330 {
331 static_assert(!std::is_same<RawAllocator, any_allocator>::value,
332 "cannot type-erase in direct_storage");
333
334 public:
336
337 /// \effects Creates it by default-constructing the allocator.
338 /// \requires The \c RawAllcoator must be default constructible.
339 direct_storage() = default;
340
341 /// \effects Creates it by moving in an allocator object.
342 direct_storage(allocator_type&& allocator) noexcept
343 : allocator_type(detail::move(allocator))
344 {
345 }
346
347 /// @{
348 /// \effects Moves the \c direct_storage object.
349 /// This will move the stored allocator.
351
353 {
354 allocator_type::operator=(detail::move(other));
355 return *this;
356 }
357 /// @}
358
359 /// @{
360 /// \returns A (\c const) reference to the stored allocator.
362 {
363 return *this;
364 }
365
366 const allocator_type& get_allocator() const noexcept
367 {
368 return *this;
369 }
370 /// @}
371
372 protected:
373 ~direct_storage() noexcept = default;
374
375 bool is_composable() const noexcept
376 {
378 }
379 };
380
381 /// An alias template for \ref allocator_storage using the \ref direct_storage policy without a mutex.
382 /// It has the effect of giving any RawAllocator the interface with all member functions,
383 /// avoiding the need to wrap it inside the \ref allocator_traits.
384 /// \ingroup memory_storage
385 template <class RawAllocator>
386 WPI_ALIAS_TEMPLATE(allocator_adapter,
388
389 /// \returns A new \ref allocator_adapter object created by forwarding to the constructor.
390 /// \relates allocator_adapter
391 template <class RawAllocator>
392 auto make_allocator_adapter(RawAllocator&& allocator) noexcept
394 {
395 return {detail::forward<RawAllocator>(allocator)};
396 }
397
398/// An alias template for \ref allocator_storage using the \ref direct_storage policy with a mutex.
399/// It has a similar effect as \ref allocator_adapter but performs synchronization.
400/// The \c Mutex will default to \c std::mutex if threading is supported,
401/// otherwise there is no default.
402/// \ingroup memory_storage
403#if WPI_HOSTED_IMPLEMENTATION
404 template <class RawAllocator, class Mutex = std::mutex>
407#else
408 template <class RawAllocator, class Mutex>
411#endif
412
413#if WPI_HOSTED_IMPLEMENTATION
414 /// \returns A new \ref thread_safe_allocator object created by forwarding to the constructor/
415 /// \relates thread_safe_allocator
416 template <class RawAllocator>
417 auto make_thread_safe_allocator(RawAllocator&& allocator)
419 {
420 return detail::forward<RawAllocator>(allocator);
421 }
422#endif
423
424 /// \returns A new \ref thread_safe_allocator object created by forwarding to the constructor,
425 /// specifying a certain mutex type.
426 /// \requires It requires threading support from the implementation.
427 /// \relates thread_safe_allocator
428 template <class Mutex, class RawAllocator>
429 auto make_thread_safe_allocator(RawAllocator&& allocator)
431 {
432 return detail::forward<RawAllocator>(allocator);
433 }
434
435 namespace detail
436 {
438 {
439 };
441 {
442 };
444 {
445 };
446
447 reference_stateful reference_type(std::true_type stateful, std::false_type shared);
448 reference_stateless reference_type(std::false_type stateful, std::true_type shared);
449 reference_stateless reference_type(std::false_type stateful, std::false_type shared);
450 reference_shared reference_type(std::true_type stateful, std::true_type shared);
451
452 template <class RawAllocator, class Tag>
454
455 // reference to stateful: stores a pointer to an allocator
456 template <class RawAllocator>
458 {
459 protected:
460 reference_storage_impl() noexcept : alloc_(nullptr) {}
461
462 reference_storage_impl(RawAllocator& allocator) noexcept : alloc_(&allocator) {}
463
464 bool is_valid() const noexcept
465 {
466 return alloc_ != nullptr;
467 }
468
469 RawAllocator& get_allocator() const noexcept
470 {
471 WPI_MEMORY_ASSERT(alloc_ != nullptr);
472 return *alloc_;
473 }
474
475 private:
476 RawAllocator* alloc_;
477 };
478
479 // reference to stateless: store in static storage
480 template <class RawAllocator>
482 {
483 protected:
484 reference_storage_impl() noexcept = default;
485
486 reference_storage_impl(const RawAllocator&) noexcept {}
487
488 bool is_valid() const noexcept
489 {
490 return true;
491 }
492
493 RawAllocator& get_allocator() const noexcept
494 {
495 static RawAllocator alloc;
496 return alloc;
497 }
498 };
499
500 // reference to shared: stores RawAllocator directly
501 template <class RawAllocator>
503 {
504 protected:
505 reference_storage_impl() noexcept = default;
506
507 reference_storage_impl(const RawAllocator& alloc) noexcept : alloc_(alloc) {}
508
509 bool is_valid() const noexcept
510 {
511 return true;
512 }
513
514 RawAllocator& get_allocator() const noexcept
515 {
516 return alloc_;
517 }
518
519 private:
520 mutable RawAllocator alloc_;
521 };
522 } // namespace detail
523
524 /// Specifies whether or not a RawAllocator has shared semantics.
525 /// It is shared, if - like \ref allocator_reference - if multiple objects refer to the same internal allocator and if it can be copied.
526 /// This sharing is stateful, however, stateless allocators are not considered shared in the meaning of this traits. <br>
527 /// If a \c RawAllocator is shared, it will be directly embedded inside \ref reference_storage since it already provides \ref allocator_reference like semantics, so there is no need to add them manually,<br>
528 /// Specialize it for your own types, if they provide sharing semantics and can be copied.
529 /// They also must provide an `operator==` to check whether two allocators refer to the same shared one.
530 /// \note This makes no guarantees about the lifetime of the shared object, the sharing allocators can either own or refer to a shared object.
531 /// \ingroup memory_storage
532 template <class RawAllocator>
533 struct is_shared_allocator : std::false_type
534 {
535 };
536
537 /// A StoragePolicy that stores a reference to an allocator.
538 /// For stateful allocators it only stores a pointer to an allocator object and copying/moving only copies the pointer.
539 /// For stateless allocators it does not store anything, an allocator will be constructed as needed.
540 /// For allocators that are already shared (determined through \ref is_shared_allocator) it will store the allocator type directly.
541 /// \note It does not take ownership over the allocator in the stateful case, the user has to ensure that the allocator object stays valid.
542 /// In the other cases the lifetime does not matter.
543 /// \ingroup memory_storage
544 template <class RawAllocator>
546#ifndef DOXYGEN
547 : WPI_EBO(detail::reference_storage_impl<
548 typename allocator_traits<RawAllocator>::allocator_type,
549 decltype(detail::reference_type(
550 typename allocator_traits<RawAllocator>::is_stateful{},
551 is_shared_allocator<RawAllocator>{}))>)
552#endif
553 {
557 RawAllocator>::is_stateful{},
559
560 public:
562
563 /// Default constructor.
564 /// \effects If the allocator is stateless, this has no effect and the object is usable as an allocator.
565 /// If the allocator is stateful, creates an invalid reference without any associated allocator.
566 /// Then it must not be used.
567 /// If the allocator is shared, default constructs the shared allocator.
568 /// If the shared allocator does not have a default constructor, this constructor is ill-formed.
569 reference_storage() noexcept = default;
570
571 /// \effects Creates it from a stateless or shared allocator.
572 /// It will not store anything, only creates the allocator as needed.
573 /// \requires The \c RawAllocator is stateless or shared.
574 reference_storage(const allocator_type& alloc) noexcept : storage(alloc) {}
575
576 /// \effects Creates it from a reference to a stateful allocator.
577 /// It will store a pointer to this allocator object.
578 /// \note The user has to take care that the lifetime of the reference does not exceed the allocator lifetime.
579 reference_storage(allocator_type& alloc) noexcept : storage(alloc) {}
580
581 /// @{
582 /// \effects Copies the \c allocator_reference object.
583 /// Only copies the pointer to it in the stateful case.
584 reference_storage(const reference_storage&) noexcept = default;
585 reference_storage& operator=(const reference_storage&) noexcept = default;
586 /// @}
587
588 /// \returns Whether or not the reference is valid.
589 /// It is only invalid, if it was created by the default constructor and the allocator is stateful.
590 explicit operator bool() const noexcept
591 {
592 return storage::is_valid();
593 }
594
595 /// \returns Returns a reference to the allocator.
596 /// \requires The reference must be valid.
598 {
599 return storage::get_allocator();
600 }
601
602 protected:
603 ~reference_storage() noexcept = default;
604
605 bool is_composable() const noexcept
606 {
608 }
609 };
610
611 /// Specialization of the class template \ref reference_storage that is type-erased.
612 /// It is triggered by the tag type \ref any_allocator.
613 /// The specialization can store a reference to any allocator type.
614 /// \ingroup memory_storage
615 template <>
617 {
618 class base_allocator
619 {
620 public:
621 using is_stateful = std::true_type;
622
623 virtual ~base_allocator() = default;
624
625 virtual void clone(void* storage) const noexcept = 0;
626
627 void* allocate_node(std::size_t size, std::size_t alignment)
628 {
629 return allocate_impl(1, size, alignment);
630 }
631
632 void* allocate_array(std::size_t count, std::size_t size, std::size_t alignment)
633 {
634 return allocate_impl(count, size, alignment);
635 }
636
637 void deallocate_node(void* node, std::size_t size, std::size_t alignment) noexcept
638 {
639 deallocate_impl(node, 1, size, alignment);
640 }
641
642 void deallocate_array(void* array, std::size_t count, std::size_t size,
643 std::size_t alignment) noexcept
644 {
645 deallocate_impl(array, count, size, alignment);
646 }
647
648 void* try_allocate_node(std::size_t size, std::size_t alignment) noexcept
649 {
650 return try_allocate_impl(1, size, alignment);
651 }
652
653 void* try_allocate_array(std::size_t count, std::size_t size,
654 std::size_t alignment) noexcept
655 {
656 return try_allocate_impl(count, size, alignment);
657 }
658
659 bool try_deallocate_node(void* node, std::size_t size,
660 std::size_t alignment) noexcept
661 {
662 return try_deallocate_impl(node, 1, size, alignment);
663 }
664
665 bool try_deallocate_array(void* array, std::size_t count, std::size_t size,
666 std::size_t alignment) noexcept
667 {
668 return try_deallocate_impl(array, count, size, alignment);
669 }
670
671 // count 1 means node
672 virtual void* allocate_impl(std::size_t count, std::size_t size,
673 std::size_t alignment) = 0;
674 virtual void deallocate_impl(void* ptr, std::size_t count, std::size_t size,
675 std::size_t alignment) noexcept = 0;
676
677 virtual void* try_allocate_impl(std::size_t count, std::size_t size,
678 std::size_t alignment) noexcept = 0;
679
680 virtual bool try_deallocate_impl(void* ptr, std::size_t count, std::size_t size,
681 std::size_t alignment) noexcept = 0;
682
683 std::size_t max_node_size() const
684 {
685 return max(query::node_size);
686 }
687
688 std::size_t max_array_size() const
689 {
690 return max(query::array_size);
691 }
692
693 std::size_t max_alignment() const
694 {
695 return max(query::alignment);
696 }
697
698 virtual bool is_composable() const noexcept = 0;
699
700 protected:
701 enum class query
702 {
703 node_size,
704 array_size,
705 alignment
706 };
707
708 virtual std::size_t max(query q) const = 0;
709 };
710
711 public:
712 using allocator_type = WPI_IMPL_DEFINED(base_allocator);
713
714 /// \effects Creates it from a reference to any stateful RawAllocator.
715 /// It will store a pointer to this allocator object.
716 /// \note The user has to take care that the lifetime of the reference does not exceed the allocator lifetime.
717 template <class RawAllocator>
718 reference_storage(RawAllocator& alloc) noexcept
719 {
720 static_assert(sizeof(basic_allocator<RawAllocator>)
721 <= sizeof(basic_allocator<default_instantiation>),
722 "requires all instantiations to have certain maximum size");
723 ::new (static_cast<void*>(&storage_)) basic_allocator<RawAllocator>(alloc);
724 }
725
726 // \effects Creates it from any stateless RawAllocator.
727 /// It will not store anything, only creates the allocator as needed.
728 /// \requires The \c RawAllocator is stateless.
729 template <class RawAllocator>
731 const RawAllocator& alloc,
733 {
734 static_assert(sizeof(basic_allocator<RawAllocator>)
735 <= sizeof(basic_allocator<default_instantiation>),
736 "requires all instantiations to have certain maximum size");
737 ::new (static_cast<void*>(&storage_)) basic_allocator<RawAllocator>(alloc);
738 }
739
740 /// \effects Creates it from the internal base class for the type-erasure.
741 /// Has the same effect as if the actual stored allocator were passed to the other constructor overloads.
742 /// \note This constructor is used internally to avoid double-nesting.
743 reference_storage(const WPI_IMPL_DEFINED(base_allocator) & alloc) noexcept
744 {
745 alloc.clone(&storage_);
746 }
747
748 /// \effects Creates it from the internal base class for the type-erasure.
749 /// Has the same effect as if the actual stored allocator were passed to the other constructor overloads.
750 /// \note This constructor is used internally to avoid double-nesting.
751 reference_storage(WPI_IMPL_DEFINED(base_allocator) & alloc) noexcept
752 : reference_storage(static_cast<const base_allocator&>(alloc))
753 {
754 }
755
756 /// @{
757 /// \effects Copies the \c reference_storage object.
758 /// It only copies the pointer to the allocator.
760 {
761 other.get_allocator().clone(&storage_);
762 }
763
765 {
766 get_allocator().~allocator_type();
767 other.get_allocator().clone(&storage_);
768 return *this;
769 }
770 /// @}
771
772 /// \returns A reference to the allocator.
773 /// The actual type is implementation-defined since it is the base class used in the type-erasure,
774 /// but it provides the full RawAllocator member functions.
775 /// \note There is no way to access any custom member functions of the allocator type.
777 {
778 auto mem = static_cast<void*>(&storage_);
779 return *static_cast<base_allocator*>(mem);
780 }
781
782 protected:
784 {
785 get_allocator().~allocator_type();
786 }
787
788 bool is_composable() const noexcept
789 {
790 return get_allocator().is_composable();
791 }
792
793 private:
794 template <class RawAllocator>
795 class basic_allocator
796 : public base_allocator,
798 typename allocator_traits<RawAllocator>::allocator_type,
799 decltype(detail::reference_type(typename allocator_traits<
800 RawAllocator>::is_stateful{},
801 is_shared_allocator<RawAllocator>{}))>
802 {
803 using traits = allocator_traits<RawAllocator>;
805 using storage = detail::reference_storage_impl<
807 decltype(detail::reference_type(typename allocator_traits<
808 RawAllocator>::is_stateful{},
809 is_shared_allocator<RawAllocator>{}))>;
810
811 public:
812 // non stateful
813 basic_allocator(const RawAllocator& alloc) noexcept : storage(alloc) {}
814
815 // stateful
816 basic_allocator(RawAllocator& alloc) noexcept : storage(alloc) {}
817
818 private:
819 typename traits::allocator_type& get() const noexcept
820 {
821 return storage::get_allocator();
822 }
823
824 void clone(void* storage) const noexcept override
825 {
826 ::new (storage) basic_allocator(get());
827 }
828
829 void* allocate_impl(std::size_t count, std::size_t size,
830 std::size_t alignment) override
831 {
832 auto&& alloc = get();
833 if (count == 1u)
834 return traits::allocate_node(alloc, size, alignment);
835 else
836 return traits::allocate_array(alloc, count, size, alignment);
837 }
838
839 void deallocate_impl(void* ptr, std::size_t count, std::size_t size,
840 std::size_t alignment) noexcept override
841 {
842 auto&& alloc = get();
843 if (count == 1u)
844 traits::deallocate_node(alloc, ptr, size, alignment);
845 else
846 traits::deallocate_array(alloc, ptr, count, size, alignment);
847 }
848
849 void* try_allocate_impl(std::size_t count, std::size_t size,
850 std::size_t alignment) noexcept override
851 {
852 auto&& alloc = get();
853 if (count == 1u)
854 return detail::try_allocate_node(composable{}, alloc, size, alignment);
855 else
856 return detail::try_allocate_array(composable{}, alloc, count, size,
857 alignment);
858 }
859
860 bool try_deallocate_impl(void* ptr, std::size_t count, std::size_t size,
861 std::size_t alignment) noexcept override
862 {
863 auto&& alloc = get();
864 if (count == 1u)
865 return detail::try_deallocate_node(composable{}, alloc, ptr, size,
866 alignment);
867 else
868 return detail::try_deallocate_array(composable{}, alloc, ptr, count, size,
869 alignment);
870 }
871
872 bool is_composable() const noexcept override
873 {
874 return composable::value;
875 }
876
877 std::size_t max(query q) const override
878 {
879 auto&& alloc = get();
880 if (q == query::node_size)
881 return traits::max_node_size(alloc);
882 else if (q == query::array_size)
883 return traits::max_array_size(alloc);
884 return traits::max_alignment(alloc);
885 }
886 };
887
888 // use a stateful instantiation to determine size and alignment
889 // base_allocator is stateful
890 using default_instantiation = basic_allocator<base_allocator>;
891 alignas(default_instantiation) mutable char storage_[sizeof(default_instantiation)];
892 };
893
894 /// An alias template for \ref allocator_storage using the \ref reference_storage policy.
895 /// It will store a reference to the given allocator type. The tag type \ref any_allocator enables type-erasure.
896 /// Wrap the allocator in a \ref thread_safe_allocator if you want thread safety.
897 /// \ingroup memory_storage
898 template <class RawAllocator>
899 WPI_ALIAS_TEMPLATE(allocator_reference,
901
902 /// \returns A new \ref allocator_reference object by forwarding the allocator to the constructor.
903 /// \relates allocator_reference
904 template <class RawAllocator>
905 auto make_allocator_reference(RawAllocator&& allocator) noexcept
907 {
908 return {detail::forward<RawAllocator>(allocator)};
909 }
910
911 /// An alias for the \ref reference_storage specialization using type-erasure.
912 /// \ingroup memory_storage
914
915 /// An alias for \ref allocator_storage using the \ref any_reference_storage.
916 /// It will store a reference to any RawAllocator.
917 /// This is the same as passing the tag type \ref any_allocator to the alias \ref allocator_reference.
918 /// Wrap the allocator in a \ref thread_safe_allocator if you want thread safety.
919 /// \ingroup memory_storage
921
922 /// \returns A new \ref any_allocator_reference object by forwarding the allocator to the constructor.
923 /// \relates any_allocator_reference
924 template <class RawAllocator>
925 auto make_any_allocator_reference(RawAllocator&& allocator) noexcept
927 {
928 return {detail::forward<RawAllocator>(allocator)};
929 }
930 } // namespace memory
931} // namespace wpi
932
933#endif // WPI_MEMORY_ALLOCATOR_STORAGE_HPP_INCLUDED
The default specialization of the wpi::memory::allocator_traits.
constexpr T & get(wpi::array< T, N > &arr) noexcept
Definition array.h:66
This class is a wrapper around std::array that does compile time size checking.
Definition array.h:26
An alias template for allocator_storage using the direct_storage policy without a mutex.
Definition allocator_storage.hpp:387
auto make_allocator_adapter(RawAllocator &&allocator) noexcept -> allocator_adapter< typename std::decay< RawAllocator >::type >
Definition allocator_storage.hpp:392
An alias template for allocator_storage using the reference_storage policy.
Definition allocator_storage.hpp:900
auto make_allocator_reference(RawAllocator &&allocator) noexcept -> allocator_reference< typename std::decay< RawAllocator >::type >
Definition allocator_storage.hpp:905
A RawAllocator that stores another allocator.
Definition allocator_storage.hpp:97
void * allocate_node(std::size_t size, std::size_t alignment)
Definition allocator_storage.hpp:176
std::size_t max_node_size() const
Definition allocator_storage.hpp:205
typename StoragePolicy::allocator_type allocator_type
Definition allocator_storage.hpp:106
std::size_t max_alignment() const
Definition allocator_storage.hpp:219
allocator_storage(allocator_storage &&other) noexcept
Definition allocator_storage.hpp:149
std::size_t max_array_size() const
Definition allocator_storage.hpp:212
allocator_storage & operator=(allocator_storage &&other) noexcept
Definition allocator_storage.hpp:157
auto get_allocator() noexcept -> decltype(std::declval< storage_policy >().get_allocator())
Definition allocator_storage.hpp:277
void deallocate_array(void *ptr, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:197
auto lock() noexcept -> implementation_defined
Definition allocator_storage.hpp:294
void * allocate_array(std::size_t count, std::size_t size, std::size_t alignment)
Definition allocator_storage.hpp:183
bool try_deallocate_node(void *ptr, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:254
allocator_storage(const allocator_storage< OtherPolicy, Mutex > &other,)
Definition allocator_storage.hpp:138
void * try_allocate_array(std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:244
auto get_allocator() const noexcept -> decltype(std::declval< const storage_policy >().get_allocator())
Definition allocator_storage.hpp:283
Mutex mutex
Definition allocator_storage.hpp:108
allocator_storage & operator=(const allocator_storage &)=default
allocator_storage(const allocator_storage &)=default
bool try_deallocate_array(void *ptr, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:263
bool is_composable() const noexcept
Definition allocator_storage.hpp:312
typename traits::is_stateful is_stateful
Definition allocator_storage.hpp:109
allocator_storage(Alloc &&alloc,)
Definition allocator_storage.hpp:127
void deallocate_node(void *ptr, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:190
auto lock() const noexcept -> implementation_defined
Definition allocator_storage.hpp:300
StoragePolicy storage_policy
Definition allocator_storage.hpp:107
void * try_allocate_node(std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:235
The default specialization of the allocator_traits for a RawAllocator.
Definition allocator_traits.hpp:292
static void deallocate_array(allocator_type &state, void *array, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:328
traits_detail::allocator_type< Allocator > allocator_type
Definition allocator_traits.hpp:294
static void * allocate_array(allocator_type &state, std::size_t count, std::size_t size, std::size_t alignment)
Definition allocator_traits.hpp:308
static std::size_t max_array_size(const allocator_type &state)
Definition allocator_traits.hpp:346
static std::size_t max_node_size(const allocator_type &state)
Definition allocator_traits.hpp:338
static std::size_t max_alignment(const allocator_type &state)
Definition allocator_traits.hpp:354
static void * allocate_node(allocator_type &state, std::size_t size, std::size_t alignment)
Definition allocator_traits.hpp:298
static void deallocate_node(allocator_type &state, void *node, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:318
decltype(traits_detail::is_stateful< Allocator >(traits_detail::full_concept{})) is_stateful
Definition allocator_traits.hpp:295
The default specialization of the composable_allocator_traits for a ComposableAllocator.
Definition allocator_traits.hpp:500
static void * try_allocate_node(allocator_type &state, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:504
static void * try_allocate_array(allocator_type &state, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:513
static bool try_deallocate_node(allocator_type &state, void *node, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:522
static bool try_deallocate_array(allocator_type &state, void *array, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_traits.hpp:531
Definition threading.hpp:62
RawAllocator & get_allocator() const noexcept
Definition allocator_storage.hpp:514
bool is_valid() const noexcept
Definition allocator_storage.hpp:509
RawAllocator & get_allocator() const noexcept
Definition allocator_storage.hpp:469
reference_storage_impl() noexcept
Definition allocator_storage.hpp:460
reference_storage_impl(RawAllocator &allocator) noexcept
Definition allocator_storage.hpp:462
bool is_valid() const noexcept
Definition allocator_storage.hpp:464
bool is_valid() const noexcept
Definition allocator_storage.hpp:488
RawAllocator & get_allocator() const noexcept
Definition allocator_storage.hpp:493
Definition allocator_storage.hpp:453
A StoragePolicy that stores the allocator directly.
Definition allocator_storage.hpp:330
~direct_storage() noexcept=default
const allocator_type & get_allocator() const noexcept
Definition allocator_storage.hpp:366
allocator_type & get_allocator() noexcept
Definition allocator_storage.hpp:361
direct_storage & operator=(direct_storage &&other) noexcept
Definition allocator_storage.hpp:352
direct_storage(direct_storage &&other) noexcept
Definition allocator_storage.hpp:350
typename allocator_traits< RawAllocator >::allocator_type allocator_type
Definition allocator_storage.hpp:335
direct_storage(allocator_type &&allocator) noexcept
Definition allocator_storage.hpp:342
bool is_composable() const noexcept
Definition allocator_storage.hpp:375
Specialization of the class template reference_storage that is type-erased.
Definition allocator_storage.hpp:617
allocator_type & get_allocator() const noexcept
Definition allocator_storage.hpp:776
bool is_composable() const noexcept
Definition allocator_storage.hpp:788
reference_storage(const RawAllocator &alloc,) noexcept
It will not store anything, only creates the allocator as needed.
Definition allocator_storage.hpp:730
reference_storage(RawAllocator &alloc) noexcept
Definition allocator_storage.hpp:718
reference_storage(implementation_defined &alloc) noexcept
Definition allocator_storage.hpp:751
reference_storage(const reference_storage &other) noexcept
Definition allocator_storage.hpp:759
implementation_defined allocator_type
Definition allocator_storage.hpp:712
reference_storage(const implementation_defined &alloc) noexcept
Definition allocator_storage.hpp:743
reference_storage & operator=(const reference_storage &other) noexcept
Definition allocator_storage.hpp:764
~reference_storage() noexcept
Definition allocator_storage.hpp:783
A StoragePolicy that stores a reference to an allocator.
Definition allocator_storage.hpp:553
allocator_type & get_allocator() const noexcept
Definition allocator_storage.hpp:597
bool is_composable() const noexcept
Definition allocator_storage.hpp:605
~reference_storage() noexcept=default
typename allocator_traits< RawAllocator >::allocator_type allocator_type
Definition allocator_storage.hpp:561
reference_storage() noexcept=default
Default constructor.
reference_storage(allocator_type &alloc) noexcept
Definition allocator_storage.hpp:579
reference_storage & operator=(const reference_storage &) noexcept=default
reference_storage(const reference_storage &) noexcept=default
An alias template for allocator_storage using the direct_storage policy with a mutex.
Definition allocator_storage.hpp:410
auto make_thread_safe_allocator(RawAllocator &&allocator) -> thread_safe_allocator< typename std::decay< RawAllocator >::type, Mutex >
Definition allocator_storage.hpp:429
Configuration macros.
#define WPI_ALIAS_TEMPLATE(Name,...)
Definition config.hpp:73
auto ptr(T p) -> const void *
Converts p to const void* for pointer formatting.
Definition format.h:3821
detail namespace with internal helper functions
Definition input_adapters.h:32
constexpr auto count() -> int
Definition base.h:1028
constexpr common_t< T1, T2 > max(const T1 x, const T2 y) noexcept
Compile-time pairwise maximum function.
Definition max.hpp:41
Implement std::hash so that hash_code can be used in STL containers.
Definition PointerIntPair.h:280
typename std::conditional< is_thread_safe_allocator< RawAllocator >::value, no_mutex, Mutex >::type mutex_for
Definition threading.hpp:54
void * try_allocate_node(std::true_type, Alloc &alloc, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:25
bool try_deallocate_node(std::true_type, Alloc &alloc, void *ptr, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:41
locked_allocator< Alloc, Mutex > lock_allocator(Alloc &a, Mutex &m)
Definition threading.hpp:146
T && forward(typename std::remove_reference< T >::type &t) noexcept
Definition utility.hpp:31
reference_stateful reference_type(std::true_type stateful, std::false_type shared)
std::remove_reference< T >::type && move(T &&arg) noexcept
Definition utility.hpp:25
bool try_deallocate_array(std::true_type, Alloc &alloc, void *ptr, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:49
void * try_allocate_array(std::true_type, Alloc &alloc, std::size_t count, std::size_t size, std::size_t alignment) noexcept
Definition allocator_storage.hpp:33
Memory namespace.
Definition heap_allocator.hpp:20
Foonathan namespace.
Definition ntcore_cpp.h:26
Tag type that enables type-erasure in reference_storage.
Definition allocator_storage.hpp:322
Definition allocator_storage.hpp:444
Definition allocator_storage.hpp:438
Definition allocator_storage.hpp:441
Traits that check whether a type models concept ComposableAllocator.
Definition allocator_traits.hpp:596
Specifies whether or not a RawAllocator has shared semantics.
Definition allocator_storage.hpp:534
A dummy Mutex class that does not lock anything.
Definition threading.hpp:27
The mutex types.
#define WPI_ENABLE_IF(Expr)
Definition utility.hpp:78
#define WPI_SFINAE(Expr)
Definition utility.hpp:85
#define WPI_REQUIRES(Expr)
Definition utility.hpp:70
#define WPI_MEMORY_UNREACHABLE(Msg)
Definition assert.hpp:48
#define WPI_MEMORY_ASSERT(Expr)
Definition assert.hpp:46