boost/spirit/home/support/detail/hold_any.hpp
/*=============================================================================
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 <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 <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(std::type_info const& src, std::type_info 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
{
std::type_info 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 std::type_info const& get_type()
{
return 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 std::type_info const& get_type()
{
return 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);
return i;
}
inline std::ostream&
operator<< (std::ostream& o, empty const&)
{
return o;
}
}
///////////////////////////////////////////////////////////////////////////
class hold_any
{
public:
// constructors
template <typename T>
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;
}
std::type_info const& type() const
{
return table->get_type();
}
template <typename T>
T const& cast() const
{
if (type() != typeid(T))
throw bad_any_cast(type(), 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() == 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(), 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