libnabo 1.1.2
any.hpp
1//
2// Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers.
3//
4// See also:
5// + http://en.cppreference.com/w/cpp/any
6// + http://en.cppreference.com/w/cpp/experimental/any
7// + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any
8// + https://cplusplus.github.io/LWG/lwg-active.html#2509
9//
10//
11// Copyright (c) 2016 Denilson das Merc�s Amorim
12//
13// Distributed under the Boost Software License, Version 1.0. (See accompanying
14// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
15//
16#ifndef LINB_ANY_HPP
17#define LINB_ANY_HPP
18#pragma once
19#include <typeinfo>
20#include <type_traits>
21#include <stdexcept>
22
23namespace linb
24{
25
26class bad_any_cast : public std::bad_cast
27{
28public:
29 const char* what() const noexcept override
30 {
31 return "bad any cast";
32 }
33};
34
35class any final
36{
37public:
39 any() :
40 vtable(nullptr)
41 {
42 }
43
45 any(const any& rhs) :
46 vtable(rhs.vtable)
47 {
48 if(!rhs.empty())
49 {
50 rhs.vtable->copy(rhs.storage, this->storage);
51 }
52 }
53
56 any(any&& rhs) noexcept :
57 vtable(rhs.vtable)
58 {
59 if(!rhs.empty())
60 {
61 rhs.vtable->move(rhs.storage, this->storage);
62 rhs.vtable = nullptr;
63 }
64 }
65
68 {
69 this->clear();
70 }
71
76 template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
77 any(ValueType&& value)
78 {
79 static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
80 "T shall satisfy the CopyConstructible requirements.");
81 this->construct(std::forward<ValueType>(value));
82 }
83
85 any& operator=(const any& rhs)
86 {
87 any(rhs).swap(*this);
88 return *this;
89 }
90
95 any& operator=(any&& rhs) noexcept
96 {
97 any(std::move(rhs)).swap(*this);
98 return *this;
99 }
100
105 template<typename ValueType, typename = typename std::enable_if<!std::is_same<typename std::decay<ValueType>::type, any>::value>::type>
106 any& operator=(ValueType&& value)
107 {
108 static_assert(std::is_copy_constructible<typename std::decay<ValueType>::type>::value,
109 "T shall satisfy the CopyConstructible requirements.");
110 any(std::forward<ValueType>(value)).swap(*this);
111 return *this;
112 }
113
115 void clear() noexcept
116 {
117 if(!empty())
118 {
119 this->vtable->destroy(storage);
120 this->vtable = nullptr;
121 }
122 }
123
125 bool empty() const noexcept
126 {
127 return this->vtable == nullptr;
128 }
129
131 const std::type_info& type() const noexcept
132 {
133 return empty()? typeid(void) : this->vtable->type();
134 }
135
137 void swap(any& rhs) noexcept
138 {
139 if(this->vtable != rhs.vtable)
140 {
141 any tmp(std::move(rhs));
142
143 // move from *this to rhs.
144 rhs.vtable = this->vtable;
145 if(this->vtable != nullptr)
146 {
147 this->vtable->move(this->storage, rhs.storage);
148 //this->vtable = nullptr; -- uneeded, see below
149 }
150
151 // move from tmp (previously rhs) to *this.
152 this->vtable = tmp.vtable;
153 if(tmp.vtable != nullptr)
154 {
155 tmp.vtable->move(tmp.storage, this->storage);
156 tmp.vtable = nullptr;
157 }
158 }
159 else // same types
160 {
161 if(this->vtable != nullptr)
162 this->vtable->swap(this->storage, rhs.storage);
163 }
164 }
165
166private: // Storage and Virtual Method Table
167
168 union storage_union
169 {
170 using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of<void*>::value>::type;
171
172 void* dynamic;
173 stack_storage_t stack; // 2 words for e.g. shared_ptr
174 };
175
177 struct vtable_type
178 {
179 // Note: The caller is responssible for doing .vtable = nullptr after destructful operations
180 // such as destroy() and/or move().
181
183 const std::type_info& (*type)() noexcept;
184
187 void(*destroy)(storage_union&) noexcept;
188
191 void(*copy)(const storage_union& src, storage_union& dest);
192
195 void(*move)(storage_union& src, storage_union& dest) noexcept;
196
198 void(*swap)(storage_union& lhs, storage_union& rhs) noexcept;
199 };
200
202 template<typename T>
203 struct vtable_dynamic
204 {
205 static const std::type_info& type() noexcept
206 {
207 return typeid(T);
208 }
209
210 static void destroy(storage_union& storage) noexcept
211 {
212 //assert(reinterpret_cast<T*>(storage.dynamic));
213 delete reinterpret_cast<T*>(storage.dynamic);
214 }
215
216 static void copy(const storage_union& src, storage_union& dest)
217 {
218 dest.dynamic = new T(*reinterpret_cast<const T*>(src.dynamic));
219 }
220
221 static void move(storage_union& src, storage_union& dest) noexcept
222 {
223 dest.dynamic = src.dynamic;
224 src.dynamic = nullptr;
225 }
226
227 static void swap(storage_union& lhs, storage_union& rhs) noexcept
228 {
229 // just exchage the storage pointers.
230 std::swap(lhs.dynamic, rhs.dynamic);
231 }
232 };
233
235 template<typename T>
236 struct vtable_stack
237 {
238 static const std::type_info& type() noexcept
239 {
240 return typeid(T);
241 }
242
243 static void destroy(storage_union& storage) noexcept
244 {
245 reinterpret_cast<T*>(&storage.stack)->~T();
246 }
247
248 static void copy(const storage_union& src, storage_union& dest)
249 {
250 new (&dest.stack) T(reinterpret_cast<const T&>(src.stack));
251 }
252
253 static void move(storage_union& src, storage_union& dest) noexcept
254 {
255 // one of the conditions for using vtable_stack is a nothrow move constructor,
256 // so this move constructor will never throw a exception.
257 new (&dest.stack) T(std::move(reinterpret_cast<T&>(src.stack)));
258 destroy(src);
259 }
260
261 static void swap(storage_union& lhs, storage_union& rhs) noexcept
262 {
263 std::swap(reinterpret_cast<T&>(lhs.stack), reinterpret_cast<T&>(rhs.stack));
264 }
265 };
266
268 template<typename T>
269 struct requires_allocation :
270 std::integral_constant<bool,
271 !(std::is_nothrow_move_constructible<T>::value // N4562 �6.3/3 [any.class]
272 && sizeof(T) <= sizeof(storage_union::stack)
273 && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)>
274 {};
275
277 template<typename T>
278 static vtable_type* vtable_for_type()
279 {
280 using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type;
281 static vtable_type table = {
282 VTableType::type, VTableType::destroy,
283 VTableType::copy, VTableType::move,
284 VTableType::swap,
285 };
286 return &table;
287 }
288
289protected:
290 template<typename T>
291 friend const T* any_cast(const any* operand) noexcept;
292 template<typename T>
293 friend T* any_cast(any* operand) noexcept;
294
296 bool is_typed(const std::type_info& t) const
297 {
298 return is_same(this->type(), t);
299 }
300
307 static bool is_same(const std::type_info& a, const std::type_info& b)
308 {
309#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE
310 return &a == &b;
311#else
312 return a == b;
313#endif
314 }
315
317 template<typename T>
318 const T* cast() const noexcept
319 {
320 return requires_allocation<typename std::decay<T>::type>::value?
321 reinterpret_cast<const T*>(storage.dynamic) :
322 reinterpret_cast<const T*>(&storage.stack);
323 }
324
326 template<typename T>
327 T* cast() noexcept
328 {
329 return requires_allocation<typename std::decay<T>::type>::value?
330 reinterpret_cast<T*>(storage.dynamic) :
331 reinterpret_cast<T*>(&storage.stack);
332 }
333
334private:
335 storage_union storage; // on offset(0) so no padding for align
336 vtable_type* vtable;
337
338 template<typename ValueType, typename T>
339 typename std::enable_if<requires_allocation<T>::value>::type
340 do_construct(ValueType&& value)
341 {
342 storage.dynamic = new T(std::forward<ValueType>(value));
343 }
344
345 template<typename ValueType, typename T>
346 typename std::enable_if<!requires_allocation<T>::value>::type
347 do_construct(ValueType&& value)
348 {
349 new (&storage.stack) T(std::forward<ValueType>(value));
350 }
351
354 template<typename ValueType>
355 void construct(ValueType&& value)
356 {
357 using T = typename std::decay<ValueType>::type;
358
359 this->vtable = vtable_for_type<T>();
360
361 do_construct<ValueType,T>(std::forward<ValueType>(value));
362 }
363};
364
365
366
367namespace detail
368{
369 template<typename ValueType>
370 inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::true_type)
371 {
372 return std::move(*p);
373 }
374
375 template<typename ValueType>
376 inline ValueType any_cast_move_if_true(typename std::remove_reference<ValueType>::type* p, std::false_type)
377 {
378 return *p;
379 }
380}
381
383template<typename ValueType>
384inline ValueType any_cast(const any& operand)
385{
386 auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand);
387 if(p == nullptr) throw bad_any_cast();
388 return *p;
389}
390
392template<typename ValueType>
393inline ValueType any_cast(any& operand)
394{
395 auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
396 if(p == nullptr) throw bad_any_cast();
397 return *p;
398}
399
409template<typename ValueType>
410inline ValueType any_cast(any&& operand)
411{
412#ifdef ANY_IMPL_ANY_CAST_MOVEABLE
413 // https://cplusplus.github.io/LWG/lwg-active.html#2509
414 using can_move = std::integral_constant<bool,
415 std::is_move_constructible<ValueType>::value
416 && !std::is_lvalue_reference<ValueType>::value>;
417#else
418 using can_move = std::false_type;
419#endif
420
421 auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand);
422 if(p == nullptr) throw bad_any_cast();
423 return detail::any_cast_move_if_true<ValueType>(p, can_move());
424}
425
428template<typename T>
429inline const T* any_cast(const any* operand) noexcept
430{
431 if(operand == nullptr || !operand->is_typed(typeid(T)))
432 return nullptr;
433 else
434 return operand->cast<T>();
435}
436
439template<typename T>
440inline T* any_cast(any* operand) noexcept
441{
442 if(operand == nullptr || !operand->is_typed(typeid(T)))
443 return nullptr;
444 else
445 return operand->cast<T>();
446}
447
448}
449
450namespace std
451{
452 inline void swap(linb::any& lhs, linb::any& rhs) noexcept
453 {
454 lhs.swap(rhs);
455 }
456}
457
458#endif
Definition any.hpp:36
void clear() noexcept
If not empty, destroys the contained object.
Definition any.hpp:115
const std::type_info & type() const noexcept
If *this has a contained object of type T, typeid(T); otherwise typeid(void).
Definition any.hpp:131
any()
Constructs an object of type any with an empty state.
Definition any.hpp:39
any(any &&rhs) noexcept
Constructs an object of type any with a state equivalent to the original state of other.
Definition any.hpp:56
any(const any &rhs)
Constructs an object of type any with an equivalent state as other.
Definition any.hpp:45
void swap(any &rhs) noexcept
Exchange the states of *this and rhs.
Definition any.hpp:137
bool empty() const noexcept
Returns true if *this has no contained object, otherwise false.
Definition any.hpp:125
friend const T * any_cast(const any *operand) noexcept
If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object contained by o...
Definition any.hpp:429
any(ValueType &&value)
Constructs an object of type any that contains an object of type T direct-initialized with std::forwa...
Definition any.hpp:77
any & operator=(ValueType &&value)
Has the same effect as any(std::forward<ValueType>(value)).swap(*this).
Definition any.hpp:106
any & operator=(any &&rhs) noexcept
Has the same effect as any(std::move(rhs)).swap(*this).
Definition any.hpp:95
~any()
Same effect as this->clear().
Definition any.hpp:67
any & operator=(const any &rhs)
Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown.
Definition any.hpp:85
Definition any.hpp:27