Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Collection.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Containers_Collection_h_
5#define _Stroika_Foundation_Containers_Collection_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
10#include "Stroika/Foundation/Common/Concepts.h"
11#include "Stroika/Foundation/Containers/Common.h"
14
15/**
16 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
17 *
18 * TODO:
19 * @todo Have Difference/Union/Intersection??? methods/?? Do research....
20 *
21 * @todo Consider adding RetainAll (Set<T>) API - like in Collection.h, and Java. Key diff is was force
22 * use of SET as arg - not another Bag? Or maybe overload with different container types as args?
23 * COULD do 2 versions - one with Iterable<T> and one with Set<T>. trick is to get definition
24 * to work without untoward dependencies between set and bag code? I think that means
25 * most of the check impl needs to be in the envelope to avoid always building it through vtables.
26 *
27 * @todo Perhaps add a Shake() method that produces a NEW Collection of a possibly different backend TYPE!
28 * with a random ordering.
29 *
30 * Or add Bag<> class that does basically this.
31 *
32 * @todo Document relationship with Java Collection<T> (http://docs.oracle.com/javase/7/docs/api/java/util/Collection.html)
33 * Similar but Stroika container interface/container connection, Equals handling, and other language specific
34 * issues. Java has more 'all' variants (and retainAll - see possible todo above). And java makes this
35 * strictly an interface - used by lots of other subtypes (like set<>) - which we may wish to do as
36 * well (not sure - sterl feels this is a very bad idea, and I'm ambivalent).
37 */
38
40
42 using Traversal::IInputIterator;
43 using Traversal::IIterableOfTo;
44 using Traversal::Iterable;
45 using Traversal::Iterator;
46
47 /**
48 * \brief A Collection<T> is a container to manage an un-ordered collection of items, without equality defined for T
49 *
50 * A Collection<T> is a container pattern to manage an un-ordered collection of items,
51 * without equality defined for T. This is both an abstract interface, but the Collection<T>
52 * class it actually concrete because it automatically binds to a default implementation.
53 *
54 * \note This means that the order in which items appear during iteration is <em>arbitrary</em> and
55 * may vary from collection to collection type.
56 *
57 * Some sub-types of Collection<T> - like SortedCollection<> - do make specific promises
58 * about ordering.
59 *
60 * A Collection<T> is the simplest kind of collection. It allows addition and
61 * removal of elements, but makes no guarantees about element ordering.
62 *
63 * \em Performance
64 * Collections are typically designed to optimize item addition and iteration.
65 * They are fairly slow at item access (as they have no keys). Removing items
66 * is usually slow, except in the context of an Iterator<T>, where it is usually
67 * very fast.
68 *
69 * \note See <a href="./ReadMe.md">ReadMe.md</a> for common features of all Stroika containers (especially
70 * constructors, iterators, etc)
71 *
72 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
73 *
74 * \em Concrete Implementations:
75 * o @see Concrete::Collection_Array<>
76 * o @see Concrete::Collection_LinkedList<>
77 * o @see Concrete::Collection_stdforward_list<>
78 * o @see Concrete::SortedCollection_stdmultiset<>
79 * o @see Concrete::SortedCollection_SkipList<>
80 *
81 * \note Shake
82 * Considered adding a Shake() method (when this was called Bag<T>), but that would restrict
83 * the use to backends capable of this randomizing of order (eg. not hash tables
84 * or trees), and is incompatible with the idea of subtypes like SortedCollection<T>. Also
85 * since a Collection<> intrinsically has no ordering, I'm not totally sure what it would
86 * mean to Shake/change its ordering?
87 *
88 * \note EQUALS_COMPARER (operator==, operator!=)
89 * We do not provide a notion of 'Equals' or operator==, operator!=, because
90 * its not clear how to compare collections (no ordering defined, sometimes sorted, no equals defined for ELTS etc)
91 *
92 * The caller may use the inherited (from Iterable<>) SetEquals, MultiSetEquals, or SequenceEquals()
93 * as appropriate. Methods that require and equals comparer, take one as argument with appropriate defaulting.
94 *
95 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
96 * static_assert (not equality_comparable<Collection<...>>);
97 *
98 * o No comparisons are provided, because there is no intrinsic way to compare collections for equality, less etc. (order not defined)
99 * See inherited Iterable<>::SequentialEquals, Iterable<>::MultiSetEquals, , Iterable<>::SetEquals.
100 */
101 template <typename T>
102 class [[nodiscard]] Collection : public Iterable<T> {
103 private:
104 using inherited = Iterable<T>;
105
106 public:
107 /**
108 * Use this typedef in templates to recover the basic functional container pattern of concrete types.
109 */
111
112 public:
113 /**
114 * @see inherited::value_type
115 */
117
118 protected:
119 class _IRep;
120
121 public:
122 /**
123 * For the CTOR overload with ITERABLE_OF_ADDABLE, its anything that supports c.begin(), c.end () to find
124 * all the elements, and which has elements (iterated) convertible to T.
125 *
126 * \par Example Usage
127 * \code
128 * Sequence<int> s;
129 * std::vector<int> v;
130 *
131 * Collection<int> c1 = {1, 2, 3};
132 * Collection<int> c2 = c1;
133 * Collection<int> c3{ c1 };
134 * Collection<int> c4{ c1.begin (), c1.end () };
135 * Collection<int> c5{ s };
136 * Collection<int> c6{ v };
137 * Collection<int> c7{ v.begin (), v.end () };
138 * Collection<int> c8{ move (c1) };
139 * \endcode
140 *
141 * \note Most other containers (e.g. Set<>, Sequence<>) have the 'ITERABLE_OF_ADDABLE&& src' CTOR be explicit, whereas Collection does not.
142 * This is because converting to a Set or Sequence has some semantics, and the programmer should be clear on this. But a Collection<>
143 * acts just like an Iterable<T> (except that its modifiable). So allow this case to be non-explicit.
144 *
145 * \note <a href="ReadMe.md#Container Constructors">See general information about container constructors that applies here</a>
146 */
147 Collection ();
148 Collection (Collection&&) noexcept = default;
149 Collection (const Collection&) noexcept = default;
150 Collection (const initializer_list<value_type>& src);
151 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
152 requires (not derived_from<remove_cvref_t<ITERABLE_OF_ADDABLE>, Collection<T>>)
153 Collection (ITERABLE_OF_ADDABLE&& src)
154#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
155 : Collection{}
156 {
157 AddAll (forward<ITERABLE_OF_ADDABLE> (src));
158 _AssertRepValidType ();
159 }
160#endif
161 ;
162
163 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
164 Collection (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end);
165
166 protected:
167 explicit Collection (shared_ptr<_IRep>&& src) noexcept;
168 explicit Collection (const shared_ptr<_IRep>& src) noexcept;
169
170 public:
171 nonvirtual Collection& operator= (Collection&&) noexcept = default;
172 nonvirtual Collection& operator= (const Collection&) = default;
173
174 public:
175 /**
176 * \brief Compares items with TRAITS::EqualsCompareFunctionType::Equals, and returns true if any match.
177 */
178 template <Common::IEqualsComparer<T> EQUALS_COMPARER = equal_to<T>>
179 nonvirtual bool Contains (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer = {}) const;
180
181 public:
182 /**
183 * Add the given item(s) to this Collection<T>. Note - if the given items are already present, another
184 * copy will be added. No promises are made about where the added value will appear in iteration.
185 *
186 * \note overload taking Iterator<T>* addedAt returns an iterator pointing at the added item.
187 *
188 * \note mutates container
189 */
190 nonvirtual void Add (ArgByValueType<value_type> item);
191 nonvirtual void Add (ArgByValueType<value_type> item, Iterator<T>* addedAt);
192
193 public:
194 /**
195 * @aliases AddAll/2 is alias for .net AddRange ()
196 *
197 * \note mutates container
198 */
199 template <IInputIterator<T> ITERATOR_OF_ADDABLE, sentinel_for<remove_cvref_t<ITERATOR_OF_ADDABLE>> ITERATOR_OF_ADDABLE2>
200 nonvirtual void AddAll (ITERATOR_OF_ADDABLE&& start, ITERATOR_OF_ADDABLE2&& end);
201 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE>
202 nonvirtual void AddAll (ITERABLE_OF_ADDABLE&& items);
203
204 public:
205 /**
206 * This function requires that the iterator 'i' came from this container.
207 *
208 * The value pointed to by 'i' is updated - replaced with the value 'newValue'.
209 *
210 * \note if you update the container, and wish to continue iterating, you must pass in an &i to nextI,
211 * and continue using that iterator (i is invalid after this call). It is left undefined, whether or not you
212 * will encounter the item 'i' again in iteration (for example, if the container is sorted, and you change
213 * the associated value to larger, you probably will encounter it again).
214 *
215 * \note - this nextI value is therefore of very little value, since you cannot reliably continue iterating with it, knowing
216 * what you will get next. @todo consider if this should be deprecated.
217 * http://stroika-bugs.sophists.com/browse/STK-922
218 *
219 * This is basically the same as {Remove (i); Add (newValue);} except that it might perform better.
220 *
221 * \note mutates container
222 */
223 nonvirtual void Update (const Iterator<value_type>& i, ArgByValueType<value_type> newValue, Iterator<value_type>* nextI = nullptr);
224
225 public:
226 /**
227 * \brief Remove () the argument value (which must exist)
228 *
229 * \see RemoveIf() to remove an item that may not exist
230 *
231 * This will reduce the size of the container by one.
232 *
233 * \param nextI - if provided (not null) - will be filled in with the next value after where iterator i
234 * is pointing - since i is invalidated by changing the container)
235 *
236 * \note mutates container
237 */
238 template <Common::IPotentiallyComparer<T> EQUALS_COMPARER = equal_to<T>>
239 nonvirtual void Remove (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer = {});
240 nonvirtual void Remove (const Iterator<value_type>& i, Iterator<value_type>* nextI = nullptr);
241
242 public:
243 /**
244 * \brief RemoveIf () the (the first matching) argument value, if present. Returns true if item removed.
245 *
246 * It is legal to remove something that is not there. This function removes the first instance of item
247 * (or each item for the 'items' overload), meaning that another instance of item could still be in the
248 * Collection<T> after the remove.
249 *
250 * The overload Remove(PREDICATE) applies the function p(T) -> bool and deletes the first entry (if any) that return true for the predicate.
251 *
252 * \note mutates container
253 */
254 template <Common::IEqualsComparer<T> EQUALS_COMPARER = equal_to<T>>
255 nonvirtual bool RemoveIf (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer = {});
256 template <predicate<T> PREDICATE>
257 nonvirtual bool RemoveIf (PREDICATE&& p);
258
259 public:
260 /**
261 * \brief RemoveAll removes all, or all matching (predicate, iterator range, equals comparer or whatever) items.
262 *
263 * The no-arg overload removes all (quickly).
264 *
265 * The overloads that remove some subset of the items returns the number of items so removed.
266 *
267 * The overload with Iterator<T> arguments (start/end) must be iterators from this container.
268 *
269 * \note mutates container
270 */
271 nonvirtual void RemoveAll ();
272 template <Common::IEqualsComparer<T> EQUALS_COMPARER = equal_to<T>>
273 nonvirtual size_t RemoveAll (const Iterator<value_type>& start, const Iterator<value_type>& end, EQUALS_COMPARER&& equalsComparer = {});
274 template <IIterableOfTo<T> ITERABLE_OF_ADDABLE, typename EQUALS_COMPARER = equal_to<T>>
275 nonvirtual size_t RemoveAll (const ITERABLE_OF_ADDABLE& c, EQUALS_COMPARER&& equalsComparer = {});
276 template <predicate<T> PREDICATE>
277 nonvirtual size_t RemoveAll (PREDICATE&& p);
278
279 public:
280 /**
281 * \brief 'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to Collection, and improve that case to clone properties from this rep (such is rep type, comparisons etc).
282 */
283 template <typename RESULT_CONTAINER = Collection<T>, invocable<T> ELEMENT_MAPPER>
284 nonvirtual RESULT_CONTAINER Map (ELEMENT_MAPPER&& elementMapper) const
285 requires (convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, typename RESULT_CONTAINER::value_type> or
286 convertible_to<invoke_result_t<ELEMENT_MAPPER, T>, optional<typename RESULT_CONTAINER::value_type>>);
287
288 public:
289 /**
290 * \brief return a subset of the Collection for which includeIfTrue returns true.
291 *
292 * Defaults to returning a Collection<>, but pass in Iterable<T> type argument to get a lazy-evaluated iterable result.
293 *
294 * \par Example Usage
295 * \code
296 * Collection<int> c { 1, 2, 3, 4, 5, 6 };
297 * EXPECT_TRUE (c.Where ([] (int i) { return i % 2 == 0; }).SetEquals (Iterable<int> { 2, 4, 6 }));
298 * EXPECT_TRUE (c.Where<Iterable<int>> ([] (int i) { return i % 2 == 0; }).SetEquals (Iterable<int> { 2, 4, 6 })); // to get lazy evaluation
299 * \endcode
300 *
301 */
302 template <derived_from<Iterable<T>> RESULT_CONTAINER = Collection<T>, predicate<T> INCLUDE_PREDICATE>
303 nonvirtual RESULT_CONTAINER Where (INCLUDE_PREDICATE&& includeIfTrue) const;
304
305 public:
306 /**
307 * @aliases RemoveAll ().
308 *
309 * \note mutates container
310 */
311 nonvirtual void clear ();
312
313 public:
314 /**
315 * @aliases Remove ().
316 *
317 * \note mutates container
318 */
319 template <Common::IEqualsComparer<T> EQUALS_COMPARER = equal_to<T>>
320 nonvirtual void erase (ArgByValueType<value_type> item, EQUALS_COMPARER&& equalsComparer = {});
321 nonvirtual Iterator<value_type> erase (const Iterator<value_type>& i);
322
323 public:
324 /**
325 * operator+ is syntactic sugar on Add() or AddAll() - depending on the overload.
326 *
327 * *DEVELOPER NOTE*
328 * Note - we use an overload
329 * of Collection<T> for the container case instead of a template, because I'm not sure how to use specializations
330 * to distinguish the two cases. If I can figure that out, this can transparently be
331 * replaced with operator+= (X), with appropriate specializations.
332 *
333 * \note mutates container
334 */
335 nonvirtual Collection& operator+= (ArgByValueType<value_type> item);
336 nonvirtual Collection& operator+= (const Iterable<value_type>& items);
337
338 protected:
339 /**
340 * \brief Utility to get WRITABLE underlying shared_ptr (replacement for what we normally do - _SafeReadWriteRepAccessor<_IRep>{this}._GetWriteableRep ())
341 * but where we also handle the cloning/patching of the associated iterator
342 *
343 * When you have a non-const operation (such as Remove) with an argument of an Iterator<>, then due to COW,
344 * you may end up cloning the container rep, and yet the Iterator<> contains a pointer to the earlier rep (and so maybe unusable).
345 *
346 * Prior to Stroika 2.1b14, this was handled elegantly, and automatically, by the iterator patching mechanism. But that was deprecated (due to cost, and
347 * rarity of use), in favor of this more restricted feature, where we just patch the iterators on an as-needed basis.
348 *
349 * \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)
350 */
351 nonvirtual tuple<_IRep*, Iterator<value_type>> _GetWritableRepAndPatchAssociatedIterator (const Iterator<value_type>& i);
352
353 protected:
354 /**
355 */
356 template <typename T2>
357 using _SafeReadRepAccessor = typename inherited::template _SafeReadRepAccessor<T2>;
358
359 protected:
360 /**
361 */
362 template <typename T2>
363 using _SafeReadWriteRepAccessor = typename inherited::template _SafeReadWriteRepAccessor<T2>;
364
365 protected:
366 nonvirtual void _AssertRepValidType () const;
367 };
368
369 /**
370 * \brief Implementation detail for Collection<T> implementors.
371 *
372 * Protected abstract interface to support concrete implementations of
373 * the Collection<T> container API.
374 */
375 template <typename T>
376 class Collection<T>::_IRep : public Iterable<T>::_IRep {
377 private:
378 using inherited = typename Iterable<T>::_IRep;
379
380 protected:
381 _IRep () = default;
382
383 public:
384 virtual ~_IRep () = default;
385
386 public:
387 virtual shared_ptr<_IRep> CloneEmpty () const = 0;
388 virtual shared_ptr<_IRep> CloneAndPatchIterator (Iterator<value_type>* i) const = 0;
389 // if oAddedI != nullptr, on output, its filled in with iterator pointing to added item
390 virtual void Add (ArgByValueType<value_type> item, Iterator<value_type>* oAddedI) = 0;
391 virtual void Update (const Iterator<value_type>& i, ArgByValueType<T> newValue, Iterator<value_type>* nextI) = 0;
392 virtual void Remove (const Iterator<value_type>& i, Iterator<value_type>* nextI) = 0;
393 };
394
395 /**
396 * Basic operator overload with the obvious meaning (Collection<T> copy and Collection<T>::AddAll)
397 */
398 template <typename T>
399 Collection<T> operator+ (const Iterable<T>& lhs, const Collection<T>& rhs);
400 template <typename T>
401 Collection<T> operator+ (const Collection<T>& lhs, const Iterable<T>& rhs);
402 template <typename T>
403 Collection<T> operator+ (const Collection<T>& lhs, const Collection<T>& rhs);
404
405}
406
407/*
408 ********************************************************************************
409 ***************************** Implementation Details ***************************
410 ********************************************************************************
411 */
412#include "Collection.inl"
413
414#endif /*_Stroika_Foundation_Containers_Collection_h_ */
Implementation detail for Collection<T> implementors.
Definition Collection.h:376
A Collection<T> is a container to manage an un-ordered collection of items, without equality defined ...
Definition Collection.h:102
nonvirtual void AddAll(ITERATOR_OF_ADDABLE &&start, ITERATOR_OF_ADDABLE2 &&end)
typename inherited::value_type value_type
Definition Collection.h:116
nonvirtual void Update(const Iterator< value_type > &i, ArgByValueType< value_type > newValue, Iterator< value_type > *nextI=nullptr)
nonvirtual void Remove(ArgByValueType< value_type > item, EQUALS_COMPARER &&equalsComparer={})
Remove () the argument value (which must exist)
nonvirtual RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
'override' Iterable<>::Map () function so RESULT_CONTAINER defaults to Collection,...
nonvirtual void erase(ArgByValueType< value_type > item, EQUALS_COMPARER &&equalsComparer={})
nonvirtual bool RemoveIf(ArgByValueType< value_type > item, EQUALS_COMPARER &&equalsComparer={})
RemoveIf () the (the first matching) argument value, if present. Returns true if item removed.
nonvirtual RESULT_CONTAINER Where(INCLUDE_PREDICATE &&includeIfTrue) const
return a subset of the Collection for which includeIfTrue returns true.
nonvirtual void Add(ArgByValueType< value_type > item)
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
Collection< T > operator+(const Iterable< T > &lhs, const Collection< T > &rhs)