22#ifndef LSST_CPPUTILS_CACHE_H
23#define LSST_CPPUTILS_CACHE_H
29#include "boost/multi_index_container.hpp"
30#include "boost/multi_index/sequenced_index.hpp"
31#include "boost/multi_index/hashed_index.hpp"
32#include "boost/multi_index/composite_key.hpp"
33#include "boost/multi_index/member.hpp"
34#include "boost/format.hpp"
42#ifdef LSST_CACHE_DEBUG
74template <
typename Key,
typename Value,
typename KeyHash,
typename KeyPred>
86 _container.template get<Hash>().reserve(maxElements);
87#ifdef LSST_CACHE_DEBUG
88 _debuggingEnabled =
false;
91 _requests.reserve(maxElements);
102#ifdef LSST_CACHE_DEBUG
130 template <
typename Generator>
154 void add(Key
const&
key, Value
const& value);
189 auto result = _lookup(
key);
217#ifdef LSST_CACHE_DEBUG
218 void enableDebugging() { _debuggingEnabled =
true; }
227 _container.template get<Sequence>().pop_back();
239 typedef boost::multi_index_container<
241 boost::multi_index::indexed_by<
242 boost::multi_index::sequenced<boost::multi_index::tag<Sequence>>,
243 boost::multi_index::hashed_unique<
244 boost::multi_index::tag<Hash>,
245 boost::multi_index::member<Element, Key, &Element::first>,
246 KeyHash>>> Container;
254 auto const& hashContainer = _container.template get<Hash>();
255 auto it = hashContainer.find(key);
256 bool found = (it != hashContainer.end());
258 _container.relocate(_container.template get<Sequence>().begin(),
259 _container.template project<Sequence>(it));
261#ifdef LSST_CACHE_DEBUG
262 if (_debuggingEnabled) {
263 _requests.push_back(key);
272 void _addNew(Key
const& key, Value
const& value) {
273 _container.template get<Sequence>().emplace_front(key, value);
278 Container _container;
279#ifdef LSST_CACHE_DEBUG
280 bool _debuggingEnabled;
292template <
typename Key,
typename Value,
typename KeyHash,
typename KeyPred>
293template <
typename Generator>
298 auto result = _lookup(key);
300 return result.first->second;
302 Value value = func(key);
307template <
typename Key,
typename Value,
typename KeyHash,
typename KeyPred>
309 auto result = _lookup(key);
311 return result.first->second;
314 (boost::format(
"Unable to find key: %s") % key).str());
317template <
typename Key,
typename Value,
typename KeyHash,
typename KeyPred>
319 auto result = _lookup(key);
320 if (!result.second) {
325template <
typename Key,
typename Value,
typename KeyHash,
typename KeyPred>
328 result.reserve(size());
329 for (
auto & keyValue : _container.template get<Sequence>()) {
330 result.push_back(keyValue.first);
335template <
typename Key,
typename Value,
typename KeyHash,
typename KeyPred>
338 _container.template get<Sequence>().pop_back();
342#ifdef LSST_CACHE_DEBUG
343template <
typename Key,
typename Value,
typename KeyHash,
typename KeyPred>
345 if (!_debuggingEnabled) {
348 std::string filename = (boost::format(
"lsst-cache-%s-%d.dat") %
351 for (
auto const& key : _requests) {
355 std::cerr <<
"Wrote cache requests to " << filename <<
": " << _hits <<
"/" << _total <<
" hits";
361namespace utils = cpputils;
#define LSST_EXCEPT(type,...)
Cache of most recently used values.
std::vector< Key > keys() const
Return all keys in the cache, most recent first.
std::size_t size() const
Return the number of values in the cache.
Value operator()(Key const &key, Generator func)
Lookup or generate a value.
bool contains(Key const &key)
Does the cache contain the key?
Cache(Cache const &)=default
std::size_t capacity() const
Return the capacity of the cache.
void flush()
Empty the cache.
void add(Key const &key, Value const &value)
Add a value to the cache.
Cache(std::size_t maxElements=0)
Ctor.
Cache & operator=(Cache &&)=default
Value operator[](Key const &key)
std::optional< Value > get(Key const &key)
Return the cached value if it exists.
void reserve(std::size_t maxElements)
Change the capacity of the cache.
Cache & operator=(Cache const &)=default
std::string demangleType(std::string const _typeName)