mirror of
https://github.com/greg7mdp/parallel-hashmap.git
synced 2026-08-29 08:34:39 +08:00
support most extended APIs for sets as we do for maps.
This commit is contained in:
Vendored
+102
-85
@@ -2929,7 +2929,7 @@ public:
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// phmap expension: emplace_with_hash
|
||||
// phmap extension: emplace_with_hash
|
||||
// ----------------------------------
|
||||
// same as emplace, but hashval is provided
|
||||
// --------------------------------------------------------------------
|
||||
@@ -3081,6 +3081,8 @@ public:
|
||||
return {iterator(inner, &sets_[0] + num_tables, res.first), res.second};
|
||||
}
|
||||
|
||||
// lazy_emplace
|
||||
// ------------
|
||||
template <class K = key_type, class F>
|
||||
iterator lazy_emplace(const key_arg<K>& key, F&& f) {
|
||||
auto hashval = this->hash(key);
|
||||
@@ -3090,15 +3092,109 @@ public:
|
||||
return make_iterator(&inner, set.lazy_emplace_with_hash(key, hashval, std::forward<F>(f)));
|
||||
}
|
||||
|
||||
// emplace_single
|
||||
// --------------
|
||||
template <class K = key_type, class F>
|
||||
void emplace_single(const key_arg<K>& key, F&& f) {
|
||||
auto hashval = this->hash(key);
|
||||
void emplace_single_with_hash(const key_arg<K>& key, size_t &hashval, F&& f) {
|
||||
Inner& inner = sets_[subidx(hashval)];
|
||||
auto& set = inner.set_;
|
||||
typename Lockable::UniqueLock m(inner);
|
||||
set.emplace_single_with_hash(key, hashval, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <class K = key_type, class F>
|
||||
void emplace_single(const key_arg<K>& key, F&& f) {
|
||||
auto hashval = this->hash(key);
|
||||
emplace_single_with_hash<K, F>(key, hashval, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// if set contains key, lambda is called with the value_type (under read lock protection),
|
||||
// and if_contains returns true. This is a const API and lambda should not modify the value
|
||||
// -----------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F>
|
||||
bool if_contains(const key_arg<K>& key, F&& f) const {
|
||||
return const_cast<parallel_hash_set*>(this)->template
|
||||
modify_if_impl<K, F, typename Lockable::SharedLock>(key, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// if set contains key, lambda is called with the value_type without read lock protection,
|
||||
// and if_contains_unsafe returns true. This is a const API and lambda should not modify the value
|
||||
// This should be used only if we know that no other thread may be mutating the set at the time.
|
||||
// -----------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F>
|
||||
bool if_contains_unsafe(const key_arg<K>& key, F&& f) const {
|
||||
return const_cast<parallel_hash_set*>(this)->template
|
||||
modify_if_impl<K, F, LockableBaseImpl<phmap::NullMutex>::DoNothing>(key, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// if map contains key, lambda is called with the value_type (under write lock protection),
|
||||
// and modify_if returns true. This is a non-const API and lambda is allowed to modify the mapped value
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F>
|
||||
bool modify_if(const key_arg<K>& key, F&& f) {
|
||||
return modify_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F, class L>
|
||||
bool modify_if_impl(const key_arg<K>& key, F&& f) {
|
||||
#if __cplusplus >= 201703L
|
||||
static_assert(std::is_invocable<F, value_type&>::value);
|
||||
#endif
|
||||
L m;
|
||||
auto ptr = this->template find_ptr<K, L>(key, this->hash(key), m);
|
||||
if (ptr == nullptr)
|
||||
return false;
|
||||
std::forward<F>(f)(*ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
// if map contains key, lambda is called with the mapped value (under write lock protection).
|
||||
// If the lambda returns true, the key is subsequently erased from the map (the write lock
|
||||
// is only released after erase).
|
||||
// returns true if key was erased, false otherwise.
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F>
|
||||
bool erase_if(const key_arg<K>& key, F&& f) {
|
||||
return erase_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <class K = key_type, class F, class L>
|
||||
bool erase_if_impl(const key_arg<K>& key, F&& f) {
|
||||
#if __cplusplus >= 201703L
|
||||
static_assert(std::is_invocable<F, value_type&>::value);
|
||||
#endif
|
||||
L m;
|
||||
auto it = this->template find<K, L>(key, this->hash(key), m);
|
||||
if (it == this->end()) return false;
|
||||
if (std::forward<F>(f)(const_cast<value_type &>(*it)))
|
||||
{
|
||||
this->erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// if map does not contains key, it is inserted and the mapped value is value-constructed
|
||||
// with the provided arguments (if any), as with try_emplace.
|
||||
// if map already contains key, then the lambda is called with the mapped value (under
|
||||
// write lock protection) and can update the mapped value.
|
||||
// returns true if key was not already present, false otherwise.
|
||||
// ---------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class FExists, class FEmplace>
|
||||
bool lazy_emplace_l(const key_arg<K>& key, FExists&& fExists, FEmplace&& fEmplace) {
|
||||
typename Lockable::UniqueLock m;
|
||||
auto res = this->find_or_prepare_insert(key, m);
|
||||
Inner* inner = std::get<0>(res);
|
||||
if (std::get<2>(res))
|
||||
inner->set_.lazy_emplace_at(std::get<1>(res), std::forward<FEmplace>(fEmplace));
|
||||
else {
|
||||
auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
|
||||
std::forward<FExists>(fExists)(const_cast<value_type &>(*it)); // in case of the set, non "key" part of value_type can be changed
|
||||
}
|
||||
return std::get<2>(res);
|
||||
}
|
||||
|
||||
// Extension API: support iterating over all values
|
||||
//
|
||||
// flat_hash_set<std::string> s;
|
||||
@@ -3554,8 +3650,9 @@ class parallel_hash_map : public parallel_hash_set<N, RefSet, Mtx_, Policy, Hash
|
||||
using Lockable = phmap::LockableImpl<Mtx_>;
|
||||
|
||||
public:
|
||||
using key_type = typename Policy::key_type;
|
||||
using key_type = typename Policy::key_type;
|
||||
using mapped_type = typename Policy::mapped_type;
|
||||
using value_type = typename Base::value_type;
|
||||
template <class K>
|
||||
using key_arg = typename KeyArgImpl::template type<K, key_type>;
|
||||
|
||||
@@ -3692,43 +3789,6 @@ public:
|
||||
return try_emplace_with_hash(hashval, k, std::forward<Args>(args)...).first;
|
||||
}
|
||||
|
||||
// if map contains key, lambda is called with the mapped value (under read lock protection),
|
||||
// and if_contains returns true. This is a const API and lambda should not modify the value
|
||||
// -----------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F>
|
||||
bool if_contains(const key_arg<K>& key, F&& f) const {
|
||||
return const_cast<parallel_hash_map*>(this)->template
|
||||
modify_if_impl<K, F, typename Lockable::SharedLock>(key, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// if map contains key, lambda is called with the mapped value without read lock protection,
|
||||
// and if_contains_unsafe returns true. This is a const API and lambda should not modify the value
|
||||
// This should be used only if we know that no other thread may be mutating the map at the time.
|
||||
// -----------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F>
|
||||
bool if_contains_unsafe(const key_arg<K>& key, F&& f) const {
|
||||
return const_cast<parallel_hash_map*>(this)->template
|
||||
modify_if_impl<K, F, LockableBaseImpl<phmap::NullMutex>::DoNothing>(key, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// if map contains key, lambda is called with the mapped value (under write lock protection),
|
||||
// and modify_if returns true. This is a non-const API and lambda is allowed to modify the mapped value
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F>
|
||||
bool modify_if(const key_arg<K>& key, F&& f) {
|
||||
return modify_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// if map contains key, lambda is called with the mapped value (under write lock protection).
|
||||
// If the lambda returns true, the key is subsequently erased from the map (the write lock
|
||||
// is only released after erase).
|
||||
// returns true if key was erased, false otherwise.
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
template <class K = key_type, class F>
|
||||
bool erase_if(const key_arg<K>& key, F&& f) {
|
||||
return erase_if_impl<K, F, typename Lockable::UniqueLock>(key, std::forward<F>(f));
|
||||
}
|
||||
|
||||
// if map does not contains key, it is inserted and the mapped value is value-constructed
|
||||
// with the provided arguments (if any), as with try_emplace.
|
||||
// if map already contains key, then the lambda is called with the mapped value (under
|
||||
@@ -3746,21 +3806,7 @@ public:
|
||||
std::forward_as_tuple(std::forward<Args>(args)...));
|
||||
else {
|
||||
auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
|
||||
std::forward<F>(f)(Policy::value(&*it));
|
||||
}
|
||||
return std::get<2>(res);
|
||||
}
|
||||
|
||||
template <class K = key_type, class FExists, class FEmplace>
|
||||
bool lazy_emplace_l(const key_arg<K>& key, FExists&& fExists, FEmplace&& fEmplace) {
|
||||
typename Lockable::UniqueLock m;
|
||||
auto res = this->find_or_prepare_insert(key, m);
|
||||
typename Base::Inner* inner = std::get<0>(res);
|
||||
if (std::get<2>(res))
|
||||
inner->set_.lazy_emplace_at(std::get<1>(res), std::forward<FEmplace>(fEmplace));
|
||||
else {
|
||||
auto it = this->iterator_at(inner, inner->set_.iterator_at(std::get<1>(res)));
|
||||
std::forward<FExists>(fExists)(Policy::value(&*it));
|
||||
std::forward<F>(f)(const_cast<value_type &>(*it)); // in case of the set, non "key" part of value_type can be changed
|
||||
}
|
||||
return std::get<2>(res);
|
||||
}
|
||||
@@ -3778,35 +3824,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
template <class K = key_type, class F, class L>
|
||||
bool modify_if_impl(const key_arg<K>& key, F&& f) {
|
||||
#if __cplusplus >= 201703L
|
||||
static_assert(std::is_invocable<F, mapped_type&>::value);
|
||||
#endif
|
||||
L m;
|
||||
auto ptr = this->template find_ptr<K, L>(key, this->hash(key), m);
|
||||
if (ptr == nullptr)
|
||||
return false;
|
||||
std::forward<F>(f)(Policy::value(ptr));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class K = key_type, class F, class L>
|
||||
bool erase_if_impl(const key_arg<K>& key, F&& f) {
|
||||
#if __cplusplus >= 201703L
|
||||
static_assert(std::is_invocable<F, mapped_type&>::value);
|
||||
#endif
|
||||
L m;
|
||||
auto it = this->template find<K, L>(key, this->hash(key), m);
|
||||
if (it == this->end()) return false;
|
||||
if (std::forward<F>(f)(Policy::value(&*it)))
|
||||
{
|
||||
this->erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <class K, class V>
|
||||
std::pair<iterator, bool> insert_or_assign_impl(K&& k, V&& v) {
|
||||
|
||||
+102
-101
@@ -9,131 +9,132 @@ namespace phmap {
|
||||
namespace priv {
|
||||
namespace {
|
||||
|
||||
TEST(THIS_TEST_NAME, ThreadSafeContains) {
|
||||
// We can't test mutable keys, or non-copyable keys with ThisMap.
|
||||
// Test that the nodes have the proper API.
|
||||
TEST(THIS_TEST_NAME, IfContains) {
|
||||
// ----------------
|
||||
// test if_contains
|
||||
// ----------------
|
||||
|
||||
using Map = ThisMap<int, int>;
|
||||
|
||||
{
|
||||
// ----------------
|
||||
// test if_contains
|
||||
// ----------------
|
||||
Map m = { {1, 7}, {2, 9} };
|
||||
const Map& const_m(m);
|
||||
Map m = { {1, 7}, {2, 9} };
|
||||
const Map& const_m(m);
|
||||
|
||||
auto val = 0;
|
||||
auto get_value = [&val](const int& v) { val = v; };
|
||||
EXPECT_TRUE(const_m.if_contains(2, get_value));
|
||||
EXPECT_EQ(val, 9);
|
||||
auto val = 0;
|
||||
auto get_value = [&val](const Map::value_type& v) { val = v.second; };
|
||||
EXPECT_TRUE(const_m.if_contains(2, get_value));
|
||||
EXPECT_EQ(val, 9);
|
||||
|
||||
EXPECT_FALSE(m.if_contains(3, get_value));
|
||||
}
|
||||
EXPECT_FALSE(m.if_contains(3, get_value));
|
||||
}
|
||||
|
||||
{
|
||||
// --------------
|
||||
// test modify_if
|
||||
// --------------
|
||||
Map m = { {1, 7}, {2, 9} };
|
||||
TEST(THIS_TEST_NAME, ModifyIf) {
|
||||
// --------------
|
||||
// test modify_if
|
||||
// --------------
|
||||
using Map = ThisMap<int, int>;
|
||||
Map m = { {1, 7}, {2, 9} };
|
||||
|
||||
auto set_value = [](int& v) { v = 11; };
|
||||
EXPECT_TRUE(m.modify_if(2, set_value));
|
||||
EXPECT_EQ(m[2], 11);
|
||||
auto set_value = [](Map::value_type& v) { v.second = 11; };
|
||||
EXPECT_TRUE(m.modify_if(2, set_value));
|
||||
EXPECT_EQ(m[2], 11);
|
||||
|
||||
EXPECT_FALSE(m.modify_if(3, set_value)); // because m[3] does not exist
|
||||
}
|
||||
EXPECT_FALSE(m.modify_if(3, set_value)); // because m[3] does not exist
|
||||
}
|
||||
|
||||
{
|
||||
// ------------------
|
||||
// test try_emplace_l
|
||||
// ------------------
|
||||
Map m = { {1, 7}, {2, 9} };
|
||||
TEST(THIS_TEST_NAME, TryEmplaceL) {
|
||||
// ------------------
|
||||
// test try_emplace_l
|
||||
// ------------------
|
||||
using Map = ThisMap<int, int>;
|
||||
Map m = { {1, 7}, {2, 9} };
|
||||
|
||||
// overwrite an existing value
|
||||
m.try_emplace_l(2, [](int& v) { v = 5; });
|
||||
EXPECT_EQ(m[2], 5);
|
||||
// overwrite an existing value
|
||||
m.try_emplace_l(2, [](Map::value_type& v) { v.second = 5; });
|
||||
EXPECT_EQ(m[2], 5);
|
||||
|
||||
// insert a value that is not already present. Will be default initialised to 0 and lambda not called
|
||||
m.try_emplace_l(3,
|
||||
[](int& v) { v = 6; }, // called only when key was already present
|
||||
1); // argument to construct new value is key not present
|
||||
EXPECT_EQ(m[3], 1);
|
||||
// insert a value that is not already present. Will be default initialised to 0 and lambda not called
|
||||
m.try_emplace_l(3,
|
||||
[](Map::value_type& v) { v.second = 6; }, // called only when key was already present
|
||||
1); // argument to construct new value is key not present
|
||||
EXPECT_EQ(m[3], 1);
|
||||
|
||||
// insert a value that is not already present, provide argument to value-construct it
|
||||
m.try_emplace_l(4,
|
||||
[](int& ) {}, // called only when key was already present
|
||||
999); // argument to construct new value is key not present
|
||||
// insert a value that is not already present, provide argument to value-construct it
|
||||
m.try_emplace_l(4,
|
||||
[](Map::value_type& ) {}, // called only when key was already present
|
||||
999); // argument to construct new value is key not present
|
||||
|
||||
EXPECT_EQ(m[4], 999);
|
||||
}
|
||||
EXPECT_EQ(m[4], 999);
|
||||
}
|
||||
|
||||
{
|
||||
// --------------------
|
||||
// test lazy_emplace_l
|
||||
// --------------------
|
||||
Map m = { {1, 7}, {2, 9} };
|
||||
TEST(THIS_TEST_NAME, LazyEmplaceL) {
|
||||
// --------------------
|
||||
// test lazy_emplace_l
|
||||
// --------------------
|
||||
using Map = ThisMap<int, int>;
|
||||
Map m = { {1, 7}, {2, 9} };
|
||||
|
||||
// insert a value that is not already present.
|
||||
// right now m[5] does not exist
|
||||
m.lazy_emplace_l(5,
|
||||
[](int& v) { v = 6; }, // called only when key was already present
|
||||
[](const Map::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present
|
||||
// insert a value that is not already present.
|
||||
// right now m[5] does not exist
|
||||
m.lazy_emplace_l(5,
|
||||
[](Map::value_type& v) { v.second = 6; }, // called only when key was already present
|
||||
[](const Map::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present
|
||||
EXPECT_EQ(m[5], 13);
|
||||
|
||||
EXPECT_EQ(m[5], 13);
|
||||
// change a value that is present. Currently m[5] == 13
|
||||
m.lazy_emplace_l(5,
|
||||
[](Map::value_type& v) { v.second = 6; }, // called only when key was already present
|
||||
[](const Map::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present
|
||||
EXPECT_EQ(m[5], 6);
|
||||
}
|
||||
|
||||
// change a value that is present. Currently m[5] == 13
|
||||
m.lazy_emplace_l(5,
|
||||
[](int& v) { v = 6; }, // called only when key was already present
|
||||
[](const Map::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present
|
||||
EXPECT_EQ(m[5], 6);
|
||||
}
|
||||
TEST(THIS_TEST_NAME, EraseIf) {
|
||||
// -------------
|
||||
// test erase_if
|
||||
// -------------
|
||||
using Map = ThisMap<int, int>;
|
||||
Map m = { {1, 7}, {2, 9}, {5, 6} };
|
||||
|
||||
{
|
||||
// -------------
|
||||
// test erase_if
|
||||
// -------------
|
||||
Map m = { {1, 7}, {2, 9}, {5, 6} };
|
||||
EXPECT_EQ(m.erase_if(9, [](Map::value_type& v) { assert(0); return v.second == 12; }), false); // m[9] not present - lambda not called
|
||||
EXPECT_EQ(m.erase_if(5, [](Map::value_type& v) { return v.second == 12; }), false); // m[5] == 6, so erase not performed
|
||||
EXPECT_EQ(m[5], 6);
|
||||
EXPECT_EQ(m.erase_if(5, [](Map::value_type& v) { return v.second == 6; }), true); // lambda returns true, so m[5] erased
|
||||
EXPECT_EQ(m[5], 0);
|
||||
}
|
||||
|
||||
EXPECT_EQ(m.erase_if(9, [](int& v) { assert(0); return v==12; }), false); // m[9] not present - lambda not called
|
||||
EXPECT_EQ(m.erase_if(5, [](int& v) { return v==12; }), false); // m[5] == 6, so erase not performed
|
||||
EXPECT_EQ(m[5], 6);
|
||||
EXPECT_EQ(m.erase_if(5, [](int& v) { return v==6; }), true); // lambda returns true, so m[5] erased
|
||||
EXPECT_EQ(m[5], 0);
|
||||
}
|
||||
TEST(THIS_TEST_NAME, ForEach) {
|
||||
// -------------
|
||||
// test for_each
|
||||
// -------------
|
||||
using Map = ThisMap<int, int>;
|
||||
Map m = { {1, 7}, {2, 8}, {5, 11} };
|
||||
|
||||
{
|
||||
// -------------
|
||||
// test for_each
|
||||
// -------------
|
||||
Map m = { {1, 7}, {2, 8}, {5, 11} };
|
||||
// increment all values by 1
|
||||
m.for_each_m([](Map::value_type &pair) { ++pair.second; });
|
||||
|
||||
// increment all values by 1
|
||||
m.for_each_m([](Map::value_type &pair) { ++pair.second; });
|
||||
|
||||
int counter = 0;
|
||||
m.for_each([&counter](const Map::value_type &pair) {
|
||||
int counter = 0;
|
||||
m.for_each([&counter](const Map::value_type &pair) {
|
||||
++counter;
|
||||
EXPECT_EQ(pair.first + 7, pair.second);
|
||||
});
|
||||
EXPECT_EQ(counter, 3);
|
||||
}
|
||||
|
||||
{
|
||||
// --------------------
|
||||
// test emplace_single
|
||||
// --------------------
|
||||
Map m = { {1, 4}, {11, 4} };
|
||||
|
||||
// emplace_single insert a value if not already present, else removes it
|
||||
for (int i=0; i<12; ++i)
|
||||
m.emplace_single(i, [i](const Map::constructor& ctor) { ctor(i, 4); });
|
||||
EXPECT_EQ(m.count(0), 1);
|
||||
EXPECT_EQ(m.count(1), 0);
|
||||
EXPECT_EQ(m.count(2), 1);
|
||||
EXPECT_EQ(m.count(11), 0);
|
||||
}
|
||||
|
||||
EXPECT_EQ(counter, 3);
|
||||
}
|
||||
|
||||
TEST(THIS_TEST_NAME, EmplaceSingle) {
|
||||
// --------------------
|
||||
// test emplace_single
|
||||
// --------------------
|
||||
using Map = ThisMap<int, int>;
|
||||
Map m = { {1, 4}, {11, 4} };
|
||||
|
||||
// emplace_single insert a value if not already present, else removes it
|
||||
for (int i=0; i<12; ++i)
|
||||
m.emplace_single(i, [i](const Map::constructor& ctor) { ctor(i, 4); });
|
||||
EXPECT_EQ(m.count(0), 1);
|
||||
EXPECT_EQ(m.count(1), 0);
|
||||
EXPECT_EQ(m.count(2), 1);
|
||||
EXPECT_EQ(m.count(11), 0);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
} // namespace priv
|
||||
} // namespace phmap
|
||||
|
||||
+116
-18
@@ -9,26 +9,124 @@ namespace phmap {
|
||||
namespace priv {
|
||||
namespace {
|
||||
|
||||
TEST(THIS_TEST_NAME, ThreadSafeContains) {
|
||||
// We can't test mutable keys, or non-copyable keys with ThisSet.
|
||||
// Test that the nodes have the proper API.
|
||||
using Set = phmap::THIS_HASH_SET<int>;
|
||||
|
||||
{
|
||||
// --------------------
|
||||
// test emplace_single
|
||||
// --------------------
|
||||
Set m = { {1}, {11} };
|
||||
|
||||
// emplace_single insert a value if not already present, else removes it
|
||||
for (int i=0; i<12; ++i)
|
||||
m.emplace_single(i, [i](const Set::constructor& ctor) { ctor(i); });
|
||||
EXPECT_EQ(m.count(0), 1);
|
||||
EXPECT_EQ(m.count(1), 0);
|
||||
EXPECT_EQ(m.count(2), 1);
|
||||
EXPECT_EQ(m.count(11), 0);
|
||||
struct Entry
|
||||
{
|
||||
bool operator==(const Entry &o) const
|
||||
{
|
||||
return key == o.key; // not checking value
|
||||
}
|
||||
|
||||
// Demonstrates how to provide the hash function as a friend member function of the class
|
||||
// This can be used as an alternative to providing a std::hash<Person> specialization
|
||||
// --------------------------------------------------------------------------------------
|
||||
friend size_t hash_value(const Entry &p)
|
||||
{
|
||||
return phmap::HashState().combine(0, p.key); // not checking value
|
||||
}
|
||||
|
||||
int key;
|
||||
int value;
|
||||
};
|
||||
|
||||
TEST(THIS_TEST_NAME, IfContains) {
|
||||
// ----------------
|
||||
// test if_contains
|
||||
// ----------------
|
||||
using Set = phmap::THIS_HASH_SET<Entry>;
|
||||
Set m = { {1, 7}, {2, 9} };
|
||||
const Set& const_m(m);
|
||||
|
||||
auto val = 0;
|
||||
auto get_value = [&val](const Set::value_type& v) { val = v.value; };
|
||||
EXPECT_TRUE(const_m.if_contains(Entry{2}, get_value));
|
||||
EXPECT_EQ(val, 9);
|
||||
|
||||
EXPECT_FALSE(m.if_contains(Entry{3}, get_value));
|
||||
}
|
||||
|
||||
TEST(THIS_TEST_NAME, ModifyIf) {
|
||||
// --------------
|
||||
// test modify_if
|
||||
// --------------
|
||||
using Set = phmap::THIS_HASH_SET<Entry>;
|
||||
Set m = { {1, 7}, {2, 9} };
|
||||
|
||||
auto set_value = [](Set::value_type& v) { v.value = 11; };
|
||||
EXPECT_TRUE(m.modify_if(Entry{2}, set_value));
|
||||
|
||||
auto val = 0;
|
||||
auto get_value = [&val](const Set::value_type& v) { val = v.value; };
|
||||
EXPECT_TRUE(m.if_contains(Entry{2}, get_value));
|
||||
EXPECT_EQ(val, 11);
|
||||
|
||||
EXPECT_FALSE(m.modify_if(Entry{3}, set_value)); // because m[3] does not exist
|
||||
}
|
||||
|
||||
TEST(THIS_TEST_NAME, LazyEmplaceL) {
|
||||
// --------------------
|
||||
// test lazy_emplace_l
|
||||
// --------------------
|
||||
using Set = phmap::THIS_HASH_SET<Entry>;
|
||||
Set m = { {1, 7}, {2, 9} };
|
||||
|
||||
// insert a value that is not already present.
|
||||
// right now m[5] does not exist
|
||||
m.lazy_emplace_l(Entry{5},
|
||||
[](Set::value_type& v) { v.value = 6; }, // called only when key was already present
|
||||
[](const Set::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present
|
||||
EXPECT_EQ(m.find(Entry{5})->value, 13);
|
||||
|
||||
// change a value that is present.
|
||||
m.lazy_emplace_l(Entry{5},
|
||||
[](Set::value_type& v) { v.value = 6; }, // called only when key was already present
|
||||
[](const Set::constructor& ctor) { ctor(5, 13); }); // construct value_type in place when key not present
|
||||
EXPECT_EQ(m.find(Entry{5})->value, 6);
|
||||
}
|
||||
|
||||
TEST(THIS_TEST_NAME, EraseIf) {
|
||||
// -------------
|
||||
// test erase_if
|
||||
// -------------
|
||||
using Set = phmap::THIS_HASH_SET<Entry>;
|
||||
Set m = { {1, 7}, {2, 9}, {5, 6} };
|
||||
|
||||
EXPECT_EQ(m.erase_if(Entry{9}, [](Set::value_type& v) { assert(0); return v.value == 12; }), false); // m[9] not present - lambda not called
|
||||
EXPECT_EQ(m.erase_if(Entry{5}, [](Set::value_type& v) { return v.value == 12; }), false); // m[5] == 6, so erase not performed
|
||||
EXPECT_EQ(m.find(Entry{5})->value, 6);
|
||||
EXPECT_EQ(m.erase_if(Entry{5}, [](Set::value_type& v) { return v.value == 6; }), true); // lambda returns true, so m[5] erased
|
||||
EXPECT_EQ(m.find(Entry{5}), m.end());
|
||||
}
|
||||
|
||||
TEST(THIS_TEST_NAME, ForEach) {
|
||||
// -------------
|
||||
// test for_each
|
||||
// -------------
|
||||
using Set = phmap::THIS_HASH_SET<Entry>;
|
||||
Set m = { {1, 7}, {2, 8}, {5, 11} };
|
||||
|
||||
int counter = 0;
|
||||
m.for_each([&counter](const Set::value_type &v) {
|
||||
++counter;
|
||||
EXPECT_EQ(v.key + 6, v.value);
|
||||
});
|
||||
EXPECT_EQ(counter, 3);
|
||||
}
|
||||
|
||||
TEST(THIS_TEST_NAME, EmplaceSingle) {
|
||||
using Set = phmap::THIS_HASH_SET<int>;
|
||||
|
||||
// --------------------
|
||||
// test emplace_single
|
||||
// --------------------
|
||||
Set m = { {1}, {11} };
|
||||
|
||||
// emplace_single insert a value if not already present, else removes it
|
||||
for (int i=0; i<12; ++i)
|
||||
m.emplace_single(i, [i](const Set::constructor& ctor) { ctor(i); });
|
||||
EXPECT_EQ(m.count(0), 1);
|
||||
EXPECT_EQ(m.count(1), 0);
|
||||
EXPECT_EQ(m.count(2), 1);
|
||||
EXPECT_EQ(m.count(11), 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user