Use placement new to avoid unique_ptr allocations

This commit is contained in:
Jason Turner
2015-06-23 11:11:56 -06:00
parent 05bec3b4a8
commit 83281bff52

View File

@@ -8,6 +8,7 @@
#define CHAISCRIPT_ANY_HPP_ #define CHAISCRIPT_ANY_HPP_
#include <utility> #include <utility>
#include <array>
namespace chaiscript { namespace chaiscript {
namespace detail { namespace detail {
@@ -44,6 +45,15 @@ namespace chaiscript {
class Any { class Any {
private: private:
template<class T>
struct destruct {
void operator()(T *t) const {
t->~T();
}
};
struct Data struct Data
{ {
Data(const std::type_info &t_type) Data(const std::type_info &t_type)
@@ -61,7 +71,7 @@ namespace chaiscript {
return m_type; return m_type;
} }
virtual std::unique_ptr<Data> clone() const = 0; virtual void clone(void *t_ptr) const = 0;
const std::type_info &m_type; const std::type_info &m_type;
}; };
@@ -81,9 +91,9 @@ namespace chaiscript {
return &m_data; return &m_data;
} }
std::unique_ptr<Data> clone() const CHAISCRIPT_OVERRIDE void clone(void *t_ptr) const CHAISCRIPT_OVERRIDE
{ {
return std::unique_ptr<Data>(new Data_Impl<T>(m_data)); new (t_ptr) Data_Impl<T>(m_data);
} }
Data_Impl &operator=(const Data_Impl&) = delete; Data_Impl &operator=(const Data_Impl&) = delete;
@@ -91,32 +101,70 @@ namespace chaiscript {
T m_data; T m_data;
}; };
std::unique_ptr<Data> m_data; static const size_t buffersize = sizeof(Data_Impl<std::shared_ptr<int>>)>sizeof(Data_Impl<std::reference_wrapper<int>>)?sizeof(Data_Impl<std::shared_ptr<int>>):sizeof(Data_Impl<std::reference_wrapper<int>>);
bool m_constructed;
mutable std::array<uint8_t, buffersize> m_data_holder;
public: inline Data *data() const
// construct/copy/destruct {
Any() = default; return reinterpret_cast<Data*>(m_data_holder.data());
}
Any(const Any &t_any)
{ void call_destructor()
if (!t_any.empty()) {
if (m_constructed)
{ {
m_data = t_any.m_data->clone(); m_constructed = false;
} else { data()->~Data();
m_data.reset();
} }
} }
#if !defined(_MSC_VER) || _MSC_VER != 1800 public:
Any(Any &&) = default; Any()
Any &operator=(Any &&t_any) = default; : m_constructed(false)
#endif {
}
// construct/copy/destruct
Any(const Any &t_any)
: m_constructed(false)
{
if (t_any.m_constructed) {
t_any.data()->clone(m_data_holder.data());
m_constructed = true;
}
}
Any(Any &&t_any)
: m_constructed(false)
{
if (t_any.m_constructed) {
t_any.m_constructed = false;
m_constructed = true;
m_data_holder = std::move(t_any.m_data_holder);
}
}
Any &operator=(Any &&t_any)
{
call_destructor();
if (t_any.m_constructed) {
t_any.m_constructed = false;
m_constructed = true;
m_data_holder = std::move(t_any.m_data_holder);
}
return *this;
}
template<typename ValueType, template<typename ValueType,
typename = typename std::enable_if<!std::is_same<Any, typename std::decay<ValueType>::type>::value>::type> typename = typename std::enable_if<!std::is_same<Any, typename std::decay<ValueType>::type>::value>::type>
explicit Any(ValueType &&t_value) explicit Any(ValueType &&t_value)
: m_data(std::unique_ptr<Data>(new Data_Impl<typename std::decay<ValueType>::type>(std::forward<ValueType>(t_value)))) : m_constructed(true)
{ {
static_assert(sizeof(Data_Impl<typename std::decay<ValueType>::type>) <= buffersize, "Buffer too small");
(void)(new (m_data_holder.data()) Data_Impl<typename std::decay<ValueType>::type>(std::forward<ValueType>(t_value)));
} }
@@ -130,9 +178,9 @@ namespace chaiscript {
template<typename ToType> template<typename ToType>
ToType &cast() const ToType &cast() const
{ {
if (m_data && typeid(ToType) == m_data->type()) if (m_constructed && typeid(ToType) == data()->type())
{ {
return *static_cast<ToType *>(m_data->data()); return *static_cast<ToType *>(data()->data());
} else { } else {
throw chaiscript::detail::exception::bad_any_cast(); throw chaiscript::detail::exception::bad_any_cast();
} }
@@ -141,26 +189,22 @@ namespace chaiscript {
~Any() ~Any()
{ {
call_destructor();
} }
// modifiers // modifiers
Any & swap(Any &t_other) Any & swap(Any &t_other)
{ {
std::swap(t_other.m_data, m_data); std::swap(t_other.m_data_holder, m_data_holder);
std::swap(t_other.m_constructed, m_constructed);
return *this; return *this;
} }
// queries
bool empty() const
{
return !bool(m_data);
}
const std::type_info & type() const const std::type_info & type() const
{ {
if (m_data) if (m_constructed) {
{ return data()->type();
return m_data->type();
} else { } else {
return typeid(void); return typeid(void);
} }