Permit non-default-constructable hashes and key-equals (#583)

Co-authored-by: Jesse <jli@planarmotor.com>
This commit is contained in:
Jesse Li 2022-09-01 11:41:09 -07:00 committed by GitHub
parent 868329f9e4
commit aab900f002
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 141 additions and 28 deletions

View File

@ -792,7 +792,7 @@ namespace etl
while (inode != bucket.end())
{
// Do we already have this key?
if (inode->key_value_pair.first == key)
if (key_equal_function(inode->key_value_pair.first, key))
{
break;
}
@ -851,8 +851,8 @@ namespace etl
::new (&node.key_value_pair) value_type(etl::move(key_value_pair));
ETL_INCREMENT_DEBUG_COUNT
// Just add the pointer to the bucket;
bucket.insert_after(bucket.before_begin(), node);
// Just add the pointer to the bucket;
bucket.insert_after(bucket.before_begin(), node);
adjust_first_last_markers_after_insert(pbucket);
@ -868,7 +868,7 @@ namespace etl
while (inode != bucket.end())
{
// Do we already have this key?
if (inode->key_value_pair.first == key)
if (key_equal_function(inode->key_value_pair.first, key))
{
break;
}
@ -885,8 +885,8 @@ namespace etl
::new (&node.key_value_pair) value_type(etl::move(key_value_pair));
ETL_INCREMENT_DEBUG_COUNT
// Add the node to the end of the bucket;
bucket.insert_after(inode_previous, node);
// Add the node to the end of the bucket;
bucket.insert_after(inode_previous, node);
adjust_first_last_markers_after_insert(&bucket);
++inode_previous;
@ -1278,6 +1278,8 @@ namespace etl
// Skip if doing self assignment
if (this != &rhs)
{
key_hash_function = rhs.hash_function();
key_equal_function = rhs.key_eq();
assign(rhs.cbegin(), rhs.cend());
}
@ -1292,6 +1294,8 @@ namespace etl
// Skip if doing self assignment
if (this != &rhs)
{
key_hash_function = rhs.hash_function();
key_equal_function = rhs.key_eq();
this->move(rhs.begin(), rhs.end());
}
@ -1304,12 +1308,14 @@ namespace etl
//*********************************************************************
/// Constructor.
//*********************************************************************
iunordered_map(pool_t& node_pool_, bucket_t* pbuckets_, size_t number_of_buckets_)
iunordered_map(pool_t& node_pool_, bucket_t* pbuckets_, size_t number_of_buckets_, hasher key_hash_function_, key_equal key_equal_function_)
: pnodepool(&node_pool_)
, pbuckets(pbuckets_)
, number_of_buckets(number_of_buckets_)
, first(pbuckets)
, last(pbuckets)
, key_hash_function(key_hash_function_)
, key_equal_function(key_equal_function_)
{
}
@ -1528,8 +1534,8 @@ namespace etl
//*************************************************************************
/// Default constructor.
//*************************************************************************
unordered_map()
: base(node_pool, buckets, MAX_BUCKETS_)
unordered_map(const THash& hash = THash(), const TKeyEqual equal = TKeyEqual())
: base(node_pool, buckets, MAX_BUCKETS_, hash, equal)
{
}
@ -1537,7 +1543,7 @@ namespace etl
/// Copy constructor.
//*************************************************************************
unordered_map(const unordered_map& other)
: base(node_pool, buckets, MAX_BUCKETS_)
: base(node_pool, buckets, MAX_BUCKETS_, other.hash_function(), other.key_eq())
{
base::assign(other.cbegin(), other.cend());
}
@ -1547,7 +1553,7 @@ namespace etl
/// Move constructor.
//*************************************************************************
unordered_map(unordered_map&& other)
: base(node_pool, buckets, MAX_BUCKETS_)
: base(node_pool, buckets, MAX_BUCKETS_, other.hash_function(), other.key_eq())
{
if (this != &other)
{
@ -1564,7 +1570,7 @@ namespace etl
//*************************************************************************
template <typename TIterator>
unordered_map(TIterator first_, TIterator last_)
: base(node_pool, buckets, MAX_BUCKETS_)
: unordered_map()
{
base::assign(first_, last_);
}
@ -1574,7 +1580,7 @@ namespace etl
/// Construct from initializer_list.
//*************************************************************************
unordered_map(std::initializer_list<ETL_OR_STD::pair<TKey, TValue>> init)
: base(node_pool, buckets, MAX_BUCKETS_)
: unordered_map()
{
base::assign(init.begin(), init.end());
}
@ -1593,12 +1599,7 @@ namespace etl
//*************************************************************************
unordered_map& operator = (const unordered_map& rhs)
{
// Skip if doing self assignment
if (this != &rhs)
{
base::assign(rhs.cbegin(), rhs.cend());
}
base::operator=(rhs);
return *this;
}
@ -1608,13 +1609,7 @@ namespace etl
//*************************************************************************
unordered_map& operator = (unordered_map&& rhs)
{
// Skip if doing self assignment
if (this != &rhs)
{
base::clear();
base::move(rhs.begin(), rhs.end());
}
base::operator=(etl::move(rhs));
return *this;
}
#endif

View File

@ -56,6 +56,61 @@ namespace
}
};
//*************************************************************************
// Non-default-constructible hasher
struct ndc_hash
{
int id;
ndc_hash(int id_) : id(id_){}
size_t operator()(size_t val) const
{
return val;
}
};
//*************************************************************************
// Non-default-constructible equality checker
struct ndc_key_eq
{
int id;
ndc_key_eq(int id_) : id(id_){}
bool operator()(size_t val1, size_t val2) const
{
return val1 == val2;
}
};
//*************************************************************************
// Hasher whose hash behaviour depends on provided data.
struct parameterized_hash
{
size_t modulus;
parameterized_hash(size_t modulus_ = 2) : modulus(modulus_){}
size_t operator()(size_t val) const
{
return val % modulus;
}
};
//*************************************************************************
// Equality checker whose behaviour depends on provided data.
struct parameterized_equal
{
size_t modulus;
// Hasher whose hash behaviour depends on provided data.
parameterized_equal(size_t modulus_ = 2) : modulus(modulus_){}
bool operator()(size_t lhs, size_t rhs) const
{
return (lhs % modulus) == (rhs % modulus);
}
};
//*************************************************************************
template <typename T1, typename T2>
bool Check_Equal(T1 begin1, T1 end1, T2 begin2)
@ -350,9 +405,9 @@ namespace
DataNDC data(initial_data.begin(), initial_data.end());
DataNDC other_data(data);
#include "etl/private/diagnostic_self_assign_overloaded_push.h"
#include "etl/private/diagnostic_self_assign_overloaded_push.h"
other_data = other_data;
#include "etl/private/diagnostic_pop.h"
#include "etl/private/diagnostic_pop.h"
bool isEqual = std::equal(data.begin(),
data.end(),
@ -894,5 +949,68 @@ namespace
CHECK_EQUAL('c', map[2]);
CHECK_EQUAL('d', map[3]);
}
TEST(test_ndc_hasher_and_key_eq) {
typedef etl::unordered_map<size_t, int, 10, 10, ndc_hash, ndc_key_eq> Map;
ndc_hash hasher1(1);
ndc_hash hasher2(2);
ndc_key_eq eq1(1);
ndc_key_eq eq2(2);
Map map1(hasher1, eq1);
CHECK_EQUAL(map1.hash_function().id, 1);
CHECK_EQUAL(map1.key_eq().id, 1);
Map map2(hasher2, eq2);
Map copyConstructed(map1);
CHECK_EQUAL(copyConstructed.hash_function().id, 1);
CHECK_EQUAL(copyConstructed.key_eq().id, 1);
Map copyAssigned(hasher2, eq2);
CHECK_EQUAL(copyAssigned.hash_function().id, 2);
CHECK_EQUAL(copyAssigned.key_eq().id, 2);
copyAssigned = map1;
CHECK_EQUAL(copyAssigned.hash_function().id, 1);
CHECK_EQUAL(copyAssigned.key_eq().id, 1);
Map moveConstructed = std::move(map1);
CHECK_EQUAL(moveConstructed.hash_function().id, 1);
CHECK_EQUAL(moveConstructed.key_eq().id, 1);
Map moveAssigned(hasher1, eq1);
CHECK_EQUAL(moveAssigned.hash_function().id, 1);
CHECK_EQUAL(moveAssigned.key_eq().id, 1);
moveAssigned = std::move(map2);
CHECK_EQUAL(moveAssigned.hash_function().id, 2);
CHECK_EQUAL(moveAssigned.key_eq().id, 2);
// make sure that map operations still work
moveAssigned[5] = 7;
CHECK_EQUAL(7, moveAssigned[5]);
}
TEST(test_parameterized_eq) {
constexpr std::size_t MODULO = 4;
parameterized_hash hash{MODULO};
parameterized_equal eq{MODULO};
// values are equal modulo 4
etl::unordered_map<std::size_t, int, 10, 10, parameterized_hash, parameterized_equal> map;
map.insert(etl::make_pair(2, 3));
const auto& constmap = map;
CHECK_EQUAL(map[10], 3);
CHECK_EQUAL(map.at(10), 3);
CHECK_EQUAL(constmap.at(10), 3);
CHECK_FALSE(map.insert(etl::make_pair(6, 7)).second);
CHECK(map.find(14) != map.end());
CHECK(constmap.find(14) != constmap.end());
map.erase(2);
CHECK(map.find(6) == map.end());
}
};
}