Skip to content

Commit 2eea220

Browse files
authored
Merge pull request #184 from maierlars/feature/transparent-hash-map-set
Transparent Hash for Map and Sets
2 parents 9c0b626 + 559a0d2 commit 2eea220

File tree

5 files changed

+186
-2
lines changed

5 files changed

+186
-2
lines changed

immer/detail/rbts/rrbtree.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <stdexcept>
2020
#include <memory>
2121
#include <numeric>
22+
#include <limits>
2223

2324
namespace immer {
2425
namespace detail {

immer/map.hpp

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ class map
114114
{
115115
auto operator()(const value_t& v) { return Hash{}(v.first); }
116116

117-
auto operator()(const K& v) { return Hash{}(v); }
117+
template<typename Key>
118+
auto operator()(const Key& v) { return Hash{}(v); }
118119
};
119120

120121
struct equal_key
@@ -124,7 +125,8 @@ class map
124125
return Equal{}(a.first, b.first);
125126
}
126127

127-
auto operator()(const value_t& a, const K& b)
128+
template<typename Key>
129+
auto operator()(const value_t& a, const Key& b)
128130
{
129131
return Equal{}(a.first, b);
130132
}
@@ -192,6 +194,21 @@ class map
192194
*/
193195
IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
194196

197+
/*!
198+
* Returns `1` when the key `k` is contained in the map or `0`
199+
* otherwise. It won't allocate memory and its complexity is
200+
* *effectively* @f$ O(1) @f$.
201+
*
202+
* This overload participates in overload resolution only if
203+
* `Hash::is_transparent` is valid and denotes a type.
204+
*/
205+
template<typename Key, typename U = Hash, typename = typename U::is_transparent>
206+
IMMER_NODISCARD size_type count(const Key& k) const
207+
{
208+
return impl_.template get<detail::constantly<size_type, 1>,
209+
detail::constantly<size_type, 0>>(k);
210+
}
211+
195212
/*!
196213
* Returns `1` when the key `k` is contained in the map or `0`
197214
* otherwise. It won't allocate memory and its complexity is
@@ -203,6 +220,21 @@ class map
203220
detail::constantly<size_type, 0>>(k);
204221
}
205222

223+
/*!
224+
* Returns a `const` reference to the values associated to the key
225+
* `k`. If the key is not contained in the map, it returns a
226+
* default constructed value. It does not allocate memory and its
227+
* complexity is *effectively* @f$ O(1) @f$.
228+
*
229+
* This overload participates in overload resolution only if
230+
* `Hash::is_transparent` is valid and denotes a type.
231+
*/
232+
template<typename Key, typename U = Hash, typename = typename U::is_transparent>
233+
IMMER_NODISCARD const T& operator[](const Key& k) const
234+
{
235+
return impl_.template get<project_value, default_value>(k);
236+
}
237+
206238
/*!
207239
* Returns a `const` reference to the values associated to the key
208240
* `k`. If the key is not contained in the map, it returns a
@@ -220,6 +252,21 @@ class map
220252
* `std::out_of_range` error. It does not allocate memory and its
221253
* complexity is *effectively* @f$ O(1) @f$.
222254
*/
255+
template<typename Key, typename U = Hash, typename = typename U::is_transparent>
256+
const T& at(const Key& k) const
257+
{
258+
return impl_.template get<project_value, error_value>(k);
259+
}
260+
261+
/*!
262+
* Returns a `const` reference to the values associated to the key
263+
* `k`. If the key is not contained in the map, throws an
264+
* `std::out_of_range` error. It does not allocate memory and its
265+
* complexity is *effectively* @f$ O(1) @f$.
266+
*
267+
* This overload participates in overload resolution only if
268+
* `Hash::is_transparent` is valid and denotes a type.
269+
*/
223270
const T& at(const K& k) const
224271
{
225272
return impl_.template get<project_value, error_value>(k);
@@ -260,6 +307,23 @@ class map
260307
detail::constantly<const T*, nullptr>>(k);
261308
}
262309

310+
311+
/*!
312+
* Returns a pointer to the value associated with the key `k`. If
313+
* the key is not contained in the map, a `nullptr` is returned.
314+
* It does not allocate memory and its complexity is *effectively*
315+
* @f$ O(1) @f$.
316+
*
317+
* This overload participates in overload resolution only if
318+
* `Hash::is_transparent` is valid and denotes a type.
319+
*/
320+
template<typename Key, typename U = Hash, typename = typename U::is_transparent>
321+
IMMER_NODISCARD const T* find(const Key& k) const
322+
{
323+
return impl_.template get<project_value_ptr,
324+
detail::constantly<const T*, nullptr>>(k);
325+
}
326+
263327
/*!
264328
* Returns whether the sets are equal.
265329
*/

immer/set.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,21 @@ class set
117117
*/
118118
IMMER_NODISCARD bool empty() const { return impl_.size == 0; }
119119

120+
/*!
121+
* Returns `1` when `value` is contained in the set or `0`
122+
* otherwise. It won't allocate memory and its complexity is
123+
* *effectively* @f$ O(1) @f$.
124+
*
125+
* This overload participates in overload resolution only if
126+
* `Hash::is_transparent` is valid and denotes a type.
127+
*/
128+
template<typename K, typename U = Hash, typename = typename U::is_transparent>
129+
IMMER_NODISCARD size_type count(const K& value) const
130+
{
131+
return impl_.template get<detail::constantly<size_type, 1>,
132+
detail::constantly<size_type, 0>>(value);
133+
}
134+
120135
/*!
121136
* Returns `1` when `value` is contained in the set or `0`
122137
* otherwise. It won't allocate memory and its complexity is
@@ -140,6 +155,22 @@ class set
140155
detail::constantly<const T*, nullptr>>(value);
141156
}
142157

158+
/*!
159+
* Returns a pointer to the value if `value` is contained in the
160+
* set, or nullptr otherwise.
161+
* It does not allocate memory and its complexity is *effectively*
162+
* @f$ O(1) @f$.
163+
*
164+
* This overload participates in overload resolution only if
165+
* `Hash::is_transparent` is valid and denotes a type.
166+
*/
167+
template<typename K, typename U = Hash, typename = typename U::is_transparent>
168+
IMMER_NODISCARD const T* find(const K& value) const
169+
{
170+
return impl_.template get<project_value_ptr,
171+
detail::constantly<const T*, nullptr>>(value);
172+
}
173+
143174
/*!
144175
* Returns whether the sets are equal.
145176
*/

test/map/generic.ipp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,50 @@ TEST_CASE("exception safety")
275275
}
276276
}
277277

278+
namespace {
279+
struct KeyType {
280+
explicit KeyType(unsigned v) : value(v) {}
281+
unsigned value;
282+
};
283+
284+
struct LookupType {
285+
explicit LookupType(unsigned v) : value(v) {}
286+
unsigned value;
287+
};
288+
289+
struct TransparentHash
290+
{
291+
using hash_type = std::hash<unsigned>;
292+
using is_transparent = void;
293+
294+
size_t operator()(KeyType const& k) const { return hash_type{}(k.value); }
295+
size_t operator()(LookupType const& k) const
296+
{
297+
return hash_type{}(k.value);
298+
}
299+
};
300+
301+
bool operator==(KeyType const& k, KeyType const& l) {
302+
return k.value == l.value;
303+
}
304+
bool operator==(KeyType const& k, LookupType const& l) {
305+
return k.value == l.value;
306+
}
307+
}
308+
309+
TEST_CASE("lookup with transparent hash")
310+
{
311+
SECTION("default")
312+
{
313+
auto m = MAP_T<KeyType, int, TransparentHash, std::equal_to<>>{};
314+
m = m.insert({KeyType{1}, 12});
315+
316+
auto const& v = m.at(LookupType{1});
317+
CHECK(v == 12);
318+
}
319+
}
320+
321+
278322
namespace {
279323

280324
class KElem

test/set/generic.ipp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,47 @@ TEST_CASE("exception safety")
457457
IMMER_TRACE_E(d.happenings);
458458
}
459459
}
460+
461+
462+
namespace {
463+
struct KeyType {
464+
explicit KeyType(unsigned v) : value(v) {}
465+
unsigned value;
466+
};
467+
468+
struct LookupType {
469+
explicit LookupType(unsigned v) : value(v) {}
470+
unsigned value;
471+
};
472+
473+
struct TransparentHash
474+
{
475+
using hash_type = std::hash<unsigned>;
476+
using is_transparent = void;
477+
478+
size_t operator()(KeyType const& k) const { return hash_type{}(k.value); }
479+
size_t operator()(LookupType const& k) const
480+
{
481+
return hash_type{}(k.value);
482+
}
483+
};
484+
485+
bool operator==(KeyType const& k, KeyType const& l) {
486+
return k.value == l.value;
487+
}
488+
bool operator==(KeyType const& k, LookupType const& l) {
489+
return k.value == l.value;
490+
}
491+
}
492+
493+
TEST_CASE("lookup with transparent hash")
494+
{
495+
SECTION("default")
496+
{
497+
auto m = SET_T<KeyType, TransparentHash, std::equal_to<>>{};
498+
m = m.insert(KeyType{1});
499+
500+
CHECK(m.count(LookupType{1}) == 1);
501+
CHECK(m.count(LookupType{2}) == 0);
502+
}
503+
}

0 commit comments

Comments
 (0)