Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 68 additions & 56 deletions include/cachemere/cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/rolling_mean.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/hana.hpp>

#ifdef _WIN32
# pragma warning(pop)
Expand Down Expand Up @@ -95,7 +94,7 @@ class Cache
/// If the provided container has `size()` and `reserve()` methods, `collect_into` will reserve
/// the appropriate amount of space in the container before inserting.
/// @param container The container in which to insert the items.
template<typename C> void collect_into(C& container) const;
template<detail::traits::CacheContainer<Key, Value> C> void collect_into(C& container) const;

/// @brief Insert a key/value pair in the cache.
/// @details If the key is new, the key/value pair will be inserted.
Expand Down Expand Up @@ -340,28 +339,26 @@ template<class K,
class SK,
class KH,
bool TS>
template<class Container>
template<detail::traits::CacheContainer<K, V> Container>
void Cache<K, V, I, E, C, SV, SK, KH, TS>::collect_into(Container& container) const
{
using namespace detail;

// Use emplace_back if container is a sequence container, or emplace if container is an associative container.
constexpr auto emplace_fn = boost::hana::if_(
traits::stl::has_emplace_back<Container, K, V>,
[](auto& seq_container, const auto& key, const auto& item) { seq_container.emplace_back(key, item.m_value); },
[](auto& assoc_container, const auto& key, const auto& item) { assoc_container.emplace(key, item.m_value); });

LockGuard guard(lock());

// Reserve space if the container has a reserve() method and a size method().
boost::hana::if_(
boost::hana::and_(traits::stl::has_reserve<Container>, traits::stl::has_size<Container>),
[&](auto& c) { c.reserve(c.size() + m_data.size()); },
[](auto&) {})(container);
if constexpr (traits::ReservableContainer<Container>) {
container.reserve(container.size() + m_data.size());
}

// Copy the cache contents to the container.
for (const auto& [key, cached_item] : m_data) {
emplace_fn(container, key, cached_item);
// Use emplace_back if container is a sequence container, or emplace if container is an associative container.
if constexpr (traits::SequenceContainer<Container, K, V>) {
container.emplace_back(key, cached_item.m_value);
} else {
container.emplace(key, cached_item.m_value);
}
}
}

Expand Down Expand Up @@ -518,8 +515,8 @@ void Cache<K, V, I, E, C, SV, SK, KH, TS>::swap(CacheType& other) noexcept
swap(m_hit_rate_acc, other.m_hit_rate_acc);
swap(m_byte_hit_rate_acc, other.m_byte_hit_rate_acc);
} catch (const std::system_error& e) {
// The only exception that can sensibly be thrown in the above block is a `system_error` when acquiring the mutexes of both caches (if the caches are
// running in thread-safe mode).
// The only exception that can sensibly be thrown in the above block is a `system_error` when acquiring the mutexes of both caches (if the caches
// are running in thread-safe mode).
//
// According to the [reference for std mutexes](https://en.cppreference.com/w/cpp/named_req/Mutex), this exception can be thrown for one of two
// reasons:
Expand Down Expand Up @@ -949,14 +946,14 @@ template<class K,
bool TS>
void Cache<K, V, I, E, C, SV, SK, KH, TS>::remove_popped_victim(DataMapIt it)
{
boost::hana::if_(
detail::traits::event::has_on_evict<K, KH, V, I>,
[&](auto& x) { return x.on_evict(it->first, it->second); },
[](auto&) {})(*m_insertion_policy);
boost::hana::if_(
detail::traits::event::has_on_evict<K, KH, V, C>,
[&](auto& x) { return x.on_evict(it->first, it->second); },
[](auto&) {})(*m_constraint_policy);
if constexpr (detail::traits::event::HasOnEvict<K, KH, V, I>) {
m_eviction_policy->on_evict(it->first, it->second);
Comment thread
dalloriam marked this conversation as resolved.
}

if constexpr (detail::traits::event::HasOnEvict<K, KH, V, C>) {
m_constraint_policy->on_evict(it->first, it->second);
}

m_data.erase(it);
}

Expand All @@ -972,11 +969,17 @@ template<class K,
void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_insert(const K& key, const CacheItem& item) const
{
// Call event handler iif the method is defined in the policy.
boost::hana::if_(detail::traits::event::has_on_insert<K, KH, V, I>, [&](auto& x) { return x.on_insert(key, item); }, [](auto&) {})(*m_insertion_policy);
if constexpr (detail::traits::event::HasOnInsert<K, KH, V, I>) {
m_insertion_policy->on_insert(key, item);
}

boost::hana::if_(detail::traits::event::has_on_insert<K, KH, V, E>, [&](auto& x) { return x.on_insert(key, item); }, [](auto&) {})(*m_eviction_policy);
if constexpr (detail::traits::event::HasOnInsert<K, KH, V, E>) {
m_eviction_policy->on_insert(key, item);
}

boost::hana::if_(detail::traits::event::has_on_insert<K, KH, V, C>, [&](auto& x) { return x.on_insert(key, item); }, [](auto&) {})(*m_constraint_policy);
if constexpr (detail::traits::event::HasOnInsert<K, KH, V, C>) {
m_constraint_policy->on_insert(key, item);
}
}

template<class K,
Expand All @@ -991,20 +994,17 @@ template<class K,
void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_update(const K& key, const CacheItem& old_item, const CacheItem& new_item) const
{
// Call event handler iif the method is defined in the policy.
boost::hana::if_(
detail::traits::event::has_on_update<K, KH, V, I>,
[&](auto& x) { return x.on_update(key, old_item, new_item); },
[](auto&) {})(*m_insertion_policy);

boost::hana::if_(
detail::traits::event::has_on_update<K, KH, V, E>,
[&](auto& x) { return x.on_update(key, old_item, new_item); },
[](auto&) {})(*m_eviction_policy);

boost::hana::if_(
detail::traits::event::has_on_update<K, KH, V, C>,
[&](auto& x) { return x.on_update(key, old_item, new_item); },
[](auto&) {})(*m_constraint_policy);
if constexpr (detail::traits::event::HasOnUpdate<K, KH, V, I>) {
m_insertion_policy->on_update(key, old_item, new_item);
}

if constexpr (detail::traits::event::HasOnUpdate<K, KH, V, E>) {
m_eviction_policy->on_update(key, old_item, new_item);
}

if constexpr (detail::traits::event::HasOnUpdate<K, KH, V, C>) {
m_constraint_policy->on_update(key, old_item, new_item);
}
}

template<class K,
Expand All @@ -1023,17 +1023,17 @@ void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_cache_hit(const K& key, const Cach
m_byte_hit_rate_acc(static_cast<uint32_t>(item.m_value_size));

// Call event handler iif the method is defined in the policy.
boost::hana::if_(
detail::traits::event::has_on_cachehit<K, KH, V, I>,
[&](auto& x) { return x.on_cache_hit(key, item); },
[](auto&) {})(*m_insertion_policy);
if constexpr (detail::traits::event::HasOnCacheHit<K, KH, V, I>) {
m_insertion_policy->on_cache_hit(key, item);
}

boost::hana::if_(detail::traits::event::has_on_cachehit<K, KH, V, E>, [&](auto& x) { return x.on_cache_hit(key, item); }, [](auto&) {})(*m_eviction_policy);
if constexpr (detail::traits::event::HasOnCacheHit<K, KH, V, E>) {
m_eviction_policy->on_cache_hit(key, item);
}

boost::hana::if_(
detail::traits::event::has_on_cachehit<K, KH, V, C>,
[&](auto& x) { return x.on_cache_hit(key, item); },
[](auto&) {})(*m_constraint_policy);
if constexpr (detail::traits::event::HasOnCacheHit<K, KH, V, C>) {
m_constraint_policy->on_cache_hit(key, item);
}
}

template<class K,
Expand All @@ -1053,11 +1053,17 @@ void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_cache_miss(const KeyView& key) con
m_byte_hit_rate_acc(0);

// Call event handler iif the method is defined in the policy.
boost::hana::if_(detail::traits::event::has_on_cachemiss<K, KH, V, I>, [&](auto& x) { return x.on_cache_miss(key); }, [](auto&) {})(*m_insertion_policy);
if constexpr (detail::traits::event::HasOnCacheMiss<K, KH, V, I>) {
m_insertion_policy->on_cache_miss(key);
}

boost::hana::if_(detail::traits::event::has_on_cachemiss<K, KH, V, E>, [&](auto& x) { return x.on_cache_miss(key); }, [](auto&) {})(*m_eviction_policy);
if constexpr (detail::traits::event::HasOnCacheMiss<K, KH, V, E>) {
m_eviction_policy->on_cache_miss(key);
}

boost::hana::if_(detail::traits::event::has_on_cachemiss<K, KH, V, C>, [&](auto& x) { return x.on_cache_miss(key); }, [](auto&) {})(*m_constraint_policy);
if constexpr (detail::traits::event::HasOnCacheMiss<K, KH, V, C>) {
m_constraint_policy->on_cache_miss(key);
}
}

template<class K,
Expand All @@ -1072,11 +1078,17 @@ template<class K,
void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_evict(const K& key, const CacheItem& item) const
{
// Call event handler iif the method is defined in the policy.
boost::hana::if_(detail::traits::event::has_on_evict<K, KH, V, I>, [&](auto& x) { return x.on_evict(key, item); }, [](auto&) {})(*m_insertion_policy);
if constexpr (detail::traits::event::HasOnEvict<K, KH, V, I>) {
m_insertion_policy->on_evict(key, item);
}

boost::hana::if_(detail::traits::event::has_on_evict<K, KH, V, E>, [&](auto& x) { return x.on_evict(key, item); }, [](auto&) {})(*m_eviction_policy);
if constexpr (detail::traits::event::HasOnEvict<K, KH, V, E>) {
m_eviction_policy->on_evict(key, item);
}

boost::hana::if_(detail::traits::event::has_on_evict<K, KH, V, C>, [&](auto& x) { return x.on_evict(key, item); }, [](auto&) {})(*m_constraint_policy);
if constexpr (detail::traits::event::HasOnEvict<K, KH, V, C>) {
m_constraint_policy->on_evict(key, item);
}
}

template<class K,
Expand Down
63 changes: 31 additions & 32 deletions include/cachemere/detail/traits.h
Original file line number Diff line number Diff line change
@@ -1,59 +1,58 @@
#pragma once

#include <boost/hana.hpp>
#include <concepts>
#include <utility>

#include <cachemere/item.h>

namespace cachemere::detail::traits {

// Traits for STL types.
namespace stl {

template<typename T>
constexpr auto has_reserve =
boost::hana::is_valid([](auto& t) -> decltype(boost::hana::traits::declval(t).reserve(std::declval<size_t>())) {})(boost::hana::type_c<T>);
template<typename T, typename... Args>
concept SequenceContainer = requires(T t, Args... args) {
{ t.emplace_back(args...) };
};

template<typename T>
constexpr auto has_size = boost::hana::is_valid([](auto& t) -> decltype(boost::hana::traits::declval(t).size()) {})(boost::hana::type_c<T>);
template<typename T, typename... Args>
concept AssociativeContainer = requires(T t, Args... args) {
{ t.emplace(args...) };
};

template<typename T, typename... Args>
constexpr auto has_emplace_back =
boost::hana::is_valid([](auto& t) -> decltype(boost::hana::traits::declval(t).emplace_back(std::declval<Args>()...)) {})(boost::hana::type_c<T>);
concept CacheContainer = SequenceContainer<T, Args...> || AssociativeContainer<T, Args...>;

} // namespace stl
template<typename T>
concept ReservableContainer = requires(T t) {
{ t.reserve(std::declval<size_t>()) };
{ t.size() } -> std::convertible_to<size_t>;
Comment thread
dalloriam marked this conversation as resolved.
};

// Traits for cache event handlers.
namespace event {

template<typename K, typename KH, typename V, template<class, class, class> typename P>
constexpr auto has_on_insert = boost::hana::is_valid(
[](auto& policy_t, auto& key_t, auto& item_t) -> decltype(boost::hana::traits::declval(policy_t).on_insert(boost::hana::traits::declval(key_t),
boost::hana::traits::declval(item_t))) {
})(boost::hana::type_c<P<K, KH, V>>, boost::hana::type_c<K>, boost::hana::type_c<Item<V>>);
concept HasOnInsert = requires(P<K, KH, V> policy, K key, Item<V> item) {
{ policy.on_insert(key, item) };
};

template<typename K, typename KH, typename V, template<class, class, class> typename P>
constexpr auto has_on_update = boost::hana::is_valid(
[](auto& policy_t, auto& key_t, auto& item_t) -> decltype(boost::hana::traits::declval(policy_t).on_update(boost::hana::traits::declval(key_t),
boost::hana::traits::declval(item_t),
boost::hana::traits::declval(item_t))) {
})(boost::hana::type_c<P<K, KH, V>>, boost::hana::type_c<K>, boost::hana::type_c<Item<V>>);
concept HasOnUpdate = requires(P<K, KH, V> policy, K key, Item<V> old_item, Item<V> new_item) {
{ policy.on_update(key, old_item, new_item) };
};

template<typename K, typename KH, typename V, template<class, class, class> typename P>
constexpr auto has_on_cachehit = boost::hana::is_valid(
[](auto& policy_t, auto& key_t, auto& item_t) -> decltype(boost::hana::traits::declval(policy_t).on_cache_hit(boost::hana::traits::declval(key_t),
boost::hana::traits::declval(item_t))) {
})(boost::hana::type_c<P<K, KH, V>>, boost::hana::type_c<K>, boost::hana::type_c<Item<V>>);
concept HasOnCacheHit = requires(P<K, KH, V> policy, K key, Item<V> item) {
{ policy.on_cache_hit(key, item) };
};

template<typename K, typename KH, typename V, template<class, class, class> typename P>
constexpr auto has_on_cachemiss = boost::hana::is_valid(
[](auto& policy_t, auto& item_t) -> decltype(boost::hana::traits::declval(policy_t).on_cache_miss(boost::hana::traits::declval(item_t))) {
})(boost::hana::type_c<P<K, KH, V>>, boost::hana::type_c<K>);
concept HasOnCacheMiss = requires(P<K, KH, V> policy, K key) {
{ policy.on_cache_miss(key) };
};

template<typename K, typename KH, typename V, template<class, class, class> typename P>
constexpr auto has_on_evict = boost::hana::is_valid(
[](auto& policy_t, auto& key_t, auto& value_t) -> decltype(boost::hana::traits::declval(policy_t).on_evict(boost::hana::traits::declval(key_t),
boost::hana::traits::declval(value_t))) {
})(boost::hana::type_c<P<K, KH, V>>, boost::hana::type_c<K>, boost::hana::type_c<Item<V>>);
concept HasOnEvict = requires(P<K, KH, V> policy, K key, Item<V> item) {
{ policy.on_evict(key, item) };
};

} // namespace event

Expand Down
86 changes: 36 additions & 50 deletions vcpkg.json
Original file line number Diff line number Diff line change
@@ -1,53 +1,39 @@
{
"name": "cachemere",
"version-semver": "0.3.0",
"dependencies": [
{
"name": "abseil",
"version>=": "20220623.1",
"features": [
"cxx17"
]
"name": "cachemere",
"version-semver": "0.3.0",
"dependencies": [
{
"name": "abseil",
"version>=": "20220623.1",
"features": ["cxx17"]
},
{
"name": "vcpkg-cmake",
"version>=": "2021-02-28",
"host": true,
"$comment": "This transitive dependency needs to be specified explicitly."
},
{
"name": "vcpkg-cmake-config",
"version>=": "2021-02-26",
"host": true,
"$comment": "This transitive dependency needs to be specified explicitly."
}
],
"default-features": ["boost"],
"features": {
"boost": {
"description": "Use boost from vcpkg",
"dependencies": ["boost-accumulators", "boost-dynamic-bitset"]
},
"tests": {
"description": "The unit tests",
"dependencies": ["gtest"]
},
"benchmarks": {
"description": "The accuracy and performance benchmarks",
"dependencies": ["benchmark", "gtest", "tbb"]
}
},
{
"name": "vcpkg-cmake",
"version>=": "2021-02-28",
"host": true,
"$comment": "This transitive dependency needs to be specified explicitly."
},
{
"name": "vcpkg-cmake-config",
"version>=": "2021-02-26",
"host": true,
"$comment": "This transitive dependency needs to be specified explicitly."
}
],
"default-features": [
"boost"
],
"features": {
"boost": {
"description": "Use boost from vcpkg",
"dependencies": [
"boost-accumulators",
"boost-dynamic-bitset",
"boost-hana"
]
},
"tests": {
"description": "The unit tests",
"dependencies": [
"gtest"
]
},
"benchmarks": {
"description": "The accuracy and performance benchmarks",
"dependencies": [
"benchmark",
"gtest",
"tbb"
]
}
},
"builtin-baseline": "3304e4ed22ae8e2a586ec4399c7d933f09475331"
"builtin-baseline": "3304e4ed22ae8e2a586ec4399c7d933f09475331"
}
Loading