1 /* Copyright 2006-2014 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 & x)218     rep_type(const value_type& x):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 && x)222     rep_type(value_type&& x):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