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