plugify 1.2.8
Loading...
Searching...
No Matches
uninitialized.hpp
1#pragma once
2
3namespace plg {
4 template <class, class Alloc, class... Args>
5 struct has_construct_impl : std::false_type {};
6
7 template <class Alloc, class... Args>
9 decltype((void) std::declval<Alloc>().construct(std::declval<Args>()...)),
10 Alloc,
11 Args...> : std::true_type {};
12
13 template <class Alloc, class... Args>
14 struct has_construct : has_construct_impl<void, Alloc, Args...> {};
15
16 // __has_destroy
17 template <class Alloc, class Pointer, class = void>
18 struct has_destroy : std::false_type {};
19
20 template <class Alloc, class Pointer>
21 struct has_destroy<Alloc, Pointer, decltype((void) std::declval<Alloc>().destroy(std::declval<Pointer>()))>
22 : std::true_type {};
23
24 template <class Alloc, class Type>
25 struct allocator_has_trivial_move_construct : std::negation<has_construct<Alloc, Type*, Type&&>> {};
26
27 template <class Type>
29
30 template <class Alloc, class T>
31 struct allocator_has_trivial_destroy : std::negation<has_destroy<Alloc, T*>> {};
32
33 template <class T, class U>
34 struct allocator_has_trivial_destroy<allocator<T>, U> : std::true_type {};
35
36 template <class Alloc, class Type>
38 : std::negation<has_construct<Alloc, Type*, const Type&>> {};
39
40 template <class Type>
42
43 // Destroy all elements in [__first, __last) from left to right using allocator destruction.
44 template <class Alloc, class Iter, class Sent>
45 void allocator_destroy(Alloc& alloc, Iter first, Sent last) {
46 for (; first != last; ++first)
47 std::allocator_traits<Alloc>::destroy(alloc, std::to_address(first));
48 }
49
50 template <class Alloc, class Iter>
52 public:
54 : alloc_(alloc), first_(first), last_(last) {}
55
56 void operator()() const {
57 allocator_destroy(alloc_, std::reverse_iterator<Iter>(last_), std::reverse_iterator<Iter>(first_));
58 }
59
60 private:
61 Alloc& alloc_;
62 Iter& first_;
63 Iter& last_;
64 };
65
66 // Copy-construct [first1, last1) in [first2, first2 + N), where N is
67 // distance(first1, last1).
68 //
69 // The caller has to ensure that first2 can hold at least N uninitialized elements. If an
70 // exception is thrown the already copied elements are destroyed in reverse order of their
71 // construction.
72 template <class Alloc, class Iter1, class Sent1, class Iter2>
73 Iter2 uninitialized_allocator_copy_impl(
74 Alloc& alloc,
78 ) {
80 auto guard = make_exception_guard(
82 );
83 while (first1 != last1) {
84 std::allocator_traits<Alloc>::construct(alloc, std::to_address(first2), *first1);
85 ++first1;
86 ++first2;
87 }
88 guard.complete();
89 return first2;
90 }
91
92 template <
93 class Alloc,
94 class In,
95 class RawTypeIn = std::remove_const_t<In>,
96 class Out>
97 // using RawTypeIn because of the allocator<T const> extension
98 requires (std::is_trivially_copy_constructible_v<RawTypeIn> &&
99 std::is_trivially_copy_assignable_v<RawTypeIn> &&
100 std::is_same_v<std::remove_const_t<In>, std::remove_const_t<Out>> &&
101 allocator_has_trivial_copy_construct<Alloc, RawTypeIn>::value)
102 Out* uninitialized_allocator_copy_impl(Alloc&, In* first1, In* last1, Out* first2) {
103 return std::copy(first1, last1, const_cast<RawTypeIn*>(first2));
104 }
105
106 template <class Alloc, class Iter1, class Sent1, class Iter2>
107 Iter2 uninitialized_allocator_copy(Alloc& alloc, Iter1 first1, Sent1 last1, Iter2 first2) {
108 return uninitialized_allocator_copy_impl(alloc, std::move(first1), std::move(last1), std::move(first2));
109 }
110
111 // __uninitialized_allocator_relocate relocates the objects in [__first, __last) into
112 // __result.
113 // Relocation means that the objects in [__first, __last) are placed into __result as-if by
114 // move-construct and destroy, except that the move constructor and destructor may never be
115 // called if they are known to be equivalent to a memcpy.
116 //
117 // Preconditions: __result doesn't contain any objects and [__first, __last) contains
118 // objects Postconditions: __result contains the objects from [__first, __last) and
119 // [__first, __last) doesn't contain any objects
120 //
121 // The strong exception guarantee is provided if any of the following are true:
122 // - is_nothrow_move_constructible<T>
123 // - is_copy_constructible<T>
124 // - is_trivially_relocatable<T>
125 template <class Alloc, class T>
126 constexpr void uninitialized_allocator_relocate(Alloc& alloc, T* first, T* last, T* result) {
127 if (std::is_constant_evaluated() ||
128 !is_trivially_relocatable<T>::value ||
129 !allocator_has_trivial_move_construct<Alloc, T>::value ||
130 !allocator_has_trivial_destroy<Alloc, T>::value
131 ) {
132 auto destruct_first = result;
133 auto guard = make_exception_guard(
134 AllocatorDestroyRangeReverse<Alloc, T*>(alloc, destruct_first, result)
135 );
136 auto iter = first;
137 while (iter != last) {
138 std::allocator_traits<Alloc>::construct(
139 alloc,
140 result,
141#if PLUGIFY_HAS_EXCEPTIONS
142 std::move_if_noexcept(*iter)
143#else
144 std::move(*iter)
145#endif // PLUGIFY_HAS_EXCEPTIONS
146 );
147 ++iter;
148 ++result;
149 }
150 guard.complete();
151 allocator_destroy(alloc, first, last);
152 } else {
153 // Casting to void* to suppress clang complaining that this is technically UB.
154 std::memcpy(static_cast<void*>(result), first, sizeof(T) * static_cast<size_t>(last - first));
155 }
156 }
157}; // namespace plg