| 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 | { |
| 48 | bad_any_cast(boost::core::typeinfo const& src, boost::core::typeinfo const& dest) |
| 49 | : from(src.name()), to(dest.name()) |
| 50 | {} |
| 51 | |
| 52 | 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 | { |
| 86 | static boost::core::typeinfo const& get_type() |
| 87 | { |
| 88 | return BOOST_CORE_TYPEID(T); |
| 89 | } |
| 90 | static void static_delete(void** x) |
| 91 | { |
| 92 | reinterpret_cast<T*>(x)->~T(); |
| 93 | } |
| 94 | static void destruct(void** x) |
| 95 | { |
| 96 | reinterpret_cast<T*>(x)->~T(); |
| 97 | } |
| 98 | static void clone(void* const* src, void** dest) |
| 99 | { |
| 100 | new (dest) T(*reinterpret_cast<T const*>(src)); |
| 101 | } |
| 102 | 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>& |
| 108 | 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>& |
| 114 | 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 | { |
| 129 | static boost::core::typeinfo const& get_type() |
| 130 | { |
| 131 | return BOOST_CORE_TYPEID(T); |
| 132 | } |
| 133 | static void static_delete(void** x) |
| 134 | { |
| 135 | // destruct and free memory |
| 136 | delete static_cast<T*>(*x); |
| 137 | } |
| 138 | static void destruct(void** x) |
| 139 | { |
| 140 | // destruct only, we'll reuse memory |
| 141 | static_cast<T*>(*x)->~T(); |
| 142 | } |
| 143 | static void clone(void* const* src, void** dest) |
| 144 | { |
| 145 | *dest = new T(*static_cast<T const*>(*src)); |
| 146 | } |
| 147 | static void move(void* const* src, void** dest) |
| 148 | { |
| 149 | *static_cast<T*>(*dest) = |
| 150 | *static_cast<T const*>(*src); |
| 151 | } |
| 152 | static std::basic_istream<Char>& |
| 153 | stream_in(std::basic_istream<Char>& i, void** obj) |
| 154 | { |
| 155 | i >> *static_cast<T*>(*obj); |
| 156 | return i; |
| 157 | } |
| 158 | static std::basic_ostream<Char>& |
| 159 | stream_out(std::basic_ostream<Char>& o, void* const* obj) |
| 160 | { |
| 161 | o << *static_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> |
| 173 | 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>& |
| 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>& |
| 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> |
| 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 | |
| 232 | basic_hold_any() |
| 233 | : table(spirit::detail::get_table<spirit::detail::empty>::template get<Char>()), |
| 234 | object(0) |
| 235 | { |
| 236 | } |
| 237 | |
| 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 | |
| 245 | ~basic_hold_any() |
| 246 | { |
| 247 | table->static_delete(&object); |
| 248 | } |
| 249 | |
| 250 | // assignment |
| 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> |
| 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> |
| 302 | static void new_object(void*& object, T const& x, mpl::true_) |
| 303 | { |
| 304 | new (&object) T(x); |
| 305 | } |
| 306 | |
| 307 | template <typename T> |
| 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> |
| 316 | basic_hold_any& operator=(T&& x) |
| 317 | { |
| 318 | return assign(std::forward<T>(x)); |
| 319 | } |
| 320 | #else |
| 321 | template <typename T> |
| 322 | basic_hold_any& operator=(T& x) |
| 323 | { |
| 324 | return assign(x); |
| 325 | } |
| 326 | |
| 327 | template <typename T> |
| 328 | basic_hold_any& operator=(T const& x) |
| 329 | { |
| 330 | return assign(x); |
| 331 | } |
| 332 | #endif |
| 333 | // copy assignment operator |
| 334 | basic_hold_any& operator=(basic_hold_any const& x) |
| 335 | { |
| 336 | return assign(x); |
| 337 | } |
| 338 | |
| 339 | // utility functions |
| 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 | |
| 347 | boost::core::typeinfo const& type() const |
| 348 | { |
| 349 | return table->get_type(); |
| 350 | } |
| 351 | |
| 352 | template <typename T> |
| 353 | 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> |
| 367 | operator T const& () const { return cast<T>(); } |
| 368 | #endif // implicit casting |
| 369 | |
| 370 | bool empty() const |
| 371 | { |
| 372 | return table == spirit::detail::get_table<spirit::detail::empty>::template get<Char>(); |
| 373 | } |
| 374 | |
| 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_>& |
| 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_>& |
| 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> |
| 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> |
| 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> |
| 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(e: bad_any_cast(operand.type(), BOOST_CORE_TYPEID(T))); |
| 442 | return *result; |
| 443 | } |
| 444 | |
| 445 | template <typename T, typename Char> |
| 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 | |