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             if (spirit::detail::get_table<T>::is_small::value)
226                 new (&object) T(x);
227             else
228                 object = new T(x);
229         }
230 
basic_hold_any()231         basic_hold_any()
232           : table(spirit::detail::get_table<spirit::detail::empty>::template get<Char>()),
233             object(0)
234         {
235         }
236 
basic_hold_any(basic_hold_any const & x)237         basic_hold_any(basic_hold_any const& x)
238           : table(spirit::detail::get_table<spirit::detail::empty>::template get<Char>()),
239             object(0)
240         {
241             assign(x);
242         }
243 
~basic_hold_any()244         ~basic_hold_any()
245         {
246             table->static_delete(&object);
247         }
248 
249         // assignment
assign(basic_hold_any const & x)250         basic_hold_any& assign(basic_hold_any const& x)
251         {
252             if (&x != this) {
253                 // are we copying between the same type?
254                 if (table == x.table) {
255                     // if so, we can avoid reallocation
256                     table->move(&x.object, &object);
257                 }
258                 else {
259                     reset();
260                     x.table->clone(&x.object, &object);
261                     table = x.table;
262                 }
263             }
264             return *this;
265         }
266 
267         template <typename T>
assign(T const & x)268         basic_hold_any& assign(T const& x)
269         {
270             // are we copying between the same type?
271             spirit::detail::fxn_ptr_table<Char>* x_table =
272                 spirit::detail::get_table<T>::template get<Char>();
273             if (table == x_table) {
274             // if so, we can avoid deallocating and re-use memory
275                 table->destruct(&object);    // first destruct the old content
276                 if (spirit::detail::get_table<T>::is_small::value) {
277                     // create copy on-top of object pointer itself
278                     new (&object) T(x);
279                 }
280                 else {
281                     // create copy on-top of old version
282                     new (object) T(x);
283                 }
284             }
285             else {
286                 if (spirit::detail::get_table<T>::is_small::value) {
287                     // create copy on-top of object pointer itself
288                     table->destruct(&object); // first destruct the old content
289                     new (&object) T(x);
290                 }
291                 else {
292                     reset();                  // first delete the old content
293                     object = new T(x);
294                 }
295                 table = x_table;      // update table pointer
296             }
297             return *this;
298         }
299 
300         // assignment operator
301 #ifdef BOOST_HAS_RVALUE_REFS
302         template <typename T>
operator =(T && x)303         basic_hold_any& operator=(T&& x)
304         {
305             return assign(std::forward<T>(x));
306         }
307 #else
308         template <typename T>
operator =(T & x)309         basic_hold_any& operator=(T& x)
310         {
311             return assign(x);
312         }
313 
314         template <typename T>
operator =(T const & x)315         basic_hold_any& operator=(T const& x)
316         {
317             return assign(x);
318         }
319 #endif
320 
321         // utility functions
swap(basic_hold_any & x)322         basic_hold_any& swap(basic_hold_any& x)
323         {
324             std::swap(table, x.table);
325             std::swap(object, x.object);
326             return *this;
327         }
328 
type() const329         boost::detail::sp_typeinfo const& type() const
330         {
331             return table->get_type();
332         }
333 
334         template <typename T>
cast() const335         T const& cast() const
336         {
337             if (type() != BOOST_SP_TYPEID(T))
338               throw bad_any_cast(type(), BOOST_SP_TYPEID(T));
339 
340             return spirit::detail::get_table<T>::is_small::value ?
341                 *reinterpret_cast<T const*>(&object) :
342                 *reinterpret_cast<T const*>(object);
343         }
344 
345 // implicit casting is disabled by default for compatibility with boost::any
346 #ifdef BOOST_SPIRIT_ANY_IMPLICIT_CASTING
347         // automatic casting operator
348         template <typename T>
operator T const&() const349         operator T const& () const { return cast<T>(); }
350 #endif // implicit casting
351 
empty() const352         bool empty() const
353         {
354             return table == spirit::detail::get_table<spirit::detail::empty>::template get<Char>();
355         }
356 
reset()357         void reset()
358         {
359             if (!empty())
360             {
361                 table->static_delete(&object);
362                 table = spirit::detail::get_table<spirit::detail::empty>::template get<Char>();
363                 object = 0;
364             }
365         }
366 
367     // these functions have been added in the assumption that the embedded
368     // type has a corresponding operator defined, which is completely safe
369     // because spirit::hold_any is used only in contexts where these operators
370     // do exist
371         template <typename Char_>
372         friend inline std::basic_istream<Char_>&
operator >>(std::basic_istream<Char_> & i,basic_hold_any<Char_> & obj)373         operator>> (std::basic_istream<Char_>& i, basic_hold_any<Char_>& obj)
374         {
375             return obj.table->stream_in(i, &obj.object);
376         }
377 
378         template <typename Char_>
379         friend inline std::basic_ostream<Char_>&
operator <<(std::basic_ostream<Char_> & o,basic_hold_any<Char_> const & obj)380         operator<< (std::basic_ostream<Char_>& o, basic_hold_any<Char_> const& obj)
381         {
382             return obj.table->stream_out(o, &obj.object);
383         }
384 
385 #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
386     private: // types
387         template <typename T, typename Char_>
388         friend T* any_cast(basic_hold_any<Char_> *);
389 #else
390     public: // types (public so any_cast can be non-friend)
391 #endif
392         // fields
393         spirit::detail::fxn_ptr_table<Char>* table;
394         void* object;
395     };
396 
397     // boost::any-like casting
398     template <typename T, typename Char>
any_cast(basic_hold_any<Char> * operand)399     inline T* any_cast (basic_hold_any<Char>* operand)
400     {
401         if (operand && operand->type() == BOOST_SP_TYPEID(T)) {
402             return spirit::detail::get_table<T>::is_small::value ?
403                 reinterpret_cast<T*>(&operand->object) :
404                 reinterpret_cast<T*>(operand->object);
405         }
406         return 0;
407     }
408 
409     template <typename T, typename Char>
any_cast(basic_hold_any<Char> const * operand)410     inline T const* any_cast(basic_hold_any<Char> const* operand)
411     {
412         return any_cast<T>(const_cast<basic_hold_any<Char>*>(operand));
413     }
414 
415     template <typename T, typename Char>
any_cast(basic_hold_any<Char> & operand)416     T any_cast(basic_hold_any<Char>& operand)
417     {
418         typedef BOOST_DEDUCED_TYPENAME remove_reference<T>::type nonref;
419 
420 
421         nonref* result = any_cast<nonref>(&operand);
422         if(!result)
423             boost::throw_exception(bad_any_cast(operand.type(), BOOST_SP_TYPEID(T)));
424         return *result;
425     }
426 
427     template <typename T, typename Char>
any_cast(basic_hold_any<Char> const & operand)428     T const& any_cast(basic_hold_any<Char> const& operand)
429     {
430         typedef BOOST_DEDUCED_TYPENAME remove_reference<T>::type nonref;
431 
432 
433         return any_cast<nonref const&>(const_cast<basic_hold_any<Char> &>(operand));
434     }
435 
436     ///////////////////////////////////////////////////////////////////////////////
437     // backwards compatibility
438     typedef basic_hold_any<char> hold_any;
439     typedef basic_hold_any<wchar_t> whold_any;
440 
441     namespace traits
442     {
443         template <typename T>
444         struct is_hold_any : mpl::false_ {};
445 
446         template <typename Char>
447         struct is_hold_any<basic_hold_any<Char> > : mpl::true_ {};
448     }
449 
450 }}    // namespace boost::spirit
451 
452 ///////////////////////////////////////////////////////////////////////////////
453 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
454 # pragma warning(pop)
455 #endif
456 
457 #endif
458