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