jaulib v1.4.1-10-ga2c96e0
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
basic_collections.hpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2024-2025 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#ifndef JAU_BASIC_COLLECTIONS_HPP_
26#define JAU_BASIC_COLLECTIONS_HPP_
27
28#include <cstdarg>
29#include <cstring>
30#include <optional>
31#include <string>
32#include <string_view>
33#include <unordered_map>
34#include <unordered_set>
35#include <jau/type_concepts.hpp>
36
37namespace jau {
38
39 /** @defgroup Collections Collection Utilities
40 * Basic collection utilities supporting std::unordered_map, std::unordered_set, etc usage
41 *
42 * @{
43 */
44
45 /**
46 * C++20: Heterogeneous Lookup in (Un)ordered Containers
47 *
48 * @see https://www.cppstories.com/2021/heterogeneous-access-cpp20/
49 */
50 struct string_hash {
51 using is_transparent = void;
52 [[nodiscard]] size_t operator()(const char *txt) const {
53 return std::hash<std::string_view>{}(txt);
54 }
55 [[nodiscard]] size_t operator()(std::string_view txt) const {
56 return std::hash<std::string_view>{}(txt);
57 }
58 [[nodiscard]] size_t operator()(const std::string &txt) const {
59 return std::hash<std::string>{}(txt);
60 }
61 };
62
63 /**
64 * std::unordered_map using K key and heterogenous jau::string_hash functor
65 */
66 template<typename K, typename V>
67 using StringlikeHashMap = std::unordered_map<K, V, jau::string_hash, std::equal_to<>>;
68
69 /**
70 * std::unordered_map using std::string key and heterogenous jau::string_hash functor
71 */
72 template<typename T>
73 using StringHashMap = std::unordered_map<std::string, T, string_hash, std::equal_to<>>;
74
75 /**
76 * std::unordered_set using std::string key and heterogenous jau::string_hash functor
77 */
78 using StringHashSet = std::unordered_set<std::string, string_hash, std::equal_to<>>;
79
80 /**
81 * std::unordered_map using std::string_view key and heterogenous jau::string_hash functor, use with care!
82 *
83 * Key values must persist through the lifecycle of the map.
84 */
85 template<typename T>
86 using StringViewHashMap = std::unordered_map<std::string_view, T, string_hash, std::equal_to<>>;
87
88 /**
89 * std::unordered_set using std::string_view key and heterogenous jau::string_hash functor, use with care!
90 *
91 * Key values must persist through the lifecycle of the set.
92 */
93 using StringViewHashSet = std::unordered_set<std::string_view, string_hash, std::equal_to<>>;
94
95 /**
96 * HashMapWrap, generic std::unordered_map exposing a more higher level API
97 *
98 * Allows using No_value for queries to signal `no value` found in map.
99 *
100 * `no_value` will be returned by reference, including mutable reference.
101 * Hence caller must ensure that this special value is detected and not written to.
102 * This path has been chosen in favor of returning a `nullptr`.
103 *
104 * @tparam Key_type key type
105 * @tparam Value_type value type
106 * @tparam Novalue_type value type representing `no value`, e.g. Value_type or std::nullptr_t for pointer.
107 * Requirement: Value_type must be constructible from Novalue_type.
108 * @tparam No_value `no value` query result of Novalue_type type, e.g. a Value_type `-1`, `MAX_VALUE` or nullptr for std::nullptr_t
109 * @tparam Hash_functor Hashing function object type, defaults to std::hash<Key_type>
110 * @tparam Query_type key query type, defaults to Key_type. Allows to sooth query parameter, e.g. string_view instead of string.
111 * Requirement: Key_type must be constructible from Query_type.
112 * @tparam KeyEqual Comparator function object type, defaults to std::equal_to<>
113 * @tparam Allocator Allocator type, defaults to std::allocator<std::pair<const Key_type, Value_type>>
114 */
115 template<typename Key_type, typename Value_type,
116 typename Novalue_type, Novalue_type No_value,
117 typename Hash_functor = std::hash<Key_type>,
118 typename Query_type = Key_type,
119 typename KeyEqual = std::equal_to<>,
120 typename Allocator = std::allocator<std::pair<const Key_type, Value_type>> >
121 requires std::constructible_from<Value_type, Novalue_type> &&
122 std::constructible_from<Key_type, Query_type>
124 public:
125 typedef Key_type key_type;
126 typedef Query_type query_type;
127 typedef Hash_functor hash_functor;
129 // typedef value_type* pointer;
130 // typedef const value_type* const_pointer;
133 typedef Novalue_type novalue_type;
134
135 typedef std::unordered_map<key_type, value_type, hash_functor, KeyEqual, Allocator> HashMapType;
136 typedef typename HashMapType::size_type size_type;
137 typedef typename HashMapType::value_type pair_type;
138
139 private:
140 HashMapType m_map;
141 value_type no_value = No_value; /// converts novalue_type No_value to lvalue value_type to return its reference
142
143 public:
144 HashMapType& map() noexcept { return m_map; }
145 const HashMapType& map() const noexcept { return m_map; }
146
147 /// Returns the no-value immutable refererence `no_value`
148 constexpr const_reference novalue() const noexcept { return no_value; }
149 /// Returns the no-value mutable refererence, `no_value` (should not be writable or written to)
150 constexpr reference novalue() noexcept { return no_value; }
151
152 size_type size() const noexcept { return m_map.size(); }
153
154 /** Clears the hash map. */
155 void clear() { m_map.clear(); }
156
157 /** Returns the immutable mapped value reference for the given key or novalue() */
158 const_reference get(const query_type& key) const {
159 auto it = m_map.find(key);
160 if( it != m_map.end() ) {
161 return it->second;
162 }
163 return novalue();
164 }
165 /** Returns the mutable mapped value reference for the given key or novalue() */
167 auto it = m_map.find(key);
168 if( it != m_map.end() ) {
169 return it->second;
170 }
171 return novalue();
172 }
173
174 /** Returns the immutable pair_type pointer for the given key or nullptr. */
175 const pair_type* find(const query_type& key) const {
176 auto it = m_map.find(key);
177 if( it != m_map.end() ) {
178 return &(*it);
179 }
180 return nullptr;
181 }
182
183 /** Returns true if the given key maps to a value or `no_value`. */
184 bool containsKey(const query_type& key) const {
185 return m_map.contains(key);
186 }
187
188 /** Returns the key reference of the first value, otherwise std::nullopt. Note: O(n) operation, slow. */
189 std::optional<const key_type&> containsValue(const_reference value) const {
190 for (const std::pair<const key_type, value_type>& n : m_map) {
191 if( n.second == value ) {
192 return std::optional<const key_type&>{n.first};
193 }
194 }
195 return std::nullopt;
196 }
197
198 /**
199 * Adds a new mapping of the value for the given key, does nothing if a mapping exists.
200 * @return true if value is newly mapped, otherwise false doing nothing.
201 */
202 bool insert(const key_type &key, const_reference obj) {
203 return m_map.insert( {key, obj} ).second;
204 }
205
206 /**
207 * Adds a new mapping of the value for the given key, does nothing if a mapping exists.
208 * @return true if value is newly mapped, otherwise false doing nothing.
209 */
210 template<class Q>
211 requires (!std::same_as<key_type, query_type>) &&
212 std::same_as<Q, query_type>
213 bool insert(const Q &key, const_reference obj) {
214 return m_map.insert( {key_type(key), obj} ).second;
215 }
216
217 /**
218 * Maps the value for the given key, overwrites old mapping if exists.
219 * @return true if value is newly mapped, otherwise false if replacing old mapping.
220 */
221 bool put(const key_type &key, const_reference obj) {
222 auto it = m_map.find(key);
223 if( it != m_map.end() ) {
224 it->second = obj;
225 return false;
226 }
227 m_map.insert( {key, obj} );
228 return true;
229 }
230
231 /**
232 * Maps the value for the given key, overwrites old mapping if exists.
233 * @return true if value is newly mapped, otherwise false if replacing old mapping.
234 */
235 template<class Q>
236 requires (!std::same_as<key_type, query_type>) &&
237 std::same_as<Q, query_type>
238 bool put(const Q &key, const_reference obj) {
239 auto it = m_map.find(key);
240 if( it != m_map.end() ) {
241 it->second = obj;
242 return false;
243 }
244 m_map.insert( {key_type(key), obj} );
245 return true;
246 }
247
248 /**
249 * Maps the value for the given key, overwrites old mapping if exists.
250 *
251 * Consider using put() if old replaced value is not of interest.
252 *
253 * @return newly successfully mapped value reference owned by this map or novalue()
254 * @see ::novalue()
255 */
257 auto it = m_map.find(key);
258 if( it != m_map.end() ) {
259 it->second = obj;
260 return it->second;
261 }
262 auto [ it2, res ] = m_map.insert( {key, obj} );
263 return res ? it2->second : novalue();
264 }
265
266 /**
267 * Maps the value for the given key, overwrites old mapping if exists.
268 *
269 * Consider using put() if old replaced value is not of interest.
270 *
271 * @return newly successfully mapped value reference owned by this map or novalue()
272 * @see ::novalue()
273 */
274 template<class Q>
275 requires (!std::same_as<key_type, query_type>) &&
276 std::same_as<Q, query_type>
278 auto it = m_map.find(key);
279 if( it != m_map.end() ) {
280 it->second = obj;
281 return it->second;
282 }
283 auto [ it2, res ] = m_map.insert( {key_type(key), obj} );
284 return res ? it2->second : novalue();
285 }
286
287 /**
288 * Maps the value for the given key, overwrites old mapping if exists.
289 *
290 * Consider using put() if old replaced value is not of interest.
291 *
292 * @return previously mapped value or `no_value`.
293 */
295 auto it = m_map.find(key);
296 if (it != m_map.end()) {
297 value_type old = it->second;
298 it->second = obj;
299 return old;
300 }
301 m_map.insert({ key, obj });
302 return no_value;
303 }
304
305 /**
306 * Maps the value for the given key, overwrites old mapping if exists.
307 *
308 * Consider using put() if old replaced value is not of interest.
309 *
310 * @return previously mapped value or `no_value`.
311 */
312 template<class Q>
313 requires (!std::same_as<key_type, query_type>) &&
314 std::same_as<Q, query_type>
316 auto it = m_map.find(key);
317 if( it != m_map.end() ) {
318 value_type old = it->second;
319 it->second = obj;
320 return old;
321 }
322 m_map.insert( {key_type(key), obj} );
323 return no_value;
324 }
325
326 /**
327 * Replaces the already mapped value for the given key, does nothing if no mapping exists.
328 * @return true if mapped value is replaced, otherwise false doing nothing.
329 */
331 auto it = m_map.find(key);
332 if( it != m_map.end() ) {
333 it->second = obj;
334 return true;
335 }
336 return false;
337 }
338
339 /**
340 * Replaces the already mapped value for the given key, does nothing if no mapping exists.
341 * @return true if mapped value is replaced, otherwise false doing nothing.
342 */
343 template<class Q>
344 requires (!std::same_as<key_type, query_type>) &&
345 std::same_as<Q, query_type>
346 bool replace(const Q &key, const_reference obj) {
347 auto it = m_map.find(key);
348 if( it != m_map.end() ) {
349 it->second = obj;
350 return true;
351 }
352 return false;
353 }
354
355 /** Removes value if mapped and returns true, otherwise returns false. */
356 bool remove(const query_type &key) {
357 auto it = m_map.find(key);
358 if( it != m_map.end() ) {
359 m_map.erase(it);
360 return true;
361 }
362 return false;
363 }
364
365 /** Removes value if mapped and returns true, otherwise returns false. */
366 template<class Q>
367 requires (!std::same_as<key_type, query_type>) &&
368 std::same_as<Q, query_type>
369 bool remove(const Q &key) {
370 auto it = m_map.find(key);
371 if( it != m_map.end() ) {
372 m_map.erase(it);
373 return true;
374 }
375 return false;
376 }
377
378 /**
379 * Removes value if mapped and returns it, otherwise returns `no_value`.
380 *
381 * Consider using remove() if removed value is not of interest.
382 *
383 * @return removed value or `no_value`.
384 */
386 auto it = m_map.find(key);
387 if( it != m_map.end() ) {
388 value_type old = it->second;
389 m_map.erase(it);
390 return old;
391 }
392 return no_value;
393 }
394
395 /**
396 * Removes value if mapped and returns it, otherwise returns `no_value`.
397 *
398 * Consider using remove() if removed value is not of interest.
399 *
400 * @return removed value or `no_value`.
401 */
402 template<class Q>
403 requires (!std::same_as<key_type, query_type>) &&
404 std::same_as<Q, query_type>
405 value_type remove2(const Q &key) {
406 auto it = m_map.find(key);
407 if( it != m_map.end() ) {
408 value_type old = it->second;
409 m_map.erase(it);
410 return old;
411 }
412 return no_value;
413 }
414 };
415
416 /**
417 * StringHashMapWrap, generic std::unordered_map exposing a more higher level API
418 *
419 * Based on HashMapWrap, using std::string key and heterogenous jau::string_hash functor
420 */
421 template<typename Value_type, typename Novalue_type, Novalue_type no_value>
423
424 /**
425 * StringHashViewMapWrap, generic std::unordered_map exposing a more higher level API, use with care!
426 *
427 * Key values must persist through the lifecycle of the map.
428 *
429 * Based on HashMapWrap, using std::string_view key and heterogenous jau::string_hash functor
430 */
431 template<typename Value_type, typename Novalue_type, Novalue_type no_value>
433
434 /**@}*/
435
436} // namespace jau
437
438#endif /* JAU_BASIC_COLLECTIONS_HPP_ */
HashMapWrap, generic std::unordered_map exposing a more higher level API.
bool insert(const Q &key, const_reference obj)
Adds a new mapping of the value for the given key, does nothing if a mapping exists.
value_type put3(const Q &key, const_reference obj)
Maps the value for the given key, overwrites old mapping if exists.
value_type remove2(const query_type &key)
Removes value if mapped and returns it, otherwise returns no_value.
bool replace(const Q &key, const_reference obj)
Replaces the already mapped value for the given key, does nothing if no mapping exists.
bool containsKey(const query_type &key) const
Returns true if the given key maps to a value or no_value.
const_reference get(const query_type &key) const
Returns the immutable mapped value reference for the given key or novalue()
reference put2(const key_type &key, const_reference obj)
Maps the value for the given key, overwrites old mapping if exists.
constexpr reference novalue() noexcept
Returns the no-value mutable refererence, no_value (should not be writable or written to)
bool put(const Q &key, const_reference obj)
Maps the value for the given key, overwrites old mapping if exists.
HashMapType & map() noexcept
converts novalue_type No_value to lvalue value_type to return its reference
bool put(const key_type &key, const_reference obj)
Maps the value for the given key, overwrites old mapping if exists.
bool remove(const Q &key)
Removes value if mapped and returns true, otherwise returns false.
std::unordered_map< key_type, value_type, hash_functor, KeyEqual, Allocator > HashMapType
void clear()
Clears the hash map.
const value_type & const_reference
value_type remove2(const Q &key)
Removes value if mapped and returns it, otherwise returns no_value.
constexpr const_reference novalue() const noexcept
Returns the no-value immutable refererence no_value
bool replace(const key_type &key, const_reference obj)
Replaces the already mapped value for the given key, does nothing if no mapping exists.
std::optional< const key_type & > containsValue(const_reference value) const
Returns the key reference of the first value, otherwise std::nullopt.
size_type size() const noexcept
bool insert(const key_type &key, const_reference obj)
Adds a new mapping of the value for the given key, does nothing if a mapping exists.
reference put2(const Q &key, const_reference obj)
Maps the value for the given key, overwrites old mapping if exists.
value_type put3(const key_type &key, const_reference obj)
Maps the value for the given key, overwrites old mapping if exists.
const pair_type * find(const query_type &key) const
Returns the immutable pair_type pointer for the given key or nullptr.
HashMapType::size_type size_type
const HashMapType & map() const noexcept
bool remove(const query_type &key)
Removes value if mapped and returns true, otherwise returns false.
HashMapType::value_type pair_type
reference get(const query_type &key)
Returns the mutable mapped value reference for the given key or novalue()
std::unordered_map< std::string_view, T, string_hash, std::equal_to<> > StringViewHashMap
std::unordered_map using std::string_view key and heterogenous jau::string_hash functor,...
HashMapWrap< std::string, Value_type, Novalue_type, no_value, jau::string_hash, std::string_view > StringHashMapWrap
StringHashMapWrap, generic std::unordered_map exposing a more higher level API.
std::unordered_map< K, V, jau::string_hash, std::equal_to<> > StringlikeHashMap
std::unordered_map using K key and heterogenous jau::string_hash functor
HashMapWrap< std::string_view, Value_type, Novalue_type, no_value, jau::string_hash > StringViewHashMapWrap
StringHashViewMapWrap, generic std::unordered_map exposing a more higher level API,...
std::unordered_set< std::string, string_hash, std::equal_to<> > StringHashSet
std::unordered_set using std::string key and heterogenous jau::string_hash functor
std::unordered_map< std::string, T, string_hash, std::equal_to<> > StringHashMap
std::unordered_map using std::string key and heterogenous jau::string_hash functor
std::unordered_set< std::string_view, string_hash, std::equal_to<> > StringViewHashSet
std::unordered_set using std::string_view key and heterogenous jau::string_hash functor,...
constexpr bool value(const Bool rhs) noexcept
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition backtrace.hpp:32
C++20: Heterogeneous Lookup in (Un)ordered Containers.
size_t operator()(const std::string &txt) const
size_t operator()(const char *txt) const
size_t operator()(std::string_view txt) const
uint8_t Value_type