/*============================================================================= Copyright (c) 2007-2010 Hartmut Kaiser Copyright (c) Christopher Diggins 2005 Copyright (c) Pablo Aguilar 2005 Copyright (c) Kevlin Henney 2001 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) The class boost::spirit::hold_any is built based on the any class published here: http://www.codeproject.com/cpp/dynamic_typing.asp. It adds support for std streaming operator<<() and operator>>(). ==============================================================================*/ #if !defined(BOOST_SPIRIT_HOLD_ANY_MAY_02_2007_0857AM) #define BOOST_SPIRIT_HOLD_ANY_MAY_02_2007_0857AM #if defined(_MSC_VER) #pragma once #endif #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////// #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) # pragma warning(push) # pragma warning(disable: 4100) // 'x': unreferenced formal parameter # pragma warning(disable: 4127) // conditional expression is constant #endif /////////////////////////////////////////////////////////////////////////////// namespace boost { namespace spirit { struct bad_any_cast : std::bad_cast { bad_any_cast(boost::detail::sp_typeinfo const& src, boost::detail::sp_typeinfo const& dest) : from(src.name()), to(dest.name()) {} virtual const char* what() const throw() { return "bad any cast"; } const char* from; const char* to; }; namespace detail { // function pointer table struct fxn_ptr_table { boost::detail::sp_typeinfo const& (*get_type)(); void (*static_delete)(void**); void (*destruct)(void**); void (*clone)(void* const*, void**); void (*move)(void* const*, void**); std::istream& (*stream_in)(std::istream&, void**); std::ostream& (*stream_out)(std::ostream&, void* const*); }; // static functions for small value-types template struct fxns; template<> struct fxns { template struct type { static boost::detail::sp_typeinfo const& get_type() { return BOOST_SP_TYPEID(T); } static void static_delete(void** x) { reinterpret_cast(x)->~T(); } static void destruct(void** x) { reinterpret_cast(x)->~T(); } static void clone(void* const* src, void** dest) { new (dest) T(*reinterpret_cast(src)); } static void move(void* const* src, void** dest) { reinterpret_cast(dest)->~T(); *reinterpret_cast(dest) = *reinterpret_cast(src); } static std::istream& stream_in (std::istream& i, void** obj) { i >> *reinterpret_cast(obj); return i; } static std::ostream& stream_out(std::ostream& o, void* const* obj) { o << *reinterpret_cast(obj); return o; } }; }; // static functions for big value-types (bigger than a void*) template<> struct fxns { template struct type { static boost::detail::sp_typeinfo const& get_type() { return BOOST_SP_TYPEID(T); } static void static_delete(void** x) { // destruct and free memory delete (*reinterpret_cast(x)); } static void destruct(void** x) { // destruct only, we'll reuse memory (*reinterpret_cast(x))->~T(); } static void clone(void* const* src, void** dest) { *dest = new T(**reinterpret_cast(src)); } static void move(void* const* src, void** dest) { (*reinterpret_cast(dest))->~T(); **reinterpret_cast(dest) = **reinterpret_cast(src); } static std::istream& stream_in(std::istream& i, void** obj) { i >> **reinterpret_cast(obj); return i; } static std::ostream& stream_out(std::ostream& o, void* const* obj) { o << **reinterpret_cast(obj); return o; } }; }; template struct get_table { typedef mpl::bool_<(sizeof(T) <= sizeof(void*))> is_small; static fxn_ptr_table* get() { static fxn_ptr_table static_table = { fxns::template type::get_type, fxns::template type::static_delete, fxns::template type::destruct, fxns::template type::clone, fxns::template type::move, fxns::template type::stream_in, fxns::template type::stream_out }; return &static_table; } }; /////////////////////////////////////////////////////////////////////// struct empty {}; inline std::istream& operator>> (std::istream& i, empty&) { // If this assertion fires you tried to insert from a std istream // into an empty hold_any instance. This simply can't work, because // there is no way to figure out what type to extract from the // stream. // The only way to make this work is to assign an arbitrary // value of the required type to the hold_any instance you want to // stream to. This assignment has to be executed before the actual // call to the operator>>(). BOOST_ASSERT(false); return i; } inline std::ostream& operator<< (std::ostream& o, empty const&) { return o; } } /////////////////////////////////////////////////////////////////////////// class hold_any { public: // constructors template hold_any(T const& x) : table(spirit::detail::get_table::get()), object(0) { if (spirit::detail::get_table::is_small::value) new (&object) T(x); else object = new T(x); } hold_any() : table(spirit::detail::get_table::get()), object(0) { } hold_any(hold_any const& x) : table(spirit::detail::get_table::get()), object(0) { assign(x); } ~hold_any() { table->static_delete(&object); } // assignment hold_any& assign(hold_any const& x) { if (&x != this) { // are we copying between the same type? if (table == x.table) { // if so, we can avoid reallocation table->move(&x.object, &object); } else { reset(); x.table->clone(&x.object, &object); table = x.table; } } return *this; } template hold_any& assign(T const& x) { // are we copying between the same type? spirit::detail::fxn_ptr_table* x_table = spirit::detail::get_table::get(); if (table == x_table) { // if so, we can avoid deallocating and re-use memory table->destruct(&object); // first destruct the old content if (spirit::detail::get_table::is_small::value) { // create copy on-top of object pointer itself new (&object) T(x); } else { // create copy on-top of old version new (object) T(x); } } else { if (spirit::detail::get_table::is_small::value) { // create copy on-top of object pointer itself table->destruct(&object); // first destruct the old content new (&object) T(x); } else { reset(); // first delete the old content object = new T(x); } table = x_table; // update table pointer } return *this; } // assignment operator template hold_any& operator=(T const& x) { return assign(x); } // utility functions hold_any& swap(hold_any& x) { std::swap(table, x.table); std::swap(object, x.object); return *this; } boost::detail::sp_typeinfo const& type() const { return table->get_type(); } template T const& cast() const { if (type() != BOOST_SP_TYPEID(T)) throw bad_any_cast(type(), BOOST_SP_TYPEID(T)); return spirit::detail::get_table::is_small::value ? *reinterpret_cast(&object) : *reinterpret_cast(object); } // implicit casting is disabled by default for compatibility with boost::any #ifdef BOOST_SPIRIT_ANY_IMPLICIT_CASTING // automatic casting operator template operator T const& () const { return cast(); } #endif // implicit casting bool empty() const { return table == spirit::detail::get_table::get(); } void reset() { if (!empty()) { table->static_delete(&object); table = spirit::detail::get_table::get(); object = 0; } } // these functions have been added in the assumption that the embedded // type has a corresponding operator defined, which is completely safe // because spirit::hold_any is used only in contexts where these operators // do exist friend std::istream& operator>> (std::istream& i, hold_any& obj) { return obj.table->stream_in(i, &obj.object); } friend std::ostream& operator<< (std::ostream& o, hold_any const& obj) { return obj.table->stream_out(o, &obj.object); } #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS private: // types template friend T* any_cast(hold_any *); #else public: // types (public so any_cast can be non-friend) #endif // fields spirit::detail::fxn_ptr_table* table; void* object; }; // boost::any-like casting template inline T* any_cast (hold_any* operand) { if (operand && operand->type() == BOOST_SP_TYPEID(T)) { return spirit::detail::get_table::is_small::value ? reinterpret_cast(&operand->object) : reinterpret_cast(operand->object); } return 0; } template inline T const* any_cast(hold_any const* operand) { return any_cast(const_cast(operand)); } template T any_cast(hold_any& operand) { typedef BOOST_DEDUCED_TYPENAME remove_reference::type nonref; #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION // If 'nonref' is still reference type, it means the user has not // specialized 'remove_reference'. // Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro // to generate specialization of remove_reference for your class // See type traits library documentation for details BOOST_STATIC_ASSERT(!is_reference::value); #endif nonref* result = any_cast(&operand); if(!result) boost::throw_exception(bad_any_cast(operand.type(), BOOST_SP_TYPEID(T))); return *result; } template T const& any_cast(hold_any const& operand) { typedef BOOST_DEDUCED_TYPENAME remove_reference::type nonref; #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION // The comment in the above version of 'any_cast' explains when this // assert is fired and what to do. BOOST_STATIC_ASSERT(!is_reference::value); #endif return any_cast(const_cast(operand)); } /////////////////////////////////////////////////////////////////////////////// }} // namespace boost::spirit /////////////////////////////////////////////////////////////////////////////// #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) # pragma warning(pop) #endif #endif