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