Qx v0.6
Qt Extensions Library
Loading...
Searching...
No Matches
qx-bimap.h
1#ifndef QX_BIMAP_H
2#define QX_BIMAP_H
3
4#include <stdexcept>
5
6// Qt Includes
7#include <QHash>
8
9// Extra-component Includes
11
12namespace Qx
13{
14
15template<typename Left, typename Right>
16class Bimap;
17
18template<typename Left, typename Right>
19concept asymmetric_bimap = !std::same_as<Left, Right>;
20
21template<typename Left, typename Right, typename Predicate>
23
24template<typename Left, typename Right, typename Predicate>
26
27template<typename Left, typename Right, typename Predicate>
29
30}
31
33namespace _QxBimapPrivate
34{
35
36template<typename Left, typename Right, typename Predicate>
37qsizetype _erase_if(Qx::Bimap<Left, Right>& bm, Predicate& pred)
38{
39 // This could be moved to a private file and made more generic for any other container types if needed
40 using ConstIterator = typename Qx::Bimap<Left, Right>::const_iterator;
41
42 qsizetype result = 0;
43
44 ConstIterator it = bm.cbegin();
45 const ConstIterator end = bm.cend();
46 while (it != end)
47 {
49 {
50 if(pred(it))
51 {
52 it = bm.erase(it);
53 result++;
54 }
55 else
56 it++;
57 }
59 {
60 if (pred(std::move(*it)))
61 {
62 it = bm.erase(it);
63 result++;
64 } else
65 it++;
66 }
67 else
68 {
69 // Delayed evaluation trick to prevent immediate static_assert failure for older compilers from before the resolution of CWG 2518
70 static_assert(sizeof(Qx::Bimap<Left, Right>) == 0, "Invalid Predicate");
71 }
72 }
73
74 return result;
75}
76
77}
80namespace Qx
81{
82
83template<typename Left, typename Right>
84class Bimap
85{
86//-Inner Classes----------------------------------------------------------------------------------------------------
87public:
88 //class iterator; Would cause internal iterator invalidation with no way to recover AFAICT, with current impl.
90 {
91 friend class Bimap<Left, Right>;
92 //-Aliases------------------------------------------------------------------------------------------------------
93 private:
94 using PIterator = typename QHash<Left, const Right*>::const_iterator;
95
96 //-Instance Variables-------------------------------------------------------------------------------------------
97 private:
98 PIterator mPItr;
99
100 //-Constructor--------------------------------------------------------------------------------------------------
101 private:
102 const_iterator(const PIterator& pitr) : mPItr(pitr) {}
103
104 public:
106
107 //-Instance Functions-------------------------------------------------------------------------------------------
108 public:
109 const Left& left() const { return mPItr.key(); }
110 const Right& right() const { return *mPItr.value(); }
111
112 //-Operators---------------------------------------------------------------------------------------------
113 public:
114 bool operator==(const const_iterator& other) const = default;
115 std::pair<const Left&, const Right&> operator*() const { return std::pair<const Left&, const Right&>(left(), right()); }
116 const_iterator& operator++() { mPItr++; return *this; }
117
119 {
120 auto cur = *this;
121 mPItr++;
122 return cur;
123 }
124 };
125
126//-Aliases------------------------------------------------------------------------------------------------------
127public:
129 using left_type = Left;
130 using right_type = Right;
134
135//-Instance Variables-------------------------------------------------------------------------------------------
136private:
139
140//-Constructor--------------------------------------------------------------------------------------------------
141public:
142 Bimap() {}
143
144 Bimap(std::initializer_list<std::pair<Left, Right>> list)
145 {
146 reserve(list.size());
147 for(auto it = list.begin(); it != list.end(); ++it)
148 insert(it->first, it->second);
149 }
150
151//-Class Functions-------------------------------------------------------------------------------------------
152private:
153 template<typename AMap, typename BMap, typename V>
154 void removeCrossReference(AMap& am, BMap& bm, const V& v)
155 {
156 if constexpr(std::same_as<AMap, BMap>)
157 Q_ASSERT(&am != &bm); // Ensure different maps are used if the types are the same
158
159 if(am.contains(v))
160 bm.remove(*am[v]);
161 }
162
163 template<typename AMap, typename BMap, typename V>
164 bool remove(AMap& am, BMap& bm, const V& v)
165 {
166 if(!am.contains(v))
167 return false;
168
169 bm.remove(*am[v]);
170 am.remove(v);
171 return true;
172 }
173
174//-Instance Functions-------------------------------------------------------------------------------------------
175private:
176 const_iterator existingRelation(const Left& l, const Right& r) const
177 {
178 auto itr = mL2R.constFind(l);
179 return (itr != mL2R.cend() && *itr.value() == r) ? const_iterator(itr) : const_iterator();
180 }
181
182 void removeCrossReferences(const Left& l, const Right& r)
183 {
184 // Remove to-be stale relations
185 removeCrossReference(mL2R, mR2L, l);
186 removeCrossReference(mR2L, mL2R, r);
187 }
188
189 const_iterator addOrUpdateRelation(const Left& l, const Right& r)
190 {
191 auto lItr = mL2R.insert(l, nullptr);
192 auto rItr = mR2L.insert(r, nullptr);
193 lItr.value() = &rItr.key();
194 rItr.value() = &lItr.key();
195 return const_iterator(lItr);
196 }
197
198public:
199 iterator begin() { return iterator(mL2R.begin()); }
200 const_iterator begin() const { return constBegin(); }
201 const_iterator cbegin() const { return constBegin(); }
202 const_iterator constBegin() const { return const_iterator(mL2R.cbegin()); }
203 iterator end() { return iterator(mL2R.end()); }
204 const_iterator end() const { return constEnd(); }
205 const_iterator cend() const { return constEnd(); }
206 const_iterator constEnd() const { return const_iterator(mL2R.cend()); }
207 const_iterator constFind(const Left& l) const requires asymmetric_bimap<Left, Right> { return constFindLeft(l); }
208 const_iterator constFind(const Right& r) const requires asymmetric_bimap<Left, Right> { return constFindRight(r); }
209 const_iterator constFindLeft(const Left& l) const { return const_iterator(mL2R.constFind(l)); }
210
211 const_iterator constFindRight(const Right& r) const
212 {
213 auto rItr = mR2L.constFind(r);
214 return const_iterator(rItr != mR2L.cend() ? mL2R.constFind(*(*rItr)) : mL2R.cend());
215 }
216
217 iterator find(const Left& l) requires asymmetric_bimap<Left, Right> { return findLeft(l); }
218 const_iterator find(const Left& l) const requires asymmetric_bimap<Left, Right> { return findLeft(l); }
219 iterator find(const Right& r) requires asymmetric_bimap<Left, Right> { return findRight(r); }
220 const_iterator find(const Right& r) const requires asymmetric_bimap<Left, Right> { return findRight(r); }
221 iterator findLeft(const Left& l) { return iterator(constFindLeft(l)); }
222 const_iterator findLeft(const Left& l) const { return const_iterator(constFindLeft(l)); }
223 iterator findRight(const Right& r) { return iterator(constFindRight(r)); }
224 const_iterator findRight(const Right& r) const { return const_iterator(constFindRight(r)); }
225
227 {
228 auto lItr = pos.mPItr;
229 auto rItr = mR2L.constFind(pos.right());
230 mR2L.erase(rItr);
231 return const_iterator(mL2R.erase(lItr));
232 }
233
234 void insert(const Bimap& other)
235 {
236 if(this == &other)
237 return;
238
239 for(auto it = other.begin(); it != other.end(); it++)
240 insert(it.left(), it.right());
241 }
242
243 const_iterator insert(const Left& l, const Right& r)
244 {
245 if(auto itr = existingRelation(l, r); itr != cend())
246 return itr;
247
248 removeCrossReferences(l, r);
249 return addOrUpdateRelation(l, r);
250 }
251
252 bool containsLeft(const Left& l) const { return mL2R.contains(l); }
253 bool containsRight(const Right& r) const { return mR2L.contains(r); }
254
255 Right fromLeft(const Left& l) const
256 {
257 return mL2R.contains(l) ? *mL2R[l] : Right();
258 }
259
260 Right fromLeft(const Left& l, const Right& defaultValue) const
261 {
262 return mL2R.contains(l) ? *mL2R[l] : defaultValue;
263 }
264
265 Left fromRight(const Right& l) const
266 {
267 return mR2L.contains(l) ? *mR2L[l] : Left();
268 }
269
270 Left fromRight(const Right& l, const Left& defaultValue) const
271 {
272 return mR2L.contains(l) ? *mR2L[l] : defaultValue;
273 }
274
275 Right from(const Left& l) const requires asymmetric_bimap<Left, Right> { return fromLeft(l); }
276 Right from(const Left& l, const Right& defaultValue) const requires asymmetric_bimap<Left, Right> { return fromLeft(l, defaultValue); }
277 Left from(const Right& r) const requires asymmetric_bimap<Left, Right> { return fromRight(r); }
278 Left from(const Right& r, const Left& defaultValue) const requires asymmetric_bimap<Left, Right> { return fromRight(r, defaultValue); }
279
280 Left toLeft(const Right& r) const { return fromRight(r); }
281 Left toLeft(const Right& r, const Left& defaultValue) const { return fromRight(r, defaultValue); }
282 Right toRight(const Left& l) const { return fromLeft(l); }
283 Right toRight(const Left& l, const Right& defaultValue) const { return fromLeft(l, defaultValue); }
284
285 bool remove(const Left& l) { return removeLeft(l); }
286 bool remove(const Right& r) { return removeRight(r); }
287 bool removeLeft(const Left& l) { return remove(mL2R, mR2L, l); }
288 bool removeRight(const Right& r) { return remove(mR2L, mL2R, r); }
289
290 template<typename Predicate>
292 qsizetype removeIf(Predicate pred) { return _QxBimapPrivate::_erase_if(*this, pred); }
293
294 Right takeRight(const Left& l)
295 {
296 Right r = fromLeft(l);
297 removeLeft(l);
298 return r;
299 }
300
301 Left takeLeft(const Right& r)
302 {
303 Left l = fromRight(r);
304 removeRight(r);
305 return l;
306 }
307
308 Right take(const Left& l) requires asymmetric_bimap<Left, Right> { return takeRight(l); }
309 Left take(const Right& r) requires asymmetric_bimap<Left, Right> { return takeLeft(r); }
310
312 {
313 mL2R.swap(other.mL2R);
314 mR2L.swap(other.mR2L);
315 }
316
317 qsizetype size() const { return mL2R.size(); }
318 qsizetype count() const { return size(); }
319 bool isEmpty() const { return size() == 0; }
320 bool empty() const { return isEmpty(); }
321 float load_factor() const { return mL2R.load_factor(); }
322
323 qsizetype capacity() const { return mL2R.capacity(); }
324 void clear() { mL2R.clear(); mR2L.clear(); }
325 void reserve(qsizetype size) { mL2R.reserve(size); mR2L.reserve(size); }
326 void squeeze() { mL2R.squeeze(); mR2L.squeeze(); }
327
328 QList<Left> lefts() const { return mL2R.keys(); }
329 QList<Right> rights() const { return mR2L.keys(); }
330
331 // Doc'ed here cause doxygen struggles with this one being separate
341 {
343 for(auto [k, v] : mL2R.asKeyValueRange())
344 rel.append(std::make_pair(k, *v));
345 }
346
347//-Operators---------------------------------------------------------------------------------------------
348public:
349 /* TODO: Having non-const versions of these that return a reference would require
350 * const_cast<>'ing away the constness of the key of the "other" map, and I'm not
351 * sure if modifying that reference directly instead of using QHash's methods would
352 * break the hash or not.
353 */
354 Right operator[](const Left& l) const requires asymmetric_bimap<Left, Right>
355 {
356 /* Alternatively these [] operators could insert a default constructed pair opposite if the
357 * key is not found, like QHash::operator[]() does by using our insert function (to handle
358 * both maps), but for now we do this.
359 */
360 if(!mL2R.contains(l))
361 throw std::invalid_argument("Access into bimap with a value it does not contain!");
362 return *mL2R[l];
363 }
364
365 Left operator[](const Right& r) const requires asymmetric_bimap<Left, Right>
366 {
367 if(!mR2L.contains(r))
368 throw std::invalid_argument("Access into bimap with a value it does not contain!");
369 return *mR2L[r];
370 }
371
372 bool operator==(const Bimap& other) const
373 {
374 const auto& oL2R = other.mL2R;
375 for (auto [l, rp] : mL2R.asKeyValueRange())
376 if(!oL2R.contains(l) || *rp != *oL2R[l])
377 return false;
378
379 return true;
380 }
381
382 bool operator!=(const Bimap& other) const = default;
383};
384
385// Doc'ed here cause doxygen struggles with this one being separate
394template<typename Left, typename Right, typename Predicate>
395qsizetype erase_if(Bimap<Left, Right>& bimap, Predicate pred) { return _QxBimapPrivate::_erase_if(bimap, pred); }
396
397}
398
399#endif // QX_BIMAP_H
The Bimap::const_iterator class provides an STL-style const iterator for Bimap.
Definition qx-bimap.h:90
const_iterator operator++(int)
Definition qx-bimap.h:118
const_iterator()
Definition qx-bimap.h:105
const_iterator & operator++()
Definition qx-bimap.h:116
const Left & left() const
Definition qx-bimap.h:109
const Right & right() const
Definition qx-bimap.h:110
bool operator==(const const_iterator &other) const =default
std::pair< const Left &, const Right & > operator*() const
Definition qx-bimap.h:115
The Bimap template class offers a rudimentary bi-directional associative map.
Definition qx-bimap.h:85
Bimap()
Definition qx-bimap.h:142
Left from(const Right &r, const Left &defaultValue) const
Definition qx-bimap.h:278
Left from(const Right &r) const
Definition qx-bimap.h:277
bool operator!=(const Bimap &other) const =default
bool operator==(const Bimap &other) const
Definition qx-bimap.h:372
void insert(const Bimap &other)
Definition qx-bimap.h:234
const_iterator constBegin() const
Definition qx-bimap.h:202
QList< Left > lefts() const
Definition qx-bimap.h:328
QList< std::pair< Left, Right > > relationships() const
Definition qx-bimap.h:340
Right fromLeft(const Left &l, const Right &defaultValue) const
Definition qx-bimap.h:260
qsizetype capacity() const
Definition qx-bimap.h:323
Left take(const Right &r)
Definition qx-bimap.h:309
const_iterator insert(const Left &l, const Right &r)
Definition qx-bimap.h:243
Left takeLeft(const Right &r)
Definition qx-bimap.h:301
Right fromLeft(const Left &l) const
Definition qx-bimap.h:255
iterator findRight(const Right &r)
Definition qx-bimap.h:223
typename QHash< Left, const Right * >::difference_type difference_type
Definition qx-bimap.h:132
const_iterator end() const
Definition qx-bimap.h:204
bool removeRight(const Right &r)
Definition qx-bimap.h:288
const_iterator findLeft(const Left &l) const
Definition qx-bimap.h:222
const_iterator begin() const
Definition qx-bimap.h:200
Left toLeft(const Right &r) const
Definition qx-bimap.h:280
Left left_type
Definition qx-bimap.h:129
bool removeLeft(const Left &l)
Definition qx-bimap.h:287
Bimap(std::initializer_list< std::pair< Left, Right > > list)
Definition qx-bimap.h:144
const_iterator constFind(const Right &r) const
Definition qx-bimap.h:208
const_iterator find(const Left &l) const
Definition qx-bimap.h:218
const_iterator constFindRight(const Right &r) const
Definition qx-bimap.h:211
qsizetype size() const
Definition qx-bimap.h:317
bool containsLeft(const Left &l) const
Definition qx-bimap.h:252
const_iterator cend() const
Definition qx-bimap.h:205
Right from(const Left &l) const
Definition qx-bimap.h:275
bool remove(const Left &l)
Definition qx-bimap.h:285
Right operator[](const Left &l) const
Definition qx-bimap.h:354
qsizetype removeIf(Predicate pred)
Definition qx-bimap.h:292
const_iterator findRight(const Right &r) const
Definition qx-bimap.h:224
Right toRight(const Left &l) const
Definition qx-bimap.h:282
Right from(const Left &l, const Right &defaultValue) const
Definition qx-bimap.h:276
const_iterator constEnd() const
Definition qx-bimap.h:206
void squeeze()
Definition qx-bimap.h:326
iterator find(const Right &r)
Definition qx-bimap.h:219
bool isEmpty() const
Definition qx-bimap.h:319
iterator find(const Left &l)
Definition qx-bimap.h:217
Left fromRight(const Right &l) const
Definition qx-bimap.h:265
const_iterator constFindLeft(const Left &l) const
Definition qx-bimap.h:209
Right right_type
Definition qx-bimap.h:130
iterator begin()
Definition qx-bimap.h:199
bool containsRight(const Right &r) const
Definition qx-bimap.h:253
void swap(Bimap< Left, Right > &other)
Definition qx-bimap.h:311
iterator end()
Definition qx-bimap.h:203
QList< Right > rights() const
Definition qx-bimap.h:329
float load_factor() const
Definition qx-bimap.h:321
Right toRight(const Left &l, const Right &defaultValue) const
Definition qx-bimap.h:283
bool remove(const Right &r)
Definition qx-bimap.h:286
const_iterator iterator
Definition qx-bimap.h:128
const_iterator cbegin() const
Definition qx-bimap.h:201
bool empty() const
Definition qx-bimap.h:320
void clear()
Definition qx-bimap.h:324
const_iterator constFind(const Left &l) const
Definition qx-bimap.h:207
iterator findLeft(const Left &l)
Definition qx-bimap.h:221
void reserve(qsizetype size)
Definition qx-bimap.h:325
qsizetype count() const
Definition qx-bimap.h:318
Left operator[](const Right &r) const
Definition qx-bimap.h:365
Right takeRight(const Left &l)
Definition qx-bimap.h:294
Right take(const Left &l)
Definition qx-bimap.h:308
Left toLeft(const Right &r, const Left &defaultValue) const
Definition qx-bimap.h:281
Left fromRight(const Right &l, const Left &defaultValue) const
Definition qx-bimap.h:270
const_iterator erase(const_iterator pos)
Definition qx-bimap.h:226
const_iterator find(const Right &r) const
Definition qx-bimap.h:220
typename QHash< Left, const Right * >::size_type size_type
Definition qx-bimap.h:133
Specifies that a bimap has different Left and Right types.
Definition qx-bimap.h:19
Specifies that a predicate is a valid, iterator based predicate for a bimap.
Definition qx-bimap.h:22
Specifies that a predicate is a valid, pair based predicate for a bimap.
Definition qx-bimap.h:25
Specifies that a predicate is a valid predicate for a bimap.
Definition qx-bimap.h:28
Specifies that a type defines a call operator for the specified argument types (with strict return).
Definition qx-concepts.h:240
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
auto asKeyValueRange() &
QHash< Key, T >::iterator begin()
qsizetype capacity() const const
QHash< Key, T >::const_iterator cbegin() const const
QHash< Key, T >::const_iterator cend() const const
void clear()
QHash< Key, T >::const_iterator constFind(const Key &key) const const
bool contains(const Key &key) const const
QHash< Key, T >::iterator end()
QHash< Key, T >::iterator erase(QHash< Key, T >::const_iterator pos)
QHash< Key, T >::iterator insert(const Key &key, const T &value)
QList< Key > keys() const const
float load_factor() const const
void reserve(qsizetype size)
qsizetype size() const const
void squeeze()
void swap(QHash< Key, T > &other)
void append(QList< T > &&value)
The qx-concepts header file provides a library of general purpose concepts as an extension of the sta...