/*============================================================================= | |
Copyright (c) 2007-2011 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 <boost/config.hpp> | |
#include <boost/type_traits/remove_reference.hpp> | |
#include <boost/type_traits/is_reference.hpp> | |
#include <boost/throw_exception.hpp> | |
#include <boost/static_assert.hpp> | |
#include <boost/mpl/bool.hpp> | |
#include <boost/assert.hpp> | |
#include <boost/detail/sp_typeinfo.hpp> | |
#include <stdexcept> | |
#include <typeinfo> | |
#include <algorithm> | |
#include <iosfwd> | |
/////////////////////////////////////////////////////////////////////////////// | |
#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<typename Small> | |
struct fxns; | |
template<> | |
struct fxns<mpl::true_> | |
{ | |
template<typename T> | |
struct type | |
{ | |
static boost::detail::sp_typeinfo const& get_type() | |
{ | |
return BOOST_SP_TYPEID(T); | |
} | |
static void static_delete(void** x) | |
{ | |
reinterpret_cast<T*>(x)->~T(); | |
} | |
static void destruct(void** x) | |
{ | |
reinterpret_cast<T*>(x)->~T(); | |
} | |
static void clone(void* const* src, void** dest) | |
{ | |
new (dest) T(*reinterpret_cast<T const*>(src)); | |
} | |
static void move(void* const* src, void** dest) | |
{ | |
reinterpret_cast<T*>(dest)->~T(); | |
*reinterpret_cast<T*>(dest) = | |
*reinterpret_cast<T const*>(src); | |
} | |
static std::istream& stream_in (std::istream& i, void** obj) | |
{ | |
i >> *reinterpret_cast<T*>(obj); | |
return i; | |
} | |
static std::ostream& stream_out(std::ostream& o, void* const* obj) | |
{ | |
o << *reinterpret_cast<T const*>(obj); | |
return o; | |
} | |
}; | |
}; | |
// static functions for big value-types (bigger than a void*) | |
template<> | |
struct fxns<mpl::false_> | |
{ | |
template<typename T> | |
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<T**>(x)); | |
} | |
static void destruct(void** x) | |
{ | |
// destruct only, we'll reuse memory | |
(*reinterpret_cast<T**>(x))->~T(); | |
} | |
static void clone(void* const* src, void** dest) | |
{ | |
*dest = new T(**reinterpret_cast<T* const*>(src)); | |
} | |
static void move(void* const* src, void** dest) | |
{ | |
(*reinterpret_cast<T**>(dest))->~T(); | |
**reinterpret_cast<T**>(dest) = | |
**reinterpret_cast<T* const*>(src); | |
} | |
static std::istream& stream_in(std::istream& i, void** obj) | |
{ | |
i >> **reinterpret_cast<T**>(obj); | |
return i; | |
} | |
static std::ostream& stream_out(std::ostream& o, void* const* obj) | |
{ | |
o << **reinterpret_cast<T* const*>(obj); | |
return o; | |
} | |
}; | |
}; | |
template<typename T> | |
struct get_table | |
{ | |
typedef mpl::bool_<(sizeof(T) <= sizeof(void*))> is_small; | |
static fxn_ptr_table* get() | |
{ | |
static fxn_ptr_table static_table = | |
{ | |
fxns<is_small>::template type<T>::get_type, | |
fxns<is_small>::template type<T>::static_delete, | |
fxns<is_small>::template type<T>::destruct, | |
fxns<is_small>::template type<T>::clone, | |
fxns<is_small>::template type<T>::move, | |
fxns<is_small>::template type<T>::stream_in, | |
fxns<is_small>::template type<T>::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 && | |
"Tried to insert from a std istream into an empty " | |
"hold_any instance"); | |
return i; | |
} | |
inline std::ostream& | |
operator<< (std::ostream& o, empty const&) | |
{ | |
return o; | |
} | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
class hold_any | |
{ | |
public: | |
// constructors | |
template <typename T> | |
explicit hold_any(T const& x) | |
: table(spirit::detail::get_table<T>::get()), object(0) | |
{ | |
if (spirit::detail::get_table<T>::is_small::value) | |
new (&object) T(x); | |
else | |
object = new T(x); | |
} | |
hold_any() | |
: table(spirit::detail::get_table<spirit::detail::empty>::get()), | |
object(0) | |
{ | |
} | |
hold_any(hold_any const& x) | |
: table(spirit::detail::get_table<spirit::detail::empty>::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 <typename T> | |
hold_any& assign(T const& x) | |
{ | |
// are we copying between the same type? | |
spirit::detail::fxn_ptr_table* x_table = | |
spirit::detail::get_table<T>::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<T>::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<T>::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 <typename T> | |
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 <typename T> | |
T const& cast() const | |
{ | |
if (type() != BOOST_SP_TYPEID(T)) | |
throw bad_any_cast(type(), BOOST_SP_TYPEID(T)); | |
return spirit::detail::get_table<T>::is_small::value ? | |
*reinterpret_cast<T const*>(&object) : | |
*reinterpret_cast<T const*>(object); | |
} | |
// implicit casting is disabled by default for compatibility with boost::any | |
#ifdef BOOST_SPIRIT_ANY_IMPLICIT_CASTING | |
// automatic casting operator | |
template <typename T> | |
operator T const& () const { return cast<T>(); } | |
#endif // implicit casting | |
bool empty() const | |
{ | |
return table == spirit::detail::get_table<spirit::detail::empty>::get(); | |
} | |
void reset() | |
{ | |
if (!empty()) | |
{ | |
table->static_delete(&object); | |
table = spirit::detail::get_table<spirit::detail::empty>::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<typename T> | |
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 <typename T> | |
inline T* any_cast (hold_any* operand) | |
{ | |
if (operand && operand->type() == BOOST_SP_TYPEID(T)) { | |
return spirit::detail::get_table<T>::is_small::value ? | |
reinterpret_cast<T*>(&operand->object) : | |
reinterpret_cast<T*>(operand->object); | |
} | |
return 0; | |
} | |
template <typename T> | |
inline T const* any_cast(hold_any const* operand) | |
{ | |
return any_cast<T>(const_cast<hold_any*>(operand)); | |
} | |
template <typename T> | |
T any_cast(hold_any& operand) | |
{ | |
typedef BOOST_DEDUCED_TYPENAME remove_reference<T>::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<nonref>::value); | |
#endif | |
nonref* result = any_cast<nonref>(&operand); | |
if(!result) | |
boost::throw_exception(bad_any_cast(operand.type(), BOOST_SP_TYPEID(T))); | |
return *result; | |
} | |
template <typename T> | |
T const& any_cast(hold_any const& operand) | |
{ | |
typedef BOOST_DEDUCED_TYPENAME remove_reference<T>::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<nonref>::value); | |
#endif | |
return any_cast<nonref const&>(const_cast<hold_any &>(operand)); | |
} | |
/////////////////////////////////////////////////////////////////////////////// | |
}} // namespace boost::spirit | |
/////////////////////////////////////////////////////////////////////////////// | |
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) | |
# pragma warning(pop) | |
#endif | |
#endif |