1/*=============================================================================
2 Copyright (c) 2007-2011 Hartmut Kaiser
3 Copyright (c) Christopher Diggins 2005
4 Copyright (c) Pablo Aguilar 2005
5 Copyright (c) Kevlin Henney 2001
6
7 Distributed under the Boost Software License, Version 1.0. (See accompanying
8 file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9
10 The class boost::spirit::hold_any is built based on the any class
11 published here: http://www.codeproject.com/cpp/dynamic_typing.asp. It adds
12 support for std streaming operator<<() and operator>>().
13==============================================================================*/
14#if !defined(BOOST_SPIRIT_HOLD_ANY_MAY_02_2007_0857AM)
15#define BOOST_SPIRIT_HOLD_ANY_MAY_02_2007_0857AM
16
17#if defined(_MSC_VER)
18#pragma once
19#endif
20
21#include <boost/config.hpp>
22#include <boost/type_traits/remove_reference.hpp>
23#include <boost/type_traits/is_reference.hpp>
24#include <boost/throw_exception.hpp>
25#include <boost/static_assert.hpp>
26#include <boost/mpl/bool.hpp>
27#include <boost/assert.hpp>
28#include <boost/core/typeinfo.hpp>
29
30#include <algorithm>
31#include <iosfwd>
32#include <stdexcept>
33#include <typeinfo>
34
35///////////////////////////////////////////////////////////////////////////////
36#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
37# pragma warning(push)
38# pragma warning(disable: 4100) // 'x': unreferenced formal parameter
39# pragma warning(disable: 4127) // conditional expression is constant
40#endif
41
42///////////////////////////////////////////////////////////////////////////////
43namespace boost { namespace spirit
44{
45 struct bad_any_cast
46 : std::bad_cast
47 {
48 bad_any_cast(boost::core::typeinfo const& src, boost::core::typeinfo const& dest)
49 : from(src.name()), to(dest.name())
50 {}
51
52 const char* what() const BOOST_NOEXCEPT_OR_NOTHROW BOOST_OVERRIDE
53 {
54 return "bad any cast";
55 }
56
57 const char* from;
58 const char* to;
59 };
60
61 namespace detail
62 {
63 // function pointer table
64 template <typename Char>
65 struct fxn_ptr_table
66 {
67 boost::core::typeinfo const& (*get_type)();
68 void (*static_delete)(void**);
69 void (*destruct)(void**);
70 void (*clone)(void* const*, void**);
71 void (*move)(void* const*, void**);
72 std::basic_istream<Char>& (*stream_in)(std::basic_istream<Char>&, void**);
73 std::basic_ostream<Char>& (*stream_out)(std::basic_ostream<Char>&, void* const*);
74 };
75
76 // static functions for small value-types
77 template <typename Small>
78 struct fxns;
79
80 template <>
81 struct fxns<mpl::true_>
82 {
83 template<typename T, typename Char>
84 struct type
85 {
86 static boost::core::typeinfo const& get_type()
87 {
88 return BOOST_CORE_TYPEID(T);
89 }
90 static void static_delete(void** x)
91 {
92 reinterpret_cast<T*>(x)->~T();
93 }
94 static void destruct(void** x)
95 {
96 reinterpret_cast<T*>(x)->~T();
97 }
98 static void clone(void* const* src, void** dest)
99 {
100 new (dest) T(*reinterpret_cast<T const*>(src));
101 }
102 static void move(void* const* src, void** dest)
103 {
104 *reinterpret_cast<T*>(dest) =
105 *reinterpret_cast<T const*>(src);
106 }
107 static std::basic_istream<Char>&
108 stream_in (std::basic_istream<Char>& i, void** obj)
109 {
110 i >> *reinterpret_cast<T*>(obj);
111 return i;
112 }
113 static std::basic_ostream<Char>&
114 stream_out(std::basic_ostream<Char>& o, void* const* obj)
115 {
116 o << *reinterpret_cast<T const*>(obj);
117 return o;
118 }
119 };
120 };
121
122 // static functions for big value-types (bigger than a void*)
123 template <>
124 struct fxns<mpl::false_>
125 {
126 template<typename T, typename Char>
127 struct type
128 {
129 static boost::core::typeinfo const& get_type()
130 {
131 return BOOST_CORE_TYPEID(T);
132 }
133 static void static_delete(void** x)
134 {
135 // destruct and free memory
136 delete static_cast<T*>(*x);
137 }
138 static void destruct(void** x)
139 {
140 // destruct only, we'll reuse memory
141 static_cast<T*>(*x)->~T();
142 }
143 static void clone(void* const* src, void** dest)
144 {
145 *dest = new T(*static_cast<T const*>(*src));
146 }
147 static void move(void* const* src, void** dest)
148 {
149 *static_cast<T*>(*dest) =
150 *static_cast<T const*>(*src);
151 }
152 static std::basic_istream<Char>&
153 stream_in(std::basic_istream<Char>& i, void** obj)
154 {
155 i >> *static_cast<T*>(*obj);
156 return i;
157 }
158 static std::basic_ostream<Char>&
159 stream_out(std::basic_ostream<Char>& o, void* const* obj)
160 {
161 o << *static_cast<T const*>(*obj);
162 return o;
163 }
164 };
165 };
166
167 template <typename T>
168 struct get_table
169 {
170 typedef mpl::bool_<(sizeof(T) <= sizeof(void*))> is_small;
171
172 template <typename Char>
173 static fxn_ptr_table<Char>* get()
174 {
175 static fxn_ptr_table<Char> static_table =
176 {
177 fxns<is_small>::template type<T, Char>::get_type,
178 fxns<is_small>::template type<T, Char>::static_delete,
179 fxns<is_small>::template type<T, Char>::destruct,
180 fxns<is_small>::template type<T, Char>::clone,
181 fxns<is_small>::template type<T, Char>::move,
182 fxns<is_small>::template type<T, Char>::stream_in,
183 fxns<is_small>::template type<T, Char>::stream_out
184 };
185 return &static_table;
186 }
187 };
188
189 ///////////////////////////////////////////////////////////////////////
190 struct empty {};
191
192 template <typename Char>
193 inline std::basic_istream<Char>&
194 operator>> (std::basic_istream<Char>& i, empty&)
195 {
196 // If this assertion fires you tried to insert from a std istream
197 // into an empty hold_any instance. This simply can't work, because
198 // there is no way to figure out what type to extract from the
199 // stream.
200 // The only way to make this work is to assign an arbitrary
201 // value of the required type to the hold_any instance you want to
202 // stream to. This assignment has to be executed before the actual
203 // call to the operator>>().
204 BOOST_ASSERT(false &&
205 "Tried to insert from a std istream into an empty "
206 "hold_any instance");
207 return i;
208 }
209
210 template <typename Char>
211 inline std::basic_ostream<Char>&
212 operator<< (std::basic_ostream<Char>& o, empty const&)
213 {
214 return o;
215 }
216 }
217
218 ///////////////////////////////////////////////////////////////////////////
219 template <typename Char>
220 class basic_hold_any
221 {
222 public:
223 // constructors
224 template <typename T>
225 explicit basic_hold_any(T const& x)
226 : table(spirit::detail::get_table<T>::template get<Char>()), object(0)
227 {
228 new_object(object, x,
229 typename spirit::detail::get_table<T>::is_small());
230 }
231
232 basic_hold_any()
233 : table(spirit::detail::get_table<spirit::detail::empty>::template get<Char>()),
234 object(0)
235 {
236 }
237
238 basic_hold_any(basic_hold_any const& x)
239 : table(spirit::detail::get_table<spirit::detail::empty>::template get<Char>()),
240 object(0)
241 {
242 assign(x);
243 }
244
245 ~basic_hold_any()
246 {
247 table->static_delete(&object);
248 }
249
250 // assignment
251 basic_hold_any& assign(basic_hold_any const& x)
252 {
253 if (&x != this) {
254 // are we copying between the same type?
255 if (table == x.table) {
256 // if so, we can avoid reallocation
257 table->move(&x.object, &object);
258 }
259 else {
260 reset();
261 x.table->clone(&x.object, &object);
262 table = x.table;
263 }
264 }
265 return *this;
266 }
267
268 template <typename T>
269 basic_hold_any& assign(T const& x)
270 {
271 // are we copying between the same type?
272 spirit::detail::fxn_ptr_table<Char>* x_table =
273 spirit::detail::get_table<T>::template get<Char>();
274 if (table == x_table) {
275 // if so, we can avoid deallocating and re-use memory
276 table->destruct(&object); // first destruct the old content
277 if (spirit::detail::get_table<T>::is_small::value) {
278 // create copy on-top of object pointer itself
279 new (&object) T(x);
280 }
281 else {
282 // create copy on-top of old version
283 new (object) T(x);
284 }
285 }
286 else {
287 if (spirit::detail::get_table<T>::is_small::value) {
288 // create copy on-top of object pointer itself
289 table->destruct(&object); // first destruct the old content
290 new (&object) T(x);
291 }
292 else {
293 reset(); // first delete the old content
294 object = new T(x);
295 }
296 table = x_table; // update table pointer
297 }
298 return *this;
299 }
300
301 template <typename T>
302 static void new_object(void*& object, T const& x, mpl::true_)
303 {
304 new (&object) T(x);
305 }
306
307 template <typename T>
308 static void new_object(void*& object, T const& x, mpl::false_)
309 {
310 object = new T(x);
311 }
312
313 // assignment operator
314#ifdef BOOST_HAS_RVALUE_REFS
315 template <typename T>
316 basic_hold_any& operator=(T&& x)
317 {
318 return assign(std::forward<T>(x));
319 }
320#else
321 template <typename T>
322 basic_hold_any& operator=(T& x)
323 {
324 return assign(x);
325 }
326
327 template <typename T>
328 basic_hold_any& operator=(T const& x)
329 {
330 return assign(x);
331 }
332#endif
333 // copy assignment operator
334 basic_hold_any& operator=(basic_hold_any const& x)
335 {
336 return assign(x);
337 }
338
339 // utility functions
340 basic_hold_any& swap(basic_hold_any& x)
341 {
342 std::swap(table, x.table);
343 std::swap(object, x.object);
344 return *this;
345 }
346
347 boost::core::typeinfo const& type() const
348 {
349 return table->get_type();
350 }
351
352 template <typename T>
353 T const& cast() const
354 {
355 if (type() != BOOST_CORE_TYPEID(T))
356 throw bad_any_cast(type(), BOOST_CORE_TYPEID(T));
357
358 return spirit::detail::get_table<T>::is_small::value ?
359 *reinterpret_cast<T const*>(&object) :
360 *reinterpret_cast<T const*>(object);
361 }
362
363// implicit casting is disabled by default for compatibility with boost::any
364#ifdef BOOST_SPIRIT_ANY_IMPLICIT_CASTING
365 // automatic casting operator
366 template <typename T>
367 operator T const& () const { return cast<T>(); }
368#endif // implicit casting
369
370 bool empty() const
371 {
372 return table == spirit::detail::get_table<spirit::detail::empty>::template get<Char>();
373 }
374
375 void reset()
376 {
377 if (!empty())
378 {
379 table->static_delete(&object);
380 table = spirit::detail::get_table<spirit::detail::empty>::template get<Char>();
381 object = 0;
382 }
383 }
384
385 // these functions have been added in the assumption that the embedded
386 // type has a corresponding operator defined, which is completely safe
387 // because spirit::hold_any is used only in contexts where these operators
388 // do exist
389 template <typename Char_>
390 friend inline std::basic_istream<Char_>&
391 operator>> (std::basic_istream<Char_>& i, basic_hold_any<Char_>& obj)
392 {
393 return obj.table->stream_in(i, &obj.object);
394 }
395
396 template <typename Char_>
397 friend inline std::basic_ostream<Char_>&
398 operator<< (std::basic_ostream<Char_>& o, basic_hold_any<Char_> const& obj)
399 {
400 return obj.table->stream_out(o, &obj.object);
401 }
402
403#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
404 private: // types
405 template <typename T, typename Char_>
406 friend T* any_cast(basic_hold_any<Char_> *);
407#else
408 public: // types (public so any_cast can be non-friend)
409#endif
410 // fields
411 spirit::detail::fxn_ptr_table<Char>* table;
412 void* object;
413 };
414
415 // boost::any-like casting
416 template <typename T, typename Char>
417 inline T* any_cast (basic_hold_any<Char>* operand)
418 {
419 if (operand && operand->type() == BOOST_CORE_TYPEID(T)) {
420 return spirit::detail::get_table<T>::is_small::value ?
421 reinterpret_cast<T*>(&operand->object) :
422 reinterpret_cast<T*>(operand->object);
423 }
424 return 0;
425 }
426
427 template <typename T, typename Char>
428 inline T const* any_cast(basic_hold_any<Char> const* operand)
429 {
430 return any_cast<T>(const_cast<basic_hold_any<Char>*>(operand));
431 }
432
433 template <typename T, typename Char>
434 T any_cast(basic_hold_any<Char>& operand)
435 {
436 typedef BOOST_DEDUCED_TYPENAME remove_reference<T>::type nonref;
437
438
439 nonref* result = any_cast<nonref>(&operand);
440 if(!result)
441 boost::throw_exception(e: bad_any_cast(operand.type(), BOOST_CORE_TYPEID(T)));
442 return *result;
443 }
444
445 template <typename T, typename Char>
446 T const& any_cast(basic_hold_any<Char> const& operand)
447 {
448 typedef BOOST_DEDUCED_TYPENAME remove_reference<T>::type nonref;
449
450
451 return any_cast<nonref const&>(const_cast<basic_hold_any<Char> &>(operand));
452 }
453
454 ///////////////////////////////////////////////////////////////////////////////
455 // backwards compatibility
456 typedef basic_hold_any<char> hold_any;
457 typedef basic_hold_any<wchar_t> whold_any;
458
459 namespace traits
460 {
461 template <typename T>
462 struct is_hold_any : mpl::false_ {};
463
464 template <typename Char>
465 struct is_hold_any<basic_hold_any<Char> > : mpl::true_ {};
466 }
467
468}} // namespace boost::spirit
469
470///////////////////////////////////////////////////////////////////////////////
471#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
472# pragma warning(pop)
473#endif
474
475#endif
476