Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
KeyedCollection.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Containers_KeyedCollection_h_
5#define _Stroika_Foundation_Containers_KeyedCollection_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include "Stroika/Foundation/Common/Common.h"
11#include "Stroika/Foundation/Common/Concepts.h"
13
14/*
15 * \file
16 *
17 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
18 *
19 */
20
22
24 using Common::IEqualsComparer;
25 using Traversal::IInputIterator;
26 using Traversal::IIterableOfTo;
27 using Traversal::Iterable;
28 using Traversal::Iterator;
29
30 /**
31 */
32 template <typename POTENTIAL_KEY_EXTRACTOR, typename T, typename KEY_TYPE>
33 concept IKeyedCollection_KeyExctractor =
34 is_invocable_v<POTENTIAL_KEY_EXTRACTOR, T> and is_convertible_v<std::invoke_result_t<POTENTIAL_KEY_EXTRACTOR, T>, KEY_TYPE>;
35
36 /**
37 * \note KEY_EXTRACTOR_TYPE defaults in such a way that you can specify a function (std::function or lambda) in the constructor.
38 * Specify a specific function object to allow for slightly more efficient, but less flexible use.
39 *
40 * \par Example Usage
41 * \code
42 * namespace Private_ {
43 * struct Obj_ {
44 * type_index fTypeIndex; // KEY
45 * int otherData{}; //
46 * };
47 * using My_Extractor_ = decltype ([] (const Obj_& t) -> type_index { return t.fTypeIndex; });
48 * using My_Traits_ = Containers::KeyedCollection_DefaultTraits<Obj_, type_index, My_Extractor_>;
49 * }
50 * void RunAll ()
51 * {
52 * using namespace Private_;
53 * {
54 * KeyedCollection<Obj_, type_index, My_Traits_> s2;
55 * s2.Add (Obj_{typeid (int)});
56 * s2.Add (Obj_{typeid (long int)});
57 * }
58 * {
59 * // Or slightly more flexibly, but less efficiently
60 * KeyedCollection<Obj_, type_index> s2{My_Extractor_{}};
61 * s2.Add (Obj_{typeid (int)});
62 * s2.Add (Obj_{typeid (long int)});
63 * }
64 * }
65 * \endcode
66 */
67 template <typename T, typename KEY_TYPE, typename KEY_EXTRACTOR_TYPE = function<KEY_TYPE (ArgByValueType<T>)>>
69 /**
70 * Default extractor if not specified in constructor (e.g. default-constructor KeyedCollection())
71 */
72 using KeyExtractorType = KEY_EXTRACTOR_TYPE;
73 };
74
75 /**
76 * KeyedCollection can be templated with a KeyExtractorType that allows it to be used with default construction
77 * and no key extractor specfied.
78 *
79 * But the default definition - using std::function - requires the constructor to provide an extractor function
80 * since the default for this std::function is not callable.
81 */
82 template <typename T, typename KEY_TYPE, typename TRAITS>
84 is_invocable_v<typename TRAITS::KeyExtractorType, T> and
85 std::is_convertible_v<std::invoke_result_t<typename TRAITS::KeyExtractorType, T>, KEY_TYPE> and
86 default_initializable<typename TRAITS::KeyExtractorType> and not same_as<typename TRAITS::KeyExtractorType, function<KEY_TYPE (T)>> and
87 not same_as<typename TRAITS::KeyExtractorType, function<KEY_TYPE (const T&)>>;
88
89 /**
90 * \brief a cross between Mapping<KEY, T> and Collection<T> and Set<T>
91 *
92 * KeyedCollection adds most access patterns used in Mapping to a Collection, but stores only a single
93 * object. The idea is to have the ID (from a Mapping<ID,Obj>) stored directly in the 'Obj', but still
94 * be able to do lookups based on the ID.
95 *
96 * This can also be thought of as much like a Set<T> - where the element T has a known KEY field.
97 * You could easily make a Set<T> like this, but then lookups would require you passed in a 'T' instead
98 * of just the Key. That lookup by key is basically the 'feature' that KeyedCollection provides.
99 *
100 * \note Considered using Set<T> (to avoid using this concept) with helper mkComparerByIDExtract.
101 * auto Set<T>::mkComparerByIDExtract (const function<ID (T)>& idExtractor) -> ElementEqualityComparerType
102 * {
103 * return [idExtractor] (const T& lhs, const T& rhs) { return idExtractor (lhs) == idExtractor (rhs); };
104 * }
105 * This is close (for adding/removing items works fine). But - you need to be able to lookup items by
106 * ID, and that required forcing the type T to be constructible just from an ID (awkward and potentially
107 * expensive).
108 *
109 * \note you can only Add a T/value_type, not a KEY_TYPE, but 'lookup-style' operations can operate on KEY_TYPE.
110 *
111 * \note Not based on, but useful to refer to .Net KeyedCollection<KEY,T>
112 * @see https://msdn.microsoft.com/en-us/library/ms132438%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
113 *
114 * \note Important that KeyedCollection<>::Add () will REPLACE the value, unlike with set.
115 *
116 * \em Concrete Implementations:
117 * o @see Concrete::KeyedCollection_Array<>
118 * o @see Concrete::KeyedCollection_LinkedList<>
119 * o @see Concrete::KeyedCollection_stdhashset<>
120 * o @see Concrete::SortedKeyedCollection_stdset<>
121 * o @see Concrete::SortedKeyedCollection_SkipList<>
122 *
123 * \em Factory:
124 * @see Concrete::KeyedCollection_Factory<> to see default implementations.
125 *
126 * \note <a href="ReadMe.md#Container Element comparisons">Container Element comparisons</a>:
127 * See about ElementInOrderComparerType, ElementThreeWayComparerType and GetElementThreeWayComparer etc
128 *
129 * \note Design Choice:
130 * Could either embed the 'extractor' function in the container type TRAITS or passed in as a
131 * constructor argument to the container.
132 * As-part-of-TRAITS: Logically cannot change for a given container type, so unambiguous how stuff pulled out of T (extractor etc)
133 * THis makes the most sense, but its inconvenient in usage.
134 * As constructor argument to container:
135 * This might appear slightly more loosie goosy - but no more so than having different comparers for Set<T>. And its more
136 * convenient in use/practice.
137 *
138 * \note See <a href="./ReadMe.md">ReadMe.md</a> for common features of all Stroika containers (especially
139 * constructors, iterators, etc)
140 *
141 * TODO:
142 * @todo Consider adding methods from Set<T>, like Union, Intersection, Difference etc. Or way to create
143 * Set easily (maybe more logical).
144 */
145 template <typename T, typename KEY_TYPE, typename TRAITS = KeyedCollection_DefaultTraits<T, KEY_TYPE>>
146 class [[nodiscard]] KeyedCollection : public Iterable<T> {
147
148 private:
149 using inherited = Iterable<T>;
150
151 protected:
152 class _IRep;
153
154 public:
155 /**
156 * @see inherited::value_type - this is 'T' (not KEY_TYPE)
157 */
159
160 public:
161 /**
162 * Just a short-hand for the 'TRAITS' part of KeyedCollection<T,KEY_TYPE,TRAITS>. This is often handy to use in
163 * building other templates.
164 */
165 using TraitsType = TRAITS;
166
167 public:
168 /**
169 * Use this typedef in templates to recover the basic functional container pattern of concrete types.
170 */
172
173 public:
174 /**
175 */
176 using KeyType = KEY_TYPE;
177
178 public:
179 /**
180 */
181 using key_type = KEY_TYPE;
182
183 public:
184 /**
185 * This is the type of the 'extractor' function, which can be static (so occupy no space) but defaults to
186 * function<KEY_TYPE (ArgByValueType<T>)>> - specified in the TRAITS argument to KeyedCollection<>
187 */
188 using KeyExtractorType = typename TRAITS::KeyExtractorType;
189 static_assert (IKeyedCollection_KeyExctractor<KeyExtractorType, T, KEY_TYPE>);
190
191 public:
192 /**
193 * \note @see also EqualsComparer{} to compare whole KeyedCollection<>s
194 */
196 Common::ComparisonRelationDeclaration<Common::ComparisonRelationType::eEquals, function<bool (ArgByValueType<KEY_TYPE>, ArgByValueType<KEY_TYPE>)>>;
197 static_assert (IEqualsComparer<KeyEqualityComparerType, key_type>);
198
199 public:
200 /**
201 * For the CTOR overload with ITERABLE_OF_ADDABLE, its anything that supports c.begin(), c.end () to find
202 * all the elements and for which the result of c.begin ().
203 *
204 * All constructors either copy their source comparer (copy/move CTOR), or use the provided argument comparer
205 * (which in turn defaults to equal_to<T>).
206 *
207 * If TRAITS (TraitsType) has a valid default extractor, enable certain constructors.
208 *
209 * \note For efficiency sake, the base constructor takes a templated EQUALS_COMPARER (avoiding translation to function<bool(T,T)>>
210 * so the REP can see the actual type, but the container API itself erases this specific type using std::function.
211 *
212 * \pre EqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER - for constructors with that type parameter
213 *
214 * \par Example Usage
215 * \code
216 * struct T1 {
217 * int key;
218 * int value;
219 * };
220 * struct T1_Key_Extractor {
221 * int operator() (const T1& t) const { return t.key; };
222 * };
223 * using T1_Traits = KeyedCollection_DefaultTraits<T1, int, T1_Key_Extractor>;
224 *
225 * KeyedCollection<T1, int> kc1 {[] (T1 e) { return e.key; }}; // specify extractor explicitly
226 * KeyedCollection<T1, int, T1_Traits> kc2 {[] (T1 e) { return e.key; }};
227 * KeyedCollection<T1, int, T1_Traits> kc3{}; // get the extractor from the TRAITS
228 * \endcode
229 *
230 * \note The constructor argument KEY_EQUALS_COMPARER must be declared to be an equals_comparers to avoid
231 * ambiguity/accidental mixups between inorder and equals (or three way) comparers. Consider wrapping lambdas with Common::DeclareEqualsComparer
232 *
233 * \par Example Usage
234 * \code
235 * KeyedCollection<DiskInfoType, filesystem::path> result{[] (DiskInfoType e) { return e.fDeviceName; }};
236 * \endcode
237 *
238 * \note <a href="ReadMe.md#Container Constructors">See general information about container constructors that applies here</a>
239 */
240 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER = equal_to<KEY_TYPE>>
241 KeyedCollection (KEY_EQUALS_COMPARER&& keyComparer = KEY_EQUALS_COMPARER{})
243 KeyedCollection (KeyedCollection&&) noexcept = default;
244 KeyedCollection (const KeyedCollection&) noexcept = default;
245 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER = equal_to<KEY_TYPE>>
246 KeyedCollection (const KeyExtractorType& keyExtractor, KEY_EQUALS_COMPARER&& keyComparer = KEY_EQUALS_COMPARER{});
247 KeyedCollection (const initializer_list<value_type>& src);
248 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE, IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER = equal_to<KEY_TYPE>>
249 KeyedCollection (ITERABLE_OF_ADDABLE&& src)
250 requires (IKeyedCollection_ExtractorCanBeDefaulted<T, KEY_TYPE, TRAITS> and
251 not derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, KeyedCollection<T, KEY_TYPE, TRAITS>>)
252#if qCompilerAndStdLib_template_Requires_templateDeclarationMatchesOutOfLine2_Buggy
253 : KeyedCollection{}
254 {
255 AddAll (forward<ITERABLE_OF_ADDABLE> (src));
256 _AssertRepValidType ();
257 }
258#endif
259 ;
260 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE, IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER = equal_to<KEY_TYPE>>
261 KeyedCollection (KEY_EQUALS_COMPARER&& keyComparer, ITERABLE_OF_ADDABLE&& src)
262 requires (IKeyedCollection_ExtractorCanBeDefaulted<T, KEY_TYPE, TRAITS> and
263 not derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, KeyedCollection<T, KEY_TYPE, TRAITS>>)
264#if qCompilerAndStdLib_template_Requires_templateDeclarationMatchesOutOfLine2_Buggy
265 : KeyedCollection{KeyExtractorType{}, forward<KEY_EQUALS_COMPARER> (keyComparer)}
266 {
267 AddAll (src);
268 _AssertRepValidType ();
269 }
270#endif
271 ;
272 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER, IIterableOfTo<T> ITERABLE_OF_ADDABLE>
273 KeyedCollection (const KeyExtractorType& keyExtractor, KEY_EQUALS_COMPARER&& keyComparer, ITERABLE_OF_ADDABLE&& src);
274 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
275 KeyedCollection (const KeyExtractorType& keyExtractor, ITERABLE_OF_ADDABLE&& src)
276 requires (IEqualsComparer<KEY_TYPE, equal_to<KEY_TYPE>>);
277 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2,
278 IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER = equal_to<KEY_TYPE>>
279 KeyedCollection (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
280 requires (IKeyedCollection_ExtractorCanBeDefaulted<T, KEY_TYPE, TRAITS>);
281 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2,
282 IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER = equal_to<KEY_TYPE>>
283 KeyedCollection (KEY_EQUALS_COMPARER&& keyComparer, ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end)
284 requires (IKeyedCollection_ExtractorCanBeDefaulted<T, KEY_TYPE, TRAITS>);
285 template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER, IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
286 KeyedCollection (const KeyExtractorType& keyExtractor, KEY_EQUALS_COMPARER&& keyComparer, ITERATOR_OF_ADDABLE&& start,
287 ITERATOR_OF_ADDABLE2&& end);
288
289 protected:
290 explicit KeyedCollection (shared_ptr<_IRep>&& rep) noexcept;
291 explicit KeyedCollection (const shared_ptr<_IRep>& rep) noexcept;
292
293 public:
294 /**
295 */
296 nonvirtual KeyedCollection& operator= (KeyedCollection&&) = default;
297 nonvirtual KeyedCollection& operator= (const KeyedCollection&) = default;
298
299 public:
300 /**
301 * Returns the extractor.
302 */
303 nonvirtual KeyExtractorType GetKeyExtractor () const;
304
305 public:
306 /**
307 * Returns the key equality comparer (but wrapped as a function<> object, so perhaps slightly less efficient
308 * to use than the type passed in via CTOR). Actually - could be MUCH less efficient if what was passed in was less<T> for example,
309 * an ordered comparer, and that gets mapped to an equality comparer. But this will work.
310 */
311 nonvirtual KeyEqualityComparerType GetKeyEqualityComparer () const;
312
313 public:
314 /**
315 * Keys () returns an Iterable object with just the key part of the KeyedCollection.
316 *
317 * Note this method may not return a collection which is sorted. Note also, the
318 * returned value is a copy of the keys (by value) - at least logically (implementations
319 * maybe smart enough to use lazy copying).
320 *
321 * \em Design Note:
322 * The analogous method in C#.net - Dictionary<TKey, TValue>.KeyCollection
323 * (http://msdn.microsoft.com/en-us/library/yt2fy5zk(v=vs.110).aspx) returns a live reference
324 * to the underlying keys. We could have (fairly easily) done that, but I didn't see the point.
325 *
326 * In .net, the typical model is that you have a pointer to an object, and pass around that
327 * pointer (so by reference semantics) - so this returning a live reference makes more sense there.
328 *
329 * Since Stroika containers are logically copy-by-value (even though lazy-copied), it made more
330 * sense to apply that lazy-copy (copy-on-write) paradigm here, and make the returned set of
331 * keys a logical copy at the point 'keys' is called.
332 *
333 * See:
334 * @see Values ()
335 */
336 nonvirtual Iterable<KeyType> Keys () const;
337
338 public:
339 /**
340 * \note Contains (T) is same as Contains (KeyExtractor(t)) - it only looks at the key, not the rest of the value
341 */
342 nonvirtual bool Contains (ArgByValueType<KeyType> item) const;
343 nonvirtual bool Contains (ArgByValueType<value_type> item) const;
344
345 public:
346 /**
347 * Like Contains - but a Set<> can use a comparison that only examines a part of T,
348 * making it useful to be able to return the rest of T.
349 */
350 nonvirtual optional<value_type> Lookup (ArgByValueType<KeyType> key) const;
351 nonvirtual bool Lookup (ArgByValueType<KeyType> key, optional<value_type>* item) const;
352 nonvirtual bool Lookup (ArgByValueType<KeyType> key, value_type* item) const;
353 nonvirtual bool Lookup (ArgByValueType<KeyType> key, nullptr_t) const;
354
355 public:
356 /**
357 * @aliases LookupOrException
358 */
359 template <typename THROW_IF_MISSING>
360 nonvirtual value_type LookupChecked (ArgByValueType<KeyType> key, THROW_IF_MISSING&& throwIfMissing) const;
361
362 public:
363 /**
364 * Always safe to call. If result of Lookup () !has_value, returns argument 'default' or 'sentinel' value.
365 *
366 * @aliases LookupOrDefault
367 */
368 nonvirtual value_type LookupValue (ArgByValueType<KeyType> key, ArgByValueType<value_type> defaultValue = value_type{}) const;
369
370 public:
371 /**
372 * Add when T is already present has may have no effect (logically has no effect) on the
373 * container (not an error or exception) (except that if T contains fields not part of comparison, those will be updated).
374 *
375 * So for a user-defined type T (or any type where the caller specifies the compare function)
376 * it is left undefined whether or not the new (not included) attributes associated with the added
377 * item make it into the Set.
378 *
379 * return true if new item, and false if simply updated
380 *
381 * \note - because Add and Update logic are identical, KeyedCollection<> has no explicit Update (iterator) method.
382 * it COULD someday add an overload with optional Iterator<> hint argument, like in STL
383 *
384 * \note mutates container
385 */
386 nonvirtual bool Add (ArgByValueType<value_type> item);
387
388 public:
389 /**
390 * \note AddAll/2 is alias for .net AddRange ()
391 *
392 * Returns the number if items actually added (not necessarily same as end-start)
393 *
394 * \note mutates container
395 */
396 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
397 nonvirtual unsigned int AddAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end);
398 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
399 nonvirtual unsigned int AddAll (ITERABLE_OF_ADDABLE&& items);
400
401 public:
402 /**
403 * \brief Remove the argument value (which must exist) from the KeyedCollection.
404 *
405 * @see RemoveIf () for when the argument might not exist
406 *
407 * \note mutates container
408 *
409 * \param nextI - if provided (not null) - will be filled in with the next value after where iterator i is pointing - since i is invalidated by changing the container)
410 */
411 nonvirtual void Remove (const Iterator<value_type>& i, const Iterator<value_type>* nextI = nullptr);
412 nonvirtual void Remove (ArgByValueType<KeyType> item);
413 nonvirtual void Remove (ArgByValueType<value_type> item);
414
415 public:
416 /**
417 * RemoveIf item if present. Whether present or not it does the same thing and
418 * assures the item is no longer present, but returns true iff the call made a change (removed
419 * the item).
420 *
421 * Note - we chose to return true in the case of removal because this is the case most likely
422 * when a caller would want to take action.
423 *
424 * \par Example Usage
425 * \code
426 * if (s.RemoveIf (n)) {
427 * write_to_disk(n);
428 * }
429 * \endcode
430 *
431 * @see Remove ()
432 *
433 * \note mutates container
434 */
435 nonvirtual bool RemoveIf (ArgByValueType<KeyType> item);
436 nonvirtual bool RemoveIf (ArgByValueType<value_type> item);
437
438 public:
439 /**
440 * \brief RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items.
441 *
442 * The no-arg overload removes all (quickly).
443 *
444 * The overloads that remove some subset of the items returns the number of items so removed.
445 *
446 * \note mutates container
447 */
448 template <IInputIterator<T> ITERATOR_OF_ADDABLE>
449 nonvirtual size_t RemoveAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE&& end);
450 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
451 nonvirtual size_t RemoveAll (const ITERABLE_OF_ADDABLE& s);
452 nonvirtual void RemoveAll ();
453 template <predicate<T> PREDICATE>
454 nonvirtual size_t RemoveAll (PREDICATE&& p);
455
456 public:
457 /**
458 * \brief 'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to KeyedCollection, and improve that case to clone properties from this rep (such is rep type, extractor etc).
459 */
460 template <typename RESULT_CONTAINER = KeyedCollection<T, KEY_TYPE, TRAITS>, invocable<T> ELEMENT_MAPPER>
461 nonvirtual RESULT_CONTAINER Map (ELEMENT_MAPPER&& elementMapper) const
462 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, typename RESULT_CONTAINER::value_type> or
463 convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, optional<typename RESULT_CONTAINER::value_type>>);
464
465 public:
466 /**
467 * Apply the function function to each element, and return all the ones for which it was true.
468 *
469 * @aliases Subset - as it constructs a subset.
470 *
471 * @see Iterable<T>::Where
472 */
473 template <derived_from<Iterable<T>> RESULT_CONTAINER = KeyedCollection<T, KEY_TYPE, TRAITS>, predicate<T> INCLUDE_PREDICATE>
474 nonvirtual RESULT_CONTAINER Where (INCLUDE_PREDICATE&& includeIfTrue) const;
475
476 public:
477 struct EqualsComparer;
478
479 public:
480 /**
481 * Simply indirect to KeyedCollection<>::EqualsComparer
482 */
483 nonvirtual bool operator== (const KeyedCollection& rhs) const;
484 nonvirtual bool operator== (const Iterable<value_type>& rhs) const;
485
486 public:
487 /**
488 * Synonym for Add/AddAll.
489 *
490 * Design note use Addll/RemoveAll() for CONTAINER variant - since can easily lead to ambiguity/confusion
491 *
492 * \note mutates container
493 */
494 nonvirtual KeyedCollection& operator+= (ArgByValueType<value_type> item);
495 nonvirtual KeyedCollection& operator+= (const Iterable<value_type>& items);
496
497 public:
498 /**
499 * Synonym for Remove/RemoveAll.
500 *
501 * Design note use Addll/RemoveAll() for CONTAINER variant - since can easily lead to ambiguity/confusion
502 *
503 * \note mutates container
504 */
505 nonvirtual KeyedCollection& operator-= (ArgByValueType<value_type> item);
506 nonvirtual KeyedCollection& operator-= (const Iterable<value_type>& items);
507
508 public:
509 /**
510 * @aliases RemoveAll ().
511 *
512 * \note mutates container
513 */
514 nonvirtual void clear ();
515
516 public:
517 /**
518 * @aliases Remove ().
519 *
520 * \note mutates container
521 */
522 nonvirtual void erase (ArgByValueType<value_type> item);
523 nonvirtual Iterator<value_type> erase (const Iterator<value_type>& i);
524
525 protected:
526 /**
527 * \brief Utility to get WRITABLE underlying shared_ptr (replacement for what we normally do - _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ())
528 * but where we also handle the cloning/patching of the associated iterator
529 *
530 * When you have a non-const operation (such as Remove) with an argument of an Iterator<>, then due to COW,
531 * you may end up cloning the container rep, and yet the Iterator<> contains a pointer to the earlier rep (and so maybe unusable).
532 *
533 * Prior to Stroika 2.1b14, this was handled elegantly, and automatically, by the iterator patching mechanism. But that was deprecated (due to cost, and
534 * rarity of use), in favor of this more restricted feature, where we just patch the iterators on an as-needed basis.
535 *
536 * \todo @todo - could be smarter about moves and avoid some copies here - I think, and this maybe performance sensitive enough to look into that... (esp for COMMON case where no COW needed)
537 */
538 nonvirtual tuple<_IRep*, Iterator<value_type>> _GetWritableRepAndPatchAssociatedIterator (const Iterator<value_type>& i);
539
540 protected:
541 /**
542 */
543 template <typename T2>
544 using _SafeReadRepAccessor = typename inherited::template _SafeReadRepAccessor<T2>;
545
546 protected:
547 /**
548 */
549 template <typename T2>
550 using _SafeReadWriteRepAccessor = typename inherited::template _SafeReadWriteRepAccessor<T2>;
551
552 protected:
553 nonvirtual void _AssertRepValidType () const;
554 };
555
556 /**
557 * \brief Implementation detail for KeyedCollection<KEY_TYPE, T, TRAITS> implementors.
558 *
559 * Protected abstract interface to support concrete implementations of
560 * the KeyedCollection<KEY_TYPE, T, TRAITS> container API.
561 */
562 template <typename T, typename KEY_TYPE, typename TRAITS>
563 class KeyedCollection<T, KEY_TYPE, TRAITS>::_IRep : public Iterable<T>::_IRep {
564
565 private:
566 using inherited = typename Iterable<T>::_IRep;
567
568 public:
569 virtual KeyExtractorType GetKeyExtractor () const = 0;
571 virtual shared_ptr<_IRep> CloneEmpty () const = 0;
572 virtual shared_ptr<_IRep> CloneAndPatchIterator (Iterator<value_type>* i) const = 0;
573 // always clear/set item, and ensure return value == item->IsValidItem());
574 // 'item' arg CAN be nullptr
575 virtual bool Lookup (ArgByValueType<KeyType> key, optional<value_type>* item) const = 0;
576 // return true if new item, and false if simply updated
577 virtual bool Add (ArgByValueType<value_type> item) = 0;
578 virtual void Remove (const Iterator<value_type>& i, Iterator<value_type>* nextI) = 0;
579 // returns true iff a change made, false if elt was not present
580 virtual bool RemoveIf (ArgByValueType<KEY_TYPE> key) = 0;
581 };
582
583 /**
584 * logically indirect to @Set<KeyType>::EqualsComparer (using this->GetEqualsComparer ()) to compare the keys
585 * Order is meaningless in comparing KeyedCollection - this compares just by Set equality of the keys.
586 */
587 template <typename T, typename KEY_TYPE, typename TRAITS>
588 struct KeyedCollection<T, KEY_TYPE, TRAITS>::EqualsComparer
589 : Common::ComparisonRelationDeclarationBase<Common::ComparisonRelationType::eEquals> {
590 constexpr EqualsComparer () = default;
591 nonvirtual bool operator() (const KeyedCollection& lhs, const KeyedCollection& rhs) const;
592 nonvirtual bool operator() (const KeyedCollection& lhs, const Iterable<value_type>& rhs) const;
593 nonvirtual bool operator() (const Iterable<value_type>& lhs, const KeyedCollection& rhs) const;
594 };
595
596}
597
598/*
599 ********************************************************************************
600 ******************************* Implementation Details *************************
601 ********************************************************************************
602 */
603#include "KeyedCollection.inl"
604
605#endif /*_Stroika_Foundation_Containers_KeyedCollection_h_ */
Implementation detail for KeyedCollection<KEY_TYPE, T, TRAITS> implementors.
a cross between Mapping<KEY, T> and Collection<T> and Set<T>
nonvirtual bool Add(ArgByValueType< value_type > item)
nonvirtual KeyExtractorType GetKeyExtractor() const
nonvirtual bool RemoveIf(ArgByValueType< KeyType > item)
nonvirtual RESULT_CONTAINER Where(INCLUDE_PREDICATE &&includeIfTrue) const
nonvirtual size_t RemoveAll(ITERATOR_OF_ADDABLE &&start, ITERATOR_OF_ADDABLE &&end)
RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items...
nonvirtual KeyEqualityComparerType GetKeyEqualityComparer() const
typename TRAITS::KeyExtractorType KeyExtractorType
nonvirtual void Remove(const Iterator< value_type > &i, const Iterator< value_type > *nextI=nullptr)
Remove the argument value (which must exist) from the KeyedCollection.
nonvirtual unsigned int AddAll(ITERATOR_OF_ADDABLE &&start, ITERATOR_OF_ADDABLE2 &&end)
nonvirtual optional< value_type > Lookup(ArgByValueType< KeyType > key) const
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to KeyedCollection,...
nonvirtual value_type LookupChecked(ArgByValueType< KeyType > key, THROW_IF_MISSING &&throwIfMissing) const
Implementation detail for iterator implementors.
Definition Iterable.h:1569
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
T value_type
value_type is an alias for the type iterated over - like vector<T>::value_type
Definition Iterable.h:248
An Iterator<T> is a copyable object which allows traversing the contents of some container....
Definition Iterator.h:225
conditional_t<(sizeof(CHECK_T)<=2 *sizeof(void *)) and is_trivially_copyable_v< CHECK_T >, CHECK_T, const CHECK_T & > ArgByValueType
This is an alias for 'T' - but how we want to pass it on stack as formal parameter.
Definition TypeHints.h:32