On 11/18/20 6:02 PM, Paul Backus wrote:
On Wednesday, 18 November 2020 at 22:29:17 UTC, Steven Schveighoffer wrote:
I have a struct like this:
struct S
{
int x;
int y;
}
and I want a default comparison. The problem is, that comparison
doesn't have a default, and requires I implement opCmp. While this is
useful for the compiler, there's no default I know of that is an easy
one-liner.
Here's a stab at a totally generic version that I haven't unit tested at
all, except to verify that it works for your example struct S:
auto cmp(T, U)(auto ref T lhs, auto ref U rhs)
{
import core.lifetime: forward;
static if (__traits(compiles, lhs.opCmp(rhs)))
return forward!lhs.opCmp(forward!rhs);
else static if (__traits(compiles, rhs.opCmp(lhs)))
return -forward!rhs.opCmp(forward!lhs);
else
return lhs < rhs ? -1 : lhs > rhs ? 1 : 0;
}
mixin template defaultOpCmp()
{
import std.traits: isAggregateType;
static assert(isAggregateType!(typeof(this)),
"opCmp can only be overloaded for aggregate types.");
auto opCmp()(auto ref typeof(this) other)
{
import std.traits: ReturnType, CommonType, Fields;
import std.meta: Map = staticMap;
alias cmpType(T) = ReturnType!((T lhs, T rhs) => cmp(lhs, rhs));
alias Result = CommonType!(Map!(cmpType, Fields!(typeof(this))));
Result result;
static foreach (i, _; typeof(this).tupleof)
if (result == 0)
result = cmp(this.tupleof[i], other.tupleof[i]);
return result;
}
}
Yeah, something like this might be useful in druntime. But it makes you
wonder if we wouldn't be better off without opCmp but instead with
opBinary(string s : "<") and friends.
One thing that sucks is that opCmp might do more operations than are
necessary for the actual comparison, because it has to generate the
numeric result.
-Steve