1#pragma once
2
3#include <map>
4#include <tuple>
5#include <mutex>
6#include <ext/function_traits.h>
7
8
9/** The simplest cache for a free function.
10 * You can also pass a static class method or lambda without captures.
11 * The size is unlimited. Values are stored permanently and never evicted.
12 * But single record or all cache can be manually dropped.
13 * Mutex is used for synchronization.
14 * Suitable only for the simplest cases.
15 *
16 * Usage
17 *
18 * SimpleCache<decltype(func), &func> func_cached;
19 * std::cerr << func_cached(args...);
20 */
21template <typename F, F* f>
22class SimpleCache
23{
24private:
25 using Key = typename function_traits<F>::arguments_decay;
26 using Result = typename function_traits<F>::result;
27
28 std::map<Key, Result> cache;
29 mutable std::mutex mutex;
30
31public:
32 template <typename... Args>
33 Result operator() (Args &&... args)
34 {
35 {
36 std::lock_guard lock(mutex);
37
38 Key key{std::forward<Args>(args)...};
39 auto it = cache.find(key);
40
41 if (cache.end() != it)
42 return it->second;
43 }
44
45 /// The calculations themselves are not done under mutex.
46 Result res = f(std::forward<Args>(args)...);
47
48 {
49 std::lock_guard lock(mutex);
50
51 cache.emplace(std::forward_as_tuple(args...), res);
52 }
53
54 return res;
55 }
56
57 template <typename... Args>
58 void update(Args &&... args)
59 {
60 Result res = f(std::forward<Args>(args)...);
61 {
62 std::lock_guard lock(mutex);
63
64 Key key{std::forward<Args>(args)...};
65 cache[key] = std::move(res);
66 }
67 }
68
69 size_t size() const
70 {
71 std::lock_guard lock(mutex);
72 return cache.size();
73 }
74
75 void drop()
76 {
77 std::lock_guard lock(mutex);
78 cache.clear();
79 }
80};
81