1 /* Copyright 2006-2018 Joaquin M Lopez Munoz. 2 * Distributed under the Boost Software License, Version 1.0. 3 * (See accompanying file LICENSE_1_0.txt or copy at 4 * http://www.boost.org/LICENSE_1_0.txt) 5 * 6 * See http://www.boost.org/libs/flyweight for library home page. 7 */ 8 9 #ifndef BOOST_FLYWEIGHT_KEY_VALUE_HPP 10 #define BOOST_FLYWEIGHT_KEY_VALUE_HPP 11 12 #if defined(_MSC_VER) 13 #pragma once 14 #endif 15 16 #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */ 17 #include <boost/detail/workaround.hpp> 18 #include <boost/flyweight/detail/perfect_fwd.hpp> 19 #include <boost/flyweight/detail/value_tag.hpp> 20 #include <boost/flyweight/key_value_fwd.hpp> 21 #include <boost/mpl/assert.hpp> 22 #include <boost/type_traits/aligned_storage.hpp> 23 #include <boost/type_traits/alignment_of.hpp> 24 #include <boost/type_traits/is_same.hpp> 25 #include <new> 26 27 /* key-value policy: flywewight lookup is based on Key, which also serves 28 * to construct Value only when needed (new factory entry). key_value is 29 * used to avoid the construction of temporary values when such construction 30 * is expensive. 31 * Optionally, KeyFromValue extracts the key from a value, which 32 * is needed in expressions like this: 33 * 34 * typedef flyweight<key_value<Key,Value> > fw_t; 35 * fw_t fw; 36 * Value v; 37 * fw=v; // no key explicitly given 38 * 39 * If no KeyFromValue is provided, this latter expression fails to compile. 40 */ 41 42 namespace boost{ 43 44 namespace flyweights{ 45 46 namespace detail{ 47 48 template<typename Key,typename Value,typename KeyFromValue> 49 struct optimized_key_value:value_marker 50 { 51 typedef Key key_type; 52 typedef Value value_type; 53 54 class rep_type 55 { 56 public: 57 /* template ctors */ 58 59 #define BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY(args) \ 60 :value_ptr(0) \ 61 { \ 62 new(spc_ptr())key_type(BOOST_FLYWEIGHT_FORWARD(args)); \ 63 } 64 BOOST_FLYWEIGHT_PERFECT_FWD(explicit rep_type,BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY)65 BOOST_FLYWEIGHT_PERFECT_FWD( 66 explicit rep_type, 67 BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY) 68 69 #undef BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY 70 71 rep_type(const rep_type& x):value_ptr(x.value_ptr) 72 { 73 if(!x.value_ptr)new(key_ptr())key_type(*x.key_ptr()); 74 } 75 rep_type(const value_type & x)76 rep_type(const value_type& x):value_ptr(&x){} 77 78 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) rep_type(rep_type && x)79 rep_type(rep_type&& x):value_ptr(x.value_ptr) 80 { 81 if(!x.value_ptr)new(key_ptr())key_type(std::move(*x.key_ptr())); 82 } 83 rep_type(value_type && x)84 rep_type(value_type&& x):value_ptr(&x){} 85 #endif 86 ~rep_type()87 ~rep_type() 88 { 89 if(!value_ptr) key_ptr()->~key_type(); 90 else if(value_cted())value_ptr->~value_type(); 91 } 92 operator const key_type&() const93 operator const key_type&()const 94 { 95 if(value_ptr)return key_from_value(*value_ptr); 96 else return *key_ptr(); 97 } 98 operator const value_type&() const99 operator const value_type&()const 100 { 101 /* This is always called after construct_value() or copy_value(), 102 * so we access spc directly rather than through value_ptr to 103 * save us an indirection. 104 */ 105 106 return *static_cast<value_type*>(spc_ptr()); 107 } 108 109 private: 110 friend struct optimized_key_value; 111 spc_ptr() const112 void* spc_ptr()const{return static_cast<void*>(&spc);} value_cted() const113 bool value_cted()const{return value_ptr==spc_ptr();} 114 key_ptr() const115 key_type* key_ptr()const 116 { 117 return static_cast<key_type*>(static_cast<void*>(&spc)); 118 } 119 key_from_value(const value_type & x)120 static const key_type& key_from_value(const value_type& x) 121 { 122 KeyFromValue k; 123 return k(x); 124 } 125 construct_value() const126 void construct_value()const 127 { 128 if(!value_cted()){ 129 /* value_ptr must be ==0, oherwise copy_value would have been called */ 130 131 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) 132 key_type k(std::move(*key_ptr())); 133 #else 134 key_type k(*key_ptr()); 135 #endif 136 137 key_ptr()->~key_type(); 138 value_ptr= /* guarantees key won't be re-dted at ~rep_type if the */ 139 static_cast<value_type*>(spc_ptr())+1; /* next statement throws */ 140 value_ptr=new(spc_ptr())value_type(k); 141 } 142 } 143 copy_value() const144 void copy_value()const 145 { 146 if(!value_cted())value_ptr=new(spc_ptr())value_type(*value_ptr); 147 } 148 149 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) move_value() const150 void move_value()const 151 { 152 if(!value_cted())value_ptr= 153 new(spc_ptr())value_type(std::move(const_cast<value_type&>(*value_ptr))); 154 } 155 #endif 156 157 mutable typename boost::aligned_storage< 158 (sizeof(key_type)>sizeof(value_type))? 159 sizeof(key_type):sizeof(value_type), 160 (boost::alignment_of<key_type>::value > 161 boost::alignment_of<value_type>::value)? 162 boost::alignment_of<key_type>::value: 163 boost::alignment_of<value_type>::value 164 >::type spc; 165 mutable const value_type* value_ptr; 166 }; 167 construct_valueboost::flyweights::detail::optimized_key_value168 static void construct_value(const rep_type& r) 169 { 170 r.construct_value(); 171 } 172 copy_valueboost::flyweights::detail::optimized_key_value173 static void copy_value(const rep_type& r) 174 { 175 r.copy_value(); 176 } 177 178 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) move_valueboost::flyweights::detail::optimized_key_value179 static void move_value(const rep_type& r) 180 { 181 r.move_value(); 182 } 183 #endif 184 }; 185 186 template<typename Key,typename Value> 187 struct regular_key_value:value_marker 188 { 189 typedef Key key_type; 190 typedef Value value_type; 191 192 class rep_type 193 { 194 public: 195 /* template ctors */ 196 197 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)&&\ 198 !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)&&\ 199 BOOST_WORKAROUND(__GNUC__,<=4)&&(__GNUC__<4||__GNUC_MINOR__<=4) 200 201 /* GCC 4.4.2 (and probably prior) bug: the default ctor generated by the 202 * variadic temmplate ctor below fails to value-initialize key. 203 */ 204 rep_type()205 rep_type():key(),value_ptr(0){} 206 #endif 207 208 #define BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY(args) \ 209 :key(BOOST_FLYWEIGHT_FORWARD(args)),value_ptr(0){} 210 BOOST_FLYWEIGHT_PERFECT_FWD(explicit rep_type,BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY)211 BOOST_FLYWEIGHT_PERFECT_FWD( 212 explicit rep_type, 213 BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY) 214 215 #undef BOOST_FLYWEIGHT_PERFECT_FWD_CTR_BODY 216 217 rep_type(const rep_type& x):key(x.key),value_ptr(0){} rep_type(const value_type &)218 rep_type(const value_type&):key(no_key_from_value_failure()){} 219 220 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) rep_type(rep_type && x)221 rep_type(rep_type&& x):key(std::move(x.key)),value_ptr(0){} rep_type(value_type &&)222 rep_type(value_type&&):key(no_key_from_value_failure()){} 223 #endif 224 ~rep_type()225 ~rep_type() 226 { 227 if(value_ptr)value_ptr->~value_type(); 228 } 229 operator const key_type&() const230 operator const key_type&()const{return key;} 231 operator const value_type&() const232 operator const value_type&()const 233 { 234 /* This is always called after construct_value(),so we access spc 235 * directly rather than through value_ptr to save us an indirection. 236 */ 237 238 return *static_cast<value_type*>(spc_ptr()); 239 } 240 241 private: 242 friend struct regular_key_value; 243 spc_ptr() const244 void* spc_ptr()const{return static_cast<void*>(&spc);} 245 246 struct no_key_from_value_failure 247 { 248 BOOST_MPL_ASSERT_MSG( 249 false, 250 NO_KEY_FROM_VALUE_CONVERSION_PROVIDED, 251 (key_type,value_type)); 252 253 operator const key_type&()const; 254 }; 255 construct_value() const256 void construct_value()const 257 { 258 if(!value_ptr)value_ptr=new(spc_ptr())value_type(key); 259 } 260 261 key_type key; 262 mutable typename boost::aligned_storage< 263 sizeof(value_type), 264 boost::alignment_of<value_type>::value 265 >::type spc; 266 mutable const value_type* value_ptr; 267 }; 268 construct_valueboost::flyweights::detail::regular_key_value269 static void construct_value(const rep_type& r) 270 { 271 r.construct_value(); 272 } 273 274 /* copy_value() and move_value() can't really ever be called, provided to avoid 275 * compile errors (it is the no_key_from_value_failure compile error we want to 276 * appear in these cases). 277 */ 278 copy_valueboost::flyweights::detail::regular_key_value279 static void copy_value(const rep_type&){} 280 281 #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) move_valueboost::flyweights::detail::regular_key_value282 static void move_value(const rep_type&){} 283 #endif 284 }; 285 286 } /* namespace flyweights::detail */ 287 288 template<typename Key,typename Value,typename KeyFromValue> 289 struct key_value: 290 mpl::if_< 291 is_same<KeyFromValue,no_key_from_value>, 292 detail::regular_key_value<Key,Value>, 293 detail::optimized_key_value<Key,Value,KeyFromValue> 294 >::type 295 {}; 296 297 } /* namespace flyweights */ 298 299 } /* namespace boost */ 300 301 #endif 302