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/detail/sp_typeinfo.hpp>
29 
30 #include <stdexcept>
31 #include <typeinfo>
32 #include <algorithm>
33 #include <iosfwd>
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 ///////////////////////////////////////////////////////////////////////////////
43 namespace boost { namespace spirit
44 {
45     struct bad_any_cast
46       : std::bad_cast
47     {
bad_any_castboost::spirit::bad_any_cast48         bad_any_cast(boost::detail::sp_typeinfo const& src, boost::detail::sp_typeinfo const& dest)
49           : from(src.name()), to(dest.name())
50         {}
51 
whatboost::spirit::bad_any_cast52         virtual const char* what() const throw() { return "bad any cast"; }
53 
54         const char* from;
55         const char* to;
56     };
57 
58     namespace detail
59     {
60         // function pointer table
61         template <typename Char>
62         struct fxn_ptr_table
63         {
64             boost::detail::sp_typeinfo const& (*get_type)();
65             void (*static_delete)(void**);
66             void (*destruct)(void**);
67             void (*clone)(void* const*, void**);
68             void (*move)(void* const*, void**);
69             std::basic_istream<Char>& (*stream_in)(std::basic_istream<Char>&, void**);
70             std::basic_ostream<Char>& (*stream_out)(std::basic_ostream<Char>&, void* const*);
71         };
72 
73         // static functions for small value-types
74         template <typename Small>
75         struct fxns;
76 
77         template <>
78         struct fxns<mpl::true_>
79         {
80             template<typename T, typename Char>
81             struct type
82             {
get_typeboost::spirit::detail::fxns::type83                 static boost::detail::sp_typeinfo const& get_type()
84                 {
85                     return BOOST_SP_TYPEID(T);
86                 }
static_deleteboost::spirit::detail::fxns::type87                 static void static_delete(void** x)
88                 {
89                     reinterpret_cast<T*>(x)->~T();
90                 }
destructboost::spirit::detail::fxns::type91                 static void destruct(void** x)
92                 {
93                     reinterpret_cast<T*>(x)->~T();
94                 }
cloneboost::spirit::detail::fxns::type95                 static void clone(void* const* src, void** dest)
96                 {
97                     new (dest) T(*reinterpret_cast<T const*>(src));
98                 }
moveboost::spirit::detail::fxns::type99                 static void move(void* const* src, void** dest)
100                 {
101                     *reinterpret_cast<T*>(dest) =
102                         *reinterpret_cast<T const*>(src);
103                 }
104                 static std::basic_istream<Char>&
stream_inboost::spirit::detail::fxns::type105                 stream_in (std::basic_istream<Char>& i, void** obj)
106                 {
107                     i >> *reinterpret_cast<T*>(obj);
108                     return i;
109                 }
110                 static std::basic_ostream<Char>&
stream_outboost::spirit::detail::fxns::type111                 stream_out(std::basic_ostream<Char>& o, void* const* obj)
112                 {
113                     o << *reinterpret_cast<T const*>(obj);
114                     return o;
115                 }
116             };
117         };
118 
119         // static functions for big value-types (bigger than a void*)
120         template <>
121         struct fxns<mpl::false_>
122         {
123             template<typename T, typename Char>
124             struct type
125             {
get_typeboost::spirit::detail::fxns::type126                 static boost::detail::sp_typeinfo const& get_type()
127                 {
128                     return BOOST_SP_TYPEID(T);
129                 }
static_deleteboost::spirit::detail::fxns::type130                 static void static_delete(void** x)
131                 {
132                     // destruct and free memory
133                     delete (*reinterpret_cast<T**>(x));
134                 }
destructboost::spirit::detail::fxns::type135                 static void destruct(void** x)
136                 {
137                     // destruct only, we'll reuse memory
138                     (*reinterpret_cast<T**>(x))->~T();
139                 }
cloneboost::spirit::detail::fxns::type140                 static void clone(void* const* src, void** dest)
141                 {
142                     *dest = new T(**reinterpret_cast<T* const*>(src));
143                 }
moveboost::spirit::detail::fxns::type144                 static void move(void* const* src, void** dest)
145                 {
146                     **reinterpret_cast<T**>(dest) =
147                         **reinterpret_cast<T* const*>(src);
148                 }
149                 static std::basic_istream<Char>&
stream_inboost::spirit::detail::fxns::type150                 stream_in(std::basic_istream<Char>& i, void** obj)
151                 {
152                     i >> **reinterpret_cast<T**>(obj);
153                     return i;
154                 }
155                 static std::basic_ostream<Char>&
stream_outboost::spirit::detail::fxns::type156                 stream_out(std::basic_ostream<Char>& o, void* const* obj)
157                 {
158                     o << **reinterpret_cast<T* const*>(obj);
159                     return o;
160                 }
161             };
162         };
163 
164         template <typename T>
165         struct get_table
166         {
167             typedef mpl::bool_<(sizeof(T) <= sizeof(void*))> is_small;
168 
169             template <typename Char>
getboost::spirit::detail::get_table170             static fxn_ptr_table<Char>* get()
171             {
172                 static fxn_ptr_table<Char> static_table =
173                 {
174                     fxns<is_small>::template type<T, Char>::get_type,
175                     fxns<is_small>::template type<T, Char>::static_delete,
176                     fxns<is_small>::template type<T, Char>::destruct,
177                     fxns<is_small>::template type<T, Char>::clone,
178                     fxns<is_small>::template type<T, Char>::move,
179                     fxns<is_small>::template type<T, Char>::stream_in,
180                     fxns<is_small>::template type<T, Char>::stream_out
181                 };
182                 return &static_table;
183             }
184         };
185 
186         ///////////////////////////////////////////////////////////////////////
187         struct empty {};
188 
189         template <typename Char>
190         inline std::basic_istream<Char>&
operator >>(std::basic_istream<Char> & i,empty &)191         operator>> (std::basic_istream<Char>& i, empty&)
192         {
193             // If this assertion fires you tried to insert from a std istream
194             // into an empty hold_any instance. This simply can't work, because
195             // there is no way to figure out what type to extract from the
196             // stream.
197             // The only way to make this work is to assign an arbitrary
198             // value of the required type to the hold_any instance you want to
199             // stream to. This assignment has to be executed before the actual
200             // call to the operator>>().
201             BOOST_ASSERT(false &&
202                 "Tried to insert from a std istream into an empty "
203                 "hold_any instance");
204             return i;
205         }
206 
207         template <typename Char>
208         inline std::basic_ostream<Char>&
operator <<(std::basic_ostream<Char> & o,empty const &)209         operator<< (std::basic_ostream<Char>& o, empty const&)
210         {
211             return o;
212         }
213     }
214 
215     ///////////////////////////////////////////////////////////////////////////
216     template <typename Char>
217     class basic_hold_any
218     {
219     public:
220         // constructors
221         template <typename T>
basic_hold_any(T const & x)222         explicit basic_hold_any(T const& x)
223           : table(spirit::detail::get_table<T>::template get<Char>()), object(0)
224         {
225             new_object(object, x,
226                 typename spirit::detail::get_table<T>::is_small());
227         }
228 
basic_hold_any()229         basic_hold_any()
230           : table(spirit::detail::get_table<spirit::detail::empty>::template get<Char>()),
231             object(0)
232         {
233         }
234 
basic_hold_any(basic_hold_any const & x)235         basic_hold_any(basic_hold_any const& x)
236           : table(spirit::detail::get_table<spirit::detail::empty>::template get<Char>()),
237             object(0)
238         {
239             assign(x);
240         }
241 
~basic_hold_any()242         ~basic_hold_any()
243         {
244             table->static_delete(&object);
245         }
246 
247         // assignment
assign(basic_hold_any const & x)248         basic_hold_any& assign(basic_hold_any const& x)
249         {
250             if (&x != this) {
251                 // are we copying between the same type?
252                 if (table == x.table) {
253                     // if so, we can avoid reallocation
254                     table->move(&x.object, &object);
255                 }
256                 else {
257                     reset();
258                     x.table->clone(&x.object, &object);
259                     table = x.table;
260                 }
261             }
262             return *this;
263         }
264 
265         template <typename T>
assign(T const & x)266         basic_hold_any& assign(T const& x)
267         {
268             // are we copying between the same type?
269             spirit::detail::fxn_ptr_table<Char>* x_table =
270                 spirit::detail::get_table<T>::template get<Char>();
271             if (table == x_table) {
272             // if so, we can avoid deallocating and re-use memory
273                 table->destruct(&object);    // first destruct the old content
274                 if (spirit::detail::get_table<T>::is_small::value) {
275                     // create copy on-top of object pointer itself
276                     new (&object) T(x);
277                 }
278                 else {
279                     // create copy on-top of old version
280                     new (object) T(x);
281                 }
282             }
283             else {
284                 if (spirit::detail::get_table<T>::is_small::value) {
285                     // create copy on-top of object pointer itself
286                     table->destruct(&object); // first destruct the old content
287                     new (&object) T(x);
288                 }
289                 else {
290                     reset();                  // first delete the old content
291                     object = new T(x);
292                 }
293                 table = x_table;      // update table pointer
294             }
295             return *this;
296         }
297 
298         template <typename T>
new_object(void * & object,T const & x,mpl::true_)299         static void new_object(void*& object, T const& x, mpl::true_)
300         {
301             new (&object) T(x);
302         }
303 
304         template <typename T>
new_object(void * & object,T const & x,mpl::false_)305         static void new_object(void*& object, T const& x, mpl::false_)
306         {
307             object = new T(x);
308         }
309 
310         // assignment operator
311 #ifdef BOOST_HAS_RVALUE_REFS
312         template <typename T>
operator =(T && x)313         basic_hold_any& operator=(T&& x)
314         {
315             return assign(std::forward<T>(x));
316         }
317 #else
318         template <typename T>
operator =(T & x)319         basic_hold_any& operator=(T& x)
320         {
321             return assign(x);
322         }
323 
324         template <typename T>
operator =(T const & x)325         basic_hold_any& operator=(T const& x)
326         {
327             return assign(x);
328         }
329 #endif
330         // copy assignment operator
operator =(basic_hold_any const & x)331         basic_hold_any& operator=(basic_hold_any const& x)
332         {
333             return assign(x);
334         }
335 
336         // utility functions
swap(basic_hold_any & x)337         basic_hold_any& swap(basic_hold_any& x)
338         {
339             std::swap(table, x.table);
340             std::swap(object, x.object);
341             return *this;
342         }
343 
type() const344         boost::detail::sp_typeinfo const& type() const
345         {
346             return table->get_type();
347         }
348 
349         template <typename T>
cast() const350         T const& cast() const
351         {
352             if (type() != BOOST_SP_TYPEID(T))
353               throw bad_any_cast(type(), BOOST_SP_TYPEID(T));
354 
355             return spirit::detail::get_table<T>::is_small::value ?
356                 *reinterpret_cast<T const*>(&object) :
357                 *reinterpret_cast<T const*>(object);
358         }
359 
360 // implicit casting is disabled by default for compatibility with boost::any
361 #ifdef BOOST_SPIRIT_ANY_IMPLICIT_CASTING
362         // automatic casting operator
363         template <typename T>
operator T const&() const364         operator T const& () const { return cast<T>(); }
365 #endif // implicit casting
366 
empty() const367         bool empty() const
368         {
369             return table == spirit::detail::get_table<spirit::detail::empty>::template get<Char>();
370         }
371 
reset()372         void reset()
373         {
374             if (!empty())
375             {
376                 table->static_delete(&object);
377                 table = spirit::detail::get_table<spirit::detail::empty>::template get<Char>();
378                 object = 0;
379             }
380         }
381 
382     // these functions have been added in the assumption that the embedded
383     // type has a corresponding operator defined, which is completely safe
384     // because spirit::hold_any is used only in contexts where these operators
385     // do exist
386         template <typename Char_>
387         friend inline std::basic_istream<Char_>&
operator >>(std::basic_istream<Char_> & i,basic_hold_any<Char_> & obj)388         operator>> (std::basic_istream<Char_>& i, basic_hold_any<Char_>& obj)
389         {
390             return obj.table->stream_in(i, &obj.object);
391         }
392 
393         template <typename Char_>
394         friend inline std::basic_ostream<Char_>&
operator <<(std::basic_ostream<Char_> & o,basic_hold_any<Char_> const & obj)395         operator<< (std::basic_ostream<Char_>& o, basic_hold_any<Char_> const& obj)
396         {
397             return obj.table->stream_out(o, &obj.object);
398         }
399 
400 #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
401     private: // types
402         template <typename T, typename Char_>
403         friend T* any_cast(basic_hold_any<Char_> *);
404 #else
405     public: // types (public so any_cast can be non-friend)
406 #endif
407         // fields
408         spirit::detail::fxn_ptr_table<Char>* table;
409         void* object;
410     };
411 
412     // boost::any-like casting
413     template <typename T, typename Char>
any_cast(basic_hold_any<Char> * operand)414     inline T* any_cast (basic_hold_any<Char>* operand)
415     {
416         if (operand && operand->type() == BOOST_SP_TYPEID(T)) {
417             return spirit::detail::get_table<T>::is_small::value ?
418                 reinterpret_cast<T*>(&operand->object) :
419                 reinterpret_cast<T*>(operand->object);
420         }
421         return 0;
422     }
423 
424     template <typename T, typename Char>
any_cast(basic_hold_any<Char> const * operand)425     inline T const* any_cast(basic_hold_any<Char> const* operand)
426     {
427         return any_cast<T>(const_cast<basic_hold_any<Char>*>(operand));
428     }
429 
430     template <typename T, typename Char>
431     T any_cast(basic_hold_any<Char>& operand)
432     {
433         typedef BOOST_DEDUCED_TYPENAME remove_reference<T>::type nonref;
434 
435 
436         nonref* result = any_cast<nonref>(&operand);
437         if(!result)
438             boost::throw_exception(bad_any_cast(operand.type(), BOOST_SP_TYPEID(T)));
439         return *result;
440     }
441 
442     template <typename T, typename Char>
443     T const& any_cast(basic_hold_any<Char> const& operand)
444     {
445         typedef BOOST_DEDUCED_TYPENAME remove_reference<T>::type nonref;
446 
447 
448         return any_cast<nonref const&>(const_cast<basic_hold_any<Char> &>(operand));
449     }
450 
451     ///////////////////////////////////////////////////////////////////////////////
452     // backwards compatibility
453     typedef basic_hold_any<char> hold_any;
454     typedef basic_hold_any<wchar_t> whold_any;
455 
456     namespace traits
457     {
458         template <typename T>
459         struct is_hold_any : mpl::false_ {};
460 
461         template <typename Char>
462         struct is_hold_any<basic_hold_any<Char> > : mpl::true_ {};
463     }
464 
465 }}    // namespace boost::spirit
466 
467 ///////////////////////////////////////////////////////////////////////////////
468 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
469 # pragma warning(pop)
470 #endif
471 
472 #endif
473