Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
Memoizer.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Cache_Memoizer_h_
5#define _Stroika_Foundation_Cache_Memoizer_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <optional>
10#include <vector>
11
12#include "Stroika/Foundation/Cache/Common.h"
13#include "Stroika/Foundation/Cache/LRUCache.h"
15#include "Stroika/Foundation/Common/Common.h"
16#include "Stroika/Foundation/Common/Concepts.h"
18#include "Stroika/Foundation/Containers/Mapping.h"
19
20/**
21 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
22 *
23 * TODO:
24 * @todo maybe allow passing in Cache object as CTOR parameter as a way to specify the hash function etc (for LRUCache with hash)
25 * (to a large degree DONE by Stroika v3.0d23)
26 *
27 * @todo Investigate if better arg order for template or instantiation guide might reduce number of explicit
28 * args needed for template
29 */
30
32
33#if qCompilerAndStdLib_template_template_argument_as_different_template_paramters_Buggy || qCompilerAndStdLib_template_template_auto_deduced_Buggy
34#define qStroika_template_template_BWA(...) __VA_ARGS__, typename...
35#else
36#define qStroika_template_template_BWA(...) __VA_ARGS__
37#endif
38
39 /**
40 * \brief Cache the results of expensive computations transparently
41 *
42 * @see https://en.wikipedia.org/wiki/Memoization
43 *
44 * TODO:
45 * o @todo Asked https://softwareengineering.stackexchange.com/questions/377020/c-templates-combining-deduction-with-default-template-arguments
46 * to see how to improve
47 *
48 * o @todo maybe update https://softwareengineering.stackexchange.com/questions/375257/how-can-i-aggregate-this-large-data-set-to-reduce-the-overhead-of-calculating-th/375303#375303 with this... if/when I get it working well...
49 *
50 * \par Example Usage
51 * \code
52 * unsigned int totalCallsCount{};
53 * Memoizer<int, LRUCache, int, int> memoizer{[&totalCallsCount](int a, int b) { ++totalCallsCount; return a + b; }};
54 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
55 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
56 *
57 * // or even simpler declare memoizer as:
58 * Memoizer memoizer = Cache::Factory::Memoizer::Make ([&totalCallsCount] (int a, int b) { totalCallsCount++; return a + b; });
59 * \endcode
60 *
61 * \see Factory::Memoizer::Make () for more simple to use examples.
62 *
63 * \note Memoizer works well with LRUCache, or TimedCache.
64 *
65 * \note \em Thread-Safety <a href="Thread-Safety.md">Same as (worse case of) underlying CACHE template argument, and argument function. Since the function will typically be fully reentrant, this comes down to the re-entrancy of the argument Cache.</a>
66 */
67 template <typename RESULT, template <qStroika_template_template_BWA (typename, typename)> class CACHE = LRUCache, typename... ARGS>
68 requires (ICache<CACHE<tuple<ARGS...>, RESULT>, tuple<ARGS...>, RESULT>)
69 class Memoizer {
70 public:
71 /**
72 */
73 Memoizer (const function<RESULT (ARGS...)>& f, CACHE<tuple<ARGS...>, RESULT>&& cache = CACHE<tuple<ARGS...>, RESULT>{});
74 Memoizer (Memoizer&& from) noexcept = default;
75 Memoizer (const Memoizer& from) = default;
76
77 public:
78 nonvirtual Memoizer& operator= (Memoizer&& rhs) noexcept = default;
79 nonvirtual Memoizer& operator= (const Memoizer& rhs) = default;
80
81 public:
82 /**
83 * \note this function is not const, because it modifies the state of the object/cache.
84 */
85 nonvirtual RESULT operator() (ARGS... args);
86
87 private:
88 function<RESULT (ARGS...)> fFunction_;
89 CACHE<tuple<ARGS...>, RESULT> fCache_;
90 };
91
92 namespace Factory::Memoizer {
93
94 /**
95 * @brief Factory function to make a memoizer out of any argument function.
96 *
97 * \par Example Usage (simple):
98 * \code
99 * unsigned int totalCallsCount{};
100 * Memoizer memoizer = Cache::Factory::Memoizer::Make ([&totalCallsCount] (int a, int b) {
101 * totalCallsCount++;
102 * return a + b;
103 * });
104 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
105 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
106 * \endcode
107 *
108 * \par Example Usage (force using LRUCache):
109 * \code
110 * unsigned int totalCallsCount{};
111 * Memoizer memoizer = Cache::Factory::Memoizer::Make<Cache::LRUCache> ([&totalCallsCount] (int a, int b) {
112 * totalCallsCount++;
113 * return a + b;
114 * });
115 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
116 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
117 * \endcode
118 *
119 * \par Example Usage (force using TimedCache):
120 * \code
121 * unsigned int totalCallsCount{};
122 * Memoizer memoizer = Cache::Factory::Memoizer::Make<Cache::TimedCache> ([&totalCallsCount] (int a, int b) {
123 * totalCallsCount++;
124 * return a + b;
125 * });
126 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
127 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
128 * \endcode
129 *
130 * \par Example Usage (use InternallySyncrhonized - or other special/custom cache):
131 * \code
132 * using namespace Cache::LRUCacheSupport;
133 * unsigned int totalCallsCount{};
134 * // use internally synchronized cache for memoizer
135 * template <typename K, typename V>
136 * using MyCache_ = Cache::LRUCache<K,V,InternallySynchronizedTraits<DefaultTraits<K,V>>>;
137 * Memoizer memoizer = Cache::Factory::Memoizer::Make<MyCache_> ([&totalCallsCount] (int a, int b) {
138 * totalCallsCount++;
139 * return a + b;
140 * });
141 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
142 * EXPECT_TRUE (memoizer (1, 1) == 2 and totalCallsCount == 1);
143 * \endcode
144 *
145 */
146 template <template <qStroika_template_template_BWA (typename, typename)> typename CACHE = Cache::LRUCache, typename FUNCTION>
147 auto Make (FUNCTION&& f);
148
149 }
150
151}
152
153/*
154 ********************************************************************************
155 ***************************** Implementation Details ***************************
156 ********************************************************************************
157 */
158#include "Memoizer.inl"
159
160#endif /*_Stroika_Foundation_Cache_Memoizer_h_*/
LRUCache implements a simple least-recently-used caching strategy, with optional hashing (of keys) to...
Definition LRUCache.h:224
Cache the results of expensive computations transparently.
Definition Memoizer.h:69