Bond
 
Loading...
Searching...
No Matches
any.h
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4/*
5 * This header implements bond::detail::any class which provides similar functionality to
6 * the boost::any which (1) uses user-defined CTTI instead of a built-in RTTI and (2) uses
7 * user-defined storage size for small object optimization.
8 */
9
10
11#pragma once
12
13#include <bond/core/config.h>
14
15#include "mpl.h"
16
17#include <bond/core/config.h>
18
19#include <boost/assert.hpp>
20#include <boost/static_assert.hpp>
21#include <boost/utility/enable_if.hpp>
22
23#include <cstdint>
24#include <type_traits>
25
26
27namespace bond
28{
29namespace detail
30{
31
32template <template <typename> class TypeId, std::size_t Size>
33class any
34{
35 BOOST_STATIC_ASSERT(Size >= sizeof(void*));
36
37public:
38 any() BOND_NOEXCEPT
39 : _id{}
40 {}
41
42 any(const any& other)
43 {
44 emplace_uninitialized(other);
45 }
46
47 template <typename T>
48 any(const T& value)
49 {
50 emplace_uninitialized(value);
51 }
52
53 ~any()
54 {
55 destroy();
56 }
57
58 any& operator=(const any& other)
59 {
60 return assign(other);
61 }
62
63 template <typename T>
64 any& operator=(const T& value)
65 {
66 return assign(value);
67 }
68
69 bool empty() const BOND_NOEXCEPT
70 {
71 return _id == 0;
72 }
73
74 explicit operator bool() const BOND_NOEXCEPT
75 {
76 return !empty();
77 }
78
79 template <typename T>
80 T* cast() BOND_NOEXCEPT
81 {
82 return TypeId<T>::value == _id ? functions::template table<T>::unsafe_cast(*this) : nullptr;
83 }
84
85 template <typename T>
86 const T* cast() const BOND_NOEXCEPT
87 {
88 return TypeId<T>::value == _id ? functions::template table<T>::unsafe_cast(*this) : nullptr;
89 }
90
91 bool operator==(const any& other) const
92 {
93 return _id == other._id && (empty() || _functions.compare(*this, other));
94 }
95
96 bool operator!=(const any& other) const
97 {
98 return !(*this == other);
99 }
100
101private:
102 using storage = typename std::aligned_storage<Size>::type;
103
104 struct functions
105 {
106 void (*destroy)(any& x);
107
108 void (*assign)(any& x, const any& other);
109
110 void (*emplace_uninitialized)(any& x, const any& other);
111
112 bool (*compare)(const any& x, const any& y);
113
114
115 template <typename T, bool IsSmall>
116 struct impl;
117
118 template <typename T>
119 struct impl<T, true>
120 {
121 static T* unsafe_cast(any& x) BOND_NOEXCEPT
122 {
123 BOOST_ASSERT(TypeId<T>::value == x._id);
124 return static_cast<T*>(x.data());
125 }
126
127 static const T* unsafe_cast(const any& x) BOND_NOEXCEPT
128 {
129 BOOST_ASSERT(TypeId<T>::value == x._id);
130 return static_cast<const T*>(x.data());
131 }
132
133 static void emplace_uninitialized(any& x, const T& value)
134 {
135 new (x.data()) T{ value };
136 }
137
138 static void destroy(any& x)
139 {
140 unsafe_cast(x)->~T();
141 }
142 };
143
144 template <typename T>
145 struct impl<T, false>
146 {
147 static T*& unsafe_cast(any& x) BOND_NOEXCEPT
148 {
149 BOOST_ASSERT(TypeId<T>::value == x._id);
150 return *static_cast<T**>(x.data());
151 }
152
153 static const T* unsafe_cast(const any& x) BOND_NOEXCEPT
154 {
155 BOOST_ASSERT(TypeId<T>::value == x._id);
156 return *static_cast<const T* const*>(x.data());
157 }
158
159 static void emplace_uninitialized(any& x, const T& value)
160 {
161 *static_cast<T**>(x.data()) = new T{ value };
162 }
163
164 static void destroy(any& x)
165 {
166 T*& ptr = unsafe_cast(x);
167 delete ptr;
168 ptr = nullptr;
169 }
170 };
171
172 template <typename T>
173 struct table : impl<T, (sizeof(T) <= sizeof(storage))>
174 {
175 BOOST_STATIC_ASSERT(TypeId<T>::value != 0);
176
177 using base = impl<T, (sizeof(T) <= sizeof(storage))>;
178
179 template <typename U = T, typename boost::enable_if<std::is_copy_assignable<U> >::type* = nullptr>
180 static void assign(any& x, const T& value)
181 {
182 *base::unsafe_cast(x) = value;
183 }
184
185 template <typename U = T, typename boost::disable_if<std::is_copy_assignable<U> >::type* = nullptr>
186 static void assign(any& x, const T& value)
187 {
188 // TODO: Cache allocated buffer and reuse.
189 base::destroy(x);
190 emplace_uninitialized(x, value);
191 }
192
193 static void emplace_uninitialized(any& x, const T& value)
194 {
195 base::emplace_uninitialized(x, value);
196 x._id = TypeId<T>::value; // Update the id if emplace_uninitialized did not throw.
197 }
198
199 static functions make() BOND_NOEXCEPT
200 {
201 return
202 {
203 base::destroy,
204 [](any& x, const any& other) { assign(x, *base::unsafe_cast(other)); },
205 [](any& x, const any& other) { emplace_uninitialized(x, *base::unsafe_cast(other)); },
206 [](const any& x, const any& y) { return *base::unsafe_cast(x) == *base::unsafe_cast(y); }
207 };
208 }
209 };
210 };
211
212
213 const void* data() const BOND_NOEXCEPT
214 {
215 return &_storage;
216 }
217
218 void* data() BOND_NOEXCEPT
219 {
220 return &_storage;
221 }
222
224 template <typename T>
225 void emplace_uninitialized(const T& value)
226 {
227 functions::template table<T>::emplace_uninitialized(*this, value);
228 _functions = functions::template table<T>::make();
229 }
230
232 void emplace_uninitialized(const any& other)
233 {
234 if (!other.empty())
235 {
236 other._functions.emplace_uninitialized(*this, other);
237 _functions = other._functions;
238 }
239 else
240 {
241 _id = {};
242 }
243 }
244
245 template <typename T>
246 any& assign(const T& value)
247 {
248 if (!try_assign_same(value))
249 {
250 // TODO: Cache allocated buffer and reuse.
251 destroy();
252 emplace_uninitialized(value);
253 }
254
255 return *this;
256 }
257
258 template <typename T>
259 bool try_assign_same(const T& value)
260 {
261 if (_id == TypeId<T>::value)
262 {
263 functions::template table<T>::assign(*this, value);
264 return true;
265 }
266
267 return false;
268 }
269
270 bool try_assign_same(const any& other)
271 {
272 if (_id == other._id)
273 {
274 if (!empty())
275 {
276 _functions.assign(*this, other);
277 }
278
279 return true;
280 }
281
282 return false;
283 }
284
285 void destroy()
286 {
287 if (!empty())
288 {
289 _functions.destroy(*this);
290 _id = {};
291 }
292 }
293
294
295 storage _storage;
296 functions _functions;
297 std::uint32_t _id;
298};
299
300
301template <typename T, template <typename> class TypeId, std::size_t Size>
302inline T* any_cast(any<TypeId, Size>* x) BOND_NOEXCEPT
303{
304 BOOST_ASSERT(x);
305 return x->template cast<T>();
306}
307
308template <typename T, template <typename> class TypeId, std::size_t Size>
309inline const T* any_cast(const any<TypeId, Size>* x) BOND_NOEXCEPT
310{
311 BOOST_ASSERT(x);
312 return x->template cast<T>();
313}
314
315
316} // namespace detail
317
318} // namespace bond
namespace bond
Definition: apply.h:17
boost::enable_if< is_signed_int_or_enum< SignedT >, bool >::type operator==(const Variant &variant, SignedT value)
Compares variant for equality against the provided signed integer or enum value.
Definition: metadata.h:24