Line | Count | Source |
1 | | // |
2 | | // immer: immutable data structures for C++ |
3 | | // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente |
4 | | // |
5 | | // This software is distributed under the Boost Software License, Version 1.0. |
6 | | // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt |
7 | | // |
8 | | |
9 | | #pragma once |
10 | | |
11 | | #include <immer/detail/util.hpp> |
12 | | #include <immer/memory_policy.hpp> |
13 | | |
14 | | #include <cstddef> |
15 | | |
16 | | namespace immer { |
17 | | |
18 | | namespace detail { |
19 | | |
20 | | template <typename U, typename MP> |
21 | | struct gc_atom_impl; |
22 | | |
23 | | template <typename U, typename MP> |
24 | | struct refcount_atom_impl; |
25 | | |
26 | | } // namespace detail |
27 | | |
28 | | /*! |
29 | | * Immutable box for a single value of type `T`. |
30 | | * |
31 | | * The box is always copiable and movable. The `T` copy or move |
32 | | * operations are never called. Since a box is immutable, copying or |
33 | | * moving just copy the underlying pointers. |
34 | | */ |
35 | | template <typename T, typename MemoryPolicy = default_memory_policy> |
36 | | class box |
37 | | { |
38 | | friend struct detail::gc_atom_impl<T, MemoryPolicy>; |
39 | | friend struct detail::refcount_atom_impl<T, MemoryPolicy>; |
40 | | |
41 | | struct holder : MemoryPolicy::refcount |
42 | | { |
43 | | T value; |
44 | | |
45 | | template <typename... Args> |
46 | | holder(Args&&... args) |
47 | 321k | : value(std::forward<Args>(args)...) |
48 | 321k | { |
49 | 321k | } immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::holder::holder<char const (&) [4]>(char const (&) [4]) Line | Count | Source | 47 | 39.3k | : value(std::forward<Args>(args)...) | 48 | 39.3k | { | 49 | 39.3k | } |
immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::holder::holder<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&) Line | Count | Source | 47 | 282k | : value(std::forward<Args>(args)...) | 48 | 282k | { | 49 | 282k | } |
immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::holder::holder<>() Line | Count | Source | 47 | 1 | : value(std::forward<Args>(args)...) | 48 | 1 | { | 49 | 1 | } |
|
50 | | }; |
51 | | |
52 | | using heap = typename MemoryPolicy::heap::type; |
53 | | |
54 | | holder* impl_ = nullptr; |
55 | | |
56 | | box(holder* impl) |
57 | | : impl_{impl} |
58 | | { |
59 | | } |
60 | | |
61 | | public: |
62 | 871k | const holder* impl() const { return impl_; }; |
63 | | |
64 | | using value_type = T; |
65 | | using memory_policy = MemoryPolicy; |
66 | | |
67 | | /*! |
68 | | * Constructs a box holding `T{}`. |
69 | | */ |
70 | | box() |
71 | 1 | : impl_{detail::make<heap, holder>()} |
72 | 1 | { |
73 | 1 | } |
74 | | |
75 | | /*! |
76 | | * Constructs a box holding `T{arg}` |
77 | | */ |
78 | | template <typename Arg, |
79 | | typename Enable = std::enable_if_t< |
80 | | !std::is_same<box, std::decay_t<Arg>>::value>, |
81 | | // this is similar to std::is_constructible but works around the |
82 | | // fact that is_constructible is ill-formed for incomplete types |
83 | | typename = decltype(T(std::declval<Arg>()))> |
84 | | box(Arg&& arg) |
85 | 321k | : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))} |
86 | 321k | { |
87 | 321k | } immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::box<char const (&) [4], void, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(char const (&) [4]) Line | Count | Source | 85 | 39.3k | : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))} | 86 | 39.3k | { | 87 | 39.3k | } |
immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&) Line | Count | Source | 85 | 282k | : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))} | 86 | 282k | { | 87 | 282k | } |
|
88 | | |
89 | | /*! |
90 | | * Constructs a box holding `T{arg1, arg2, args...}` |
91 | | */ |
92 | | template <typename Arg1, |
93 | | typename Arg2, |
94 | | typename... Args, |
95 | | // this is similar to std::is_constructible but works around the |
96 | | // fact that is_constructible is ill-formed for incomplete types |
97 | | typename = decltype(T(std::declval<Arg1>(), |
98 | | std::declval<Arg2>(), |
99 | | std::declval<Args>()...))> |
100 | | box(Arg1&& arg1, Arg2&& arg2, Args&&... args) |
101 | | : impl_{detail::make<heap, holder>(std::forward<Arg1>(arg1), |
102 | | std::forward<Arg2>(arg2), |
103 | | std::forward<Args>(args)...)} |
104 | | { |
105 | | } |
106 | | |
107 | | friend void swap(box& a, box& b) |
108 | 3.92M | { |
109 | 3.92M | using std::swap; |
110 | 3.92M | swap(a.impl_, b.impl_); |
111 | 3.92M | } |
112 | | |
113 | 3.55M | box(box&& other) { swap(*this, other); } |
114 | | box(const box& other) |
115 | 820k | : impl_(other.impl_) |
116 | 820k | { |
117 | 820k | impl_->inc(); |
118 | 820k | } |
119 | | box& operator=(box&& other) |
120 | 371k | { |
121 | 371k | swap(*this, other); |
122 | 371k | return *this; |
123 | 371k | } |
124 | | box& operator=(const box& other) |
125 | | { |
126 | | auto aux = other; |
127 | | swap(*this, aux); |
128 | | return *this; |
129 | | } |
130 | | ~box() |
131 | 4.69M | { |
132 | 4.69M | if (impl_ && impl_->dec()) { |
133 | 321k | impl_->~holder(); |
134 | 321k | heap::deallocate(sizeof(holder), impl_); |
135 | 321k | } |
136 | 4.69M | } |
137 | | |
138 | | /*! Query the current value. */ |
139 | 505k | IMMER_NODISCARD const T& get() const { return impl_->value; } |
140 | | |
141 | | /*! Conversion to the boxed type. */ |
142 | 285k | operator const T&() const { return get(); } |
143 | | |
144 | | /*! Access via dereference */ |
145 | | const T& operator*() const { return get(); } |
146 | | |
147 | | /*! Access via pointer member access */ |
148 | | const T* operator->() const { return &get(); } |
149 | | |
150 | | /*! |
151 | | * Returns a new box built by applying the `fn` to the underlying |
152 | | * value. |
153 | | * |
154 | | * @rst |
155 | | * |
156 | | * **Example** |
157 | | * .. literalinclude:: ../example/box/box.cpp |
158 | | * :language: c++ |
159 | | * :dedent: 8 |
160 | | * :start-after: update/start |
161 | | * :end-before: update/end |
162 | | * |
163 | | * @endrst |
164 | | */ |
165 | | template <typename Fn> |
166 | | IMMER_NODISCARD box update(Fn&& fn) const& |
167 | | { |
168 | | return std::forward<Fn>(fn)(get()); |
169 | | } |
170 | | template <typename Fn> |
171 | | IMMER_NODISCARD box&& update(Fn&& fn) && |
172 | | { |
173 | | if (impl_->unique()) |
174 | | impl_->value = std::forward<Fn>(fn)(std::move(impl_->value)); |
175 | | else |
176 | | *this = std::forward<Fn>(fn)(impl_->value); |
177 | | return std::move(*this); |
178 | | } |
179 | | }; |
180 | | |
181 | | template <typename T, typename MP> |
182 | | IMMER_NODISCARD bool operator==(const box<T, MP>& a, const box<T, MP>& b) |
183 | 392k | { |
184 | 392k | return a.impl() == b.impl() || a.get() == b.get(); |
185 | 392k | } |
186 | | template <typename T, typename MP> |
187 | | IMMER_NODISCARD bool operator!=(const box<T, MP>& a, const box<T, MP>& b) |
188 | 43.3k | { |
189 | 43.3k | return a.impl() != b.impl() && a.get() != b.get(); |
190 | 43.3k | } |
191 | | template <typename T, typename MP> |
192 | | IMMER_NODISCARD bool operator<(const box<T, MP>& a, const box<T, MP>& b) |
193 | | { |
194 | | return a.impl() != b.impl() && a.get() < b.get(); |
195 | | } |
196 | | |
197 | | template <typename T, typename MP, typename T2> |
198 | | IMMER_NODISCARD auto operator==(const box<T, MP>& a, T2&& b) |
199 | | -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value, |
200 | | decltype(a.get() == b)> |
201 | | { |
202 | | return a.get() == b; |
203 | | } |
204 | | template <typename T, typename MP, typename T2> |
205 | | IMMER_NODISCARD auto operator!=(const box<T, MP>& a, T2&& b) |
206 | | -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value, |
207 | | decltype(a.get() != b)> |
208 | | { |
209 | | return a.get() != b; |
210 | | } |
211 | | template <typename T, typename MP, typename T2> |
212 | | IMMER_NODISCARD auto operator<(const box<T, MP>& a, T2&& b) |
213 | | -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value, |
214 | | decltype(a.get() < b)> |
215 | | { |
216 | | return a.get() < b; |
217 | | } |
218 | | |
219 | | template <typename T2, typename T, typename MP> |
220 | | IMMER_NODISCARD auto operator==(T2&& b, const box<T, MP>& a) |
221 | | -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value, |
222 | | decltype(a.get() == b)> |
223 | | { |
224 | | return a.get() == b; |
225 | | } |
226 | | template <typename T2, typename T, typename MP> |
227 | | IMMER_NODISCARD auto operator!=(T2&& b, const box<T, MP>& a) |
228 | | -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value, |
229 | | decltype(a.get() != b)> |
230 | | { |
231 | | return a.get() != b; |
232 | | } |
233 | | template <typename T2, typename T, typename MP> |
234 | | IMMER_NODISCARD auto operator<(T2&& b, const box<T, MP>& a) |
235 | | -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value, |
236 | | decltype(b < a.get())> |
237 | | { |
238 | | return b < a.get(); |
239 | | } |
240 | | |
241 | | } // namespace immer |
242 | | |
243 | | namespace std { |
244 | | |
245 | | template <typename T, typename MP> |
246 | | struct hash<immer::box<T, MP>> |
247 | | { |
248 | | std::size_t operator()(const immer::box<T, MP>& x) const |
249 | | { |
250 | | return std::hash<T>{}(*x); |
251 | | } |
252 | | }; |
253 | | |
254 | | } // namespace std |