text: /* Copyright 2006-2014 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/detail/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/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 optimized_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_ptr(0)                                          \
  {                                                        \
    new(spc_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_ptr(x.value_ptr)
      {
        if(!x.value_ptr)new(key_ptr())key_type(*x.key_ptr());
      }
  
      rep_type(const value_type& x):value_ptr(&x){}
  
  #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
      rep_type(rep_type&& x):value_ptr(x.value_ptr)
      {
        if(!x.value_ptr)new(key_ptr())key_type(std::move(*x.key_ptr()));
      }
  
      rep_type(value_type&& x):value_ptr(&x){}
  #endif
  
      ~rep_type()
      {
        if(!value_ptr)       key_ptr()->~key_type();
        else if(value_cted())value_ptr->~value_type();
      }
  
      operator const key_type&()const
      {
        if(value_ptr)return key_from_value(*value_ptr);
        else         return *key_ptr();
      }
  
      operator const value_type&()const
      {
        /* This is always called after construct_value() or copy_value(),
         * so we access spc directly rather than through value_ptr to
         * save us an indirection.
         */
  
        return *static_cast<value_type*>(spc_ptr());
      }
  
    private:
      friend struct optimized_key_value;
  
      void* spc_ptr()const{return static_cast<void*>(&spc);}
      bool  value_cted()const{return value_ptr==spc_ptr();}
  
      key_type* key_ptr()const
      {
        return static_cast<key_type*>(static_cast<void*>(&spc));
      }
  
      static const key_type& key_from_value(const value_type& x)
      {
        KeyFromValue k;
        return k(x);
      }
  
      void construct_value()const
      {
        if(!value_cted()){
          /* value_ptr must be ==0, oherwise copy_value would have been called */
  
  #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
          key_type k(std::move(*key_ptr()));
  #else
          key_type k(*key_ptr());
  #endif
  
          key_ptr()->~key_type();
          value_ptr= /* guarantees key won't be re-dted at ~rep_type if the */
            static_cast<value_type*>(spc_ptr())+1; /* next statement throws */
          value_ptr=new(spc_ptr())value_type(k);
        }
      }
  
      void copy_value()const
      {
        if(!value_cted())value_ptr=new(spc_ptr())value_type(*value_ptr);
      }
  
  #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
      void move_value()const
      {
        if(!value_cted())value_ptr=
          new(spc_ptr())value_type(std::move(const_cast<value_type&>(*value_ptr)));
      }
  #endif
  
      mutable typename boost::aligned_storage<
        (sizeof(key_type)>sizeof(value_type))?
          sizeof(key_type):sizeof(value_type),
        (boost::alignment_of<key_type>::value >
         boost::alignment_of<value_type>::value)?
          boost::alignment_of<key_type>::value:
          boost::alignment_of<value_type>::value
      >::type                                    spc;
      mutable const value_type*                  value_ptr;
    };
  
    static void construct_value(const rep_type& r)
    {
      r.construct_value();
    }
  
    static void copy_value(const rep_type& r)
    {
      r.copy_value();
    }
  
  #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
    static void move_value(const rep_type& r)
    {
      r.move_value();
    }
  #endif
  };
  
  template<typename Key,typename Value>
  struct regular_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_ptr(0){}
  #endif
  
  #define BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY(args) \
    :key(BOOST_FLYWEIGHT_FORWARD(args)),value_ptr(0){}
  
    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_ptr(0){}
      rep_type(const value_type& x):key(no_key_from_value_failure()){}
  
  #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
      rep_type(rep_type&& x):key(std::move(x.key)),value_ptr(0){}
      rep_type(value_type&& x):key(no_key_from_value_failure()){}
  #endif
  
      ~rep_type()
      {
        if(value_ptr)value_ptr->~value_type();
      }
  
      operator const key_type&()const{return key;}
  
      operator const value_type&()const
      {
        /* This is always called after construct_value(),so we access spc
         * directly rather than through value_ptr to save us an indirection.
         */
  
        return *static_cast<value_type*>(spc_ptr());
      }
  
    private:
      friend struct regular_key_value;
  
      void* spc_ptr()const{return static_cast<void*>(&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 construct_value()const
      {
        if(!value_ptr)value_ptr=new(spc_ptr())value_type(key);
      }
  
      key_type                                 key;
      mutable typename boost::aligned_storage<
        sizeof(value_type),
        boost::alignment_of<value_type>::value
      >::type                                  spc;
      mutable const value_type*                value_ptr;
    };
  
    static void construct_value(const rep_type& r)
    {
      r.construct_value();
    }
  
    /* copy_value() and move_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_value(const rep_type&){}
  
  #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
    static void move_value(const rep_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::regular_key_value<Key,Value>,
      detail::optimized_key_value<Key,Value,KeyFromValue>
    >::type
  {};
  
  } /* namespace flyweights */
  
  } /* namespace boost */
  
  #endif
  