1 | #pragma once |
2 | |
3 | #include <typeinfo> |
4 | #include <type_traits> |
5 | #include <stdexcept> |
6 | namespace mbgl { |
7 | namespace util { |
8 | |
9 | class bad_any_cast : public std::bad_cast { |
10 | public: |
11 | const char* what() const noexcept override { |
12 | return "bad any_cast<>()" ; |
13 | } |
14 | }; |
15 | /** |
16 | * A variant of `std::any` for non-copyable types. |
17 | * |
18 | * Use `unique_any` for non-copyable types (e.g. `std::unique_ptr<T>`) |
19 | * or to ensure that no copies are made of copyable types that are |
20 | * moved in. |
21 | * |
22 | * `uniqe_any` differs from `std::any` in that it does not support copy construction |
23 | * or copy assignment. It also does not require the contained type to be copy |
24 | * constructible. |
25 | * |
26 | * The `any_cast<T>()` methods work similar to `std::any_cast<T>()` except that |
27 | * non-copyable types may only be cast to references. |
28 | * |
29 | * Example usage: |
30 | * unique_any u1(3); |
31 | * auto u2 = unique_any(std::move(u1)); // u1 is moved from |
32 | * int i = any_cast<int>(u2); |
33 | * |
34 | * unique_any u2; |
35 | * u2 = std::unique_ptr<int>(new int); |
36 | * std::unique_ptr<int> iPtr = any_cast<std::unique_ptr<int>>(std::move(u2)); |
37 | * |
38 | * Inspired by linb::any (https://github.com/thelink2012/any) and the |
39 | * libc++ implementation (https://github.com/llvm-mirror/libcxx). |
40 | */ |
41 | class unique_any final |
42 | { |
43 | public: |
44 | unique_any() = default; |
45 | |
46 | //Copy constructor (deleted) |
47 | unique_any(const unique_any& rhs) = delete; |
48 | |
49 | unique_any(unique_any&& rhs) : vtable(rhs.vtable) { |
50 | if (vtable) { |
51 | vtable->move(src: std::move(rhs.storage), dest&: storage); |
52 | } |
53 | rhs.vtable = nullptr; |
54 | } |
55 | |
56 | // Constructs with a direct-initilizated object of type ValueType |
57 | template <typename ValueType, |
58 | typename _Vt = std::decay_t<ValueType>, |
59 | typename = std::enable_if_t<!std::is_same<_Vt, unique_any>::value> > |
60 | unique_any(ValueType&& value) { |
61 | create(std::forward<ValueType>(value)); |
62 | } |
63 | |
64 | ~unique_any() { |
65 | reset(); |
66 | } |
67 | |
68 | unique_any& operator=(unique_any&& rhs) { |
69 | unique_any(std::move(rhs)).swap(rhs&: *this); |
70 | return *this; |
71 | } |
72 | |
73 | template <class ValueType, |
74 | typename = std::enable_if_t<!std::is_same<std::decay_t<ValueType>, unique_any>::value> > |
75 | unique_any& operator=(ValueType&& rhs) { |
76 | unique_any(std::forward<ValueType>(rhs)).swap(rhs&: *this); |
77 | return *this; |
78 | } |
79 | |
80 | void reset() { |
81 | if (vtable) { |
82 | vtable->destroy(storage); |
83 | vtable = nullptr; |
84 | } |
85 | } |
86 | |
87 | void swap(unique_any& rhs) { |
88 | if (this == &rhs) { |
89 | return; |
90 | } else { |
91 | unique_any tmp(std::move(rhs)); |
92 | rhs.vtable = vtable; |
93 | if (rhs.vtable) { |
94 | rhs.vtable->move(src: std::move(storage), dest&: rhs.storage); |
95 | } |
96 | vtable = tmp.vtable; |
97 | if (vtable) { |
98 | vtable->move(src: std::move(tmp.storage), dest&: storage); |
99 | } |
100 | } |
101 | } |
102 | |
103 | const std::type_info& type() const { |
104 | return !has_value()? typeid(void) : vtable->type(); |
105 | } |
106 | |
107 | bool has_value() const { |
108 | return vtable != nullptr; |
109 | } |
110 | |
111 | private: |
112 | |
113 | union Storage { |
114 | using StackStorage = std::aligned_storage_t<3*sizeof(void*), std::alignment_of<void*>::value>; |
115 | Storage() = default; |
116 | |
117 | void * dynamic { nullptr }; |
118 | StackStorage stack; |
119 | }; |
120 | |
121 | template<typename T> |
122 | struct AllocateOnStack : std::integral_constant<bool, |
123 | sizeof(T) <= sizeof(Storage::stack) |
124 | && std::alignment_of<T>::value <= std::alignment_of<Storage::StackStorage>::value |
125 | && std::is_nothrow_move_constructible<T>::value> { |
126 | }; |
127 | |
128 | struct VTable { |
129 | virtual ~VTable() = default; |
130 | virtual void move(Storage&& src, Storage& dest) = 0; |
131 | virtual void destroy(Storage&) = 0; |
132 | virtual const std::type_info& type() = 0; |
133 | }; |
134 | |
135 | template <typename ValueType> |
136 | struct VTableHeap : public VTable { |
137 | void move(Storage&& src, Storage& dest) override { |
138 | dest.dynamic = src.dynamic; |
139 | src.dynamic = nullptr; |
140 | } |
141 | |
142 | void destroy(Storage& s) override { |
143 | delete reinterpret_cast<ValueType*>(s.dynamic); |
144 | } |
145 | |
146 | const std::type_info& type() override { |
147 | return typeid(ValueType); |
148 | } |
149 | }; |
150 | |
151 | template <typename ValueType> |
152 | struct VTableStack : public VTable { |
153 | void move(Storage&& src, Storage& dest) override { |
154 | new (&dest.stack) ValueType(std::move(reinterpret_cast<ValueType&>(src.stack))); |
155 | destroy(s&: src); |
156 | } |
157 | |
158 | void destroy(Storage& s) override { |
159 | reinterpret_cast<ValueType&>(s.stack).~ValueType(); |
160 | } |
161 | |
162 | const std::type_info& type() override { |
163 | return typeid(ValueType); |
164 | } |
165 | }; |
166 | |
167 | template <typename ValueType> |
168 | static VTable* vtableForType() { |
169 | using VTableType = std::conditional_t<AllocateOnStack<ValueType>::value, VTableStack<ValueType>, VTableHeap<ValueType> >; |
170 | static VTableType vtable; |
171 | return &vtable; |
172 | } |
173 | |
174 | template <typename ValueType, typename _Vt> |
175 | std::enable_if_t<AllocateOnStack<_Vt>::value> |
176 | createStorage(ValueType&& value) { |
177 | new (&storage.stack) _Vt(std::forward<ValueType>(value)); |
178 | } |
179 | |
180 | template <typename ValueType, typename _Vt> |
181 | std::enable_if_t<!AllocateOnStack<_Vt>::value> |
182 | createStorage(ValueType&& value) { |
183 | storage.dynamic = new _Vt(std::forward<ValueType>(value)); |
184 | } |
185 | |
186 | template <typename ValueType> |
187 | void create(ValueType&& value) { |
188 | using _Vt = std::decay_t<ValueType>; |
189 | vtable = vtableForType<_Vt>(); |
190 | createStorage<ValueType, _Vt>(std::forward<ValueType>(value)); |
191 | } |
192 | |
193 | VTable* vtable { nullptr }; |
194 | Storage storage; |
195 | |
196 | protected: |
197 | template<class ValueType> |
198 | friend const ValueType* any_cast(const unique_any* operand) ; |
199 | |
200 | template<class ValueType> |
201 | friend ValueType* any_cast(unique_any* operand) ; |
202 | |
203 | template<typename ValueType, typename _Vt = std::decay_t<ValueType> > |
204 | ValueType* cast() |
205 | { |
206 | return reinterpret_cast<ValueType *>( |
207 | AllocateOnStack<_Vt>::value ? &storage.stack : storage.dynamic); |
208 | } |
209 | }; |
210 | |
211 | template<typename ValueType> |
212 | inline const ValueType* any_cast(const unique_any* any) |
213 | { |
214 | return any_cast<ValueType>(const_cast<unique_any *>(any)); |
215 | } |
216 | |
217 | template<typename ValueType> |
218 | inline ValueType* any_cast(unique_any* any) |
219 | { |
220 | if(any == nullptr || any->type() != typeid(ValueType)) |
221 | return nullptr; |
222 | else |
223 | return any->cast<ValueType>(); |
224 | } |
225 | |
226 | template<typename ValueType, typename _Vt = std::decay_t<ValueType> > |
227 | inline ValueType any_cast(const unique_any& any) |
228 | { |
229 | static_assert(std::is_constructible<ValueType, const _Vt&>::value, |
230 | "any_cast type can't construct copy of contained object" ); |
231 | auto temp = any_cast<_Vt>(&any); |
232 | if (temp == nullptr) { |
233 | throw bad_any_cast(); |
234 | } |
235 | return static_cast<ValueType>(*temp); |
236 | } |
237 | |
238 | template<typename ValueType, typename _Vt = std::decay_t<ValueType> > |
239 | inline ValueType any_cast(unique_any& any) |
240 | { |
241 | static_assert(std::is_constructible<ValueType, const _Vt&>::value, |
242 | "any_cast type can't construct copy of contained object" ); |
243 | auto temp = any_cast<_Vt>(&any); |
244 | if (temp == nullptr) { |
245 | throw bad_any_cast(); |
246 | } |
247 | return static_cast<ValueType>(*temp); |
248 | } |
249 | |
250 | template<typename ValueType, typename _Vt = std::remove_cv_t<ValueType> > |
251 | inline ValueType any_cast(unique_any&& any) |
252 | { |
253 | auto temp = any_cast<_Vt>(&any); |
254 | if (temp == nullptr) { |
255 | throw bad_any_cast(); |
256 | } |
257 | auto retValue = static_cast<ValueType>(std::move(*temp)); |
258 | any.reset(); |
259 | return std::move(retValue); |
260 | } |
261 | |
262 | } // namespace util |
263 | } // namespace mbgl |
264 | |
265 | namespace std { |
266 | |
267 | inline void swap(mbgl::util::unique_any& lhs, mbgl::util::unique_any& rhs) { |
268 | lhs.swap(rhs); |
269 | } |
270 | |
271 | } // namespace std |
272 | |