boost/flyweight/key_value.hpp
/* Copyright 2006-2024 Joaquin M Lopez Munoz.
* 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)
*
* See http://www.boost.org/libs/flyweight for library home page.
*/
#ifndef BOOST_FLYWEIGHT_KEY_VALUE_HPP
#define BOOST_FLYWEIGHT_KEY_VALUE_HPP
#if defined(_MSC_VER)
#pragma once
#endif
#include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
#include <boost/assert.hpp>
#include <boost/config/workaround.hpp>
#include <boost/flyweight/detail/perfect_fwd.hpp>
#include <boost/flyweight/detail/value_tag.hpp>
#include <boost/flyweight/key_value_fwd.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>
#include <boost/type_traits/declval.hpp>
#include <boost/type_traits/is_same.hpp>
#include <new>
/* key-value policy: flywewight lookup is based on Key, which also serves
* to construct Value only when needed (new factory entry). key_value is
* used to avoid the construction of temporary values when such construction
* is expensive.
* Optionally, KeyFromValue extracts the key from a value, which
* is needed in expressions like this:
*
* typedef flyweight<key_value<Key,Value> > fw_t;
* fw_t fw;
* Value v;
* fw=v; // no key explicitly given
*
* If no KeyFromValue is provided, this latter expression fails to compile.
*/
namespace boost{
namespace flyweights{
namespace detail{
template<typename Key,typename Value,typename KeyFromValue>
struct variant_key_value:value_marker
{
typedef Key key_type;
typedef Value value_type;
class rep_type
{
public:
/* template ctors */
#define BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY(args) \
:value_cted(false) \
{ \
new(key_ptr())key_type(BOOST_FLYWEIGHT_FORWARD(args)); \
}
BOOST_FLYWEIGHT_PERFECT_FWD(
explicit rep_type,
BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY)
#undef BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY
rep_type(const rep_type& x):value_cted(false)
{
if(!x.value_cted)new(key_ptr())key_type(*x.key_ptr());
else new(key_ptr())key_type(key_from_value(*x.value_ptr()));
}
rep_type(const value_type& x):value_cted(false)
{
new(key_ptr())key_type(key_from_value(x));
}
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
rep_type(rep_type&& x):value_cted(false)
{
if(!x.value_cted)new(key_ptr())key_type(std::move(*x.key_ptr()));
else new(key_ptr())key_type(key_from_value(*x.value_ptr()));
}
rep_type(value_type&& x):value_cted(false)
{
new(key_ptr())key_type(key_from_value(x));
}
#endif
~rep_type()
{
if(value_cted)value_ptr()->~value_type();
else key_ptr()->~key_type();
}
operator const key_type&()const
BOOST_NOEXCEPT_IF(noexcept(
boost::declval<KeyFromValue>()(boost::declval<const value_type&>())))
{
if(value_cted)return key_from_value(*value_ptr());
else return *key_ptr();
}
operator const value_type&()const
{
BOOST_ASSERT(value_cted);
return *value_ptr();
}
private:
friend struct variant_key_value;
key_type* key_ptr()const
{
return static_cast<key_type*>(static_cast<void*>(&key_spc));
}
value_type* value_ptr()const
{
return static_cast<value_type*>(static_cast<void*>(&value_spc));
}
static const key_type& key_from_value(const value_type& x)
{
KeyFromValue k;
return k(x);
}
void key_construct_value()const
{
if(!value_cted){
new(value_ptr())value_type(*key_ptr());
key_ptr()->~key_type();
value_cted=true;
}
}
void copy_construct_value(const value_type& x)const
{
if(!value_cted){
new(value_ptr())value_type(x);
key_ptr()->~key_type();
value_cted=true;
}
}
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
void move_construct_value(value_type&& x)const
{
if(!value_cted){
new(value_ptr())value_type(std::move(x));
key_ptr()->~key_type();
value_cted=true;
}
}
#endif
mutable typename boost::aligned_storage<
sizeof(key_type),
boost::alignment_of<key_type>::value
>::type key_spc;
mutable typename boost::aligned_storage<
sizeof(value_type),
boost::alignment_of<value_type>::value
>::type value_spc;
mutable bool value_cted;
};
static void key_construct_value(const rep_type& r)
{
r.key_construct_value();
}
static void copy_construct_value(const rep_type& r,const value_type& x)
{
r.copy_construct_value(x);
}
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
static void move_construct_value(const rep_type& r,value_type&& x)
{
r.move_construct_value(std::move(x));
}
#endif
};
template<typename Key,typename Value>
struct product_key_value:value_marker
{
typedef Key key_type;
typedef Value value_type;
class rep_type
{
public:
/* template ctors */
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)&&\
!defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)&&\
BOOST_WORKAROUND(__GNUC__,<=4)&&(__GNUC__<4||__GNUC_MINOR__<=4)
/* GCC 4.4.2 (and probably prior) bug: the default ctor generated by the
* variadic temmplate ctor below fails to value-initialize key.
*/
rep_type():key(),value_cted(false){}
#endif
#define BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY(args) \
:key(BOOST_FLYWEIGHT_FORWARD(args)),value_cted(false){}
BOOST_FLYWEIGHT_PERFECT_FWD(
explicit rep_type,
BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY)
#undef BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY
rep_type(const rep_type& x):key(x.key),value_cted(false){}
rep_type(const value_type&):key(no_key_from_value_failure()){}
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
rep_type(rep_type&& x):key(std::move(x.key)),value_cted(false){}
rep_type(value_type&&):key(no_key_from_value_failure()){}
#endif
~rep_type()
{
if(value_cted)value_ptr()->~value_type();
}
operator const key_type&()const BOOST_NOEXCEPT{return key;}
operator const value_type&()const
{
BOOST_ASSERT(value_cted);
return *value_ptr();
}
private:
friend struct product_key_value;
value_type* value_ptr()const
{
return static_cast<value_type*>(static_cast<void*>(&value_spc));
}
struct no_key_from_value_failure
{
BOOST_MPL_ASSERT_MSG(
false,
NO_KEY_FROM_VALUE_CONVERSION_PROVIDED,
(key_type,value_type));
operator const key_type&()const;
};
void key_construct_value()const
{
if(!value_cted){
new(value_ptr())value_type(key);
value_cted=true;
}
}
key_type key;
mutable typename boost::aligned_storage<
sizeof(value_type),
boost::alignment_of<value_type>::value
>::type value_spc;
mutable bool value_cted;
};
static void key_construct_value(const rep_type& r)
{
r.key_construct_value();
}
/* [copy|move]_construct_value() can't really ever be called, provided to
* avoid compile errors (it is the no_key_from_value_failure compile error
* we want to appear in these cases).
*/
static void copy_construct_value(const rep_type&,const value_type&){}
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
static void move_construct_value(const rep_type&,value_type&&){}
#endif
};
} /* namespace flyweights::detail */
template<typename Key,typename Value,typename KeyFromValue>
struct key_value:
mpl::if_<
is_same<KeyFromValue,no_key_from_value>,
detail::product_key_value<Key,Value>,
detail::variant_key_value<Key,Value,KeyFromValue>
>::type
{};
} /* namespace flyweights */
} /* namespace boost */
#endif