libGimbal 0.1.0
C17-Based Extended Standard Library and Cross-Language Runtime Framework
Loading...
Searching...
No Matches
gimbal_hash_set.hpp
1#ifndef GIMBAL_HASHSET_HPP
2#define GIMBAL_HASHSET_HPP
3
6#include "../objects/gimbal_context.hpp"
7
8namespace gbl {
9
10namespace tags {
11 struct HashSet {};
12}
13
14/* TODUZ:
15 * 1) Exceptions:
16 * * need to propagate Thread::lastError from C API to C++ wrapper as exception
17 * * generalize, because this will be used frequently
18 * 2) C++ STL std::unordered_set interop
19 * 3) Iterator, ForwardIterator generalization
20 * 4) C++ STL container-compatible iterator overloads
21 */
22
23template<typename K = void*,
24 typename H = std::hash<K>,
25 typename P = std::equal_to<K>>
26class HashSet;
27
28template<typename K = void*>
29class HashSetView;
30
31template<typename C,
32 typename K>
33struct HashSetIterator;
34
35template<typename CRTP, typename K=void*>
36class HashSetBase:
37 public tags::HashSet
38{
39public:
40 using HashSetBaseType = HashSetBase<CRTP, K>;
41 using DerivedType = CRTP;
42 using KeyType = K;
43 using key_type = KeyType;
44 using value_type = KeyType;
45 using reference = value_type&;
46 using const_reference = const value_type&;
47 using pointer = value_type*;
48 using const_pointer = const value_type*;
50 using allocator_type = Context*;
51 using size_type = Size;
52 using difference_type = ptrdiff_t;
53
54public:
55
56 operator const GblHashSet*() const;
57
58 decltype(auto) getSet (void) const;
59 decltype(auto) getSet (void);
60
61 size_type size (void) const noexcept;
62 size_type bucket_count(void) const noexcept;
63 size_type bucket_size (void) const noexcept;
64 bool empty (void) const noexcept;
65 GblContext* context (void) const noexcept;
66
67 const_pointer get (const key_type& key) const noexcept;
68
69 const_reference at (const key_type& key) const;
70
71 size_type count (const key_type& key) const noexcept;
72 bool contains (const key_type& key) const noexcept;
73 const_pointer probe (size_type position) const noexcept;
74 const_iterator find (const key_type& key) const noexcept;
75
76 template<typename F>
77 requires std::is_invocable_r_v<bool, F, const K&>
78 bool for_each (F&& fn) const noexcept;
79
80 const_iterator next(const_iterator* pPrev) const;
81
82 const_iterator cbegin(void) const;
83 auto begin(void) const;
84
85 const_iterator cend(void) const;
86 auto end(void) const;
87};
88
89
90
91template<typename K>
92class HashSetView final: public HashSetBase<HashSetView<K>, K> {
93 friend class HashSetBase<HashSetView<K>, K>;
94private:
95 const GblHashSet* pGblHashSet_ = nullptr;
96protected:
97 const GblHashSet* set_ (void) const;
98public:
99 HashSetView (void) = default;
100 HashSetView (const GblHashSet& gblHashSet);
101 HashSetView (const GblHashSet* pGblHashSet);
102
103 bool isValid (void) const;
104};
105
106
107template<typename K,
108 typename H,
109 typename P>
110HashSetView(HashSet<K, H, P> base) -> HashSetView<K>;
111
112
113template<typename K,
114 typename H,
115 typename P>
116class HashSet:
117 public GblHashSet,
118 public HashSetBase<HashSet<K, H, P>, K>
119{
120 friend class HashSetBase<HashSet<K, H, P>, K>;
121
122public:
123 using BaseType = HashSetBase<HashSet<K, H, P>, K>;
124 using reference = typename BaseType::reference;
125 using const_reference = typename BaseType::const_reference;
126 using pointer = typename BaseType::pointer;
127 using const_pointer = typename BaseType::const_pointer;
128 using size_type = typename BaseType::size_type;
129 using key_type = typename BaseType::key_type;
130 using value_type = typename BaseType::value_type;
131 using hasher = H;
132 using key_equal = P;
133 using iterator = HashSetIterator<HashSet<K, H, P>, K>;
134private:
135 const hasher& hasher_;
136 const key_equal& comparator_;
137
138 static Hash hasherCb_ (const GblHashSet* pSet, const void* pKey);
139 static Bool comparatorCb_ (const GblHashSet* pSet, const void* pKey1, const void* pKey2);
140 static void destructCb_ (const GblHashSet* pSet, void* pKey);
141
142protected:
143 const GblHashSet* set_ (void) const;
144 GblHashSet* set_ (void);
145
146public:
147
148 HashSet (size_type capacity,
149 const H& hash = H(),
150 const P& pred = P(),
151 Context* pCtx = nullptr);
152 HashSet (size_type capacity,
153 const H& hash,
154 Context* pContext);
155 HashSet (size_type capacity,
156 Context* pContext);
157 HashSet (void);
158
159 HashSet (type_compatible_container_readable<value_type> auto&& init,
160 size_type capacity = 0,
161 const H hasher = H(),
162 const P pred = P(),
163 Context* pCtx = nullptr);
164 HashSet (type_compatible_container_readable<value_type> auto&& init,
165 size_type capacity,
166 H hasher,
167 Context* pCtx);
168 HashSet (type_compatible_container_readable<value_type> auto&& init,
169 size_type capacity,
170 Context* pCtx);
171
172 HashSet (type_compatible_iterator_readable<value_type> auto first,
173 type_compatible_iterator_readable<value_type> auto last,
174 size_type capacity = 0,
175 const H& hash = H(),
176 const P& pred = P(),
177 Context* pCtx = nullptr);
178
179 HashSet (type_compatible_iterator_readable<value_type> auto first,
180 type_compatible_iterator_readable<value_type> auto last,
181 size_type capacity,
182 const H& hash,
183 Context* pCtx);
184
185 HashSet (type_compatible_iterator_readable<value_type> auto first,
186 type_compatible_iterator_readable<value_type> auto last,
187 size_type capacity,
188 Context* pCtx);
189
190 HashSet (HashSet& other);
191 HashSet (HashSet& other, Context* pCtx);
192
193 HashSet (HashSet&& other);
194 HashSet (HashSet&& other, Context* pCtx);
195
196 ~HashSet (void);
197
198 HashSet& operator= (const HashSet& other);
199 HashSet& operator= (const HashSet&& other);
200 HashSet& operator= (type_compatible_container_readable<value_type> auto&& init);
201
202 pointer get (const key_type& key) noexcept;
203 reference at (const key_type& key);
204 iterator find (const key_type& key) noexcept;
205
206 pointer set (const key_type& key) noexcept;
207
208 bool insert (const key_type& key) noexcept; //needs to throw exception
209 void insert_or_assign(const key_type& key) noexcept;
210
211 bool erase (const key_type& key) noexcept;
212 pointer extract (const key_type& key) noexcept;
213 void clear (void) noexcept;
214
215 template<typename... Args>
216 bool emplace (const key_type& key, Args&&... args); //needs to throw exception
217 template<typename... Args>
218 bool try_emplace (const key_type& key, Args&&... args) noexcept;
219
220 pointer probe (size_type position) noexcept;
221
222 iterator next (iterator* pPrev);
223
224 iterator begin (void);
225 iterator end (void);
226};
227
228template<typename C,
229 typename K>
230struct HashSetIterator:
231 public GblHashSetIter,
232 public std::iterator<std::forward_iterator_tag, K>
233{
234 using object_type = C;
235 constexpr static bool is_const = std::is_const_v<C>;
236 using iterator_type = HashSetIterator<C, K>;
237 using const_iterator_type = HashSetIterator<std::add_const_t<C>, K>;
238 using nonconst_iterator_type = HashSetIterator<std::remove_const_t<C>, K>;
239 using value_type = K;
240 using reference = std::conditional_t<std::is_const_v<C>,
241 std::add_lvalue_reference_t<std::add_const_t<K>>,
242 std::add_lvalue_reference_t<K>>;
243 using const_reference = std::add_const_t<reference>;
244 using pointer = std::conditional_t<std::is_const_v<C>,
245 std::add_pointer_t<std::add_const_t<K>>,
246 std::add_pointer_t<K>>;
247 using difference_type = std::ptrdiff_t;
248 using iterator_category = std::forward_iterator_tag;
249
250 C* set(void) { return static_cast<C*>(this->pSet); }
251 const C* set(void) const { return static_cast<const C*>(this->pSet); }
252
253 reference operator*(void) {
254 return *reinterpret_cast<pointer>(set()->probe(this->bucketIdx));
255 }
256
257 const_reference operator*(void) const {
258 return *reinterpret_cast<const pointer>(set()->probe(this->bucketIdx));
259 }
260
261 pointer operator->(void) {
262 return reinterpret_cast<pointer>(set()->probe(this->bucketIdx));
263 }
264
265 const pointer operator->(void) const {
266 return reinterpret_cast<const pointer>(set()->probe(this->bucketIdx));
267 }
268
269 iterator_type& operator++ () {
270 auto nextIt = set()->next(this);
271 this->bucketIdx = nextIt.bucketIdx;
272 return *this;
273 }
274
275 iterator_type operator++ (int) {
276 const auto temp(*this);
277 ++*this;
278 return temp;
279 }
280
281 bool operator== (const iterator_type& rhs) const { return this->pSet == rhs.pSet && this->bucketIdx == rhs.bucketIdx; }
282 bool operator<= (const iterator_type& rhs) const { return this->pSet == rhs.pSet && this->bucketIdx <= rhs.bucketIdx; }
283 bool operator< (const iterator_type& rhs) const { return this->pSet == rhs.pSet && this->bucketIdx < rhs.bucketIdx; }
284 bool operator> (const iterator_type& rhs) const { return this->pSet == rhs.pSet && this->bucketIdx > rhs.bucketIdx; }
285 bool operator>= (const iterator_type& rhs) const { return this->pSet == rhs.pSet && this->bucketIdx >= rhs.bucketIdx; }
286 bool operator!= (const iterator_type& rhs) const { return !(*this == rhs); }
287
288
289 HashSetIterator(C* pCont=nullptr, Size bucketIdx=-1):
290 GblHashSetIter({pCont, bucketIdx}) {}
291
292 HashSetIterator(const GblHashSetIter gblIt):
293 HashSetIterator(static_cast<C*>(gblIt.pSet), gblIt.bucketIdx) {}
294
295 HashSetIterator(const const_iterator_type& rhs) requires is_const:
296 HashSetIterator(rhs.pSet, rhs.bucketIdx) {}
297
298 HashSetIterator(const nonconst_iterator_type& rhs):
299 HashSetIterator(rhs.pSet, rhs.bucketIdx) {}
300
301 const C* getContainer(void) const { return reinterpret_cast<C*>(GblHashSetIter_container(this)); }
302 bool valid(void) const { return GblHashSetIter_valid(this); }
303 K& value(void) { return *reinterpret_cast<K*>(GblHashSetIter_value(this)); }
304
305};
306
307
308// ========== INLINE IMPLEMENTATIONS ==========
309
310// --------- HashSetBase ---------
311
312
313template<typename CRTP, typename K>
314inline decltype(auto) HashSetBase<CRTP, K>::getSet(void) const { return static_cast<const CRTP*>(this)->set_(); }
315
316template<typename CRTP, typename K>
317inline decltype(auto) HashSetBase<CRTP, K>::getSet(void) { return static_cast<CRTP*>(this)->set_(); }
318
319template<typename CRTP, typename K>
320inline auto HashSetBase<CRTP, K>::size(void) const noexcept -> size_type { return GblHashSet_size(getSet()); }
321
322template<typename CRTP, typename K>
323inline auto HashSetBase<CRTP, K>::bucket_count(void) const noexcept -> size_type { return GblHashSet_bucketCount(getSet()); }
324
325template<typename CRTP, typename K>
326inline auto HashSetBase<CRTP, K>::bucket_size(void) const noexcept -> size_type { return GblHashSet_bucketSize(getSet()); }
327
328template<typename CRTP, typename K>
329inline bool HashSetBase<CRTP, K>::empty(void) const noexcept { return GblHashSet_empty(getSet()); }
330
331template<typename CRTP, typename K>
332inline GblContext* HashSetBase<CRTP, K>::context(void) const noexcept { return GblHashSet_context(getSet()); }
333
334template<typename CRTP, typename K>
335inline HashSetBase<CRTP, K>::operator const GblHashSet*() const { return getSet(); }
336
337template<typename CRTP, typename K>
338inline auto HashSetBase<CRTP, K>::get(const key_type& key) const noexcept -> const_pointer {
339 return static_cast<const_pointer>(GblHashSet_get(getSet(), &key));
340}
341
342template<typename CRTP, typename K>
343inline auto HashSetBase<CRTP, K>::at(const key_type& key) const -> const_reference {
344 const GblCallRecord* pRecord = nullptr;
345 const key_type* pKey = nullptr;
346 GblThread_callRecordSet(NULL, NULL);
347 pKey = reinterpret_cast<const key_type*>(GblHashSet_at(getSet(), &key));
348 pRecord = GblThread_callRecord(NULL);
349 Exception::checkThrow(*pRecord);
350 return *pKey;
351}
352
353template<typename CRTP, typename K>
354inline auto HashSetBase<CRTP, K>::count(const key_type& key) const noexcept -> size_type {
355 return GblHashSet_count(getSet(), &key);
356}
357
358template<typename CRTP, typename K>
359inline bool HashSetBase<CRTP, K>::contains(const key_type& key) const noexcept {
360 return GblHashSet_contains(getSet(), &key);
361}
362
363template<typename CRTP, typename K>
364inline auto HashSetBase<CRTP, K>::find(const key_type& key) const noexcept -> const_iterator {
365 return GblHashSet_find(getSet(), &key);
366}
367
368template<typename CRTP, typename K>
369inline auto HashSetBase<CRTP, K>::probe(size_type position) const noexcept -> const_pointer {
370 return GblHashSet_probe(getSet(), position);
371}
372
373template<typename CRTP, typename K>
374template<typename F>
375 requires std::is_invocable_r_v<bool, F, const K&>
376inline bool HashSetBase<CRTP, K>::for_each(F&& fn) const noexcept {
377 return GblHashSet_foreach(getSet(), [](const GblHashSet* pSelf,
378 void* pKey,
379 void* pUd) -> Bool
380 {
381 GBL_UNUSED(pSelf);
382 auto* pFn = static_cast<F*>(pUd);
383 return (*pFn)(*static_cast<const_pointer>(pKey));
384 },
385 &fn);
386}
387
388template<typename CRTP, typename K>
389inline auto HashSetBase<CRTP, K>::next(const_iterator* pPrev) const -> const_iterator {
390 return GblHashSet_next(getSet(), pPrev);
391}
392
393template<typename CRTP, typename K>
394inline auto HashSetBase<CRTP, K>::cbegin(void) const -> const_iterator {
395 return GblHashSet_next(getSet(), nullptr);
396}
397
398template<typename CRTP, typename K>
399inline auto HashSetBase<CRTP, K>::begin(void) const { return cbegin(); }
400
401template<typename CRTP, typename K>
402inline auto HashSetBase<CRTP, K>::cend(void) const -> const_iterator { return const_iterator(this, bucket_count()); }
403
404template<typename CRTP, typename K>
405inline auto HashSetBase<CRTP, K>::end(void) const { return cend(); }
406
407
408// -------- HashSetView ---------
409
410
411
412template<typename K>
413inline const GblHashSet* HashSetView<K>::set_(void) const { return pGblHashSet_; }
414
415template<typename K>
416inline HashSetView<K>::HashSetView(const GblHashSet& gblHashSet):
417 pGblHashSet_(&gblHashSet) {}
418
419template<typename K>
420inline HashSetView<K>::HashSetView(const GblHashSet* pGblHashSet):
421 pGblHashSet_(pGblHashSet) {}
422
423template<typename K>
424inline bool HashSetView<K>::isValid(void) const { return pGblHashSet_; }
425
426// ------- HashSet --------
427template<typename K, typename H, typename P>
428inline const GblHashSet* HashSet<K, H, P>::set_(void) const { return static_cast<const GblHashSet*>(this); }
429
430template<typename K, typename H, typename P>
431inline GblHashSet* HashSet<K, H, P>::set_(void) { return static_cast<GblHashSet*>(this); }
432
433template<typename K, typename H, typename P>
434inline HashSet<K, H, P>::HashSet(size_type capacity,
435 const H& hash,
436 const P& pred,
437 Context* pCtx):
438 hasher_(hash),
439 comparator_(pred)
440{
442 sizeof(K),
443 hasherCb_,
444 comparatorCb_,
445 destructCb_,
446 capacity,
447 pCtx,
448 this);
449}
450
451template<typename K, typename H, typename P>
452inline HashSet<K, H, P>::HashSet(size_type capacity,
453 const H& hash,
454 Context* pContext):
455 HashSet(capacity, hash, P(), pContext) {}
456
457template<typename K, typename H, typename P>
458inline HashSet<K, H, P>::HashSet(size_type capacity,
459 Context* pContext):
460 HashSet(capacity, H(), pContext) {}
461
462template<typename K, typename H, typename P>
463inline HashSet<K, H, P>::HashSet(void):
464 HashSet(0) {}
465
466template<typename K, typename H, typename P>
467inline HashSet<K, H, P>::HashSet(type_compatible_container_readable<value_type> auto&& init,
468 size_type capacity,
469 const H hasher,
470 const P pred,
471 Context* pCtx):
472 HashSet(capacity, hasher, pred, pCtx)
473{
474 for(auto&& it: init) insert(std::forward<decltype(it)>(it));
475}
476
477template<typename K, typename H, typename P>
478inline HashSet<K, H, P>::HashSet(type_compatible_container_readable<value_type> auto&& init,
479 size_type capacity,
480 H hasher,
481 Context* pCtx):
482 HashSet(std::forward<decltype(init)>(init), capacity, hasher, P(), pCtx) {}
483
484template<typename K, typename H, typename P>
485inline HashSet<K, H, P>::HashSet(type_compatible_container_readable<value_type> auto&& init,
486 size_type capacity,
487 Context* pCtx):
488 HashSet(std::forward<decltype(init)>(init), capacity, H(), pCtx) {}
489
490template<typename K, typename H, typename P>
491inline HashSet<K, H, P>::HashSet(type_compatible_iterator_readable<value_type> auto first,
492 type_compatible_iterator_readable<value_type> auto last,
493 size_type capacity,
494 const H& hash,
495 const P& pred,
496 Context* pCtx):
497 HashSet(capacity, hash, pred, pCtx)
498{
499 for(auto it = first; it < last; ++it) {
500 insert(*it);
501 }
502}
503
504template<typename K, typename H, typename P>
505inline HashSet<K, H, P>::HashSet(type_compatible_iterator_readable<value_type> auto first,
506 type_compatible_iterator_readable<value_type> auto last,
507 size_type capacity,
508 const H& hash,
509 Context* pCtx):
510 HashSet(std::move(first), std::move(last), capacity, hash, P(), pCtx) {}
511
512template<typename K, typename H, typename P>
513inline HashSet<K, H, P>::HashSet(type_compatible_iterator_readable<value_type> auto first,
514 type_compatible_iterator_readable<value_type> auto last,
515 size_type capacity,
516 Context* pCtx):
517 HashSet(std::move(first), std::move(last), capacity, H(), pCtx) {}
518
519
520
521template<typename K, typename H, typename P>
522inline HashSet<K, H, P>::HashSet(HashSet& other, Context* pCtx) {
523 GblHashSet_clone(this, &other, pCtx);
524}
525
526template<typename K, typename H, typename P>
527inline HashSet<K, H, P>::HashSet(HashSet& other):
528 HashSet(other, other.context()) {}
529
530template<typename K, typename H, typename P>
531inline HashSet<K, H, P>::~HashSet(void) {
532 GblHashSet_destruct(this);
533}
534
535template<typename K, typename H, typename P>
536inline auto HashSet<K, H, P>::operator=(const HashSet& other) -> HashSet& {
537 GblHashSet_assignClone(this, &other);
538 return *this;
539}
540
541template<typename K, typename H, typename P>
542inline auto HashSet<K, H, P>::operator=(const HashSet&& other) -> HashSet& {
543 GblHashSet_assignMove(this, &other);
544 return *this;
545}
546
547template<typename K, typename H, typename P>
548inline auto HashSet<K, H, P>::operator=(type_compatible_container_readable<value_type> auto&& init) -> HashSet& {
549 clear();
550 for(auto&& it: init) insert(std::forward<decltype(it)>(it));
551 return *this;
552}
553
554template<typename K, typename H, typename P>
555inline auto HashSet<K, H, P>::get(const key_type& key) noexcept -> pointer {
556 return static_cast<pointer>(GblHashSet_get(this, &key));
557}
558
559template<typename K, typename H, typename P>
560inline auto HashSet<K, H, P>::at(const key_type& key) -> reference {
561 key_type* pKey = nullptr;
562 GblThread_callRecordSet(NULL, NULL);
563 pKey = reinterpret_cast<key_type*>(GblHashSet_at(this, &key));
564 Exception::checkThrow(*GblThread_callRecord(NULL));
565 return *pKey;
566}
567
568template<typename K, typename H, typename P>
569inline auto HashSet<K, H, P>::find(const key_type& key) noexcept -> iterator {
570 return GblHashSet_find(this, &key);
571}
572
573template<typename K, typename H, typename P>
574inline auto HashSet<K, H, P>::set(const key_type& key) noexcept -> pointer {
575 return static_cast<pointer>(GblHashSet_set(this, &key));
576}
577
578template<typename K, typename H, typename P>
579inline bool HashSet<K, H, P>::insert(const key_type& key) noexcept {
580 return GblHashSet_insert(this, &key);
581}
582
583template<typename K, typename H, typename P>
584inline void HashSet<K, H, P>::insert_or_assign(const key_type& key) noexcept {
585 GblHashSet_insertOrAssign(this, &key);
586}
587
588template<typename K, typename H, typename P>
589inline bool HashSet<K, H, P>::erase(const key_type& key) noexcept {
590 return GblHashSet_erase(this, &key);
591}
592
593template<typename K, typename H, typename P>
594inline auto HashSet<K, H, P>::extract(const key_type& key) noexcept -> pointer {
595 return GblHashSet_extract(this, &key);
596}
597
598template<typename K, typename H, typename P>
599inline void HashSet<K, H, P>::clear(void) noexcept {
600 GblHashSet_clear(this);
601}
602
603template<typename K, typename H, typename P>
604inline auto HashSet<K, H, P>::probe(size_type position) noexcept -> pointer {
605 return reinterpret_cast<pointer>(GblHashSet_probe(this, position));
606}
607
608template<typename K, typename H, typename P>
609template<typename... Args>
610inline bool HashSet<K, H, P>::try_emplace(const key_type& key, Args&&... args) noexcept {
611 pointer pData = static_cast<pointer>(GblHashSet_emplace(this, &key));
612 if(pData) new (pData) key_type(std::forward<Args>(args)...);
613 return pData;
614}
615
616template<typename K, typename H, typename P>
617template<typename... Args>
618inline bool HashSet<K, H, P>::emplace(const key_type& key, Args&&... args) {
619 pointer pData = static_cast<pointer>(GblHashSet_emplace(this, &key));
620 GBL_ASSERT(pData);
621 new (pData) key_type(std::forward<Args>(args)...);
622 return true;
623}
624
625template<typename K, typename H, typename P>
626inline auto HashSet<K, H, P>::next(iterator* pPrev) -> iterator {
627 return GblHashSet_next(this, pPrev);
628}
629
630template<typename K, typename H, typename P>
631inline auto HashSet<K, H, P>::begin(void) -> iterator {
632 return next(nullptr);
633}
634
635template<typename K, typename H, typename P>
636inline auto HashSet<K, H, P>::end(void) -> iterator {
637 return iterator(this, this->bucket_count());
638}
639
640template<typename K, typename H, typename P>
641inline Hash HashSet<K, H, P>::hasherCb_(const GblHashSet* pSet, const void* pKey) {
642 return static_cast<const HashSet<K, H, P>*>(pSet)->hasher_(*static_cast<const K*>(pKey));
643}
644
645template<typename K, typename H, typename P>
646inline Bool HashSet<K, H, P>::comparatorCb_ (const GblHashSet* pSet, const void* pKey1, const void* pKey2) {
647 return static_cast<const HashSet<K, H, P>*>(pSet)->comparator_(*static_cast<const K*>(pKey1), *static_cast<const K*>(pKey2));
648}
649
650template<typename K, typename H, typename P>
651inline void HashSet<K, H, P>::destructCb_(const GblHashSet* pSet, void* pKey) {
652 GBL_UNUSED(pSet);
653 static_cast<K*>(pKey)->~K();
654}
655
656
657
658}
659
660#endif // GIMBAL_HASHSET_HPP
#define GBL_UNUSED(...)
#define GblHashSet_construct(...)
#define GBL_ASSERT(...)
Captures a result, its stringified message, and a source context.
Hash-table based abstract associative container with C++-style STL std::unoredered_set API.
Iterator structure used for iterating over GblHashSet.