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