#include "Tutorial.h" void ComparableTutorial() { /// .SgnCompare and CombineCompare /// Traditional approach of C language of representing comparison results was 3-state: /// comparing a and b results in negative value (if a < b), zero (if a == b) or positive /// value (a > b). In C++ standard library, comparisons are usually represented with `bool` /// predicates. /// However, with `bool` predicate it becomes somewhat more difficult to provide /// comparisons for composite types: struct Foo { String a; int b; int c; // we want to order Foo instances by a first, then b, then c bool operator<(const Foo& x) const { return a < x.a ? true : a == x.a ? b < x.b ? true : b == x.b ? false : c < x.c : false; } }; /// U++ provides standard function `SgnCompare`, which returns negative value/zero/positive /// in "C style": int a = 1; int b = 2; DUMP(SgnCompare(a, b)); DUMP(SgnCompare(b, a)); DUMP(SgnCompare(a, a)); /// Default implementation of `SgnCompare` calls `Compare` method of value: struct MyClass { int val; int Compare(const MyClass& x) const { return SgnCompare(val, x.val); } }; /// `SgnCompare` is now defined for `MyClass`: MyClass u, v; u.val = 1; v.val = 2; DUMP(SgnCompare(u, v)); DUMP(SgnCompare(v, u)); DUMP(SgnCompare(v, v)); /// Now getting back to `Foo`, with `SgnCompare` `operator<` becomes much less difficult: struct Foo2 { String a; int b; int c; bool operator<(const Foo2& x) const { int q = SgnCompare(a, x.a); if(q) return q < 0; q = SgnCompare(b, x.b); if(q) return q < 0; q = SgnCompare(c, x.c); return q < 0; } }; /// Alternatively, it is possible to define just `Compare` method and use `Comparable` /// ^https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern:CRTP idiom^ to /// define all relation operators: struct Foo3 : Comparable { String a; int b; int c; int Compare(const Foo3& x) const { int q = SgnCompare(a, x.a); if(q) return q; q = SgnCompare(b, x.b); if(q) return q; return SgnCompare(c, x.c); } }; Foo3 m, n; m.a = "A"; m.b = 1; m.c = 2; n.a = "A"; n.b = 1; n.c = 3; DUMP(m < n); DUMP(m == n); DUMP(m != n); DUMP(SgnCompare(m, n)); /// While the content of `Compare` method is trivial, it can be further simplified using /// `CombineCompare` helper class: struct Foo4 : Comparable { String a; int b; int c; int Compare(const Foo4& x) const { return CombineCompare(a, x.a)(b, x.b)(c, x.c); } }; Foo4 o, p; o.a = "A"; o.b = 1; o.c = 2; p.a = "A"; p.b = 1; p.c = 3; DUMP(o < p); DUMP(o == p); DUMP(o != p); DUMP(SgnCompare(o, p)); /// }