Qx v0.6.2
Qt Extensions Library
Loading...
Searching...
No Matches
qx-lopmap.h
1#ifndef QX_LOPMAP_H
2#define QX_LOPMAP_H
3
4// Standard Library Includes
5#include <set>
6#include <unordered_map>
7
8// Extra-component Includes
10
11// TODO: When C++23 is used, this is a good candidate for flat_multiset over multiset
12
13namespace Qx
14{
15
16template<typename Key, typename T, typename Compare = std::less<T>>
17 requires std::predicate<Compare, T, T>
18class Lopmap;
19
20template<typename Key, typename T, typename Compare, typename Predicate>
22
23template<typename Key, typename T, typename Predicate>
25
26template<typename Key, typename T, typename Compare, typename Predicate>
28
29}
30
32namespace _QxPrivate
33{
34
35template<typename Key, typename T, typename Compare, typename Predicate>
36qsizetype _erase_if(Qx::Lopmap<Key, T>& lm, Predicate& pred)
37{
38 // This could be moved to a private file and made more generic for any other container types if needed,
39 // though its tricky since here for the pair predicate we pass a const T
40 using Iterator = typename Qx::Lopmap<Key, T>::iterator;
41
42 typename Qx::Lopmap<Key, T>::size_type result = 0;
43
44 Iterator it = lm.cbegin();
45 const Iterator end = lm.cend();
46 while(it != end)
47 {
49 {
50 if(pred(it))
51 {
52 it = lm.erase(it);
53 result++;
54 }
55 else
56 it++;
57 }
59 {
60 if(pred(std::move(*it)))
61 {
62 it = lm.erase(it);
63 result++;
64 }
65 else
66 it++;
67 }
68 else
69 {
70 // Delayed evaluation trick to prevent immediate static_assert failure for older compilers from before the resolution of CWG 2518
71 static_assert(sizeof(Qx::Lopmap<Key, T>) == 0, "Invalid Predicate");
72 }
73 }
74
75 return result;
76}
77
78}
81namespace Qx
82{
83
84template<typename Key, typename T, typename Compare>
85 requires std::predicate<Compare, T, T>
86class Lopmap
87{
88//-Inner Classes----------------------------------------------------------------------------------------------------
89private:
90 struct Data
91 {
92 const Key* keyPtr;
93 T value;
94 };
95
96 struct DataCompare
97 {
98 Compare cmp;
99 bool operator()(const Data& lhs, const Data& rhs) const { return cmp(lhs.value, rhs.value); }
100 };
101
102//-Aliases----------------------------------------------------------------------------------------------------------
103private:
104 using StorageContainer = std::multiset<Data, DataCompare>;
105 using StorageItr = typename StorageContainer::const_iterator;
106 using StorageRevItr = typename StorageContainer::const_reverse_iterator;
107 using LookupContainer = std::unordered_map<Key, StorageItr>;
108
109public:
111 {
112 friend class Lopmap<Key, T, Compare>;
113 //-Aliases------------------------------------------------------------------------------------------------------
114 public:
115 using iterator_category = typename StorageItr::iterator_category;
116
117 //-Instance Variables-------------------------------------------------------------------------------------------
118 private:
119 StorageItr mStorageItr;
120
121 //-Constructor--------------------------------------------------------------------------------------------------
122 private:
123 const_iterator(const StorageItr& sItr) : mStorageItr(sItr) {}
124
125 public:
127
128 //-Instance Functions-------------------------------------------------------------------------------------------
129 public:
130 const Key& key() const { return *mStorageItr->keyPtr; }
131 const T& value() const { return mStorageItr->value; }
132
133 //-Operators---------------------------------------------------------------------------------------------
134 public:
135 bool operator==(const const_iterator& other) const = default;
136 const T& operator*() const { return value(); }
137 const_iterator& operator++() { mStorageItr++; return *this; }
138
140 {
141 auto cur = *this;
142 mStorageItr++;
143 return cur;
144 }
145
146 const_iterator& operator--() { mStorageItr--; return *this; }
147
149 {
150 auto cur = *this;
151 mStorageItr--;
152 return cur;
153 }
154
155 const T* operator->() const { return &mStorageItr->value; }
156 };
157
158 // Could be greatly simplified with the above by using a base type, but that makes documentation hard...
160 {
162 friend class Lopmap<Key, T, Compare>;
163 //-Aliases------------------------------------------------------------------------------------------------------
164 public:
165 using iterator_category = typename StorageRevItr::iterator_category;
166
167 //-Instance Variables-------------------------------------------------------------------------------------------
168 private:
169 StorageRevItr mStorageItr;
170
171 //-Constructor--------------------------------------------------------------------------------------------------
172 private:
173 const_reverse_iterator(const StorageRevItr& sItr) : mStorageItr(sItr) {}
174
175 public:
177
178 //-Instance Functions-------------------------------------------------------------------------------------------
179 public:
180 const Key& key() const { return *mStorageItr->keyPtr; }
181 const T& value() const { return mStorageItr->value; }
182
183 //-Operators---------------------------------------------------------------------------------------------
184 public:
185 bool operator==(const const_reverse_iterator& other) const = default;
186 const T& operator*() const { return value(); }
187 const_reverse_iterator& operator++() { mStorageItr++; return *this; }
188
189 const_reverse_iterator operator++(int)
190 {
191 auto cur = *this;
192 mStorageItr++;
193 return cur;
194 }
195
196 const_reverse_iterator& operator--() { mStorageItr--; return *this; }
197
198 const_reverse_iterator operator--(int)
199 {
200 auto cur = *this;
201 mStorageItr--;
202 return cur;
203 }
204
205 const T* operator->() const { return &mStorageItr->value; }
207 };
208
209//-Aliases (cont.)-------------------------------------------------------------------------------------------------
210public:
217 using difference_type = typename StorageContainer::difference_type;
218 using key_type = Key;
219 using mapped_Type = T;
220 using size_type = typename StorageContainer::size_type;
221 using value_compare = Compare;
222
223//-Instance Variables-------------------------------------------------------------------------------------------
224private:
225 StorageContainer mStorage;
226 LookupContainer mLookup;
227
228//-Constructor--------------------------------------------------------------------------------------------------
229public:
231
232 Lopmap(std::initializer_list<std::pair<Key, T>> list)
233 {
234 for(auto it = list.begin(); it != list.end(); ++it)
235 insert(it->first, it->second);
236 }
237
238//-Instance Functions-------------------------------------------------------------------------------------------
239private:
240 StorageItr lookupStorage(const Key& key) const
241 {
242 auto lItr = mLookup.find(key);
243 return lItr != mLookup.end() ? lItr->second : mStorage.cend();
244 }
245
246 iterator insert_impl(const Key& key, const T& value, const StorageItr* hint)
247 {
248 /* Insert key to lookup map if missing, and get iterator to the element regardless. The
249 * inserted storage iterator is null since it will be updated in a moment either way.
250 */
251 auto [lItr, isNew] = mLookup.emplace(key, StorageItr());
252
253 if(!isNew)
254 {
255 // Don't do anything if the value is the same as the current, to avoid iterator invalidation
256 if(lItr->second->value == value)
257 return iterator(lItr->second);
258
259 // Otherwise, we have to erase and re-insert, which unfortunately invalidates iterators pointing to key
260 mStorage.erase(lItr->second);
261 }
262
263 // Store the new data
264 auto sItr = hint ? mStorage.emplace_hint(*hint, &lItr->first, value) : mStorage.emplace(&lItr->first, value);
265
266 // Synchronize map
267 lItr->second = sItr;
268
269 return iterator(sItr);
270 }
271
272public:
273 iterator begin() { return iterator(mStorage.begin()); }
274 const_iterator begin() const { return constBegin(); }
275 const_iterator cbegin() const { return constBegin(); }
276 const_iterator constBegin() const { return const_iterator(mStorage.cbegin()); }
277 iterator end() { return iterator(mStorage.end()); }
278 const_iterator end() const { return constEnd(); }
279 const_iterator cend() const { return constEnd(); }
280 const_iterator constEnd() const { return const_iterator(mStorage.cend()); }
281 reverse_iterator rbegin() { return reverse_iterator(mStorage.rbegin()); }
285 reverse_iterator rend() { return reverse_iterator(mStorage.rend()); }
289
290 iterator find(const Key& key) { return iterator(lookupStorage(key)); }
291 const_iterator find(const Key& key) const { return constFind(key); }
292 const_iterator constFind(const Key& key) const { return const_iterator(lookupStorage(key)); }
293 iterator lowerBound(const T& value) { return std::as_const(*this).lowerBound(value); }
294
296 {
297 // Use dummy key since only value is used for ordering
298 return const_iterator(mStorage.lower_bound(Data{nullptr, value}));
299 }
300
301 iterator upperBound(const T& value) { return std::as_const(*this).upperBound(value); }
302
304 {
305 // Use dummy key since only value is used for ordering
306 return const_iterator(mStorage.upper_bound(Data{nullptr, value}));
307 }
308
309 std::pair<iterator, iterator> equal_range(const T& value) { return std::as_const(*this).equal_range(value); }
310
311 std::pair<const_iterator, const_iterator> equal_range(const T& value) const
312 {
313 // Use dummy key since only value is used for ordering
314 auto sItrPair = mStorage.equal_range(Data{nullptr, value});
315 return std::make_pair<const_iterator, const_iterator>(sItrPair.first, sItrPair.second);
316 }
317
318 const T& first() const { Q_ASSERT(!mStorage.empty()); return mStorage.cbegin()->value; }
319 const Key& firstKey() const { Q_ASSERT(!mStorage.empty()); return *mStorage.cbegin()->keyPtr; }
320
321 const T& last() const { Q_ASSERT(!mStorage.empty()); return mStorage.cend()->value; }
322 const Key& lastKey() const { Q_ASSERT(!mStorage.empty()); return *mStorage.cend()->keyPtr; }
323
324 Key key(const T& value, const Key& defaultKey = Key()) const
325 {
326 for(const auto& data : std::as_const(mStorage))
327 if(data.value == value)
328 return *data.keyPtr;
329
330 return defaultKey;
331 }
332
334 {
335 Q_ASSERT(pos != constEnd());
336 mLookup.erase(pos.key());
337 return iterator(mStorage.erase(pos.mStorageItr));
338 }
339
341 {
342 Q_ASSERT(first != constEnd() && last != constEnd());
343
344 // Have to iterate manually here to keep containers synced since
345 // mLookup is traversed in an unspecified order
346 StorageItr sItr = first.mStorageItr;
347 while(sItr != last.mStorageItr)
348 {
349 mLookup.erase(*sItr->keyPtr);
350 sItr = mStorage.erase(sItr);
351 }
352
353 return iterator(sItr);
354 }
355
356 void insert(Lopmap&& other)
357 {
358 // Have to copy due to underlying iterator being const, but original container is emptied
359 insert(std::as_const(other));
360
361 other.mStorage.clear();
362 other.mLookup.clear();
363 }
364
365 void insert(const Lopmap& other)
366 {
367 if(this == &other)
368 return;
369
370 for(auto itr = other.mStorage.begin(); itr != other.mStorage.end(); itr++)
371 insert(*itr->keyPtr, itr->value);
372 }
373
374 iterator insert(const Key& key, const T& value) { return insert_impl(key, value, nullptr); }
375 iterator insert(const_iterator pos, const Key& key, const T& value) { return insert_impl(key, value, &pos.mStorageItr); }
376 bool contains(const Key& key) const { return mLookup.contains(key); }
377
378 size_type remove(const Key& key)
379 {
380 auto lItr = mLookup.find(key);
381 if(lItr != mLookup.cend())
382 {
383 auto sItr = lItr->second;
384 mLookup.erase(lItr);
385 mStorage.erase(sItr);
386 return 1;
387 }
388
389 return 0;
390 }
391
392 template<typename Predicate>
394 qsizetype removeIf(Predicate pred) { return _QxPrivate::_erase_if(*this, pred); }
395
396 T take(const Key& key)
397 {
398 auto lItr = mLookup.find(key);
399 if(lItr != mLookup.cend())
400 {
401 auto sItr = lItr->second;
402 T t = sItr->value;
403 mLookup.erase(lItr);
404 mStorage.erase(sItr);
405 return t;
406 }
407
408 return T();
409 }
410
411 void swap(Lopmap<Key, T>& other)
412 {
413 mLookup.swap(other.mLookup);
414 mStorage.swap(other.mStorage);
415 }
416
417 qsizetype size() const { return mStorage.size(); }
418 qsizetype count() const { return size(); }
419 bool isEmpty() const { return size() == 0; }
420 bool empty() const { return isEmpty(); }
421
422 void clear() { mLookup.clear(); mStorage.clear(); }
423
424 T value(const Key& key, const T& defaultValue = T()) const
425 {
426 auto sItr = lookupStorage(key);
427 return sItr != mStorage.cend() ? sItr->value : defaultValue;
428 }
429
431 {
432 QList<Key> ks;
433 for(const auto& data : std::as_const(mStorage))
434 ks.append(*data.keyPtr);
435 return ks;
436 }
437
438 QList<Key> keys(const T& value) const
439 {
440 QList<Key> ks;
441 for(const auto& data : std::as_const(mStorage))
442 if(data.value == value)
443 ks.append(*data.keyPtr);
444 return ks;
445 }
446
448 {
449 QList<T> vs;
450 for(const auto& data : std::as_const(mStorage))
451 vs.append(data.value);
452 return vs;
453 }
454
455//-Operators---------------------------------------------------------------------------------------------
456public:
457 T operator[](const Key& key) const { return value(key); }
458
459 bool operator==(const Lopmap& other) const { return mStorage == other.mStorage; }
460 bool operator!=(const Lopmap& other) const = default;
461};
462
463// Doc'ed here cause doxygen struggles with this one being separate
472template<typename Key, typename T, typename Predicate>
473qsizetype erase_if(Lopmap<Key, T>& lopmap, Predicate pred) { return _QxPrivate::_erase_if(lopmap, pred); }
474
475}
476
477#endif // QX_LOPMMAP_H
The Lopmap::const_iterator class provides an STL-style const iterator for Lopmap.
Definition qx-lopmap.h:111
const T * operator->() const
Definition qx-lopmap.h:155
const_iterator operator--(int)
Definition qx-lopmap.h:148
const_iterator & operator++()
Definition qx-lopmap.h:137
bool operator==(const const_iterator &other) const =default
const_iterator operator++(int)
Definition qx-lopmap.h:139
typename StorageItr::iterator_category iterator_category
Definition qx-lopmap.h:115
const T & operator*() const
Definition qx-lopmap.h:136
const_iterator & operator--()
Definition qx-lopmap.h:146
const Key & key() const
Definition qx-lopmap.h:130
const_iterator()
Definition qx-lopmap.h:126
const T & value() const
Definition qx-lopmap.h:131
The Lopmap::const_reverse_iterator class provides an STL-style const reverse iterator for Lopmap.
Definition qx-lopmap.h:160
The Lopmap class is a template class that provides an "lopsided" associative array.
Definition qx-lopmap.h:87
std::pair< iterator, iterator > equal_range(const T &value)
Definition qx-lopmap.h:309
void insert(Lopmap &&other)
Definition qx-lopmap.h:356
Key key_type
Definition qx-lopmap.h:218
size_type remove(const Key &key)
Definition qx-lopmap.h:378
bool isEmpty() const
Definition qx-lopmap.h:419
bool contains(const Key &key) const
Definition qx-lopmap.h:376
const_iterator upperBound(const T &value) const
Definition qx-lopmap.h:303
iterator insert(const_iterator pos, const Key &key, const T &value)
Definition qx-lopmap.h:375
T operator[](const Key &key) const
Definition qx-lopmap.h:457
const_reverse_iterator rend() const
Definition qx-lopmap.h:286
void insert(const Lopmap &other)
Definition qx-lopmap.h:365
const_iterator begin() const
Definition qx-lopmap.h:274
const_reverse_iterator crend() const
Definition qx-lopmap.h:287
iterator erase(const_iterator first, const_iterator last)
Definition qx-lopmap.h:340
iterator end()
Definition qx-lopmap.h:277
const_iterator constBegin() const
Definition qx-lopmap.h:276
QList< T > values() const
Definition qx-lopmap.h:447
qsizetype size() const
Definition qx-lopmap.h:417
Key key(const T &value, const Key &defaultKey=Key()) const
Definition qx-lopmap.h:324
iterator lowerBound(const T &value)
Definition qx-lopmap.h:293
qsizetype count() const
Definition qx-lopmap.h:418
const_iterator cbegin() const
Definition qx-lopmap.h:275
const T & first() const
Definition qx-lopmap.h:318
void clear()
Definition qx-lopmap.h:422
iterator find(const Key &key)
Definition qx-lopmap.h:290
const_iterator end() const
Definition qx-lopmap.h:278
const Key & firstKey() const
Definition qx-lopmap.h:319
bool empty() const
Definition qx-lopmap.h:420
qsizetype removeIf(Predicate pred)
Definition qx-lopmap.h:394
Lopmap()
Definition qx-lopmap.h:230
iterator begin()
Definition qx-lopmap.h:273
T value(const Key &key, const T &defaultValue=T()) const
Definition qx-lopmap.h:424
reverse_iterator rend()
Definition qx-lopmap.h:285
T take(const Key &key)
Definition qx-lopmap.h:396
QList< Key > keys(const T &value) const
Definition qx-lopmap.h:438
const_iterator lowerBound(const T &value) const
Definition qx-lopmap.h:295
QList< Key > keys() const
Definition qx-lopmap.h:430
const_iterator find(const Key &key) const
Definition qx-lopmap.h:291
bool operator!=(const Lopmap &other) const =default
Compare value_compare
Definition qx-lopmap.h:221
const_reverse_iterator reverse_iterator
Definition qx-lopmap.h:214
const Key & lastKey() const
Definition qx-lopmap.h:322
typename StorageContainer::difference_type difference_type
Definition qx-lopmap.h:217
void swap(Lopmap< Key, T > &other)
Definition qx-lopmap.h:411
iterator upperBound(const T &value)
Definition qx-lopmap.h:301
std::pair< const_iterator, const_iterator > equal_range(const T &value) const
Definition qx-lopmap.h:311
const_reverse_iterator constReverseBegin() const
Definition qx-lopmap.h:284
const_iterator iterator
Definition qx-lopmap.h:211
const_iterator cend() const
Definition qx-lopmap.h:279
const_iterator constEnd() const
Definition qx-lopmap.h:280
const_iterator constFind(const Key &key) const
Definition qx-lopmap.h:292
bool operator==(const Lopmap &other) const
Definition qx-lopmap.h:459
reverse_iterator rbegin()
Definition qx-lopmap.h:281
typename StorageContainer::size_type size_type
Definition qx-lopmap.h:220
const_reverse_iterator rbegin() const
Definition qx-lopmap.h:282
const_reverse_iterator constReverseEnd() const
Definition qx-lopmap.h:288
Lopmap(std::initializer_list< std::pair< Key, T > > list)
Definition qx-lopmap.h:232
const_reverse_iterator crbegin() const
Definition qx-lopmap.h:283
iterator insert(const Key &key, const T &value)
Definition qx-lopmap.h:374
T mapped_Type
Definition qx-lopmap.h:219
const T & last() const
Definition qx-lopmap.h:321
iterator erase(const_iterator pos)
Definition qx-lopmap.h:333
Specifies that a type defines a call operator for the specified argument types (with strict return).
Definition qx-concepts.h:240
Specifies that a predicate is a valid, iterator based predicate for a lopmap.
Definition qx-lopmap.h:21
Specifies that a predicate is a valid, pair based predicate for a lopmap.
Definition qx-lopmap.h:24
Specifies that a predicate is a valid predicate for a lopmap.
Definition qx-lopmap.h:27
The Qx namespace is the main namespace through which all non-global functionality of the Qx library i...
Definition qx-abstracterror.cpp:13
qsizetype erase_if(Bimap< Left, Right > &bimap, Predicate pred)
Definition qx-bimap.h:395
void append(QList< T > &&value)
The qx-concepts header file provides a library of general purpose concepts as an extension of the sta...