flyweight.hpp
Loading...
Searching...
No Matches
flyweight.hpp
Go to the documentation of this file.
1
51/*
52 * This is free and unencumbered software released into the public domain.
53 *
54 * Anyone is free to copy, modify, publish, use, compile, sell, or
55 * distribute this software, either in source code form or as a compiled
56 * binary, for any purpose, commercial or non-commercial, and by any
57 * means.
58 *
59 * In jurisdictions that recognize copyright laws, the author or authors
60 * of this software dedicate any and all copyright interest in the
61 * software to the public domain. We make this dedication for the benefit
62 * of the public at large and to the detriment of our heirs and
63 * successors. We intend this dedication to be an overt act of
64 * relinquishment in perpetuity of all present and future rights to this
65 * software under copyright law.
66 *
67 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
68 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
69 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
70 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
71 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
72 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
73 * OTHER DEALINGS IN THE SOFTWARE.
74 *
75 * For more information, please refer to <http://unlicense.org/>
76 */
77#ifndef __FLYWEIGHT_HPP__
78#define __FLYWEIGHT_HPP__
79
80#include <functional>
81#include <tuple>
82#include <type_traits>
83#include <unordered_map>
84#include <utility>
85
86namespace flyweight {
87
88namespace detail {
91 constexpr static size_t hash_combine(size_t a, size_t b) {
92 return a ^ b + 0x9e3779b9 + (a << 6) + (a >> 2);
93 }
94
96 template<typename... Args>
97 struct tuple_hasher {
98 constexpr size_t operator()(const std::tuple<Args...>& value) const {
99 return hash_tuple<std::tuple<Args...>, 0, Args...>(value);
100 }
101
102 template<typename TTuple, size_t I, typename T>
103 constexpr static size_t hash_tuple(const TTuple& v) {
104 return std::hash<T>{}(std::get<I>(v));
105 }
106
107 template<typename TTuple, size_t I, typename T1, typename T2, typename... Rest>
108 constexpr static size_t hash_tuple(const TTuple& v) {
109 return hash_combine(
110 hash_tuple<TTuple, I, T1>(v),
111 hash_tuple<TTuple, I+1, T2, Rest...>(v)
112 );
113 }
114 };
115
117 template<typename T>
118 struct refcounted_value {
119 T value;
120 long long refcount;
121
124 refcounted_value(T&& value) : value(value), refcount(0) {}
125
126 operator T&() {
127 return value;
128 }
129
131 refcounted_value& reference() {
132 refcount++;
133 return *this;
134 }
135
138 bool dereference() {
139 --refcount;
140 return refcount <= 0;
141 }
142 };
143
144#if __cpp_lib_apply
145 template<typename Fn, typename... Args>
146 auto apply(Fn&& f, std::tuple<Args...>&& t) {
147 return std::apply(std::move(f), std::move(t));
148 }
149#else
150 // Unpacking tuple with int sequence on C++11
151 // Reference: https://stackoverflow.com/a/7858971
152 template<int...>
153 struct seq {};
154
155 template<int N, int... S>
156 struct gens : gens<N-1, N-1, S...> {};
157
158 template<int... S>
159 struct gens<0, S...> {
160 using type = seq<S...>;
161 };
162
163 template<typename... Args>
164 struct apply_impl {
165 template<typename Fn, int... S>
166 static auto invoke(Fn&& f, std::tuple<Args...>&& t, seq<S...>) {
167 return f(std::forward<Args>(std::get<S>(t))...);
168 }
169 };
170
171 template<typename Fn, typename... Args>
172 auto apply(Fn&& f, std::tuple<Args...>&& t) {
173 return apply_impl<Args...>::invoke(std::move(f), std::move(t), typename gens<sizeof...(Args)>::type{});
174 }
175#endif
176}
177
178
183template<typename T, typename... Args>
186 T operator()(Args&&... args) {
187 return T { std::forward<Args>(args)... };
188 }
189};
190
194template<typename T>
197 void operator()(T&) {
198 // no-op
199 }
200};
201
203template<typename T, typename Flyweight, typename ArgTuple>
205 T& value;
206
208 autorelease_value(T& value, Flyweight& flyweight, const ArgTuple& arg_tuple)
209 : value(value)
211 , arg_tuple(arg_tuple)
212 {
213 }
214
218 : value(other.flyweight.get(other.arg_tuple))
219 , flyweight(other.flyweight)
220 , arg_tuple(other.arg_tuple)
221 {
222 }
226 {
227 // release previous value
228 flyweight.release(arg_tuple);
229 // re-get the value to make sure reference counting is correct
230 value = other.flyweight.get(other.arg_tuple);
231 flyweight = other.flyweight;
232 arg_tuple = other.arg_tuple;
233 }
234
237 return value;
238 }
241 return value;
242 }
244 operator T&() {
245 return value;
246 }
247
250 flyweight.release(arg_tuple);
251 }
252
253private:
254 Flyweight& flyweight;
255 ArgTuple arg_tuple;
256};
257
271template<typename T, typename... Args>
273public:
274 using value = T;
275 using autorelease_value = autorelease_value<T, flyweight, std::tuple<Args...>>;
276
279 flyweight() : creator(default_creator<T, Args...>{}), deleter(default_deleter<T>{}) {}
280
284 template<typename Creator>
285 flyweight(Creator&& creator)
286 : creator([creator](Args&&... args) { return creator(std::forward<Args>(args)...); })
287 , deleter(default_deleter<T>{})
288 {
289 }
290
296 template<typename Creator, typename Deleter>
297 flyweight(Creator&& creator, Deleter&& deleter)
298 : creator([creator](Args&&... args) { return creator(std::forward<Args>(args)...); })
299 , deleter([deleter](T& value) { deleter(value); })
300 {
301 }
302
309 T& get(const std::tuple<Args...>& arg_tuple) {
310 auto it = map.find(arg_tuple);
311 if (it == map.end()) {
312 it = map.emplace(arg_tuple, detail::apply(creator, (std::tuple<Args...>) arg_tuple)).first;
313 }
314 return it->second;
315 }
317 template<bool uses_multiple_args = (sizeof...(Args) > 1)>
318 auto get(Args&&... args) -> typename std::enable_if<uses_multiple_args, T&>::type {
319 return get(std::tuple<Args...> { std::forward<Args>(args)... });
320 }
321
325 autorelease_value get_autorelease(const std::tuple<Args...>& arg_tuple) {
326 return {
327 get(arg_tuple),
328 *this,
329 arg_tuple,
330 };
331 }
333 template<bool uses_multiple_args = (sizeof...(Args) > 1)>
334 auto get_autorelease(Args&&... args) -> typename std::enable_if<uses_multiple_args, autorelease_value>::type {
335 return get_autorelease(std::tuple<Args...> { std::forward<Args>(args)... });
336 }
337
339 bool is_loaded(const std::tuple<Args...>& arg_tuple) const {
340 return map.find(arg_tuple) != map.end();
341 }
343 template<bool uses_multiple_args = (sizeof...(Args) > 1)>
344 auto is_loaded(Args&&... args) const -> typename std::enable_if<uses_multiple_args, bool>::type {
345 return is_loaded(std::tuple<Args...> { std::forward<Args>(args)... });
346 }
347
351 bool release(const std::tuple<Args...>& arg_tuple) {
352 auto it = map.find(arg_tuple);
353 if (it != map.end()) {
354 deleter(it->second);
355 map.erase(it);
356 return true;
357 }
358 else {
359 return false;
360 }
361 }
363 template<bool uses_multiple_args = (sizeof...(Args) > 1)>
364 auto release(Args&&... args) -> typename std::enable_if<uses_multiple_args, bool>::type {
365 return release(std::tuple<Args...> { std::forward<Args>(args)... });
366 }
367
368protected:
371 std::unordered_map<std::tuple<Args...>, T, detail::tuple_hasher<Args...>> map;
374 std::function<T(Args&&...)> creator;
377 std::function<void(T&)> deleter;
378};
379
380
391template<typename T, typename... Args>
392class flyweight_refcounted : public flyweight<detail::refcounted_value<T>, Args...> {
394public:
395 using autorelease_value = autorelease_value<T, flyweight_refcounted, std::tuple<Args...>>;
396
399
401 template<typename Creator>
403
405 template<typename Creator, typename Deleter>
407
410 T& get(const std::tuple<Args...>& arg_tuple) {
411 auto& value = base::get(arg_tuple);
412 return value.reference();
413 }
415 template<bool uses_multiple_args = (sizeof...(Args) > 1)>
416 auto get(Args&&... args) -> typename std::enable_if<uses_multiple_args, T&>::type {
417 return get(std::tuple<Args...> { std::forward<Args>(args)... });
418 }
419
422 autorelease_value get_autorelease(const std::tuple<Args...>& arg_tuple) {
423 return {
424 get(arg_tuple),
425 *this,
426 arg_tuple,
427 };
428 }
430 template<bool uses_multiple_args = (sizeof...(Args) > 1)>
431 auto get_autorelease(Args&&... args) -> typename std::enable_if<uses_multiple_args, autorelease_value>::type {
432 return get_autorelease(std::tuple<Args...> { std::forward<Args>(args)... });
433 }
434
437 size_t reference_count(const std::tuple<Args...>& arg_tuple) const {
438 auto it = base::map.find(arg_tuple);
439 if (it != base::map.end()) {
440 return it->second.refcount;
441 }
442 else {
443 return 0;
444 }
445 }
447 template<bool uses_multiple_args = (sizeof...(Args) > 1)>
448 auto reference_count(Args&&... args) const -> typename std::enable_if<uses_multiple_args, size_t>::type {
449 return reference_count(std::tuple<Args...> { std::forward<Args>(args)... });
450 }
451
454 bool release(const std::tuple<Args...>& arg_tuple) {
455 auto it = base::map.find(arg_tuple);
456 if (it != base::map.end() && it->second.dereference()) {
457 base::deleter(it->second);
458 base::map.erase(it);
459 return true;
460 }
461 else {
462 return false;
463 }
464 }
466 template<bool uses_multiple_args = (sizeof...(Args) > 1)>
467 auto release(Args&&... args) -> typename std::enable_if<uses_multiple_args, bool>::type {
468 return release(std::tuple<Args...> { std::forward<Args>(args)... });
469 }
470};
471
472}
473
474# endif // __FLYWEIGHT_HPP__
Definition: flyweight.hpp:392
flyweight_refcounted(Creator &&creator)
Definition: flyweight.hpp:402
auto release(Args &&... args) -> typename std::enable_if< uses_multiple_args, bool >::type
Alternative to flyweight_refcounted::release with the arguments unpacked.
Definition: flyweight.hpp:467
auto get_autorelease(Args &&... args) -> typename std::enable_if< uses_multiple_args, autorelease_value >::type
Alternative to flyweight_refcounted::get_autorelease with the arguments unpacked.
Definition: flyweight.hpp:431
auto reference_count(Args &&... args) const -> typename std::enable_if< uses_multiple_args, size_t >::type
Alternative to flyweight_refcounted::reference_count with the arguments unpacked.
Definition: flyweight.hpp:448
auto get(Args &&... args) -> typename std::enable_if< uses_multiple_args, T & >::type
Alternative to flyweight_refcounted::get with the arguments unpacked.
Definition: flyweight.hpp:416
flyweight_refcounted(Creator &&creator, Deleter &&deleter)
Definition: flyweight.hpp:406
autorelease_value get_autorelease(const std::tuple< Args... > &arg_tuple)
Definition: flyweight.hpp:422
T & get(const std::tuple< Args... > &arg_tuple)
Definition: flyweight.hpp:410
size_t reference_count(const std::tuple< Args... > &arg_tuple) const
Definition: flyweight.hpp:437
flyweight_refcounted()
Definition: flyweight.hpp:398
bool release(const std::tuple< Args... > &arg_tuple)
Definition: flyweight.hpp:454
Definition: flyweight.hpp:272
flyweight(Creator &&creator)
Definition: flyweight.hpp:285
flyweight(Creator &&creator, Deleter &&deleter)
Definition: flyweight.hpp:297
std::unordered_map< std::tuple< Args... >, T, detail::tuple_hasher< Args... > > map
Definition: flyweight.hpp:371
flyweight()
Definition: flyweight.hpp:279
std::function< void(T &)> deleter
Definition: flyweight.hpp:377
auto is_loaded(Args &&... args) const -> typename std::enable_if< uses_multiple_args, bool >::type
Alternative to flyweight::is_loaded with the arguments unpacked.
Definition: flyweight.hpp:344
auto get_autorelease(Args &&... args) -> typename std::enable_if< uses_multiple_args, autorelease_value >::type
Alternative to flyweight::get_autorelease with the arguments unpacked.
Definition: flyweight.hpp:334
auto release(Args &&... args) -> typename std::enable_if< uses_multiple_args, bool >::type
Alternative to flyweight::release with the arguments unpacked.
Definition: flyweight.hpp:364
bool is_loaded(const std::tuple< Args... > &arg_tuple) const
Check whether the value mapped to the passed arguments is loaded.
Definition: flyweight.hpp:339
T & get(const std::tuple< Args... > &arg_tuple)
Definition: flyweight.hpp:309
auto get(Args &&... args) -> typename std::enable_if< uses_multiple_args, T & >::type
Alternative to flyweight::get with the arguments unpacked.
Definition: flyweight.hpp:318
bool release(const std::tuple< Args... > &arg_tuple)
Definition: flyweight.hpp:351
autorelease_value get_autorelease(const std::tuple< Args... > &arg_tuple)
Definition: flyweight.hpp:325
std::function< T(Args &&...)> creator
Definition: flyweight.hpp:374
Value wrapper that releases it back to the owning flyweight upon destruction.
Definition: flyweight.hpp:204
autorelease_value(const autorelease_value &other)
Definition: flyweight.hpp:217
T & operator*()
Returns the wrapped value.
Definition: flyweight.hpp:236
~autorelease_value()
Release the value back to the owning flyweight.
Definition: flyweight.hpp:249
T & operator->()
Returns the wrapped value.
Definition: flyweight.hpp:240
autorelease_value(T &value, Flyweight &flyweight, const ArgTuple &arg_tuple)
Constructor.
Definition: flyweight.hpp:208
autorelease_value & operator=(const autorelease_value &other)
Definition: flyweight.hpp:225
Definition: flyweight.hpp:184
T operator()(Args &&... args)
Default creator implementation, just call the value constructor forwarding the passed arguments.
Definition: flyweight.hpp:186
Definition: flyweight.hpp:195
void operator()(T &)
Default deleter implementation, a no-op.
Definition: flyweight.hpp:197