• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ///////////////////////////////////////////////////////////////////////////////
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::core::typeinfo const& src, boost::core::typeinfo const& dest)
49           : from(src.name()), to(dest.name())
50         {}
51 
whatboost::spirit::bad_any_cast52         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             {
get_typeboost::spirit::detail::fxns::type86                 static boost::core::typeinfo const& get_type()
87                 {
88                     return BOOST_CORE_TYPEID(T);
89                 }
static_deleteboost::spirit::detail::fxns::type90                 static void static_delete(void** x)
91                 {
92                     reinterpret_cast<T*>(x)->~T();
93                 }
destructboost::spirit::detail::fxns::type94                 static void destruct(void** x)
95                 {
96                     reinterpret_cast<T*>(x)->~T();
97                 }
cloneboost::spirit::detail::fxns::type98                 static void clone(void* const* src, void** dest)
99                 {
100                     new (dest) T(*reinterpret_cast<T const*>(src));
101                 }
moveboost::spirit::detail::fxns::type102                 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>&
stream_inboost::spirit::detail::fxns::type108                 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>&
stream_outboost::spirit::detail::fxns::type114                 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             {
get_typeboost::spirit::detail::fxns::type129                 static boost::core::typeinfo const& get_type()
130                 {
131                     return BOOST_CORE_TYPEID(T);
132                 }
static_deleteboost::spirit::detail::fxns::type133                 static void static_delete(void** x)
134                 {
135                     // destruct and free memory
136                     delete (*reinterpret_cast<T**>(x));
137                 }
destructboost::spirit::detail::fxns::type138                 static void destruct(void** x)
139                 {
140                     // destruct only, we'll reuse memory
141                     (*reinterpret_cast<T**>(x))->~T();
142                 }
cloneboost::spirit::detail::fxns::type143                 static void clone(void* const* src, void** dest)
144                 {
145                     *dest = new T(**reinterpret_cast<T* const*>(src));
146                 }
moveboost::spirit::detail::fxns::type147                 static void move(void* const* src, void** dest)
148                 {
149                     **reinterpret_cast<T**>(dest) =
150                         **reinterpret_cast<T* const*>(src);
151                 }
152                 static std::basic_istream<Char>&
stream_inboost::spirit::detail::fxns::type153                 stream_in(std::basic_istream<Char>& i, void** obj)
154                 {
155                     i >> **reinterpret_cast<T**>(obj);
156                     return i;
157                 }
158                 static std::basic_ostream<Char>&
stream_outboost::spirit::detail::fxns::type159                 stream_out(std::basic_ostream<Char>& o, void* const* obj)
160                 {
161                     o << **reinterpret_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>
getboost::spirit::detail::get_table173             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>&
operator >>(std::basic_istream<Char> & i,empty &)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>&
operator <<(std::basic_ostream<Char> & o,empty const &)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>
basic_hold_any(T const & x)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 
basic_hold_any()232         basic_hold_any()
233           : table(spirit::detail::get_table<spirit::detail::empty>::template get<Char>()),
234             object(0)
235         {
236         }
237 
basic_hold_any(basic_hold_any const & x)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 
~basic_hold_any()245         ~basic_hold_any()
246         {
247             table->static_delete(&object);
248         }
249 
250         // assignment
assign(basic_hold_any const & x)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>
assign(T const & x)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>
new_object(void * & object,T const & x,mpl::true_)302         static void new_object(void*& object, T const& x, mpl::true_)
303         {
304             new (&object) T(x);
305         }
306 
307         template <typename T>
new_object(void * & object,T const & x,mpl::false_)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>
operator =(T && x)316         basic_hold_any& operator=(T&& x)
317         {
318             return assign(std::forward<T>(x));
319         }
320 #else
321         template <typename T>
operator =(T & x)322         basic_hold_any& operator=(T& x)
323         {
324             return assign(x);
325         }
326 
327         template <typename T>
operator =(T const & x)328         basic_hold_any& operator=(T const& x)
329         {
330             return assign(x);
331         }
332 #endif
333         // copy assignment operator
operator =(basic_hold_any const & x)334         basic_hold_any& operator=(basic_hold_any const& x)
335         {
336             return assign(x);
337         }
338 
339         // utility functions
swap(basic_hold_any & x)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 
type() const347         boost::core::typeinfo const& type() const
348         {
349             return table->get_type();
350         }
351 
352         template <typename T>
cast() const353         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>
operator T const&() const367         operator T const& () const { return cast<T>(); }
368 #endif // implicit casting
369 
empty() const370         bool empty() const
371         {
372             return table == spirit::detail::get_table<spirit::detail::empty>::template get<Char>();
373         }
374 
reset()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_>&
operator >>(std::basic_istream<Char_> & i,basic_hold_any<Char_> & obj)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_>&
operator <<(std::basic_ostream<Char_> & o,basic_hold_any<Char_> const & obj)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>
any_cast(basic_hold_any<Char> * operand)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>
any_cast(basic_hold_any<Char> const * operand)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>
any_cast(basic_hold_any<Char> & operand)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(bad_any_cast(operand.type(), BOOST_CORE_TYPEID(T)));
442         return *result;
443     }
444 
445     template <typename T, typename Char>
any_cast(basic_hold_any<Char> const & operand)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