Stroika Library 3.0d16
 
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 template <typename COMPARER, typename ARG_T>
209 ExtractComparisonTraits_v<ARG_T, remove_cvref_t<COMPARER>> == ComparisonRelationType::eEquals;
210
211 /**
212 * Checks that the argument comparer compares values of type ARG_T, and returns a (strict) in-order comparison result.
213 *
214 * This won't let confuse equal_to with actual in-order comparison functions.
215 *
216 * \see IPotentiallyComparer, and use DeclareInOrderComparer to mark a given function as an in-order comparer.
217 * \see also InOrderComparerAdapter (if you have another sort of comparer, and want to use it as an In-Order comparer).
218 * \see ITotallyOrderingComparer
219 */
220 template <typename COMPARER, typename ARG_T>
222 ExtractComparisonTraits_v<ARG_T, remove_cvref_t<COMPARER>> == ComparisonRelationType::eStrictInOrder;
223
224 /**
225 * Checks that the argument comparer compares values of type ARG_T, and returns a three-way-compare comparison result.
226 *
227 * This won't let confuse equal_to with actual in-order comparison functions.
228 *
229 * \see IPotentiallyComparer, and use DeclareInOrderComparer to mark a given function as a three-way comparer.
230 * \see ITotallyOrderingComparer
231 */
232 template <typename COMPARER, typename ARG_T>
234 ExtractComparisonTraits_v<ARG_T, remove_cvref_t<COMPARER>> == ComparisonRelationType::eThreeWayCompare;
235
236 /**
237 * Checks that the argument comparer can be converted (via ThreeWayComparerAdapter) to three_way comparer (on type T).
238 *
239 * \see IInOrderComparer, IThreeWayComparer, and ThreeWayComparerAdapter
240 *
241 * \see https://en.wikipedia.org/wiki/Total_order#:~:text=A%20set%20equipped%20with%20a,a%20given%20partially%20ordered%20set.
242 *
243 * @aliases Could have been called IThreeWayAdaptableComparer
244 *
245 * \note this will NOT work with an equals_to<T> comparer! - as that's not sufficient to generate a total ordering...
246 *
247 * \note as of v3.0d10 - ITotallyOrderingComparer = IInOrderComparer or IThreeWayComparer, but this will be extended
248 * to include other sorts of comparisons (e.g. less_or_equal) - so long as ThreeWayAdaptableComparer is suitably updated.
249 */
250 template <typename COMPARER, typename ARG_T>
252
253 /**
254 * Utility class to serve as base class when constructing a comparison 'function' object comparer so ExtractComparisonTraits<> knows
255 * the type, or (with just one argument) as base for class that itself provides the operator() method.
256 *
257 * \par Example Usage
258 * \code
259 * struct String::EqualsComparer : Common::ComparisonRelationDeclarationBase<Common::ComparisonRelationType::eEquals> {
260 * nonvirtual bool operator() (const String& lhs, const String& rhs) const;
261 * };
262 * \endcode
263 */
264 template <ComparisonRelationType KIND>
266 static constexpr inline ComparisonRelationType kComparisonRelationKind{KIND}; // accessed by ExtractComparisonTraits<>
267 };
268
269 /**
270 * Utility class to combine a (comparison) function object with ComparisonRelationDeclaration, which marks it as being
271 * of a particular comparison relation kind (e.g equality vs. less than).
272 *
273 * \par Example Usage
274 * \code
275 * using KeyEqualsCompareFunctionType =
276 * ComparisonRelationDeclaration<ComparisonRelationType::eEquals, function<bool (key_type, key_type)>>
277 * ;
278 * \endcode
279 *
280 * @see DeclareEqualsComparer
281 * @see DeclareInOrderComparer
282 */
283 template <ComparisonRelationType KIND, typename ACTUAL_COMPARER>
284 requires (not is_reference_v<ACTUAL_COMPARER>)
286 public:
287 /**
288 */
289 constexpr ComparisonRelationDeclaration () = default;
290 template <typename... ARGS>
291 constexpr ComparisonRelationDeclaration (ARGS... args)
292#if !qCompilerAndStdLib_template_Requires_constraint_not_treated_constexpr_Buggy
293 requires (constructible_from<ACTUAL_COMPARER, ARGS...>)
294#endif
295 ;
297 constexpr ComparisonRelationDeclaration (ComparisonRelationDeclaration&&) noexcept = default;
298
299 constexpr ComparisonRelationDeclaration& operator= (const ComparisonRelationDeclaration&) = default;
300 constexpr ComparisonRelationDeclaration& operator= (ComparisonRelationDeclaration&&) noexcept = default;
301 };
302
303 /**
304 * \brief DeclareEqualsComparer () marks a FUNCTOR (lambda or not) as being a FUNCTOR which compares for equality
305 *
306 * DeclareEqualsComparer is a trivial wrapper on ComparisonRelationDeclaration, but takes advantage of the fact that you
307 * can deduce types on functions arguments not not on type of object for constructor (at least as of C++17).
308 *
309 * @see DeclareInOrderComparer
310 * @see EqualsComparerAdapter
311 *
312 * \note similar to InOrderComparerAdapter(), except this function ignores the TYEP of 'f' and just marks it as an InOrder comparer
313 * Whereas InOrderComparerAdapter looks at the type of 'f' and does the appropriate mapping logic.
314 */
315 template <typename FUNCTOR>
316 constexpr Common::ComparisonRelationDeclaration<ComparisonRelationType::eEquals, remove_cvref_t<FUNCTOR>> DeclareEqualsComparer (FUNCTOR&& f);
317
318 /**
319 * \brief DeclareInOrderComparer () marks a FUNCTOR (lambda or not) as being a FUNCTOR which compares for in-order
320 *
321 * DeclareInOrderComparer is a trivial wrapper on ComparisonRelationDeclaration, but takes advantage of the fact that you
322 * can deduce types on functions arguments not not on type of object for constructor (at least as of C++17).
323 *
324 * \par Example Usage:
325 * \code
326 * constexpr auto kDefaultPrioritizedName_OrderByDefault_Less =
327 * Stroika::Foundation::Common::DeclareInOrderComparer ([] (const PrioritizedName& lhs, const PrioritizedName& rhs) -> bool {
328 * if (lhs.fPriority > rhs.fPriority) {
329 * return true;
330 * }
331 * else if (lhs.fPriority < rhs.fPriority) {
332 * return false;
333 * }
334 * return lhs.fName < rhs.fName;
335 * });
336 * \endcode
337 *
338 * @see DeclareEqualsComparer
339 * @see InOrderComparerAdapter
340 *
341 * \note similar to InOrderComparerAdapter(), except this function ignores the TYEP of 'f' and just marks it as an InOrder comparer
342 * Whereas InOrderComparerAdapter looks at the type of 'f' and does the appropriate mapping logic.
343 */
344 template <typename FUNCTOR>
345 constexpr Common::ComparisonRelationDeclaration<ComparisonRelationType::eStrictInOrder, remove_cvref_t<FUNCTOR>> DeclareInOrderComparer (FUNCTOR&& f);
346
347 /**
348 * \brief Use this to wrap any basic comparer, and produce an Equals comparer.
349 *
350 * This is done by querying the 'type' of the baseComparer with @see ExtractComparisonTraits_v, and mapping the logic accordingly.
351 *
352 * \note attempted deduction guide, but so far doesn't seem to work well, possibly because FunctionTraits may have trouble
353 * sometimes deducing argument type (like if function overloaded).
354 */
355 template <typename ARG_T, IComparer<ARG_T> BASE_COMPARER>
357 /**
358 */
359 constexpr EqualsComparerAdapter (const BASE_COMPARER& baseComparer);
360 constexpr EqualsComparerAdapter (BASE_COMPARER&& baseComparer);
361
362 /**
363 */
364 template <typename LT, typename RT>
365 constexpr bool operator() (LT&& lhs, RT&& rhs) const;
366
367 private:
368 [[no_unique_address]] BASE_COMPARER fBASE_COMPARER_;
369 };
370 template <typename BASE_COMPARER>
371 EqualsComparerAdapter (BASE_COMPARER bc)
373
374 /**
375 * \brief Use this to wrap any basic comparer, and produce a Less comparer
376 *
377 * \note this requires the argument comparer is eStrictInOrder, eInOrderOrEquals, or eThreeWayCompare
378 *
379 * \note attempted deduction guide, but so far doesn't seem to work well, possibly because FunctionTraits may have trouble
380 * sometimes deducing argument type (like if function overloaded).
381 */
382 template <typename T, IComparer<T> BASE_COMPARER>
383 requires (ExtractComparisonTraits_v<T, BASE_COMPARER> == ComparisonRelationType::eStrictInOrder or
384 ExtractComparisonTraits_v<T, BASE_COMPARER> == ComparisonRelationType::eInOrderOrEquals or
385 ExtractComparisonTraits_v<T, BASE_COMPARER> == ComparisonRelationType::eThreeWayCompare)
387 /**
388 */
389 constexpr InOrderComparerAdapter (const BASE_COMPARER& baseComparer);
390 constexpr InOrderComparerAdapter (BASE_COMPARER&& baseComparer);
391
392 /**
393 */
394 template <typename LT, typename RT>
395 constexpr bool operator() (LT&& lhs, RT&& rhs) const;
396
397 private:
398 [[no_unique_address]] BASE_COMPARER fBASE_COMPARER_;
399 };
400 template <typename BASE_COMPARER>
401 InOrderComparerAdapter (BASE_COMPARER bc)
403
404 /**
405 * \brief Use this to wrap a basic (ITotallyOrderingComparer) comparer, and produce a Three-Way comparer
406 *
407 * \note attempted deduction guide, but so far doesn't seem to work well, possibly because FunctionTraits may have trouble
408 * sometimes deducing argument type (like if function overloaded).
409 *
410 * \@todo extend set of comparers ITotallyOrderingComparer - easy - to include less_or_equal, etc...
411 * \note this will NOT work with an equals_to<T> comparer! - as that's not sufficient to generate a total ordering...
412 */
413 template <typename T, ITotallyOrderingComparer<T> BASE_COMPARER>
414 struct ThreeWayComparerAdapter : ComparisonRelationDeclarationBase<ComparisonRelationType::eThreeWayCompare> {
415 /**
416 */
417 constexpr ThreeWayComparerAdapter (const BASE_COMPARER& baseComparer);
418 constexpr ThreeWayComparerAdapter (BASE_COMPARER&& baseComparer);
419
420 /**
421 */
422 template <typename LT, typename RT>
423 constexpr strong_ordering operator() (LT&& lhs, RT&& rhs) const;
424
425 private:
426 [[no_unique_address]] BASE_COMPARER fBASE_COMPARER_;
427 };
428 template <typename BASE_COMPARER>
429 ThreeWayComparerAdapter (BASE_COMPARER bc)
431
432 /**
433 * \brief ThreeWayComparer for optional types, like builtin one, except this lets you pass in explicit 'T' comparer for the T in optional<T>
434 *
435 * You dont need this when the default comparer for 'T' works as you wish. But for example, ThreeWayComparer<optional<String>> - where you want
436 * to use a case insensitive comparer for the strings, is tricky. THIS class solves that, by letting you pass in explicitly the
437 * 'base comparer'.
438 */
439 template <typename ARG_T, IComparer<ARG_T> TCOMPARER = std::compare_three_way>
440 struct OptionalThreeWayComparer : ComparisonRelationDeclarationBase<ComparisonRelationType::eThreeWayCompare> {
441 constexpr OptionalThreeWayComparer (TCOMPARER&& tComparer);
442 constexpr OptionalThreeWayComparer (const TCOMPARER& tComparer);
443 constexpr strong_ordering operator() (const optional<ARG_T>& lhs, const optional<ARG_T>& rhs) const;
444
445 private:
446 [[no_unique_address]] TCOMPARER fTComparer_;
447 };
448
449 /**
450 * 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
451 * different sized values and just returning an int, but don't want the warnings about overflow etc.
452 */
453 template <typename FROM_INT_TYPE>
454 constexpr strong_ordering CompareResultNormalizer (FROM_INT_TYPE f);
455
456 /**
457 */
458 constexpr int ToInt (strong_ordering f)
459 {
460 if (f == strong_ordering::less) {
461 return -1;
462 }
463 else if (f == strong_ordering::equal or f == strong_ordering::equivalent) {
464 return 0;
465 }
466 else {
467 return 1;
468 }
469 }
470
471 /**
472 * Map equals to equals, but less becomes greater and greater becomes less
473 */
474 constexpr strong_ordering ReverseCompareOrder (strong_ordering so);
475
476}
477
478/*
479 ********************************************************************************
480 ***************************** Implementation Details ***************************
481 ********************************************************************************
482 */
483#include "Compare.inl"
484
485#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:356
Use this to wrap any basic comparer, and produce a Less comparer.
Definition Compare.h:386
ThreeWayComparer for optional types, like builtin one, except this lets you pass in explicit 'T' comp...
Definition Compare.h:440
Use this to wrap a basic (ITotallyOrderingComparer) comparer, and produce a Three-Way comparer.
Definition Compare.h:414