Skip to content

Instantly share code, notes, and snippets.

@Gnomorian
Created June 7, 2023 02:25
Show Gist options
  • Save Gnomorian/bd81ee29ddcc01eb794a97217eebf237 to your computer and use it in GitHub Desktop.
Save Gnomorian/bd81ee29ddcc01eb794a97217eebf237 to your computer and use it in GitHub Desktop.
LRU C++ std implementation example
#pragma once
#include <list>
#include <unordered_map>
#include <optional>
#include <iostream>
#include <typeinfo>
template<typename K, typename V, size_t MaxEntries>
class LRUCache
{
public:
using key = K;
using value_type = V;
bool put(const K& k, const V& v)
{
if (index.contains(k))
{
return false;
}
if (size() >= MaxEntries)
{
// find the oldest movable buffer so we can remove it
auto itemToRemove{ items.rbegin() };
if (itemToRemove != items.rend())
{
index.erase(itemToRemove->first);
// convert the reverse iterator to a forward iterator, removing the item.
auto forwardIter{ itemToRemove.base() };
if (forwardIter != items.end())
{
items.erase(--forwardIter);
}
else
{
items.pop_back();
}
}
}
items.emplace_front(k, v);
index.emplace(k, items.begin());
return true;
}
std::optional<V*> get(const K& k)
{
if (auto itr{ index.find(k) }; itr == index.end())
{
return {};
}
else
{
items.splice(items.begin(), items, itr->second);
return &(itr->second->second);
}
}
void erase(const K& k)
{
auto itr{ index.find(k) };
if (itr->second->second.cacheMoveable())
{
items.erase(itr->second);
index.erase(itr);
}
}
template<typename C>
void forEach(C& callback) const
{
for (auto& [k, v] : items)
{
callback(k, v);
}
}
size_t size() const
{
return items.size();
}
template<typename C> friend std::basic_ostream<C>& operator<<(std::basic_ostream<C>& stream, LRUCache<key, value_type, MaxEntries>& lru)
{
stream << typeid(lru).name() << "\n";
auto printPair = [i=0, &stream](const std::string& key, const int value) mutable
{
stream << i << ") Key=" << key << ", value=" << value << "\n";
++i;
};
lru.forEach(printPair);
return stream;
}
private:
/*std::list stores items (pair<K,V>) in
most-recently-used to least-recently-used order.*/
std::list<std::pair<K, V>> items;
//unordered_map acts as an index to the items store above.
std::unordered_map<K, typename decltype(items)::iterator> index;
};
#include "lru.h"
#include <iostream>
int main()
{
LRUCache<std::string, int, 3> cache;
cache.put("london", 10);
cache.put("New York", 8);
cache.put("Paris", 5);
std::cout << "original state and order after inserting 3 elements.\n" << cache << std::endl;
cache.get("london");
cache.get("New York");
std::cout << "after getting london and new york, paris should be the oldest\n" << cache << std::endl;
cache.put("Christchurch", 10);
std::cout << "added christchurch, paris should be removed.\n" << cache << std::endl;
}
/*
output:
original state and order after inserting 3 elements.
class LRUCache<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int,3>
0) Key=Paris, value=5
1) Key=New York, value=8
2) Key=london, value=10
after getting london and new york, paris should be the oldest
class LRUCache<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int,3>
0) Key=New York, value=8
1) Key=london, value=10
2) Key=Paris, value=5
added christchurch, paris should be removed.
class LRUCache<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int,3>
0) Key=Christchurch, value=10
1) Key=New York, value=8
2) Key=london, value=10
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment