Stroika Library 3.0d18
 
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 */
14
16
17 /*
18 ********************************************************************************
19 **************** Association<KEY_TYPE, MAPPED_VALUE_TYPE> **********************
20 ********************************************************************************
21 */
22 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
24 requires (IEqualsComparer<equal_to<KEY_TYPE>, KEY_TYPE>)
25 : Association{equal_to<KEY_TYPE>{}}
26 {
27 _AssertRepValidType ();
28 }
29 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
30 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER>
31 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (KEY_EQUALS_COMPARER&& keyEqualsComparer)
32 : inherited{Factory::Association_Factory<KEY_TYPE, MAPPED_VALUE_TYPE, remove_cvref_t<KEY_EQUALS_COMPARER>>::Default () (
33 forward<KEY_EQUALS_COMPARER> (keyEqualsComparer))}
34 {
35 _AssertRepValidType ();
36 }
37 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
38 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (const initializer_list<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>& src)
39 requires (IEqualsComparer<equal_to<KEY_TYPE>, KEY_TYPE>)
40 : Association{}
41 {
42 AddAll (src);
43 _AssertRepValidType ();
44 }
45 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
46 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER>
47 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (KEY_EQUALS_COMPARER&& keyEqualsComparer,
48 const initializer_list<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>& src)
49 : Association{forward<KEY_EQUALS_COMPARER> (keyEqualsComparer)}
50 {
51 AddAll (src);
52 _AssertRepValidType ();
53 }
54#if !qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
55 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
56 template <IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
57 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (ITERABLE_OF_ADDABLE&& src)
58 requires (IEqualsComparer<equal_to<KEY_TYPE>, KEY_TYPE> and
59 not derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, Association<KEY_TYPE, MAPPED_VALUE_TYPE>>)
60 : Association{}
61 {
62 AddAll (forward<ITERABLE_OF_ADDABLE> (src));
63 _AssertRepValidType ();
64 }
65#endif
66 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
67 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER, IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
68 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (KEY_EQUALS_COMPARER&& keyEqualsComparer, ITERABLE_OF_ADDABLE&& src)
69 : Association{forward<KEY_EQUALS_COMPARER> (keyEqualsComparer)}
70 {
71 AddAll (forward<ITERABLE_OF_ADDABLE> (src));
72 _AssertRepValidType ();
73 }
74 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
75 template <IInputIterator<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
76 Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
77 requires (IEqualsComparer<equal_to<KEY_TYPE>, KEY_TYPE>)
78 : Association{}
79 {
80 AddAll (forward<ITERATOR_OF_ADDABLE> (start), forward<ITERATOR_OF_ADDABLE2> (end));
81 _AssertRepValidType ();
82 }
83 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
84 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER, IInputIterator<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERATOR_OF_ADDABLE,
85 sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
86 Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (KEY_EQUALS_COMPARER&& keyEqualsComparer, ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
87 : Association{forward<KEY_EQUALS_COMPARER> (keyEqualsComparer)}
88 {
89 AddAll (forward<ITERATOR_OF_ADDABLE> (start), forward<ITERATOR_OF_ADDABLE2> (end));
90 _AssertRepValidType ();
91 }
92 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
93 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (const shared_ptr<_IRep>& rep) noexcept
94 : inherited{rep}
95 {
96 RequireNotNull (rep);
97 _AssertRepValidType ();
98 }
99 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
100 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Association (shared_ptr<_IRep>&& rep) noexcept
101 : inherited{(RequireExpression (rep != nullptr), move (rep))}
102 {
103 _AssertRepValidType ();
104 }
105 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
106 inline auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::GetKeyEqualsComparer () const -> KeyEqualsCompareFunctionType
107 {
108 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().GetKeyEqualsComparer ();
109 }
110 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
112 {
113 return this->template Map<Iterable<KEY_TYPE>> ([] (const auto& kvp) { return kvp.fKey; });
114 }
115 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
117 {
118 return this->template Map<Iterable<MAPPED_VALUE_TYPE>> ([] (const auto& kvp) { return kvp.fValue; });
119 }
120 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
121 inline auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Lookup (ArgByValueType<key_type> key) const -> Iterable<mapped_type>
122 {
123 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().Lookup (key);
124 }
125 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
126 inline optional<MAPPED_VALUE_TYPE> Association<KEY_TYPE, MAPPED_VALUE_TYPE>::LookupOne (ArgByValueType<key_type> key) const
127 {
128 auto tmp = Lookup (key);
129 if (auto i = tmp.begin ()) [[likely]] {
130 return *i;
131 }
132 return nullopt;
133 }
134 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
135 template <typename THROW_IF_MISSING>
136 inline MAPPED_VALUE_TYPE Association<KEY_TYPE, MAPPED_VALUE_TYPE>::LookupOneChecked (ArgByValueType<key_type> key,
137 const THROW_IF_MISSING& throwIfMissing) const
138 {
139 auto tmp = Lookup (key);
140 if (auto i = tmp.begin ()) [[likely]] {
141 return *i;
142 }
143 Execution::Throw (throwIfMissing);
144 }
145 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
146 inline MAPPED_VALUE_TYPE Association<KEY_TYPE, MAPPED_VALUE_TYPE>::LookupOneValue (ArgByValueType<key_type> key,
147 ArgByValueType<mapped_type> defaultValue) const
148 {
149 auto tmp = Lookup (key);
150 if (auto i = tmp.begin ()) [[likely]] {
151 return *i;
152 }
153 return defaultValue;
154 }
155 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
156 inline auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::operator[] (ArgByValueType<key_type> key) const -> const Iterable<mapped_type>
157 {
158 return Lookup (key);
159 }
160 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
161 inline bool Association<KEY_TYPE, MAPPED_VALUE_TYPE>::ContainsKey (ArgByValueType<key_type> key) const
162 {
163 return not _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().Lookup (key).empty ();
164 }
165 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
166 inline size_t Association<KEY_TYPE, MAPPED_VALUE_TYPE>::OccurrencesOf (ArgByValueType<key_type> key) const
167 {
168 return _SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().Lookup (key).size ();
169 }
170 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
171 template <Common::IEqualsComparer<MAPPED_VALUE_TYPE> VALUE_EQUALS_COMPARER>
172 inline bool Association<KEY_TYPE, MAPPED_VALUE_TYPE>::ContainsMappedValue (ArgByValueType<mapped_type> v,
173 const VALUE_EQUALS_COMPARER& valueEqualsComparer) const
174 {
175 return this->Find ([&valueEqualsComparer, &v] (const auto& t) { return valueEqualsComparer (t.fValue, v); }) != nullptr;
176 }
177 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
178 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Add (ArgByValueType<key_type> key, ArgByValueType<mapped_type> newElt)
179 {
180 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Add (key, newElt);
181 }
182 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
183 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Add (ArgByValueType<value_type> p)
184 {
185 _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().Add (p.fKey, p.fValue);
186 }
187 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
188 template <IInputIterator<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
189 void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::AddAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
190 {
191 for (auto i = forward<ITERATOR_OF_ADDABLE> (start); i != forward<ITERATOR_OF_ADDABLE2> (end); ++i) {
192 Add (*i);
193 }
194 }
195 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
196 template <IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
197 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::AddAll (ITERABLE_OF_ADDABLE&& items)
198 {
199 if constexpr (std::is_convertible_v<remove_cvref_t<ITERABLE_OF_ADDABLE>*, Iterable<value_type>*>) {
200 // very rare corner case
201 if (static_cast<const Iterable<value_type>*> (this) == static_cast<const Iterable<value_type>*> (&items)) [[unlikely]] {
202 vector<value_type> copy{std::begin (items), Iterator<value_type>{std::end (items)}}; // because you can not iterate over a container while modifying it
203 AddAll (std::begin (copy), std::end (copy));
204 return;
205 }
206 }
207 AddAll (std::begin (items), std::end (items));
208 }
209 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
210 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Remove (ArgByValueType<key_type> key)
211 {
212 Verify (_SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().RemoveIf (key)); // use RemoveIf () if key may not exist
213 }
214 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
216 {
217 Require (not i.Done ());
218 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
219 writerRep->Remove (patchedIterator, nextI);
220 }
221 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
222 inline bool Association<KEY_TYPE, MAPPED_VALUE_TYPE>::RemoveIf (ArgByValueType<key_type> key)
223 {
224 return _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ().RemoveIf (key);
225 }
226 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
228 {
229 _SafeReadRepAccessor<_IRep> tmp{this}; // important to use READ not WRITE accessor, because write accessor would have already cloned the data
230 if (not tmp._ConstGetRep ().empty ()) {
231 this->_fRep = tmp._ConstGetRep ().CloneEmpty ();
232 }
233 }
234 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
235 template <typename ITERABLE_OF_KEY_OR_ADDABLE>
236 size_t Association<KEY_TYPE, MAPPED_VALUE_TYPE>::RemoveAll (const ITERABLE_OF_KEY_OR_ADDABLE& items)
237 {
238 using ITEM_T = ranges::range_value_t<ITERABLE_OF_KEY_OR_ADDABLE>;
239 static_assert (is_convertible_v<ITEM_T, key_type> or is_convertible_v<ITEM_T, pair<key_type, mapped_type>> or
240 is_convertible_v<ITEM_T, Common::KeyValuePair<key_type, mapped_type>>);
241 if constexpr (convertible_to<const Association<KEY_TYPE, MAPPED_VALUE_TYPE>*, const ITERABLE_OF_KEY_OR_ADDABLE*>) {
242 if (this == &items) { // avoid modifying container while iterating over it
243 size_t result = this->size ();
244 RemoveAll ();
245 return result;
246 }
247 }
248 return RemoveAll (begin (items), end (items));
249 }
250 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
251 template <typename ITERATOR_OF_KEY_OR_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_KEY_OR_ADDABLE>> ITERATOR_OF_KEY_OR_ADDABLE2>
252 inline size_t Association<KEY_TYPE, MAPPED_VALUE_TYPE>::RemoveAll (ITERATOR_OF_KEY_OR_ADDABLE&& start, ITERATOR_OF_KEY_OR_ADDABLE2&& end)
253 {
254 using ITEM_T = iter_value_t<ITERATOR_OF_KEY_OR_ADDABLE>;
255 static_assert (is_convertible_v<ITEM_T, key_type> or is_convertible_v<ITEM_T, pair<key_type, mapped_type>> or
256 is_convertible_v<ITEM_T, Common::KeyValuePair<key_type, mapped_type>>);
257 size_t cnt{};
258 for (auto i = start; i != end; ++i) {
259 if constexpr (is_convertible_v<ITEM_T, key_type>) {
260 if (RemoveIf (*i)) {
261 ++cnt;
262 }
263 }
264 else if constexpr (is_convertible_v<ITEM_T, pair<key_type, mapped_type>>) {
265 if (RemoveIf (i->first)) {
266 ++cnt;
267 }
268 }
269 else if constexpr (is_convertible_v<ITEM_T, Common::KeyValuePair<key_type, mapped_type>>) {
270 if (RemoveIf (i->fKey)) {
271 ++cnt;
272 }
273 }
274 else {
276 }
277 }
278 return cnt;
279 }
280 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
281 template <predicate<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> PREDICATE>
283 {
284 size_t nRemoved{};
285 for (Iterator<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> i = this->begin (); i != this->end ();) {
286 if (p (*i)) {
287 Remove (i, &i);
288 ++nRemoved;
289 }
290 else {
291 ++i;
292 }
293 }
294 return nRemoved;
295 }
296 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
297 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Update (const Iterator<value_type>& i, ArgByValueType<mapped_type> newValue,
299 {
300 Require (not i.Done ());
301 auto [writerRep, patchedIterator] = _GetWritableRepAndPatchAssociatedIterator (i);
302 writerRep->Update (patchedIterator, newValue, nextI);
303 }
304 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
305 template <IIterableOfTo<KEY_TYPE> ITERABLE_OF_KEY_TYPE>
306 void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::RetainAll (const ITERABLE_OF_KEY_TYPE& items)
307 {
308 static_assert (is_convertible_v<ranges::range_value_t<ITERABLE_OF_KEY_TYPE>, key_type>);
309 /*
310 * If items is smaller than this->size(), probably best to just remove one or two things from this
311 * (small number of lookups, and maybe no changes).
312 *
313 * If items is large, probably best to just create the entire association anew (cuz probably
314 * producing much smaller container).
315 *
316 * And both approaches avoid needing to quickly search through items repeatedly (except
317 * when we know items is small so no cost).
318 */
319 size_t thisSize = this->size ();
320
321 // Small case - just remove a few items
322 bool fewerLookupsThanAdds = items.size () <= thisSize;
323
324 if (fewerLookupsThanAdds) {
325 auto keyEqualsComparer = this->GetKeyEqualsComparer ();
326 using ITEMS_ITER_TYPE = decltype (items.begin ()); // because of new ranges (c++20) code - sentinel but find_if only templated on one iter parameter
327 for (Iterator<value_type> i = this->begin (); i != this->end ();) {
328 if (find_if<ITEMS_ITER_TYPE> (items.begin (), items.end (),
329 [&] (const KEY_TYPE& k) { return keyEqualsComparer (i->fKey, k); }) == items.end ()) {
330#if qStroika_Foundation_Debug_AssertionsChecked
331 [[maybe_unused]] size_t sz = this->size ();
332#endif
333 i = this->erase (i);
334#if qStroika_Foundation_Debug_AssertionsChecked
335 Assert (this->size () == sz - 1u);
336#endif
337 }
338 else {
339 ++i;
340 }
341 }
342 }
343 else {
344 // just recreate the association
345 Association result = Association{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()};
346 size_t nAdds{0};
347 for (auto key2Keep : items) {
348 for (auto li : this->Lookup (key2Keep)) {
349 result.Add (key2Keep, li);
350 ++nAdds;
351 }
352 }
353 // if nothing changed, we can just make no changes
354 if (thisSize != nAdds) {
355 *this = result;
356 }
357 else {
358 Assert (*this == result); // so why assign?
359 }
360 }
361 }
362 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
363 template <typename RESULT_CONTAINER, invocable<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ELEMENT_MAPPER>
364 nonvirtual RESULT_CONTAINER Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Map (ELEMENT_MAPPER&& elementMapper) const
365 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>, typename RESULT_CONTAINER::value_type> or
366 convertible_to<invoke_result_t<ELEMENT_MAPPER, KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>, optional<typename RESULT_CONTAINER::value_type>>)
367 {
368 if constexpr (same_as<RESULT_CONTAINER, Association>) {
369 // clone the rep so we retain any ordering function/etc, rep type
370 return inherited::template Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper),
371 RESULT_CONTAINER{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()});
372 }
373 else {
374 return inherited::template Map<RESULT_CONTAINER> (forward<ELEMENT_MAPPER> (elementMapper)); // default Iterable<> implementation then...
375 }
376 }
377 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
378 template <derived_from<Iterable<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>> RESULT_CONTAINER, typename INCLUDE_PREDICATE>
379 inline auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Where (INCLUDE_PREDICATE&& includeIfTrue) const -> RESULT_CONTAINER
380 requires (predicate<INCLUDE_PREDICATE, KEY_TYPE> or predicate<INCLUDE_PREDICATE, KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>)
381 {
382 if constexpr (predicate<INCLUDE_PREDICATE, KEY_TYPE>) {
383 // recurse once with a KVP predicate
384 return Where<RESULT_CONTAINER> (
385 [=] (const ArgByValueType<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>>& kvp) { return includeIfTrue (kvp.fKey); });
386 }
387 else {
388 if constexpr (same_as<RESULT_CONTAINER, Association>) {
389 // clone the rep so we retain any ordering function/etc, rep type
390 return inherited::template Where<RESULT_CONTAINER> (
391 forward<INCLUDE_PREDICATE> (includeIfTrue), RESULT_CONTAINER{_SafeReadRepAccessor<_IRep>{this}._ConstGetRep ().CloneEmpty ()});
392 }
393 else {
394 return inherited::template Where<RESULT_CONTAINER> (forward<INCLUDE_PREDICATE> (includeIfTrue)); // default Iterable<> implementation then...
395 }
396 }
397 }
398 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
399 template <typename CONTAINER_OF_KEYS>
400 auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::WithKeys (const CONTAINER_OF_KEYS& includeKeys) const -> ArchetypeContainerType
401 {
402 return Where ([=] (const key_type& key) -> bool { return includeKeys.Contains (key); });
403 }
404 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
405 auto Association<KEY_TYPE, MAPPED_VALUE_TYPE>::WithKeys (const initializer_list<key_type>& includeKeys) const -> ArchetypeContainerType
406 {
407 Iterable<key_type> ik{includeKeys};
408 return inherited::Where ([=] (const ArgByValueType<value_type>& kvp) { return ik.Contains (kvp.fKey); }, ArchetypeContainerType{});
409 }
410 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
411 template <typename CONTAINER_OF_Key_T>
412 inline CONTAINER_OF_Key_T Association<KEY_TYPE, MAPPED_VALUE_TYPE>::As () const
413 {
414 CONTAINER_OF_Key_T result;
415 for (const auto& i : *this) {
416 if constexpr (is_convertible_v<typename CONTAINER_OF_Key_T::value_type, pair<KEY_TYPE, MAPPED_VALUE_TYPE>>) {
417 // the reason we use the overload with an extra result.end () here is so it will work with std::map<> or std::vector<>
418 result.insert (result.end (), pair<KEY_TYPE, MAPPED_VALUE_TYPE>{i.fKey, i.fValue});
419 }
420 else {
421 result.insert (result.end (), value_type{i.fKey, i.fValue});
422 }
423 }
424 return result;
425 }
426 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
427 inline void
428 Association<KEY_TYPE, MAPPED_VALUE_TYPE>::Accumulate (ArgByValueType<key_type> key, ArgByValueType<mapped_type> newValue,
429 const function<mapped_type (ArgByValueType<mapped_type>, ArgByValueType<mapped_type>)>& f,
430 mapped_type initialValue)
431 {
432 Add (key, f (LookupValue (key, initialValue), newValue));
433 }
434 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
435 inline void Association<KEY_TYPE, MAPPED_VALUE_TYPE>::erase (ArgByValueType<key_type> key)
436 {
437 Remove (key);
438 }
439 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
441 {
442 Iterator<value_type> nextI{nullptr};
443 Remove (i, &nextI);
444 return nextI;
445 }
446 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
448 {
449 RemoveAll ();
450 }
451 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
452 template <IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
454 {
456 result.AddAll (items);
457 return result;
458 }
459 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
460 template <IIterableOfTo<KeyValuePair<KEY_TYPE, MAPPED_VALUE_TYPE>> ITERABLE_OF_ADDABLE>
461 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>& Association<KEY_TYPE, MAPPED_VALUE_TYPE>::operator+= (const ITERABLE_OF_ADDABLE& items)
462 {
463 AddAll (items);
464 return *this;
465 }
466 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
467 template <typename ITERABLE_OF_KEY_OR_ADDABLE>
468 inline Association<KEY_TYPE, MAPPED_VALUE_TYPE>& Association<KEY_TYPE, MAPPED_VALUE_TYPE>::operator-= (const ITERABLE_OF_KEY_OR_ADDABLE& items)
469 {
470 RemoveAll (items);
471 return *this;
472 }
473 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
475 -> tuple<_IRep*, Iterator<value_type>>
476 {
477 Require (not i.Done ());
478 using element_type = typename inherited::_SharedByValueRepType::element_type;
479 Iterator<value_type> patchedIterator = i;
480 element_type* writableRep = this->_fRep.rwget ([&] (const element_type& prevRepPtr) -> typename inherited::_SharedByValueRepType::shared_ptr_type {
481 return Debug::UncheckedDynamicCast<const _IRep&> (prevRepPtr).CloneAndPatchIterator (&patchedIterator);
482 });
483 AssertNotNull (writableRep);
484 return make_tuple (Debug::UncheckedDynamicCast<_IRep*> (writableRep), move (patchedIterator));
485 }
486 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
488 {
490 [[maybe_unused]] _SafeReadRepAccessor<_IRep> ignored{this};
491 }
492 }
493 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
495 requires (equality_comparable<MAPPED_VALUE_TYPE>)
496 {
497 return EqualsComparer<>{}(*this, rhs);
498 }
499
500 /*
501 ********************************************************************************
502 ********** Association<KEY_TYPE, MAPPED_VALUE_TYPE>::EqualsComparer ************
503 ********************************************************************************
504 */
505 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
506 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (Common::IEqualsComparer<MAPPED_VALUE_TYPE>) VALUE_EQUALS_COMPARER>
507 constexpr Association<KEY_TYPE, MAPPED_VALUE_TYPE>::EqualsComparer<VALUE_EQUALS_COMPARER>::EqualsComparer (const VALUE_EQUALS_COMPARER& valueEqualsComparer)
508 : fValueEqualsComparer{valueEqualsComparer}
509 {
510 }
511 template <typename KEY_TYPE, typename MAPPED_VALUE_TYPE>
512 template <qCompilerAndStdLib_ConstraintDiffersInTemplateRedeclaration_BWA (Common::IEqualsComparer<MAPPED_VALUE_TYPE>) VALUE_EQUALS_COMPARER>
513 bool Association<KEY_TYPE, MAPPED_VALUE_TYPE>::EqualsComparer<VALUE_EQUALS_COMPARER>::operator() (const Association& lhs, const Association& rhs) const
514 {
515 /*
516 * @todo This code is not very efficient, except in the 'quickEqualsTest' case
517 */
518 _SafeReadRepAccessor<_IRep> lhsR{&lhs};
519 _SafeReadRepAccessor<_IRep> rhsR{&rhs};
520 if (&lhsR._ConstGetRep () == &rhsR._ConstGetRep ()) {
521 // not such an unlikely test result since we use lazy copy, but this test is only an optimization and not logically required
522 return true;
523 }
524 // 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
525 if (lhsR._ConstGetRep ().size () != rhsR._ConstGetRep ().size ()) {
526 return false;
527 }
528 /*
529 * Two Associations compare equal, if they have the same domain, and map each element of that domain to the same range.
530 * They need not be in the same order to compare equals. Still - they often are, and if they are, this algorithm is faster.
531 * If they miss, we need to fall back to a slower strategy.
532 */
533 auto quickEqualsTest = [&] () -> bool {
534 auto li = lhsR._ConstGetRep ().MakeIterator ();
535 auto ri = rhs.MakeIterator ();
536 auto keyEqualsComparer = lhs.GetKeyEqualsComparer (); // arbitrarily select left side key equals comparer
537 while (not li.Done ()) {
538 Assert (not ri.Done ()); // cuz move at same time and same size
539 bool keysEqual = keyEqualsComparer (li->fKey, ri->fKey);
540 Require (keysEqual == rhs.GetKeyEqualsComparer () (li->fKey, ri->fKey)); // if fails, cuz rhs/lhs keys equals comparers disagree
541 if (not keysEqual or not fValueEqualsComparer (li->fValue, ri->fValue)) {
542 return false;
543 }
544 // if we got this far, all compared OK so far, so keep going
545 ++li;
546 ++ri;
547 }
548 Assert (ri.Done ()); // cuz LHS done and both sides iterate at same pace, and we checked both same size
549 return true;
550 };
551 if (quickEqualsTest ()) {
552 return true;
553 }
554 // OK, we failed the quick test, but the associations might still be 'equal' - just in a funny order
555 // note this is extremely expensive. We should find a better algorithm...
556 auto keyEqualsComparer = lhs.GetKeyEqualsComparer (); // arbitrarily select left side key equals comparer
557 return lhs.MultiSetEquals (rhs, Common::DeclareEqualsComparer ([&] (const value_type& lhs, const value_type& rhs) {
558 return keyEqualsComparer (lhs.fKey, rhs.fKey) and fValueEqualsComparer (lhs.fKey, rhs.fKey);
559 }));
560 }
561
562}
#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
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