Inspired by
Guava's Comparison Chain,
I decided to play around coding up something similar for my C++ library. What problem does ComparisonChain solve? It's a clean, readable form for the
comparisons that you're likely to have to perform when dealing with complex objects.
Example
Given this struct:
struct Foo {
int value;
int cost;
float weight;
const char *name;
long uuid;
};
One possible way we may want to compare it could look like this:
/**
* Compare returns < 0 if less than, 0 if equal, > 0 if greater
*/
int compare(const Foo &a, const Foo &b) {
int ret = strcmp(a.name, b.name);
// Names sort first, and if equal override everything else
if (ret == 0) {
return ret;
}
// Cost sorts second
if (a.cost != b.cost) {
/*
* Ick. This is an overflow waiting to happen, however fixing that results
* in even more verbose logic...
*/
ret = (b.cost - a.cost);
}
// Value sorts if cost is equal
if (ret == 0 && a.value != b.value) {
ret = (b.value - a.value);
}
return ret;
}
So that gets really verbose really quickly. It also gets annoying to maintain
when you have multiple orderings, and you're repeating this logic with slight
variations several times.
Compare that to a more succinct:
int compare(const Foo &a, const Foo &b) {
return ComparisonChain()
.or(a.name, b.name) // names sort first, and if equal override everything else.
.and(a.cost, b.cost) // cost sorts second
.and(a.value, b.value) // value sorts if cost is equal
.buildInt();
}
This logic is a lot easier to follow, and hides any complexity in how to safely compare two primitive values.
In order to prevent interactions from becoming too complex, the 'or' and 'and' functions are implemented with respect to the default <, and > operators
which results in easy to maintain code.
Design Considerations
ComparisonChain follows a chained operator pattern, and has similarities to a builder pattern.
Each function in the ComparisonChain is designed to return a reference to the host object, allowing multiple mutators to be called in a single statement.
template < typename tType >
ComparisonChain &and(const tType &a, const tType &b) {
// ...
return *this;
}
Full implementation is available
here on GitHub.