mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
134 lines
2.9 KiB
C++
134 lines
2.9 KiB
C++
#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<Foo3> {
|
|
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<Foo4> {
|
|
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));
|
|
|
|
///
|
|
}
|