Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
LRUCache.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Cache_LRUCache_h_
5#define _Stroika_Foundation_Cache_LRUCache_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <optional>
10#include <vector>
11
12#include "Stroika/Foundation/Cache/Common.h"
15#include "Stroika/Foundation/Common/Common.h"
17#include "Stroika/Foundation/Common/Concepts.h"
19#include "Stroika/Foundation/Containers/Mapping.h"
23
24/**
25 * \file
26 *
27 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
28 *
29 * TODO:
30 * @todo Currently we have redundant storage - _Buf, and _First, and _Last (really just need _Buf cuz
31 * has first/last, or do our own storage managemnet with secondary array? - we do the mallocs/frees.
32 * To re-free, even though order distorted by shuffles, we can always figure out which was
33 * the original array head by which has the lowest address!
34 *
35 * Also somewhat related, _Last usage is C++ unconvnetional - though maybe OK. If not more awkward
36 * in impl, consider using _fEnd? Or if it is (I think last maybe better then document clearly why
37 * its better.
38 */
39
41
42 /**
43 * LRUCacheSupport mostly for defining TRAITS object that configures the cache behavior.
44 */
45 namespace LRUCacheSupport {
46
48
49 /**
50 * @brief Check if argument TRAITS is a valid TRAITS object for TimedCache<>
51 *
52 * \note ONE of (but not both) - kTrackFreshness or kTrackExpiration
53 * \note kTrackExpiration not compatible with kAutomaticallyMarkDataAsRefreshedEachTimeAccessed
54 * \note valid meaningful TRAITS::kDefaultMaxAge or TRAITS::kPerCacheMaxAge
55 * \note KeyEqualsCompareFunctionType must always be a valid key comparer (unless/until we support KEY=void)
56 * \note KeyHashFunctionType must be a valid hash function on KEY or nullptr_t, or KEY=void (NOT YET CHECKED)
57 */
58 template <typename TRAITS, typename KEY, typename VALUE>
60 requires (TRAITS) {
61 typename TRAITS::KeyType;
62 typename TRAITS::ValueType;
63 typename TRAITS::StatsType;
64 typename TRAITS::KeyEqualsCompareFunctionType;
65 { TRAITS::kInternallySynchronized } -> convertible_to<InternallySynchronized>;
66 } and same_as<typename TRAITS::KeyType, KEY> and same_as<typename TRAITS::ValueType, VALUE> and
69
70 /**
71 * The DefaultTraits<> is a simple default traits implementation for building an TimedCache<>.
72 *
73 * \note This class was incompatibly changed in Stroika 3.0d1. It used to have a TRACK_READ_ACCESS parameter.
74 * Since Stroika 3.0d1, instead, if you wish to set that true, call Lookup (..., eTreatFoundThroughLookupAsRefreshed) instead
75 * of Lookup ()
76 *
77 * \note this class was incompatibly changed in Stroika 3.0d23. It used to have the STRICT_INORDER_COPARER as third arugment
78 * but InternallySynchronized added as new third pushing comarer to fourth.
79 *
80 * \note Use of this directly IS allowed, but its fragile, as there isn't a good way to overload or evolve definition over time
81 * so code using this directly will be more likely to not be backward compatible in the future. Better to use adapters like InternallySynchronizedTraits
82 *
83 * \note We allow KEY_EQUALS_COMPARER to be invalid (not IEqualsComparable) - cuz ExplicitTraits maybe used as part of an expression
84 * going through temporary invalid values (defaults) later overriden by WithKeyComparerTraits - for example. We just
85 * require the final TRAITs object handed to LRUCache be VALID.
86 *
87 * \see ITraits<> above
88 */
89 template <IKey KEY, IValue VALUE, InternallySynchronized INTERNALLY_SYNCHRONIZED, typename KEY_EQUALS_COMPARER, typename KEY_HASH_FUNCTION, Cache::Statistics::IStatsType STATS_TYPE>
91 /**
92 */
93 using KeyType = KEY;
94
95 /**
96 */
97 using ValueType = VALUE;
98
99 /**
100 */
101 using KeyEqualsCompareFunctionType = KEY_EQUALS_COMPARER;
102
103 /**
104 */
105 using KeyHashFunctionType = KEY_HASH_FUNCTION; // @todo support function TYPE and VALUE
106
107 /**
108 * @brief This 'automatic synchronization' feature is off (eNotKnownInternallySynchronized) by default, but can easily
109 * be turned on with InternallySynchronizedTraits
110 */
111 static constexpr inline InternallySynchronized kInternallySynchronized{INTERNALLY_SYNCHRONIZED};
112
113 /**
114 * @brief Internally synchronized 'Stats' collector type (Cache::Statistics::IStatsType). Often null stats collector.
115 */
116 using StatsType = STATS_TYPE;
117 };
118
119 /**
120 * @brief Default choices for LRUCache, if you don't specify anything else.
121 *
122 * @tparam KEY
123 * @tparam VALUE
124 */
125 template <IKey KEY, IValue VALUE>
128
129 /**
130 * @brief InternallySynchronizedTraits same as argument traits, but resetting the kInternallySynchronized to eInternallySynchronized
131 *
132 * @tparam TRAITS
133 */
134 template <typename TRAITS>
137 static constexpr inline Execution::InternallySynchronized kInternallySynchronized{Execution::InternallySynchronized::eInternallySynchronized};
138 };
139
140 /**
141 * @brief WithKeyComparerTraits same as argument traits, but resetting the KeyEqualsCompareFunctionType
142 *
143 * @tparam TRAITS
144 */
145 template <typename TRAITS, Common::IEqualsComparer<typename TRAITS::KeyType> KEY_EQUALS_COMPARER = equal_to<typename TRAITS::KeyType>>
146 struct WithKeyComparerTraits : TRAITS {
147 using KeyEqualsCompareFunctionType = KEY_EQUALS_COMPARER; // @todo support function TYPE and VALUE
148 };
149
150 /**
151 * @brief WithKeyHashTraits same as argument traits, but resetting the KeyEqualsCompareFunctionType
152 *
153 * @tparam TRAITS
154 */
155 template <typename TRAITS, typename KEY_HASH_FUNCTION = hash<typename TRAITS::KeyType>>
156 struct WithKeyHashTraits : TRAITS {
157 using KeyHashFunctionType = KEY_HASH_FUNCTION; // @todo support function TYPE and VALUE
158 };
159
160 }
161
162 /**
163 * \brief LRUCache implements a simple least-recently-used caching strategy, with optional hashing (of keys) to make it faster.
164 *
165 * \note Comparison with TimedCache
166 * The main difference between an LRUCache and TimedCache has to do with when an element is evicted from the Cache.
167 * With a TimedCache, its evicted only when its overly aged (now - when added to cache). With an LRUCache, its more random, and depends a
168 * bit on luck (when using hashing) and how recently an item was last accessed.
169 *
170 * With a TimedCache, you track 'time' associated with each cache element. With LRUCache, no such timing association exists.
171 *
172 *
173 * \note LRUCache (soon will) support(s) IValuelessCache, but requires IKeyedCache (cuz the KEY is critical to how LRUCache works - use TimedCache for
174 * keyless cache)).
175 *
176 * \par Example Usage
177 * \code
178 * LRUCache<string, string> tmp{3}; // no hashing used in cache
179 * tmp.Add ("a", "1");
180 * tmp.Add ("b", "2");
181 * tmp.Add ("c", "3");
182 * tmp.Add ("d", "4");
183 * EXPECT_TRUE (not tmp.Lookup ("a").has_value ());
184 * EXPECT_TRUE (tmp.Lookup ("b") == "2");
185 * EXPECT_TRUE (tmp.Lookup ("d") == "4");
186 * \endcode
187 *
188 * \par Example Usage
189 * \code
190 * // using deduction guides, and hash table of size 10
191 * LRUCache tmp{pair<string, string>{}, 3, 10, hash<string>{}};
192 * tmp.Add ("a", "1");
193 * tmp.Add ("b", "2");
194 * tmp.Add ("c", "3");
195 * tmp.Add ("d", "4");
196 * EXPECT_TRUE (not tmp.Lookup ("a").has_value () or *tmp.Lookup ("a") == "1"); // could be missing or found but if found same value
197 * EXPECT_TRUE (tmp.Lookup ("b") == "2");
198 * EXPECT_TRUE (tmp.Lookup ("d") == "4");
199 * \endcode
200 *
201 * \note LRUCache destroys objects when they are cleared from the cache. This guarantee is
202 * relevant only in case where the objects use significant resources, or where their lifetime has
203 * externally visible (e.g. lockfiles) impact.
204 *
205 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
206 * o No comparison of LRUCache objects is currently supported. It might make sense, but would be of questionable use.
207 *
208 * \note Satisfies Concepts:
209 * o ICache<LRUCache<KEY,VALUE>,KEY,VALUE>
210 * o moveable<LRUCache<KEY,VALUE>>
211 * o copyable<LRUCache<KEY,VALUE>>
212 *
213 * \par Implementation Note:
214 * Private (_) routines dont hold locks - the public ones do. And in unsyncrhonized builds, the 'locks' aren't really locks
215 * but Debug::AssertExternallySyncrhonizedMutex - to detect bad usage.
216 *
217 * \note \em Thread-Safety if (TRAITS::kInternallySynchronized == eInternallySynchronized) <a href='#Internally-Synchronized-Thread-Safety'>Internally-Synchronized-Thread-Safety</a>
218 * \note \em Thread-Safety if (TRAITS::kInternallySynchronized == eNotKnownInternallySynchronized) <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
219 *
220 * \note we REQUIRE (without a way to enforce) - that the STATS object be internally synchronized, so that we can
221 * maintain statistics, without requiring the lookup method be non-const; this is only for tuning/debugging, anyhow...
222 */
223 template <typename KEY, typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS = LRUCacheSupport::DefaultTraits<KEY, VALUE>>
224 class LRUCache {
225 public:
226 using TraitsType = TRAITS;
227
228 public:
229 /**
230 */
231 using KeyType = typename TRAITS::KeyType;
232
233 public:
234 /**
235 */
236 using ValueType = typename TRAITS::ValueType;
237
238 public:
239 /**
240 */
241 using KeyEqualsCompareFunctionType = typename TRAITS::KeyEqualsCompareFunctionType;
242
243 public:
244 /**
245 */
246 using KeyHashFunctionType = typename TRAITS::KeyHashFunctionType;
247
248 public:
249 /**
250 */
251 using StatsType = typename TRAITS::StatsType;
252
253 public:
254 /**
255 * There are two basic kinds of LRUCache - with hashing, and without.
256 *
257 * If there is no KEY_HASH_FUNCTION (==nullptr) - then the GetHashTableSize () always returns 1;
258 *
259 * Note the hash function can be hash<KEY_TYPE>{}, and this is one of the constructor defaults.
260 *
261 * Note cannot move easily because this contains internal pointers (fCachedElts_First_): still declare move CTOR, but its not
262 * noexcept because its really copying...
263 *
264 * Because of a couple key limitations/constraints in C++ (as of C++20) - you cannot both do template argument deduction, and default parameters).
265 * This greatly constrains how the class works (at least constructors).
266 *
267 * So this is somewhat subject to change as the language evolves (or my understanding of tricks evolves). But for now, deduction is limited.
268 *
269 * \par Example Usage
270 * \code
271 * LRUCache<string, string> tmp{3}; // no hashing, size 3, no deduced types (just defaulted ones)
272 * LRUCache t0{Factory::LRUCache::Maker<string, string>{}(3, 3)};
273 * LRUCache t1{Factory::LRUCache::Maker<String, string>{}(3, 3, hashFunction)}; // types (except key/value) deducted from arguments
274 * \endcode
275 *
276 * \todo default CTOR requires no hashing, but we could make hashing work in this case with default params - just not worth it yet --LGP 2023-12-06
277 */
278 LRUCache ()
279 requires (same_as<typename TRAITS::KeyHashFunctionType, nullptr_t> and
280 same_as<typename TRAITS::KeyEqualsCompareFunctionType, equal_to<KEY>>);
281 LRUCache (size_t maxCacheSize, const typename TRAITS::KeyEqualsCompareFunctionType& keyEqualsComparer = {})
282 requires (same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>);
283 LRUCache (size_t maxCacheSize, const typename TRAITS::KeyEqualsCompareFunctionType& keyEqualsComparer = {}, size_t hashTableSize = 1,
284 const typename TRAITS::KeyHashFunctionType& hashFunction = typename TRAITS::KeyHashFunctionType{})
285 requires (not same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>);
286 LRUCache (size_t maxCacheSize, size_t hashTableSize,
287 const typename TRAITS::KeyHashFunctionType& hashFunction = typename TRAITS::KeyHashFunctionType{})
288 requires (not same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>);
289
290 LRUCache (LRUCache&& from);
291 LRUCache (const LRUCache& from)
292 requires (same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>);
293 LRUCache (const LRUCache& from)
294 requires (not same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>);
295
296 public:
297 /**
298 */
299 nonvirtual LRUCache& operator= (LRUCache&& rhs) noexcept;
300 nonvirtual LRUCache& operator= (const LRUCache& rhs);
301
302 public:
303 /**
304 */
305 nonvirtual size_t GetMaxCacheSize () const;
306
307 public:
308 /**
309 * Size given maybe automatically adjusted upward to be a multiple of GetHashTableSize ()
310 */
311 nonvirtual void SetMaxCacheSize (size_t maxCacheSize);
312
313 public:
314 /**
315 */
316 nonvirtual KeyEqualsCompareFunctionType GetKeyEqualsCompareFunction () const;
317
318 public:
319 /**
320 */
321 nonvirtual StatsType GetStats () const;
322
323 public:
324 /**
325 */
326 nonvirtual size_t GetHashTableSize () const;
327
328 public:
329 /**
330 */
331 nonvirtual KeyHashFunctionType GetKeyHashFunction () const;
332
333 public:
334 /**
335 * Clear all, or just the given elements from the cache.
336 *
337 * @todo unsure about best naming - TimedCache uses RemoveAll () - clear nice, but doesn't match usual meaning in STL, which is my convention for lower-case stl names...
338 */
339 nonvirtual void clear ();
340 nonvirtual void clear (typename Common::ArgByValueType<KEY> key);
341 nonvirtual void clear (function<bool (typename Common::ArgByValueType<KEY>)> clearPredicate);
342
343 public:
344 /**
345 * @todo see clear(function) and resolve...
346 */
347 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (predicate<KEY>) PREDICATE>
348 nonvirtual void RemoveAll (PREDICATE&& removeIfReturnsTrue);
349
350 public:
351 /**
352 * The value associated with KEY may not be present, so an missing optional value is returned.
353 *
354 * \note Unintuitively, Lookup () is non-const **intentionally** - because it updates internal data structures to track the most recently accessed item. This has implication for thread-safety!
355 *
356 * @see LookupValue ()
357 */
358 template <typename V = VALUE>
359 requires (not IValuelessCache<V>)
360 nonvirtual optional<V> Lookup (typename Common::ArgByValueType<KEY> key);
361 template <typename V = VALUE>
362 requires (IValuelessCache<V>)
363 nonvirtual optional<KEY> Lookup (typename Common::ArgByValueType<KEY> key);
364
365 public:
366 /**
367 * LookupValue () finds the value in the cache, and returns it, or if not present, uses the argument valueFetcher to retrieve it.
368 *
369 * So LookupValue (v) is equivalent to:
370 * \code
371 * if (auto o = Lookup (k)) {
372 * return o;
373 * }
374 * else {
375 * auto v = valueFetcher (k);
376 * Add (k, v);
377 * return v;
378 * }
379 * \endcode
380 *
381 * \par Example Usage
382 * \code
383 * struct Details_ {
384 * };
385 * using DetailsID = int;
386 * Details_ ReadDetailsFromFile_ (DetailsID id);
387 *
388 * Execution::Synchronized<LRUCache<DetailsID, Details_>> fDetailsCache_; // caches often helpful in multithreaded situations
389 *
390 * // returns the value from LRUCache, or automatically pages it in from file
391 * Details_ GetDetails (DetailsID id) {
392 * return
393 * fDetailsCache_->LookupValue (
394 * id,
395 * [] (DetailsID id) -> Details_ { return ReadDetailsFromFile_ (id); }
396 * );
397 * }
398 * \endcode
399 *
400 * \note - LookupValue () only caches successful lookups, and propagates any exceptions looking up.
401 * To negatively cache, be sure you use an optional<X> for the VALUE type, and then you can wrap
402 * the LookupValue function with try/catch and on failure, cache nullopt.
403 */
404 nonvirtual VALUE LookupValue (typename Common::ArgByValueType<KEY> key, const function<VALUE (typename Common::ArgByValueType<KEY>)>& valueFetcher);
405
406 public:
407 /**
408 * Add the given value to the cache. This is rarely directly used.
409 * Typically you Lookup with something like LookupValue() which implicitly does the adds.
410 */
411 template <typename V = VALUE>
412 requires (not IValuelessCache<V>)
413 nonvirtual void Add (typename Common::ArgByValueType<KEY> key, typename Common::ArgByValueType<V> value);
414 template <typename V = VALUE>
415 requires (IValuelessCache<V>)
416 nonvirtual void Add (typename Common::ArgByValueType<KEY> key);
417
418 private:
419 // like Add () but with no lock (assumes caller/public APIs lock)
420 nonvirtual void Add_ (typename Common::ArgByValueType<KEY> key, typename Common::ArgByValueType<VALUE> value);
421
422 public:
423 /**
424 * Collect all the elements of the cache, where mapping KEY and VALUE correspond to cache KEY and VALUE.
425 */
426 nonvirtual Containers::Mapping<KEY, VALUE> Elements () const;
427
428 public:
429 // * \note the overloads taking pair<KEY, VALUE> as the first argument are just tricks to allow deduction guides to
430 // work (because* you cannot specify some template parameters and then have deduction guides take effect)
431 // .
432 // find better way todo deduction guides so I can deprecate this
433 [[deprecated ("Since Stroika v3.0d5 use Cache::Factory::LRUCache_WithHash or NoHash")]] LRUCache (
434 pair<KEY, VALUE> ignored, size_t maxCacheSize = 1, const typename TRAITS::KeyEqualsCompareFunctionType& keyEqualsComparer = {},
435 size_t hashTableSize = 1, const typename TRAITS::KeyHashFunctionType& hashFunction = typename TRAITS::KeyHashFunctionType{})
436 : LRUCache{maxCacheSize, keyEqualsComparer, hashTableSize, hashFunction}
437 {
438 }
439 [[deprecated ("Since Stroika v3.0d5 use Cache::Factory::LRUCache_WithHash or NoHash")]] LRUCache (
440 pair<KEY, VALUE> ignored, size_t maxCacheSize, size_t hashTableSize,
441 const typename TRAITS::KeyHashFunctionType& hashFunction = hash<KEY>{})
442 : LRUCache{maxCacheSize, hashTableSize, hashFunction}
443 {
444 }
445
446 private:
447 const size_t fHashtableSize_{1};
448
449 private:
450 struct KeyValuePair_ {
451 KEY fKey;
452 qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE conditional_t<IValuelessCache<VALUE>, Common::Empty, VALUE> fValue;
453 };
454
455 private:
456 // invoke selected hash function, and return number 0..fHashtableSize_
457 nonvirtual size_t H_ (typename Common::ArgByValueType<KEY> k) const;
458
459 private:
460 // note if shared_mutex, it must be mutable, cuz shared locks still must be done
461 using MaybeMutexType_ =
462 conditional_t<TRAITS::kInternallySynchronized == Execution::InternallySynchronized::eInternallySynchronized, shared_timed_mutex, Debug::AssertExternallySynchronizedMutex>;
463 qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE mutable MaybeMutexType_ fMaybeMutex_;
464
465 private:
466 qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE const KeyEqualsCompareFunctionType fKeyEqualsComparer_;
467 qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE const typename TRAITS::KeyHashFunctionType fHashFunction_;
469
470 struct CacheElement_;
471 struct CacheIterator_;
472
473 nonvirtual CacheIterator_ begin_ () const;
474 nonvirtual CacheIterator_ end_ () const;
475
476 nonvirtual void ClearCache_ ();
477
478 /*
479 * Create a new LRUCache_ element (potentially bumping some old element out of the cache). This new element
480 * will be considered most recently used. Note that this routine re-orders the cache so that the most recently looked
481 * up element is first, and because of this re-ordering, its illegal to do a Lookup while
482 * a @'LRUCache_<ELEMENT>::CacheIterator_' exists for this LRUCache_.</p>
483 */
484 nonvirtual optional<KeyValuePair_>* AddNewButDontFillIn_ (typename Common::ArgByValueType<KeyType> item);
485
486 /*
487 * Check and see if the given element is in the cache. Return that element if its there, and nullptr otherwise.
488 * Note that this routine re-orders the cache so that the most recently looked up element is first, and because
489 * of this re-ordering, its illegal to do a Lookup while a @'LRUCache_<ELEMENT>::CacheIterator_' exists
490 * for this LRUCache_.
491 */
492 nonvirtual optional<KeyValuePair_>* LookupElement_ (typename Common::ArgByValueType<KeyType> item);
493
494 /*
495 */
496 nonvirtual void ShuffleToHead_ (size_t chainIdx, CacheElement_* b);
497
498 static constexpr size_t kPreallocatedHashtableSize_ =
499 same_as<typename TRAITS::KeyHashFunctionType, nullptr_t> ? 1 : 5; // size where no memory allocation overhead for lrucache
500 Memory::InlineBuffer<vector<CacheElement_>, kPreallocatedHashtableSize_> fCachedElts_BUF_{};
501 Memory::InlineBuffer<CacheElement_*, kPreallocatedHashtableSize_> fCachedElts_First_{};
502 Memory::InlineBuffer<CacheElement_*, kPreallocatedHashtableSize_> fCachedElts_Last_{};
503 };
504 static_assert (ICache<LRUCache<int, int>, int, int>); // see Satisfies Concepts
505 static_assert (movable<LRUCache<int, int>>);
506 static_assert (copyable<LRUCache<int, int>>);
507
508 /**
509 */
510 namespace Factory::LRUCache {
511 using Execution::InternallySynchronized;
512
513 /**
514 * @brief Utility to make it easier to construct a LRUCache constexpr/type name from a few parameters and types.
515 *
516 * \note MAYBE replace this with deduction guides, but not clear how?
517 *
518 * @tparam KEY
519 * @tparam VALUE
520 * @param InternallySynchronized (defaults to eNotKnownInternallySynchronized)
521 * @tparam STATS_TYPE (defaults to Statistics::StatsType_DEFAULT)
522 *
523 * \par Example Usage
524 * \code
525 * auto t0{Factory::LRUCache::Maker<string, string>{}()};
526 * auto t1{Factory::LRUCache::Maker<string, string>{}(3)};
527 * LRUCache t2{Factory::LRUCache::Maker<string, string>{}(3, kStringCIComparer_)};
528 * // Add eInternallySynchronized just as 3rd template arg to Maker<>
529 * LRUCache t3{Factory::LRUCache::Maker<string, string,InternallySynchronized::eInternallySynchronized>{}(3, kStringCIComparer_)};
530 * \endcode
531 *
532 * \par Example Usage
533 * \code
534 * auto t0{Factory::LRUCache::Maker<string, string>{}(3, 3)};
535 * auto t1{Factory::LRUCache::Maker<String, string>{}(3, 3, hashFunction)};
536 * LRUCache t2{Factory::LRUCache::Maker<String, string>{}(3, equal_to<String>{}, 3)};
537 * LRUCache t3{Factory::LRUCache::Maker<String, string, Statistics::Stats_Basic>{}(3, equal_to<String>{}, 3)}; // throw in stats object
538 * LRUCache t4{Factory::LRUCache::Maker<String, string>{}(3, kStringCIComparer_, 3)}; // alt equality comparer
539 * \endcode
540 */
541 template <typename KEY, typename VALUE, InternallySynchronized internallySynchronized = InternallySynchronized::eNotKnownInternallySynchronized,
542 typename STATS_TYPE = Statistics::StatsType_DEFAULT>
543 struct Maker {
544 /**
545 * @brief NOHASH versions
546 */
547#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _HAS_CXX23 /*vis studio uses _HAS_CXX23 */
548 template <Common::IEqualsComparer<KEY> KEY_EQUALS_COMPARER = equal_to<KEY>>
549 static auto operator() (size_t maxCacheSize = 1, KEY_EQUALS_COMPARER&& keyComparer = {});
550#else
551 template <Common::IEqualsComparer<KEY> KEY_EQUALS_COMPARER = equal_to<KEY>>
552 auto operator() (size_t maxCacheSize = 1, KEY_EQUALS_COMPARER&& keyComparer = {}) const;
553#endif
554
555 /**
556 * @brief Hashing versions
557 */
558 template <typename KEY_HASH_FUNCTION = hash<KEY>>
559#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _HAS_CXX23 /*vis studio uses _HAS_CXX23 */
560 static auto operator() (size_t maxCacheSize, size_t hashTableSize, KEY_HASH_FUNCTION&& hashFunction = {});
561#else
562 auto operator() (size_t maxCacheSize, size_t hashTableSize, KEY_HASH_FUNCTION&& hashFunction = {}) const;
563#endif
564 template <typename KEY_EQUALS_COMPARER = equal_to<KEY>, typename KEY_HASH_FUNCTION = hash<KEY>>
565#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _HAS_CXX23 /*vis studio uses _HAS_CXX23 */
566 static auto operator() (size_t maxCacheSize, KEY_EQUALS_COMPARER&& keyComparer, size_t hashTableSize,
567 KEY_HASH_FUNCTION&& hashFunction = {});
568#else
569 auto operator() (size_t maxCacheSize, KEY_EQUALS_COMPARER&& keyComparer, size_t hashTableSize, KEY_HASH_FUNCTION&& hashFunction = {}) const;
570#endif
571 };
572 }
573
574}
575
576/*
577 ********************************************************************************
578 ***************************** Implementation Details ***************************
579 ********************************************************************************
580 */
581#include "LRUCache.inl"
582
583#endif /*_Stroika_Foundation_Cache_LRUCache_h_*/
conditional_t< qStroika_Foundation_Debug_AssertionsChecked, Stats_Basic, Stats_Null > StatsType_DEFAULT
#define qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:445
LRUCache implements a simple least-recently-used caching strategy, with optional hashing (of keys) to...
Definition LRUCache.h:224
nonvirtual void RemoveAll(PREDICATE &&removeIfReturnsTrue)
A KEY is any copyable value (or the sentinal type void - indicating a keyless - single valued - cache...
any copyable type can use used as the value, or the special sentinal type - ValuelessSentinalType,...
Check if argument TRAITS is a valid TRAITS object for TimedCache<>
Definition LRUCache.h:59
conditional_t<(sizeof(CHECK_T)<=2 *sizeof(void *)) and is_trivially_copyable_v< CHECK_T >, CHECK_T, const CHECK_T & > ArgByValueType
This is an alias for 'T' - but how we want to pass it on stack as formal parameter.
Definition TypeHints.h:36
Utility to make it easier to construct a LRUCache constexpr/type name from a few parameters and types...
Definition LRUCache.h:543
STATS_TYPE StatsType
Internally synchronized 'Stats' collector type (Cache::Statistics::IStatsType). Often null stats coll...
Definition LRUCache.h:116
static constexpr InternallySynchronized kInternallySynchronized
This 'automatic synchronization' feature is off (eNotKnownInternallySynchronized) by default,...
Definition LRUCache.h:111
InternallySynchronizedTraits same as argument traits, but resetting the kInternallySynchronized to eI...
Definition LRUCache.h:136
WithKeyComparerTraits same as argument traits, but resetting the KeyEqualsCompareFunctionType.
Definition LRUCache.h:146
WithKeyHashTraits same as argument traits, but resetting the KeyEqualsCompareFunctionType.
Definition LRUCache.h:156