Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Association.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
5/*
6 ********************************************************************************
7 ***************************** Implementation Details ***************************
8 ********************************************************************************
9 */
10#include <set> //tmphack for sloppy RetainAll impl
11
16
18
19 /*
20 ********************************************************************************
21 **************** Association<KEY_TYPE, MAPPED_VALUE_TYPE> **********************
22 ********************************************************************************
23 */
24 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
26 requires (IEqualsComparer<equal_to<KEY_TYPE>, KEY_TYPE>)
27 : Association{equal_to<KEY_TYPE>{}}
28 {
29 _AssertRepValidType ();
30 }
31 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
32 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER>
33 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (KEY_EQUALS_COMPARER&& keyEqualsComparer)
34 : inherited{Factory::Association_Factory<KEY_TYPE, MAPPED_VALUE_TYPE, remove_cvref_t<KEY_EQUALS_COMPARER>>::Default () (
35 forward<KEY_EQUALS_COMPARER> (keyEqualsComparer))}
36 {
37 _AssertRepValidType ();
38 }
39 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
40 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (const initializer_list<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>& src)
41 requires (IEqualsComparer<equal_to<KEY_TYPE>, KEY_TYPE>)
42 : Association{}
43 {
44 AddAll (src);
45 _AssertRepValidType ();
46 }
47 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
48 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER>
49 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (KEY_EQUALS_COMPARER&& keyEqualsComparer,
50 const initializer_list<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>& src)
51 : Association{forward<KEY_EQUALS_COMPARER> (keyEqualsComparer)}
52 {
53 AddAll (src);
54 _AssertRepValidType ();
55 }
56#if !qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
57 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
58 template <IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
59 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (ITERABLE_OF_ADDABLE&& src)
60 requires (IEqualsComparer<equal_to<KEY_TYPE>, KEY_TYPE> and
61 not derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, Association<KEY_TYPE, MAPPED_VALUE_TYPE>>)
62 : Association{}
63 {
64 AddAll (forward<ITERABLE_OF_ADDABLE> (src));
65 _AssertRepValidType ();
66 }
67#endif
68 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
69 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER, IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
70 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (KEY_EQUALS_COMPARER&& keyEqualsComparer, ITERABLE_OF_ADDABLE&& src)
71 : Association{forward<KEY_EQUALS_COMPARER> (keyEqualsComparer)}
72 {
73 AddAll (forward<ITERABLE_OF_ADDABLE> (src));
74 _AssertRepValidType ();
75 }
76 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
77 template <IInputIterator<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
78 Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
79 requires (IEqualsComparer<equal_to<KEY_TYPE>, KEY_TYPE>)
80 : Association{}
81 {
82 AddAll (forward<ITERATOR_OF_ADDABLE> (start), forward<ITERATOR_OF_ADDABLE2> (end));
83 _AssertRepValidType ();
84 }
85 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
86 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER, IInputIterator<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERATOR_OF_ADDABLE,
87 sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
88 Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (KEY_EQUALS_COMPARER&& keyEqualsComparer, ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
89 : Association{forward<KEY_EQUALS_COMPARER> (keyEqualsComparer)}
90 {
91 AddAll (forward<ITERATOR_OF_ADDABLE> (start), forward<ITERATOR_OF_ADDABLE2> (end));
92 _AssertRepValidType ();
93 }
94 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
95 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (const shared_ptr<_IRep>& rep) noexcept
96 : inherited{rep}
97 {
98 RequireNotNull (rep);
99 _AssertRepValidType ();
100 }
101 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
102 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (shared_ptr<_IRep>&& rep) noexcept
103 : inherited{(RequireExpression (rep != nullptr), move (rep))}
104 {
105 _AssertRepValidType ();
106 }
107 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
108 inline auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::GetKeyEqualsComparer () const -> KeyEqualsCompareFunctionType
109 {
110 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().GetKeyEqualsComparer ();
111 }
112 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
114 {
115 return this->template Map<Iterable<KEY_TYPE>> ([] (const auto& kvp) { return kvp.fKey; });
116 }
117 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
119 {
120 return this->template Map<Iterable<MAPPED_VALUE_TYPE>> ([] (const auto& kvp) { return kvp.fValue; });
121 }
122 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
123 inline auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Lookup (ArgByValueType<key_type> key) const -> Iterable<mapped_type>
124 {
125 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().Lookup (key);
126 }
127 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
128 inline optional<MAPPED_VALUE_TYPE> Association<KEY_TYPE, MAPPED_VALUE_TYPE>::LookupOne (ArgByValueType<key_type> key) const
129 {
130 auto tmp = Lookup (key);
131 if (auto i = tmp.begin ()) [[likely]] {
132 return *i;
133 }
134 return nullopt;
135 }
136 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
137 template <typename THROW_IF_MISSING>
138 inline MAPPED_VALUE_TYPE Association<KEY_TYPE, MAPPED_VALUE_TYPE>::LookupOneChecked (ArgByValueType<key_type> key,
139 const THROW_IF_MISSING& throwIfMissing) const
140 {
141 auto tmp = Lookup (key);
142 if (auto i = tmp.begin ()) [[likely]] {
143 return *i;
144 }
145 Execution::Throw (throwIfMissing);
146 }
147 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
148 inline MAPPED_VALUE_TYPE Association<KEY_TYPE, MAPPED_VALUE_TYPE>::LookupOneValue (ArgByValueType<key_type> key,
149 ArgByValueType<mapped_type> defaultValue) const
150 {
151 auto tmp = Lookup (key);
152 if (auto i = tmp.begin ()) [[likely]] {
153 return *i;
154 }
155 return defaultValue;
156 }
157 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
158 inline auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::operator[] (ArgByValueType<key_type> key) const -> const Iterable<mapped_type>
159 {
160 return Lookup (key);
161 }
162 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
163 inline bool Association<KEY_TYPE, MAPPED_VALUE_TYPE>::ContainsKey (ArgByValueType<key_type> key) const
164 {
165 return not _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().Lookup (key).empty ();
166 }
167 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
168 inline size_t Association<KEY_TYPE, MAPPED_VALUE_TYPE>::OccurrencesOf (ArgByValueType<key_type> key) const
169 {
170 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().Lookup (key).size ();
171 }
172 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
173 template <Common::IEqualsComparer<MAPPED_VALUE_TYPE> VALUE_EQUALS_COMPARER>
174 inline bool Association<KEY_TYPE, MAPPED_VALUE_TYPE>::ContainsMappedValue (ArgByValueType<mapped_type> v,
175 const VALUE_EQUALS_COMPARER& valueEqualsComparer) const
176 {
177 return this->Find ([&valueEqualsComparer, &v] (const auto& t) { return valueEqualsComparer (t.fValue, v); }) != nullptr;
178 }
179 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
180 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Add (ArgByValueType<key_type> key, ArgByValueType<mapped_type> newElt)
181 {
182 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Add (key, newElt);
183 }
184 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
185 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Add (ArgByValueType<value_type> p)
186 {
187 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Add (p.fKey, p.fValue);
188 }
189 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
190 template <IInputIterator<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
191 void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::AddAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
192 {
193 for (auto i = forward<ITERATOR_OF_ADDABLE> (start); i != forward<ITERATOR_OF_ADDABLE2> (end); ++i) {
194 Add (*i);
195 }
196 }
197 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
198 template <IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
199 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::AddAll (ITERABLE_OF_ADDABLE&& items)
200 {
201 if constexpr (std::is_convertible_v<remove_cvref_t<ITERABLE_OF_ADDABLE>*, Iterable<value_type>*>) {
202 // very rare corner case
203 if (static_cast<const Iterable<value_type>*> (this) == static_cast<const Iterable<value_type>*> (&items)) [[unlikely]] {
204 vector<value_type> copy{std::begin (items), Iterator<value_type>{std::end (items)}}; // because you can not iterate over a container while modifying it
205 AddAll (std::begin (copy), std::end (copy));
206 return;
207 }
208 }
209 AddAll (std::begin (items), std::end (items));
210 }
211 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
212 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Remove (ArgByValueType<key_type> key)
213 {
214 Verify (_SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().RemoveIf (key)); // use RemoveIf () if key may not exist
215 }
216 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
218 {
219 Require (not i.Done ());
220 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
221 writerRep->Remove (patchedIterator, nextI);
222 }
223 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
224 inline bool Association<KEY_TYPE, MAPPED_VALUE_TYPE>::RemoveIf (ArgByValueType<key_type> key)
225 {
226 return _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().RemoveIf (key);
227 }
228 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
230 {
231 _SafeReadRepAccessor<_IRep> tmp{this}; // important to use READ not WRITE accessor, because write accessor would have already cloned the data
232 if (not tmp._ConstGetRep ().empty ()) {
233 this->_fRep = tmp._ConstGetRep ().CloneEmpty ();
234 }
235 }
236 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
237 template <typename ITERABLE_OF_KEY_OR_ADDABLE>
238 size_t Association<KEY_TYPE, MAPPED_VALUE_TYPE>::RemoveAll (const ITERABLE_OF_KEY_OR_ADDABLE& items)
239 {
240 using ITEM_T = ranges::range_value_t<ITERABLE_OF_KEY_OR_ADDABLE>;
241 static_assert (is_convertible_v<ITEM_T, key_type> or is_convertible_v<ITEM_T, pair<key_type, mapped_type>> or
242 is_convertible_v<ITEM_T, Common::KeyValuePair<key_type, mapped_type>>);
243 if (this == &items) { // avoid modifying container while iterating over it
244 size_t result = this->size ();
245 RemoveAll ();
246 return result;
247 }
248 else {
249 return RemoveAll (begin (items), end (items));
250 }
251 }
252 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
253 template <typename ITERATOR_OF_KEY_OR_ADDABLE>
254 inline size_t Association<KEY_TYPE, MAPPED_VALUE_TYPE>::RemoveAll (ITERATOR_OF_KEY_OR_ADDABLE start, ITERATOR_OF_KEY_OR_ADDABLE end)
255 {
256 using ITEM_T = ranges::range_value_t<ITERATOR_OF_KEY_OR_ADDABLE>;
257 static_assert (is_convertible_v<ITEM_T, key_type> or is_convertible_v<ITEM_T, pair<key_type, mapped_type>> or
258 is_convertible_v<ITEM_T, Common::KeyValuePair<key_type, mapped_type>>);
259 size_t cnt{};
260 for (auto i = start; i != end; ++i) {
261 if constexpr (is_convertible_v<ITEM_T, key_type>) {
262 if (RemoveIf (*i)) {
263 ++cnt;
264 }
265 }
266 else if constexpr (is_convertible_v<ITEM_T, pair<key_type, mapped_type>>) {
267 if (RemoveIf (i->first)) {
268 ++cnt;
269 }
270 }
271 else if constexpr (is_convertible_v<ITEM_T, Common::KeyValuePair<key_type, mapped_type>>) {
272 if (RemoveIf (i->fKey)) {
273 ++cnt;
274 }
275 }
276 else {
278 }
279 }
280 return cnt;
281 }
282 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
283 template <predicate<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> PREDICATE>
285 {
286 size_t nRemoved{};
287 for (Iterator<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> i = this->begin (); i != this->end ();) {
288 if (p (*i)) {
289 Remove (i, &i);
290 ++nRemoved;
291 }
292 else {
293 ++i;
294 }
295 }
296 return nRemoved;
297 }
298 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
299 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Update (const Iterator<value_type>& i, ArgByValueType<mapped_type> newValue,
301 {
302 Require (not i.Done ());
303 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
304 writerRep->Update (patchedIterator, newValue, nextI);
305 }
306 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
307 template <IIterableOfTo<KEY_TYPE> ITERABLE_OF_KEY_TYPE>
308 void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::RetainAll (const ITERABLE_OF_KEY_TYPE& items)
309 {
310 static_assert (is_convertible_v<ranges::range_value_t<ITERABLE_OF_KEY_TYPE>, key_type>);
311 // @see http://stroika-bugs.sophists.com/browse/STK-539
312#if 0
313 Association<KEY_TYPE, MAPPED_VALUE_TYPE> result = Association<KEY_TYPE, MAPPED_VALUE_TYPE> { _SafeReadRepAccessor<_IRep> { this } ._ConstGetRep ().CloneEmpty () };
314 for (auto key2Keep : items) {
315 if (auto l = this->Lookup (key2Keep)) {
316 result.Add (key2Keep, *l);
317 }
318 }
319 *this = result;
320#else
321 // cannot easily use STL::less because our Association class only requires KeyEqualsCompareFunctionType - SO - should use Stroika Set<> But don't want cross-dependencies if not needed
322 using SET_ITER_T = decltype (items.begin ());
323 set<KEY_TYPE> tmp{items.begin (), SET_ITER_T{items.end ()}}; // @todo - weak implementation because of 'comparison' function, and performance (if items already a set)
324 for (Iterator<value_type> i = this->begin (); i != this->end ();) {
325 if (tmp.find (i->fKey) == tmp.end ()) {
326 [[maybe_unused]] size_t sz = this->size ();
327 i = this->erase (i);
328 Assert (this->size () == sz - 1u);
329 }
330 else {
331 ++i;
332 }
333 }
334#endif
335 }
336 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
337 template <typename RESULT_CONTAINER, invocable<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ELEMENT_MAPPER>
338 nonvirtual RESULT_CONTAINER Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Map (ELEMENT_MAPPER&& elementMapper) const
339 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>, typename RESULT_CONTAINER::value_type> or
340 convertible_to<invoke_result_t<ELEMENT_MAPPER, KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>, optional<typename RESULT_CONTAINER::value_type>>)
341 {
342 if constexpr (same_as<RESULT_CONTAINER, Association>) {
343 // clone the rep so we retain any ordering function/etc, rep type
344 return inherited::template Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper),
345 RESULT_CONTAINER{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()});
346 }
347 else {
348 return inherited::template Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper)); // default Iterable<> implementation then...
349 }
350 }
351 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
352 template <derived_from<Iterable<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>> RESULT_CONTAINER, typename INCLUDE_PREDICATE>
353 inline auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Where (INCLUDE_PREDICATE&& includeIfTrue) const -> RESULT_CONTAINER
354 requires (predicate<INCLUDE_PREDICATE, KEY_TYPE> or predicate<INCLUDE_PREDICATE, KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>)
355 {
356 if constexpr (predicate<INCLUDE_PREDICATE, KEY_TYPE>) {
357 // recurse once with a KVP predicate
358 return Where<RESULT_CONTAINER> (
359 [=] (const ArgByValueType<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>& kvp) { return includeIfTrue (kvp.fKey); });
360 }
361 else {
362 if constexpr (same_as<RESULT_CONTAINER, Association>) {
363 // clone the rep so we retain any ordering function/etc, rep type
364 return inherited::template Where<RESULT_CONTAINER> (
365 forward<INCLUDE_PREDICATE> (includeIfTrue), RESULT_CONTAINER{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()});
366 }
367 else {
368 return inherited::template Where<RESULT_CONTAINER> (forward<INCLUDE_PREDICATE> (includeIfTrue)); // default Iterable<> implementation then...
369 }
370 }
371 }
372 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
373 template <typename CONTAINER_OF_KEYS>
374 auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::WithKeys (const CONTAINER_OF_KEYS& includeKeys) const -> ArchetypeContainerType
375 {
376 return Where ([=] (const key_type& key) -> bool { return includeKeys.Contains (key); });
377 }
378 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
379 auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::WithKeys (const initializer_list<key_type>& includeKeys) const -> ArchetypeContainerType
380 {
381 Iterable<key_type> ik{includeKeys};
382 return inherited::Where ([=] (const ArgByValueType<value_type>& kvp) { return ik.Contains (kvp.fKey); }, ArchetypeContainerType{});
383 }
384 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
385 template <typename CONTAINER_OF_Key_T>
386 inline CONTAINER_OF_Key_T Association<KEY_TYPE, MAPPED_VALUE_TYPE>::As () const
387 {
388 CONTAINER_OF_Key_T result;
389 for (const auto& i : *this) {
390 if constexpr (is_convertible_v<typename CONTAINER_OF_Key_T::value_type, pair<KEY_TYPE, MAPPED_VALUE_TYPE>>) {
391 // the reason we use the overload with an extra result.end () here is so it will work with std::map<> or std::vector<>
392 result.insert (result.end (), pair<KEY_TYPE, MAPPED_VALUE_TYPE>{i.fKey, i.fValue});
393 }
394 else {
395 result.insert (result.end (), value_type{i.fKey, i.fValue});
396 }
397 }
398 return result;
399 }
400 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
401 inline void
402 Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Accumulate (ArgByValueType<key_type> key, ArgByValueType<mapped_type> newValue,
403 const function<mapped_type (ArgByValueType<mapped_type>, ArgByValueType<mapped_type>)>& f,
404 mapped_type initialValue)
405 {
406 Add (key, f (LookupValue (key, initialValue), newValue));
407 }
408 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
409 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::erase (ArgByValueType<key_type> key)
410 {
411 Remove (key);
412 }
413 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
415 {
416 Iterator<value_type> nextI{nullptr};
417 Remove (i, &nextI);
418 return nextI;
419 }
420 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
422 {
423 RemoveAll ();
424 }
425 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
426 template <IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
428 {
430 result.AddAll (items);
431 return result;
432 }
433 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
434 template <IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
435 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>& Association<KEY_TYPE, MAPPED_VALUE_TYPE>::operator+= (const ITERABLE_OF_ADDABLE& items)
436 {
437 AddAll (items);
438 return *this;
439 }
440 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
441 template <typename ITERABLE_OF_KEY_OR_ADDABLE>
442 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>& Association<KEY_TYPE, MAPPED_VALUE_TYPE>::operator-= (const ITERABLE_OF_KEY_OR_ADDABLE& items)
443 {
444 RemoveAll (items);
445 return *this;
446 }
447 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
449 -> tuple<_IRep*, Iterator<value_type>>
450 {
451 Require (not i.Done ());
452 using element_type = typename inherited::_SharedByValueRepType::element_type;
453 Iterator<value_type> patchedIterator = i;
454 element_type* writableRep = this->_fRep.rwget ([&] (const element_type& prevRepPtr) -> typename inherited::_SharedByValueRepType::shared_ptr_type {
455 return Debug::UncheckedDynamicCast<const _IRep&> (prevRepPtr).CloneAndPatchIterator (&patchedIterator);
456 });
457 AssertNotNull (writableRep);
458 return make_tuple (Debug::UncheckedDynamicCast<_IRep*> (writableRep), move (patchedIterator));
459 }
460 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
462 {
464 [[maybe_unused]] _SafeReadRepAccessor<_IRep> ignored{this};
465 }
466 }
467 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
469 requires (equality_comparable<MAPPED_VALUE_TYPE>)
470 {
471 return EqualsComparer<>{}(*this, rhs);
472 }
473
474 /*
475 ********************************************************************************
476 ********** Association<KEY_TYPE, MAPPED_VALUE_TYPE>::EqualsComparer ************
477 ********************************************************************************
478 */
479 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
480 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (Common::IEqualsComparer<MAPPED_VALUE_TYPE>) VALUE_EQUALS_COMPARER>
481 constexpr Association<KEY_TYPE, MAPPED_VALUE_TYPE>::EqualsComparer<VALUE_EQUALS_COMPARER>::EqualsComparer (const VALUE_EQUALS_COMPARER& valueEqualsComparer)
482 : fValueEqualsComparer{valueEqualsComparer}
483 {
484 }
485 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
486 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (Common::IEqualsComparer<MAPPED_VALUE_TYPE>) VALUE_EQUALS_COMPARER>
487 bool Association<KEY_TYPE, MAPPED_VALUE_TYPE>::EqualsComparer<VALUE_EQUALS_COMPARER>::operator() (const Association& lhs, const Association& rhs) const
488 {
489 /*
490 * @todo This code is not very efficient, except in the 'quickEqualsTest' case
491 */
492 _SafeReadRepAccessor<_IRep> lhsR{&lhs};
493 _SafeReadRepAccessor<_IRep> rhsR{&rhs};
494 if (&lhsR._ConstGetRep () == &rhsR._ConstGetRep ()) {
495 // not such an unlikely test result since we use lazy copy, but this test is only an optimization and not logically required
496 return true;
497 }
498 // Check length, so we don't need to check both iterators for end/done; length is often a quick computation and always quick compared to this comparison algorithm
499 if (lhsR._ConstGetRep ().size () != rhsR._ConstGetRep ().size ()) {
500 return false;
501 }
502 /*
503 * Two Associations compare equal, if they have the same domain, and map each element of that domain to the same range.
504 * They need not be in the same order to compare equals. Still - they often are, and if they are, this algorithm is faster.
505 * If they miss, we need to fall back to a slower strategy.
506 */
507 auto quickEqualsTest = [&] () -> bool {
508 auto li = lhsR._ConstGetRep ().MakeIterator ();
509 auto ri = rhs.MakeIterator ();
510 auto keyEqualsComparer = lhs.GetKeyEqualsComparer (); // arbitrarily select left side key equals comparer
511 while (not li.Done ()) {
512 Assert (not ri.Done ()); // cuz move at same time and same size
513 bool keysEqual = keyEqualsComparer (li->fKey, ri->fKey);
514 Require (keysEqual == rhs.GetKeyEqualsComparer () (li->fKey, ri->fKey)); // if fails, cuz rhs/lhs keys equals comparers disagree
515 if (not keysEqual or not fValueEqualsComparer (li->fValue, ri->fValue)) {
516 return false;
517 }
518 // if we got this far, all compared OK so far, so keep going
519 ++li;
520 ++ri;
521 }
522 Assert (ri.Done ()); // cuz LHS done and both sides iterate at same pace, and we checked both same size
523 return true;
524 };
525 if (quickEqualsTest ()) {
526 return true;
527 }
528 // OK, we failed the quick test, but the associations might still be 'equal' - just in a funny order
529 // note this is extremely expensive. We should find a better algorithm...
530 auto keyEqualsComparer = lhs.GetKeyEqualsComparer (); // arbitrarily select left side key equals comparer
531 return lhs.MultiSetEquals (rhs, Common::DeclareEqualsComparer ([&] (const value_type& lhs, const value_type& rhs) {
532 return keyEqualsComparer (lhs.fKey, rhs.fKey) and fValueEqualsComparer (lhs.fKey, rhs.fKey);
533 }));
534 }
535
536}
#define AssertNotNull(p)
Definition Assertions.h:333
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:48
#define RequireNotNull(p)
Definition Assertions.h:347
#define RequireExpression(c)
Definition Assertions.h:267
#define AssertNotReached()
Definition Assertions.h:355
#define Verify(c)
Definition Assertions.h:419
An Association pairs key values with (possibly multiple or none) mapped_type values....
nonvirtual CONTAINER_OF_Key_T As() const
nonvirtual optional< mapped_type > LookupOne(ArgByValueType< key_type > key) const
Lookup and return the first (maybe arbitrarily chosen which is first) value with this key,...
nonvirtual bool RemoveIf(ArgByValueType< key_type > key)
Remove the given item, if it exists. Return true if found and removed.
nonvirtual void Remove(ArgByValueType< key_type > key)
Remove the given item (which must exist).
nonvirtual void clear()
STL-ish alias for RemoveAll ().
nonvirtual void Update(const Iterator< value_type > &i, ArgByValueType< mapped_type > newValue, Iterator< value_type > *nextI=nullptr)
nonvirtual size_t OccurrencesOf(ArgByValueType< key_type > item) const
nonvirtual bool ContainsKey(ArgByValueType< key_type > key) const
nonvirtual void RetainAll(const ITERABLE_OF_KEY_TYPE &items)
nonvirtual void Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt)
nonvirtual void erase(ArgByValueType< key_type > key)
STL-ish alias for Remove ().
nonvirtual RESULT_CONTAINER Where(INCLUDE_PREDICATE &&includeIfTrue) const
nonvirtual void AddAll(ITERABLE_OF_ADDABLE &&items)
nonvirtual Traversal::Iterable< mapped_type > Lookup(ArgByValueType< key_type > key) const
Return an Iterable<mapped_type> of all the associated items (can be empty if none)....
nonvirtual tuple< _IRep *, Iterator< value_type > > _GetWritableRepAndPatchAssociatedIterator(const Iterator< value_type > &i)
Utility to get WRITABLE underlying shared_ptr (replacement for what we normally do - _SafeReadWriteRe...
nonvirtual Iterable< key_type > Keys() const
nonvirtual void Accumulate(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newValue, const function< mapped_type(ArgByValueType< mapped_type >, ArgByValueType< mapped_type >)> &f=[](ArgByValueType< mapped_type > l, ArgByValueType< mapped_type > r) -> mapped_type { return l+r;}, mapped_type initialValue={})
like Add (key, newValue) - BUT newValue is COMBINED with the 'f' argument.
nonvirtual mapped_type LookupOneChecked(ArgByValueType< key_type > key, const THROW_IF_MISSING &throwIfMissing) const
Lookup and return the first (maybe arbitrarily chosen which is first) value with this key,...
nonvirtual bool operator==(const Association &rhs) const
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to Association,...
nonvirtual mapped_type LookupOneValue(ArgByValueType< key_type > key, ArgByValueType< mapped_type > defaultValue=mapped_type{}) const
Lookup and return the first (maybe arbitrarily chosen which is first) value with this key,...
nonvirtual const Iterable< mapped_type > operator[](ArgByValueType< key_type > key) const
Shortcut for Lookup.
nonvirtual ArchetypeContainerType WithKeys(const CONTAINER_OF_KEYS &includeKeys) const
nonvirtual bool ContainsMappedValue(ArgByValueType< mapped_type > v, const VALUE_EQUALS_COMPARER &valueEqualsComparer={}) const
nonvirtual Iterable< mapped_type > MappedValues() const
nonvirtual void RemoveAll()
RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items...
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
nonvirtual Iterator< T > begin() const
Support for ranged for, and STL syntax in general.
static constexpr default_sentinel_t end() noexcept
Support for ranged for, and STL syntax in general.
An Iterator<T> is a copyable object which allows traversing the contents of some container....
Definition Iterator.h:225
nonvirtual bool Done() const
Done () means there is nothing left in this iterator (a synonym for (it == container....
Definition Iterator.inl:147
constexpr Common::ComparisonRelationDeclaration< ComparisonRelationType::eEquals, remove_cvref_t< FUNCTOR > > DeclareEqualsComparer(FUNCTOR &&f)
DeclareEqualsComparer () marks a FUNCTOR (lambda or not) as being a FUNCTOR which compares for equali...
Definition Compare.inl:31
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43