]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
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 | ||
76 | rep_type(const value_type& x):value_ptr(&x){} | |
77 | ||
78 | #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) | |
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 | ||
84 | rep_type(value_type&& x):value_ptr(&x){} | |
85 | #endif | |
86 | ||
87 | ~rep_type() | |
88 | { | |
89 | if(!value_ptr) key_ptr()->~key_type(); | |
90 | else if(value_cted())value_ptr->~value_type(); | |
91 | } | |
92 | ||
93 | operator const key_type&()const | |
94 | { | |
95 | if(value_ptr)return key_from_value(*value_ptr); | |
96 | else return *key_ptr(); | |
97 | } | |
98 | ||
99 | 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 | ||
112 | void* spc_ptr()const{return static_cast<void*>(&spc);} | |
113 | bool value_cted()const{return value_ptr==spc_ptr();} | |
114 | ||
115 | key_type* key_ptr()const | |
116 | { | |
117 | return static_cast<key_type*>(static_cast<void*>(&spc)); | |
118 | } | |
119 | ||
120 | static const key_type& key_from_value(const value_type& x) | |
121 | { | |
122 | KeyFromValue k; | |
123 | return k(x); | |
124 | } | |
125 | ||
126 | 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 | ||
144 | 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) | |
150 | 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 | ||
168 | static void construct_value(const rep_type& r) | |
169 | { | |
170 | r.construct_value(); | |
171 | } | |
172 | ||
173 | static void copy_value(const rep_type& r) | |
174 | { | |
175 | r.copy_value(); | |
176 | } | |
177 | ||
178 | #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) | |
179 | 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 | ||
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 | ||
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){} | |
218 | rep_type(const value_type& x):key(no_key_from_value_failure()){} | |
219 | ||
220 | #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) | |
221 | rep_type(rep_type&& x):key(std::move(x.key)),value_ptr(0){} | |
222 | rep_type(value_type&& x):key(no_key_from_value_failure()){} | |
223 | #endif | |
224 | ||
225 | ~rep_type() | |
226 | { | |
227 | if(value_ptr)value_ptr->~value_type(); | |
228 | } | |
229 | ||
230 | operator const key_type&()const{return key;} | |
231 | ||
232 | 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 | ||
244 | 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 | ||
256 | 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 | ||
269 | 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 | ||
279 | static void copy_value(const rep_type&){} | |
280 | ||
281 | #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) | |
282 | 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 |