Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
Compare.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Common_Compare_h_
5#define _Stroika_Foundation_Common_Compare_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <compare>
10#include <functional>
11#include <memory>
12#include <optional>
13#include <type_traits>
14
15#include "Stroika/Foundation/Common/Common.h"
16#include "Stroika/Foundation/Common/Concepts.h"
20
21/**
22 * \file
23 *
24 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
25 */
26
28
29 /**
30 *
31 * Comparison logic:
32 *
33 * \par Total Ordering (http://mathworld.wolfram.com/TotallyOrderedSet.html)
34 * o Reflexivity: a <= a
35 * o Antisymmetry: a <= b and b <= a implies a=b
36 * o Transitivity: a <= b and b <= c implies a <= c
37 * o Comparability: either a <= b or b <= a
38 *
39 * Our comparisons ALL require Comparability; and all the <,<=,>,>= comparisons require Transitivity
40 *
41 * Types of Comparers:
42 * <, <=, ==, etc, are functions from SxS => bool. They are also called relations (where the
43 * relationship need not be comparable). But in our domain, they are all comparable, so all comparers
44 * are function<bool(T,T)> - at least logically.
45 *
46 * Most of Stroika's containers concern themselves with == testing. The value of == can always be derived from
47 * !=, <=, <, >, >=, though not necessarily very efficiently (the greater/less than ones translate one call to two).
48 *
49 * A frequently more efficient strategy is compare(T,T)-> int (<0 is <, ==0, means equals, >0 means >).
50 * This strategy was widely used in the "C" world (e.g. strcmp, quicksort, etc).
51 *
52 * It also appears to be making a comeback for C++20 (http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0515r0.pdf)
53 *
54 * std-c++ favors a handful of these predefined functions
55 * > less
56 * > equal_to (to a lesser degree)
57 * but also provides
58 * > greater
59 * > less_equal
60 * > greater_equal
61 * > not_equal_to
62 *
63 * c++ also appears to support this three-way-compare approach rarely (eg. string<>::compare).
64 *
65 * The biggest problem with how std-c++ supports these comparison operators, is that typeid(equal_to<int>) is
66 * essentially unrelated to typeid(equal_to<char>). There is no 'tag' (as with bidirectional iterators etc) to
67 * identify different classes of comparison and so no easy to to leverage the natural relationships between
68 * equal_to and less_equal.
69 *
70 * THIS is what drives how we do containers/related algorithms (less is equiv to greater for most of them)
71 */
73 eEquals,
74
75 /**
76 * e.g. less<T>, or greater<T>
77 *
78 * From http://mathworld.wolfram.com/StrictOrder.html
79 * A relation < is a strict order on a set S if it is
80 * 1. Irreflexive: a<a does not hold for any a in S.
81 * 2. Asymmetric: if a<b, then b<a does not hold.
82 * 3. Transitive: a<b and b<c implies a<c.
83 */
85
86 /**
87 * \brief <=, or >=, e.g. less_equal<T>, or greater_equal<T>
88 *
89 * \note considered the name 'partial' order here but that could be confusing, since partial order frequently
90 * refers to not covering the entire domain - not less vs. less_equal.
91 */
93
94 /**
95 * e.g. function<int(T,T)> - where < 0 return is 'in order' (eStrictInOrder), 0 means equal, and > 0 means reversed order
96 */
98
100 };
101
102 namespace Private_ {
103 template <typename ARG_T, typename COMPARE_FUNCTION>
104 struct ExtractComparisonTraits_ {};
105 template <typename ARG_T, typename COMPARE_FUNCTION>
106 requires requires (COMPARE_FUNCTION) {
107 { COMPARE_FUNCTION::kComparisonRelationKind } -> convertible_to<ComparisonRelationType>;
108 }
109 struct ExtractComparisonTraits_<ARG_T, COMPARE_FUNCTION> {
110 static constexpr ComparisonRelationType kComparisonRelationKind = COMPARE_FUNCTION::kComparisonRelationKind;
111 };
112 template <typename ARG_T, typename COMPARE_FUNCTION>
113 requires (
114 requires (COMPARE_FUNCTION c, ARG_T l, ARG_T r) {
115 { c (l, r) } -> convertible_to<strong_ordering>;
116 } and
117 not requires (COMPARE_FUNCTION) {
118 { COMPARE_FUNCTION::kComparisonRelationKind } -> convertible_to<ComparisonRelationType>;
119 })
120 struct ExtractComparisonTraits_<ARG_T, COMPARE_FUNCTION> {
121 static constexpr ComparisonRelationType kComparisonRelationKind = ComparisonRelationType::eThreeWayCompare;
122 };
123 template <typename ARG_T>
124 struct ExtractComparisonTraits_<ARG_T, equal_to<ARG_T>> {
125 static constexpr ComparisonRelationType kComparisonRelationKind = ComparisonRelationType::eEquals;
126 };
127 template <typename ARG_T>
128 struct ExtractComparisonTraits_<ARG_T, less<ARG_T>> {
129 static constexpr ComparisonRelationType kComparisonRelationKind = ComparisonRelationType::eStrictInOrder;
130 };
131 template <typename ARG_T>
132 struct ExtractComparisonTraits_<ARG_T, greater<ARG_T>> {
133 static constexpr ARG_T kComparisonRelationKind = ComparisonRelationType::eStrictInOrder;
134 };
135 template <typename ARG_T>
136 struct ExtractComparisonTraits_<ARG_T, less_equal<ARG_T>> {
137 static constexpr ComparisonRelationType kComparisonRelationKind = ComparisonRelationType::eInOrderOrEquals;
138 };
139 template <typename ARG_T>
140 struct ExtractComparisonTraits_<ARG_T, greater_equal<ARG_T>> {
141 static constexpr ComparisonRelationType kComparisonRelationKind = ComparisonRelationType::eInOrderOrEquals;
142 };
143 template <typename ARG_T>
144 struct ExtractComparisonTraits_<ARG_T, compare_three_way> {
145 static constexpr ComparisonRelationType kComparisonRelationKind = ComparisonRelationType::eThreeWayCompare;
146 };
147 }
148
149 /**
150 * This concept checks if the given function argument (COMPARER) appears to compare 'ARG_T's and return true/false.
151 * This doesn't require that that you've annotated the comparer, so it can false-positive (like mixing up
152 * an equality comparer for an in-order comparer).
153 *
154 * \see IComparer or IEqualsComparer for something stricter
155 */
156 template <typename COMPARER, typename ARG_T>
157 concept IPotentiallyComparer = relation<COMPARER, ARG_T, ARG_T> or (same_as<COMPARER, compare_three_way> and three_way_comparable<ARG_T>) or
158 (regular_invocable<COMPARER, ARG_T, ARG_T> and requires (COMPARER c, ARG_T l, ARG_T r) {
159 { c (l, r) } -> convertible_to<strong_ordering>;
160 });
161
162 /**
163 * Concept IComparer checks if the argument is a (declared comparison type) Stroika comparer object.
164 *
165 * Basically, this means we KNOW if its a LESS or EQUALS etc comparer (see ExtractComparisonTraits_v).
166 *
167 * \note Any function object (eg lambda) taking 2 ARG_T arguments and returning ComparisonRelationType works automatically).
168 */
169 template <typename POTENTIALLY_COMPARER, typename ARG_T>
170 concept IComparer = requires (POTENTIALLY_COMPARER, ARG_T) {
171 {
172 Private_::ExtractComparisonTraits_<ARG_T, remove_cvref_t<POTENTIALLY_COMPARER>>::kComparisonRelationKind
173 } -> convertible_to<ComparisonRelationType>;
174 };
175
176 /**
177 * \brief ExtractComparisonTraits_v<> extracts the @ComparisonRelationType for the given argument comparer.
178 *
179 * For common builtin types this is known with no user effort. For user-defined comparers, this will need to be declared (e.g. via ComparisonRelationDeclarationBase)
180 *
181 * This is ONLY defined for builtin c++ comparison objects, though your code can define it however you wish for
182 * specific user-defined types using ComparisonRelationDeclarationBase<>.
183 */
184 template <typename ARG_T, IComparer<ARG_T> COMPARE_FUNCTION>
186 Private_::ExtractComparisonTraits_<ARG_T, remove_cvref_t<COMPARE_FUNCTION>>::kComparisonRelationKind;
187
188 /**
189 * Checks that the argument comparer compares values of type ARG_T, and returns an equals comparison result.
190 *
191 * This won't let confuse equal_to with actual in-order comparison functions.
192 *
193 * \see IPotentiallyComparer, and use DeclareEqualsComparer to mark a given function as an in-order comparer.
194 *
195 * \par Example Usage
196 * \code
197 * static_assert (IEqualsComparer<equal_to<int>, int>);
198 * static_assert (not IEqualsComparer<less<int>, int>);
199 * \endcode
200 *
201 * \par Example Usage
202 * \code
203 * template <IEqualsComparer<KEY_TYPE> KEY_EQUALS_COMPARER = equal_to<KEY_TYPE>>
204 * KeyedCollection (KEY_EQUALS_COMPARER&& keyComparer = KEY_EQUALS_COMPARER{})
205 * ...
206 * \endcode
207 *
208 * \note see ComparisonRelationDeclarationBase<ComparisonRelationType::eEquals> to make a comparer into an 'equals' comparer
209 */
210 template <typename COMPARER, typename ARG_T>
212 ExtractComparisonTraits_v<ARG_T, remove_cvref_t<COMPARER>> == ComparisonRelationType::eEquals;
213
214 /**
215 * Checks that the argument comparer compares values of type ARG_T, and returns a (strict) in-order comparison result.
216 *
217 * This won't let confuse equal_to with actual in-order comparison functions.
218 *
219 * \see IPotentiallyComparer, and use DeclareInOrderComparer to mark a given function as an in-order comparer.
220 * \see also InOrderComparerAdapter (if you have another sort of comparer, and want to use it as an In-Order comparer).
221 * \see ITotallyOrderingComparer
222 */
223 template <typename COMPARER, typename ARG_T>
225 ExtractComparisonTraits_v<ARG_T, remove_cvref_t<COMPARER>> == ComparisonRelationType::eStrictInOrder;
226
227 /**
228 * Checks that the argument comparer compares values of type ARG_T, and returns a three-way-compare comparison result.
229 *
230 * This won't let confuse equal_to with actual in-order comparison functions.
231 *
232 * \see IPotentiallyComparer, and use DeclareInOrderComparer to mark a given function as a three-way comparer.
233 * \see ITotallyOrderingComparer
234 */
235 template <typename COMPARER, typename ARG_T>
237 ExtractComparisonTraits_v<ARG_T, remove_cvref_t<COMPARER>> == ComparisonRelationType::eThreeWayCompare;
238
239 /**
240 * Checks that the argument comparer can be converted (via ThreeWayComparerAdapter) to three_way comparer (on type T).
241 *
242 * \see IInOrderComparer, IThreeWayComparer, and ThreeWayComparerAdapter
243 *
244 * \see https://en.wikipedia.org/wiki/Total_order#:~:text=A%20set%20equipped%20with%20a,a%20given%20partially%20ordered%20set.
245 *
246 * @aliases Could have been called IThreeWayAdaptableComparer
247 *
248 * \note this will NOT work with an equals_to<T> comparer! - as that's not sufficient to generate a total ordering...
249 *
250 * \note as of v3.0d10 - ITotallyOrderingComparer = IInOrderComparer or IThreeWayComparer, but this will be extended
251 * to include other sorts of comparisons (e.g. less_or_equal) - so long as ThreeWayAdaptableComparer is suitably updated.
252 */
253 template <typename COMPARER, typename ARG_T>
255
256 /**
257 * Utility class to serve as base class when constructing a comparison 'function' object comparer so ExtractComparisonTraits<> knows
258 * the type, or (with just one argument) as base for class that itself provides the operator() method.
259 *
260 * \par Example Usage
261 * \code
262 * struct String::EqualsComparer : Common::ComparisonRelationDeclarationBase<Common::ComparisonRelationType::eEquals> {
263 * nonvirtual bool operator() (const String& lhs, const String& rhs) const;
264 * };
265 * \endcode
266 */
267 template <ComparisonRelationType KIND>
269 static constexpr inline ComparisonRelationType kComparisonRelationKind{KIND}; // accessed by ExtractComparisonTraits<>
270 };
271
272 /**
273 * Utility class to combine a (comparison) function object with ComparisonRelationDeclaration, which marks it as being
274 * of a particular comparison relation kind (e.g equality vs. less than).
275 *
276 * \par Example Usage
277 * \code
278 * using KeyEqualsCompareFunctionType =
279 * ComparisonRelationDeclaration<ComparisonRelationType::eEquals, function<bool (key_type, key_type)>>
280 * ;
281 * \endcode
282 *
283 * @see DeclareEqualsComparer
284 * @see DeclareInOrderComparer
285 */
286 template <ComparisonRelationType KIND, typename ACTUAL_COMPARER>
287 requires (not is_reference_v<ACTUAL_COMPARER>)
289 public:
290 /**
291 */
292 constexpr ComparisonRelationDeclaration () = default;
293 template <typename... ARGS>
294 constexpr ComparisonRelationDeclaration (ARGS... args)
295#if !qCompilerAndStdLib_template_Requires_constraint_not_treated_constexpr_Buggy
296 requires (constructible_from<ACTUAL_COMPARER, ARGS...>)
297#endif
298 ;
300 constexpr ComparisonRelationDeclaration (ComparisonRelationDeclaration&&) noexcept = default;
301
302 constexpr ComparisonRelationDeclaration& operator= (const ComparisonRelationDeclaration&) = default;
303 constexpr ComparisonRelationDeclaration& operator= (ComparisonRelationDeclaration&&) noexcept = default;
304 };
305
306 /**
307 * \brief DeclareEqualsComparer () marks a FUNCTOR (lambda or not) as being a FUNCTOR which compares for equality
308 *
309 * DeclareEqualsComparer is a trivial wrapper on ComparisonRelationDeclaration, but takes advantage of the fact that you
310 * can deduce types on functions arguments not not on type of object for constructor (at least as of C++17).
311 *
312 * @see DeclareInOrderComparer
313 * @see EqualsComparerAdapter
314 *
315 * \note similar to InOrderComparerAdapter(), except this function ignores the TYEP of 'f' and just marks it as an InOrder comparer
316 * Whereas InOrderComparerAdapter looks at the type of 'f' and does the appropriate mapping logic.
317 */
318 template <typename FUNCTOR>
319 constexpr Common::ComparisonRelationDeclaration<ComparisonRelationType::eEquals, remove_cvref_t<FUNCTOR>> DeclareEqualsComparer (FUNCTOR&& f);
320
321 /**
322 * \brief DeclareInOrderComparer () marks a FUNCTOR (lambda or not) as being a FUNCTOR which compares for in-order
323 *
324 * DeclareInOrderComparer is a trivial wrapper on ComparisonRelationDeclaration, but takes advantage of the fact that you
325 * can deduce types on functions arguments not not on type of object for constructor (at least as of C++17).
326 *
327 * \par Example Usage:
328 * \code
329 * constexpr auto kDefaultPrioritizedName_OrderByDefault_Less =
330 * Stroika::Foundation::Common::DeclareInOrderComparer ([] (const PrioritizedName& lhs, const PrioritizedName& rhs) -> bool {
331 * if (lhs.fPriority > rhs.fPriority) {
332 * return true;
333 * }
334 * else if (lhs.fPriority < rhs.fPriority) {
335 * return false;
336 * }
337 * return lhs.fName < rhs.fName;
338 * });
339 * \endcode
340 *
341 * @see DeclareEqualsComparer
342 * @see InOrderComparerAdapter
343 *
344 * \note similar to InOrderComparerAdapter(), except this function ignores the TYEP of 'f' and just marks it as an InOrder comparer
345 * Whereas InOrderComparerAdapter looks at the type of 'f' and does the appropriate mapping logic.
346 */
347 template <typename FUNCTOR>
348 constexpr Common::ComparisonRelationDeclaration<ComparisonRelationType::eStrictInOrder, remove_cvref_t<FUNCTOR>> DeclareInOrderComparer (FUNCTOR&& f);
349
350 /**
351 * \brief Use this to wrap any basic comparer, and produce an Equals comparer.
352 *
353 * This is done by querying the 'type' of the baseComparer with @see ExtractComparisonTraits_v, and mapping the logic accordingly.
354 *
355 * \note attempted deduction guide, but so far doesn't seem to work well, possibly because FunctionTraits may have trouble
356 * sometimes deducing argument type (like if function overloaded).
357 */
358 template <typename ARG_T, IComparer<ARG_T> BASE_COMPARER>
360 /**
361 */
362 constexpr EqualsComparerAdapter (const BASE_COMPARER& baseComparer);
363 constexpr EqualsComparerAdapter (BASE_COMPARER&& baseComparer);
364
365 /**
366 */
367 template <typename LT, typename RT>
368 constexpr bool operator() (LT&& lhs, RT&& rhs) const;
369
370 private:
371 qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE BASE_COMPARER fBASE_COMPARER_;
372 };
373 template <typename BASE_COMPARER>
374 EqualsComparerAdapter (BASE_COMPARER bc)
376
377 /**
378 * \brief Use this to wrap any basic comparer, and produce a Less comparer
379 *
380 * \note this requires the argument comparer is eStrictInOrder, eInOrderOrEquals, or eThreeWayCompare
381 *
382 * \note attempted deduction guide, but so far doesn't seem to work well, possibly because FunctionTraits may have trouble
383 * sometimes deducing argument type (like if function overloaded).
384 */
385 template <typename T, IComparer<T> BASE_COMPARER>
386 requires (ExtractComparisonTraits_v<T, BASE_COMPARER> == ComparisonRelationType::eStrictInOrder or
387 ExtractComparisonTraits_v<T, BASE_COMPARER> == ComparisonRelationType::eInOrderOrEquals or
388 ExtractComparisonTraits_v<T, BASE_COMPARER> == ComparisonRelationType::eThreeWayCompare)
390 /**
391 */
392 constexpr InOrderComparerAdapter (const BASE_COMPARER& baseComparer);
393 constexpr InOrderComparerAdapter (BASE_COMPARER&& baseComparer);
394
395 /**
396 */
397 template <typename LT, typename RT>
398 constexpr bool operator() (LT&& lhs, RT&& rhs) const;
399
400 private:
401 qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE BASE_COMPARER fBASE_COMPARER_;
402 };
403 template <typename BASE_COMPARER>
404 InOrderComparerAdapter (BASE_COMPARER bc)
406
407 /**
408 * \brief Use this to wrap a basic (ITotallyOrderingComparer) comparer, and produce a Three-Way comparer
409 *
410 * \note attempted deduction guide, but so far doesn't seem to work well, possibly because FunctionTraits may have trouble
411 * sometimes deducing argument type (like if function overloaded).
412 *
413 * \@todo extend set of comparers ITotallyOrderingComparer - easy - to include less_or_equal, etc...
414 * \note this will NOT work with an equals_to<T> comparer! - as that's not sufficient to generate a total ordering...
415 */
416 template <typename T, ITotallyOrderingComparer<T> BASE_COMPARER>
417 struct ThreeWayComparerAdapter : ComparisonRelationDeclarationBase<ComparisonRelationType::eThreeWayCompare> {
418 /**
419 */
420 constexpr ThreeWayComparerAdapter (const BASE_COMPARER& baseComparer);
421 constexpr ThreeWayComparerAdapter (BASE_COMPARER&& baseComparer);
422
423 /**
424 */
425 template <typename LT, typename RT>
426 constexpr strong_ordering operator() (LT&& lhs, RT&& rhs) const;
427
428 private:
429 qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE BASE_COMPARER fBASE_COMPARER_;
430 };
431 template <typename BASE_COMPARER>
432 ThreeWayComparerAdapter (BASE_COMPARER bc)
434
435 /**
436 * \brief ThreeWayComparer for optional types, like builtin one, except this lets you pass in explicit 'T' comparer for the T in optional<T>
437 *
438 * You dont need this when the default comparer for 'T' works as you wish. But for example, ThreeWayComparer<optional<String>> - where you want
439 * to use a case insensitive comparer for the strings, is tricky. THIS class solves that, by letting you pass in explicitly the
440 * 'base comparer'.
441 */
442 template <typename ARG_T, IComparer<ARG_T> TCOMPARER = std::compare_three_way>
443 struct OptionalThreeWayComparer : ComparisonRelationDeclarationBase<ComparisonRelationType::eThreeWayCompare> {
444 constexpr OptionalThreeWayComparer (TCOMPARER&& tComparer);
445 constexpr OptionalThreeWayComparer (const TCOMPARER& tComparer);
446 constexpr strong_ordering operator() (const optional<ARG_T>& lhs, const optional<ARG_T>& rhs) const;
447
448 private:
450 };
451
452 /**
453 * Take the given value and map it to -1, 0, 1 - without any compiler warnings. Handy for 32/64 bit etc coding when you maybe comparing
454 * different sized values and just returning an int, but don't want the warnings about overflow etc.
455 */
456 template <typename FROM_INT_TYPE>
457 constexpr strong_ordering CompareResultNormalizer (FROM_INT_TYPE f);
458
459 /**
460 */
461 constexpr int ToInt (strong_ordering f)
462 {
463 if (f == strong_ordering::less) {
464 return -1;
465 }
466 else if (f == strong_ordering::equal or f == strong_ordering::equivalent) {
467 return 0;
468 }
469 else {
470 return 1;
471 }
472 }
473
474 /**
475 * Map equals to equals, but less becomes greater and greater becomes less
476 */
477 constexpr strong_ordering ReverseCompareOrder (strong_ordering so);
478
479}
480
481/*
482 ********************************************************************************
483 ***************************** Implementation Details ***************************
484 ********************************************************************************
485 */
486#include "Compare.inl"
487
488#endif /*_Stroika_Foundation_Common_Compare_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
#define qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:445
@ eInOrderOrEquals
<=, or >=, e.g. less_equal<T>, or greater_equal<T>
constexpr Common::ComparisonRelationDeclaration< ComparisonRelationType::eStrictInOrder, remove_cvref_t< FUNCTOR > > DeclareInOrderComparer(FUNCTOR &&f)
DeclareInOrderComparer () marks a FUNCTOR (lambda or not) as being a FUNCTOR which compares for in-or...
Definition Compare.inl:44
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
constexpr strong_ordering CompareResultNormalizer(FROM_INT_TYPE f)
Definition Compare.inl:226
constexpr strong_ordering ReverseCompareOrder(strong_ordering so)
Definition Compare.inl:242
static constexpr ComparisonRelationType ExtractComparisonTraits_v
ExtractComparisonTraits_v<> extracts the @ComparisonRelationType for the given argument comparer.
Definition Compare.h:185
Use this to wrap any basic comparer, and produce an Equals comparer.
Definition Compare.h:359
Use this to wrap any basic comparer, and produce a Less comparer.
Definition Compare.h:389
ThreeWayComparer for optional types, like builtin one, except this lets you pass in explicit 'T' comp...
Definition Compare.h:443
Use this to wrap a basic (ITotallyOrderingComparer) comparer, and produce a Three-Way comparer.
Definition Compare.h:417