ATLAS Offline Software
Loading...
Searching...
No Matches
ConcurrentStrMap.icc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration.
3 */
4/**
5 * @file CxxUtils/ConcurrentStrMap.icc
6 * @author scott snyder <snyder@bnl.gov>
7 * @date Dec, 2020
8 * @brief Hash map from strings to integers allowing concurrent, lockless reads.
9 */
10
11
12namespace CxxUtils {
13
14
15#define T_CONCURRENTSTRMAP template <class VALUE, template <class> class UPDATER> \
16 requires (detail::IsConcurrentHashmapPayload<VALUE> && \
17 detail::IsUpdater<UPDATER>)
18
19#define CONCURRENTSTRMAP ConcurrentStrMap<VALUE, UPDATER>
20
21
22/**
23 * @brief Constructor.
24 * @param updater Object used to manage memory
25 * (see comments at the start of the class).
26 * @param capacity The initial table capacity.
27 * (Will be rounded up to a power of two.)
28 * @param ctx Execution context.
29 */
30T_CONCURRENTSTRMAP
31CONCURRENTSTRMAP::ConcurrentStrMap (Updater_t&& updater,
32 size_type capacity /*= 64*/,
33 const Context_t& ctx
34 /* = Updater_t::defaultContext()*/)
35 : m_impl (std::move (updater),
36 capacity,
37 Hasher(),
38 Matcher(),
39 ctx)
40{
41}
42
43
44/**
45 * @brief Constructor from another map.
46 * @param updater Object used to manage memory
47 * (see comments at the start of the class).
48 * @param capacity The initial table capacity of the new table.
49 * (Will be rounded up to a power of two.)
50 * @param ctx Execution context.
51 *
52 * (Not really a copy constructor since we need to pass @c updater.)
53 */
54T_CONCURRENTSTRMAP
55CONCURRENTSTRMAP::ConcurrentStrMap (const ConcurrentStrMap& other,
56 Updater_t&& updater,
57 size_type capacity /*= 64*/,
58 const Context_t& ctx
59 /*= Updater_t::defaultContext()*/)
60 : m_impl (std::move (updater),
61 capacity,
62 Hasher(),
63 Matcher(),
64 ctx)
65{
66 // not using reference, because our iterator doesn't return a reference
67 for (const auto p : other) {
68 this->emplace (p.first, p.second, ctx);
69 }
70}
71
72
73/**
74 * @brief Constructor from a range.
75 * @param f Start iterator for the range.
76 * @param l End iterator for the range.
77 * @param updater Object used to manage memory
78 * (see comments at the start of the class).
79 * @param capacity The initial table capacity of the new table.
80 * (Will be rounded up to a power of two.)
81 * @param ctx Execution context.
82 *
83 * Constructor from a range of pairs.
84 */
85T_CONCURRENTSTRMAP
86template <class InputIterator>
87CONCURRENTSTRMAP::ConcurrentStrMap (InputIterator f,
88 InputIterator l,
89 Updater_t&& updater,
90 size_t capacity /*= 64*/,
91 const Context_t& ctx
92 /*= Updater_t::defaultContext()*/)
93 : m_impl (std::move (updater),
94 capacity,
95 Hasher(),
96 Matcher(),
97 ctx)
98{
99 Lock_t lck = lock();
100 if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
101 {
102 for (; f != l; ++f) {
103 this->emplace (lck, std::move (f->first), f->second, ctx);
104 }
105 }
106 else {
107 for (; f != l; ++f) {
108 this->emplace (lck, f->first, f->second, ctx);
109 }
110 }
111}
112
113
114/**
115 * @brief Destructor.
116 */
117T_CONCURRENTSTRMAP
118CONCURRENTSTRMAP::~ConcurrentStrMap()
119{
120 // Need to delete the strings that we've stored.
121 auto [begin, end] = m_impl.range();
122 while (begin != end) {
123 if (begin.key() != Impl_t::nullval) {
124 delete keyAsString (begin.key());
125 }
126 begin.next();
127 }
128}
129
130
131/**
132 * @brief Return the number of items currently in the map.
133 */
134T_CONCURRENTSTRMAP
135inline
136auto CONCURRENTSTRMAP::size() const -> size_type
137{
138 return m_impl.size();
139}
140
141
142/**
143 * @brief Test if the map is currently empty.
144 */
145T_CONCURRENTSTRMAP
146inline
147bool CONCURRENTSTRMAP::empty() const
148{
149 return !m_impl.size();
150}
151
152
153/**
154 * @brief Return the current size (capacity) of the hash table.
155 */
156T_CONCURRENTSTRMAP
157inline
158auto CONCURRENTSTRMAP::capacity() const -> size_t
159{
160 return m_impl.capacity();
161}
162
163
164/**
165 * @brief Constructor.
166 * @param it Iterator of the underlying table.
167 */
168T_CONCURRENTSTRMAP
169inline
170CONCURRENTSTRMAP::const_iterator::const_iterator (typename Impl_t::const_iterator it)
171 : m_impl (it)
172{
173}
174
175
176/**
177 * @brief Test if this iterator is valid.
178 *
179 * This should be the same as testing for != end().
180 */
181T_CONCURRENTSTRMAP
182inline
183auto CONCURRENTSTRMAP::const_iterator::valid() const -> bool
184{
185 return m_impl.valid();
186}
187
188
189/**
190 * @brief iterator_facade requirement: Increment the iterator.
191 */
192T_CONCURRENTSTRMAP
193inline
194auto CONCURRENTSTRMAP::const_iterator::increment() -> void
195{
196 m_impl.next();
197}
198
199
200/**
201 * @brief iterator_facade requirement: Decrement the iterator.
202 */
203T_CONCURRENTSTRMAP
204inline
205auto CONCURRENTSTRMAP::const_iterator::decrement() -> void
206{
207 m_impl.prev();
208}
209
210
211/**
212 * @brief iterator_facade requirement: Dereference the iterator.
213 */
214T_CONCURRENTSTRMAP
215inline
216auto CONCURRENTSTRMAP::const_iterator::dereference() const
217 -> const const_iterator_value
218{
219 return const_iterator_value (*keyAsString (m_impl.key()),
220 mappedAsMapped (m_impl.value()));
221}
222
223
224/**
225 * @brief iterator_facade requirement: Equality test.
226 */
227T_CONCURRENTSTRMAP
228inline
229auto CONCURRENTSTRMAP::const_iterator::equal (const const_iterator& other) const
230 -> bool
231{
232 return !(m_impl != other.m_impl);
233}
234
235
236/**
237 * @brief Return an iterator range covering the entire map.
238 */
239T_CONCURRENTSTRMAP
240auto CONCURRENTSTRMAP::range() const -> const_iterator_range
241{
242 auto [begin, end] = m_impl.range();
243 return const_iterator_range (begin, end);
244}
245
246
247/**
248 * @brief Iterator at the start of the map.
249 */
250T_CONCURRENTSTRMAP
251inline
252auto CONCURRENTSTRMAP::begin() const -> const_iterator
253{
254 return const_iterator (m_impl.begin());
255}
256
257
258/**
259 * @brief Iterator at the end of the map.
260 */
261T_CONCURRENTSTRMAP
262inline
263auto CONCURRENTSTRMAP::end() const -> const_iterator
264{
265 return const_iterator (m_impl.end());
266}
267
268
269/**
270 * @brief Iterator at the start of the map.
271 */
272T_CONCURRENTSTRMAP
273inline
274auto CONCURRENTSTRMAP::cbegin() const -> const_iterator
275{
276 return begin();
277}
278
279
280/**
281 * @brief Iterator at the end of the map.
282 */
283T_CONCURRENTSTRMAP
284inline
285auto CONCURRENTSTRMAP::cend() const -> const_iterator
286{
287 return end();
288}
289
290
291/**
292 * @brief Test if a key is in the container.
293 * @param key The key to test.
294 */
295T_CONCURRENTSTRMAP
296inline
297bool CONCURRENTSTRMAP::contains (const key_type& key) const
298{
299 return get(key).valid();
300}
301
302
303/**
304 * @brief Return the number of times a given key is in the container.
305 * @param key The key to test.
306 *
307 * Returns either 0 or 1, depending on whether or not the key is in the map.
308 */
309T_CONCURRENTSTRMAP
310inline
311auto CONCURRENTSTRMAP::count (const key_type& key) const -> size_type
312{
313 return contains (key) ? 1 : 0;
314}
315
316
317/**
318 * @brief Look up an element in the map.
319 * @param key The element to find.
320 *
321 * Returns either an iterator referencing the found element or end().
322 */
323T_CONCURRENTSTRMAP
324inline
325auto CONCURRENTSTRMAP::find (const key_type& key) const -> const_iterator
326{
327 return const_iterator (this->get (key));
328}
329
330
331/**
332 * @brief Look up an element in the map.
333 * @param key The element to find.
334 *
335 * Returns the value associated with the key.
336 * Throws @c std::out_of_range if the key does not exist in the map.
337 */
338T_CONCURRENTSTRMAP
339auto CONCURRENTSTRMAP::at (const key_type& key) const -> mapped_type
340{
341 typename Impl_t::const_iterator it = this->get (key);
342 if (!it.valid()) {
343 throw std::out_of_range ("ConcurrentStrMap::at");
344 }
345 return mappedAsMapped (it.value());
346}
347
348
349/**
350 * @brief Return a range of iterators with entries matching @c key.
351 * @param key The element to find.
352 *
353 * As keys are unique in this container, this is either a single-element
354 * range, or both iterators are equal to end().
355 */
356T_CONCURRENTSTRMAP
357auto CONCURRENTSTRMAP::equal_range (const key_type& key) const
358 -> std::pair<const_iterator, const_iterator>
359{
360 const_iterator i1 = find (key);
361 const_iterator i2 = i1;
362 if (i2.valid()) {
363 ++i2;
364 }
365 return std::make_pair (i1, i2);
366}
367
368
369/**
370 * @brief Take a lock on the container.
371 *
372 * Take a lock on the container.
373 * The lock can then be passed to insertion methods, allowing to factor out
374 * the locking inside loops. The lock will be released when the
375 * lock object is destroyed.
376 */
377T_CONCURRENTSTRMAP
378inline
379auto CONCURRENTSTRMAP::lock() -> Lock_t
380{
381 return m_impl.lock();
382}
383
384
385/**
386 * @brief Add an element to the map.
387 * @param key The key of the new item to add.
388 * @param val The value of the new item to add.
389 * @param ctx Execution context.
390 *
391 * This will not overwrite an existing entry.
392 * The first element in the returned pair is an iterator referencing
393 * the added item. The second is a flag that is true if a new element
394 * was added.
395 */
396T_CONCURRENTSTRMAP
397inline
398auto CONCURRENTSTRMAP::emplace (const key_type& key,
399 mapped_type val,
400 const Context_t& ctx
401 /*= Updater_t::defaultContext()*/)
402 -> std::pair<const_iterator, bool>
403{
404 return put (std::make_unique<key_type> (key), val, false, ctx);
405}
406
407
408/**
409 * @brief Add an element to the map, with external locking.
410 * @param lock The lock object returned from lock().
411 * @param key The key of the new item to add.
412 * @param val The value of the new item to add.
413 * @param ctx Execution context.
414 *
415 * This will not overwrite an existing entry.
416 * The first element in the returned pair is an iterator referencing
417 * the added item. The second is a flag that is true if a new element
418 * was added.
419 */
420T_CONCURRENTSTRMAP
421inline
422auto CONCURRENTSTRMAP::emplace (const Lock_t& lock,
423 const key_type& key,
424 mapped_type val,
425 const Context_t& ctx
426 /*= Updater_t::defaultContext()*/)
427 -> std::pair<const_iterator, bool>
428{
429 return put (lock, std::make_unique<key_type> (key), val, false, ctx);
430}
431
432
433/**
434 * @brief Add an element to the map.
435 * @param key The key of the new item to add.
436 * @param val The value of the new item to add.
437 * @param ctx Execution context.
438 *
439 * This will not overwrite an existing entry.
440 * The first element in the returned pair is an iterator referencing
441 * the added item. The second is a flag that is true if a new element
442 * was added.
443 */
444T_CONCURRENTSTRMAP
445inline
446auto CONCURRENTSTRMAP::emplace (key_type&& key,
447 mapped_type val,
448 const Context_t& ctx
449 /*= Updater_t::defaultContext()*/)
450 -> std::pair<const_iterator, bool>
451{
452 return put (std::make_unique<key_type> (std::move (key)), val, false, ctx);
453}
454
455
456/**
457 * @brief Add an element to the map, with external locking.
458 * @param lock The lock object returned from lock().
459 * @param key The key of the new item to add.
460 * @param val The value of the new item to add.
461 * @param ctx Execution context.
462 *
463 * This will not overwrite an existing entry.
464 * The first element in the returned pair is an iterator referencing
465 * the added item. The second is a flag that is true if a new element
466 * was added.
467 */
468T_CONCURRENTSTRMAP
469inline
470auto CONCURRENTSTRMAP::emplace (const Lock_t& lock,
471 key_type&& key,
472 mapped_type val,
473 const Context_t& ctx
474 /*= Updater_t::defaultContext()*/)
475 -> std::pair<const_iterator, bool>
476{
477 return put (lock, std::make_unique<key_type> (std::move (key)), val, false, ctx);
478}
479
480
481/**
482 * @brief Add an element to the map, or overwrite an existing one.
483 * @param key The key of the new item to add.
484 * @param val The value of the new item to add.
485 * @param ctx Execution context.
486 *
487 * This will overwrite an existing entry.
488 * The first element in the returned pair is an iterator referencing
489 * the added item. The second is a flag that is true if a new element
490 * was added.
491 */
492T_CONCURRENTSTRMAP
493inline
494auto CONCURRENTSTRMAP::insert_or_assign (const key_type& key,
495 mapped_type val,
496 const Context_t& ctx
497 /*= Updater_t::defaultContext()*/)
498 -> std::pair<const_iterator, bool>
499{
500 return put (std::make_unique<key_type> (key), val, true, ctx);
501}
502
503
504/**
505 * @brief Add an element to the map, or overwrite an existing one,
506 * with external locking.
507 * @param lock The lock object returned from lock().
508 * @param key The key of the new item to add.
509 * @param val The value of the new item to add.
510 * @param ctx Execution context.
511 *
512 * This will overwrite an existing entry.
513 * The first element in the returned pair is an iterator referencing
514 * the added item. The second is a flag that is true if a new element
515 * was added.
516 */
517T_CONCURRENTSTRMAP
518inline
519auto CONCURRENTSTRMAP::insert_or_assign (const Lock_t& lock,
520 const key_type& key,
521 mapped_type val,
522 const Context_t& ctx
523 /*= Updater_t::defaultContext()*/)
524 -> std::pair<const_iterator, bool>
525{
526 return put (lock, std::make_unique<key_type> (key), val, true, ctx);
527}
528
529
530/**
531 * @brief Add an element to the map, or overwrite an existing one.
532 * @param key The key of the new item to add.
533 * @param val The value of the new item to add.
534 * @param ctx Execution context.
535 *
536 * This will overwrite an existing entry.
537 * The first element in the returned pair is an iterator referencing
538 * the added item. The second is a flag that is true if a new element
539 * was added.
540 */
541T_CONCURRENTSTRMAP
542inline
543auto CONCURRENTSTRMAP::insert_or_assign (key_type&& key,
544 mapped_type val,
545 const Context_t& ctx
546 /*= Updater_t::defaultContext()*/)
547 -> std::pair<const_iterator, bool>
548{
549 return put (std::make_unique<key_type> (std::move (key)), val, true, ctx);
550}
551
552
553/**
554 * @brief Add an element to the map, or overwrite an existing one,
555 * with external locking.
556 * @param lock The lock object returned from lock().
557 * @param key The key of the new item to add.
558 * @param val The value of the new item to add.
559 * @param ctx Execution context.
560 *
561 * This will overwrite an existing entry.
562 * The first element in the returned pair is an iterator referencing
563 * the added item. The second is a flag that is true if a new element
564 * was added.
565 */
566T_CONCURRENTSTRMAP
567inline
568auto CONCURRENTSTRMAP::insert_or_assign (const Lock_t& lock,
569 key_type&& key,
570 mapped_type val,
571 const Context_t& ctx
572 /*= Updater_t::defaultContext()*/)
573 -> std::pair<const_iterator, bool>
574{
575 return put (lock, std::make_unique<key_type> (std::move (key)), val, true, ctx);
576}
577
578
579/**
580 * @brief Add an element to the map.
581 * @param p The item to add.
582 * Should be a pair where first is the string key
583 * and second is the integer value.
584 * @param ctx Execution context.
585 *
586 * This will not overwrite an existing entry.
587 * The first element in the returned pair is an iterator referencing
588 * the added item. The second is a flag that is true if a new element
589 * was added.
590 *
591 * For external locking, use emplace().
592 */
593T_CONCURRENTSTRMAP
594template <class PAIR>
595inline
596auto CONCURRENTSTRMAP::insert (const PAIR& p,
597 const Context_t& ctx /*= Updater_t::defaultContext()*/)
598 -> std::pair<const_iterator, bool>
599{
600 return emplace (p.first, p.second, ctx);
601}
602
603
604/**
605 * @brief Add an element to the map.
606 * @param p The item to add.
607 * Should be a pair where first is the string key
608 * and second is the integer value.
609 * @param ctx Execution context.
610 *
611 * This will not overwrite an existing entry.
612 * The first element in the returned pair is an iterator referencing
613 * the added item. The second is a flag that is true if a new element
614 * was added.
615 */
616T_CONCURRENTSTRMAP
617template <class PAIR>
618inline
619auto CONCURRENTSTRMAP::insert (PAIR&& p,
620 const Context_t& ctx /*= Updater_t::defaultContext()*/)
621 -> std::pair<const_iterator, bool>
622{
623 return emplace (std::move (p.first), p.second, ctx);
624}
625
626
627/**
628 * @brief Insert a range of elements to the map.
629 * @param first Start of the range.
630 * @param last End of the range.
631 * @param ctx Execution context.
632 *
633 * The range should be a sequence of pairs where first is the string key
634 * and second is the integer value.
635 */
636T_CONCURRENTSTRMAP
637template <class InputIterator>
638void CONCURRENTSTRMAP::insert (InputIterator first, InputIterator last,
639 const Context_t& ctx /*= Updater_t::defaultContext()*/)
640{
641 Lock_t lck = lock();
642 if constexpr (std::is_rvalue_reference_v<typename InputIterator::reference>)
643 {
644 for (; first != last; ++first) {
645 emplace (lck, std::move (first->first), first->second, ctx);
646 }
647 }
648 else {
649 for (; first != last; ++first) {
650 emplace (lck, first->first, first->second, ctx);
651 }
652 }
653}
654
655
656/**
657 * @brief Increase the table capacity.
658 * @param capacity The new table capacity.
659 * @param ctx Execution context.
660 *
661 * No action will be taken if @c capacity is smaller
662 * than the current capacity.
663 */
664T_CONCURRENTSTRMAP
665inline
666void CONCURRENTSTRMAP::reserve (size_type capacity,
667 const Context_t& ctx
668 /*= Updater_t::defaultContext()*/)
669{
670 return m_impl.reserve (capacity, ctx);
671}
672
673
674/**
675 * @brief Increase the table capacity.
676 * @param capacity The new table capacity.
677 *
678 * No action will be taken if @c capacity is smaller
679 * than the current capacity.
680 */
681T_CONCURRENTSTRMAP
682inline
683void CONCURRENTSTRMAP::rehash (size_type capacity)
684{
685 return reserve (capacity);
686}
687
688
689/**
690 * @brief Called when this thread is no longer referencing anything
691 * from this container.
692 * @param ctx Execution context.
693 */
694T_CONCURRENTSTRMAP
695inline
696void CONCURRENTSTRMAP::quiescent (const Context_t& ctx)
697{
698 return m_impl.quiescent (ctx);
699}
700
701
702/**
703 * @brief Swap this container with another.
704 * @param other The container with which to swap.
705 *
706 * This will also call swap on the Updater object; hence, the Updater
707 * object must also support swap.
708 *
709 * This operation is NOT thread-safe. No other threads may be accessing
710 * either container during this operation.
711 */
712T_CONCURRENTSTRMAP
713void CONCURRENTSTRMAP::swap (ConcurrentStrMap& other)
714{
715 m_impl.swap (other.m_impl);
716}
717
718
719/**
720 * @brief Access the Updater instance.
721 */
722T_CONCURRENTSTRMAP
723auto CONCURRENTSTRMAP::updater() -> Updater_t&
724{
725 return m_impl.updater();
726}
727
728
729/**
730 * @brief Convert an underlying key value to a string pointer.
731 * @param val The underlying key value.
732 */
733T_CONCURRENTSTRMAP
734inline
735auto CONCURRENTSTRMAP::keyAsString (val_t val) -> const std::string*
736{
737 return reinterpret_cast<std::string*> (val);
738}
739
740
741/**
742 * @brief Convert a string pointer to an underlying key value.
743 * @param s The string pointer.
744 */
745T_CONCURRENTSTRMAP
746inline
747auto CONCURRENTSTRMAP::keyAsVal (const std::string* s) -> val_t
748{
749 return reinterpret_cast<val_t> (s);
750}
751
752
753/**
754 * @brief Convert an underlying mapped value to this type's mapped value.
755 * @param val The underlying mapped value.
756 */
757T_CONCURRENTSTRMAP
758inline
759auto CONCURRENTSTRMAP::mappedAsMapped (val_t val) -> mapped_type
760{
761 return CxxUtils::detail::UIntConv<mapped_type>::uintToVal (val);
762}
763
764
765/**
766 * @brief Convert this type's mapped value to an underlying mapped value.
767 * @param val The mapped value.
768 */
769T_CONCURRENTSTRMAP
770inline
771auto CONCURRENTSTRMAP::mappedAsVal (mapped_type val) -> val_t
772{
773 return CxxUtils::detail::UIntConv<mapped_type>::valToUInt (val);
774}
775
776
777/**
778 * @brief Do a lookup in the table.
779 * @param key The key to look up.
780 *
781 * Returns an iterator of the underlying map pointing at the found
782 * entry or end();
783 */
784T_CONCURRENTSTRMAP
785auto CONCURRENTSTRMAP::get (const key_type& key) const
786 -> typename Impl_t::const_iterator
787{
788 size_t hash = m_impl.hasher() (key);
789 return m_impl.get (keyAsVal(&key), hash);
790}
791
792
793/**
794 * @brief Insert / overwrite an entry in the table.
795 * @param key The key of the new item to add.
796 * @param val The value of the new item to add.
797 * @param overwrite If true, allow overwriting an existing entry.
798 * @param ctx Execution context.
799 *
800 * The first element in the returned pair is an iterator referencing
801 * the added item. The second is a flag that is true if a new element
802 * was added.
803 */
804T_CONCURRENTSTRMAP
805auto CONCURRENTSTRMAP::put (std::unique_ptr<key_type> key,
806 mapped_type val,
807 bool overwrite /*= true*/,
808 const Context_t& ctx /*= Updater_t::defaultContext()*/)
809 -> std::pair<const_iterator, bool>
810{
811 size_t hash = m_impl.hasher() (*key);
812 auto [it, flag] = m_impl.put (keyAsVal(key.get()), hash,
813 mappedAsVal (val),
814 overwrite, ctx);
815 if (flag) (void)key.release();
816 return std::make_pair (const_iterator (it), flag);
817}
818
819
820/**
821 * @brief Insert / overwrite an entry in the table, with external locking.
822 * @param lock The lock object returned from lock().
823 * @param key The key of the new item to add.
824 * @param val The value of the new item to add.
825 * @param overwrite If true, allow overwriting an existing entry.
826 * @param ctx Execution context.
827 *
828 * The first element in the returned pair is an iterator referencing
829 * the added item. The second is a flag that is true if a new element
830 * was added.
831 */
832T_CONCURRENTSTRMAP
833auto CONCURRENTSTRMAP::put (const Lock_t& lock,
834 std::unique_ptr<key_type> key,
835 mapped_type val,
836 bool overwrite /*= true*/,
837 const Context_t& ctx /*= Updater_t::defaultContext()*/)
838 -> std::pair<const_iterator, bool>
839{
840 size_t hash = m_impl.hasher() (*key);
841 auto [it, flag] = m_impl.put (lock,
842 keyAsVal(key.get()), hash,
843 mappedAsVal (val),
844 overwrite, ctx);
845 if (flag) (void)key.release();
846 return std::make_pair (const_iterator (it), flag);
847}
848
849
850/**
851 * @brief Hash function from the underlying representation type.
852 */
853T_CONCURRENTSTRMAP
854inline
855auto CONCURRENTSTRMAP::Hasher::operator() (const val_t p) const -> size_t
856{
857 return m_hash (*keyAsString(p));
858}
859
860
861/**
862 * @brief Hash function from a std::string.
863 */
864T_CONCURRENTSTRMAP
865inline
866auto CONCURRENTSTRMAP::Hasher::operator() (const std::string& s) const -> size_t
867{
868 return m_hash (s);
869}
870
871
872/**
873 * @brief Compare two keys (as the underlying representation type) for equality.
874 */
875T_CONCURRENTSTRMAP
876inline
877auto CONCURRENTSTRMAP::Matcher::operator() (const val_t a, const val_t b) const -> bool
878{
879 // First test if the keys (pointers) themselves are equal.
880 if (a == b) return true;
881 // Otherwise, need to test the strings to which they point.
882 return *keyAsString(a) == *keyAsString(b);
883}
884
885
886#undef T_CONCURRENTSTRMAP
887#undef CONCURRENTSTRMAP
888
889
890} // namespace CxxUtils