digitalmars.D.learn - implementing default opCmp
- Steven Schveighoffer (32/32) Nov 18 2020 I have a struct like this:
- Paul Backus (35/45) Nov 18 2020 Here's a stab at a totally generic version that I haven't unit
- Steven Schveighoffer (8/61) Nov 19 2020 Yeah, something like this might be useful in druntime. But it makes you
- ag0aep6g (8/12) Nov 18 2020 std.typecons.Tuple has opCmp. So this works:
- Steven Schveighoffer (3/17) Nov 19 2020 Ah, excellent solution! I hadn't thought of that.
- =?UTF-8?Q?Ali_=c3=87ehreli?= (8/19) Nov 19 2020 leof).opCmp(tuple(other.tupleof));
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.
The truth is, I'm not entirely caring what order these things come out
in. I just want them to be defined as having an order given that all the
members have a defined order. My expectation is that a default opCmp
would look like:
int opCmp(S other)
{
if(x == other.x)
{
if(y == other.y) return 0;
return y < other.y ? -1 : 1;
}
return x < other.x ? -1 : 1;
}
But really, as long as there is something to do this easily I don't care
what the ordering turns out to be.
I can do equality like:
return this.tupleof == other.tupleof;
I can do assignment like:
this.tupleof = other.tupleof;
How do I do something really simple for opCmp? I tried this it didn't work:
return this == other ? 0 :
this.tupleof < other.tupleof ? -1 : 1;
-Steve
Nov 18 2020
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;
}
}
Nov 18 2020
On 11/18/20 6:02 PM, Paul Backus wrote:On Wednesday, 18 November 2020 at 22:29:17 UTC, Steven Schveighoffer wrote: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. -SteveI 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; } }
Nov 19 2020
On Wednesday, 18 November 2020 at 22:29:17 UTC, Steven
Schveighoffer wrote:
How do I do something really simple for opCmp? I tried this it
didn't work:
return this == other ? 0 :
this.tupleof < other.tupleof ? -1 : 1;
std.typecons.Tuple has opCmp. So this works:
int opCmp(S other)
{
import std.typecons: tuple;
return tuple(this.tupleof).opCmp(tuple(other.tupleof));
}
Nov 18 2020
On 11/18/20 6:06 PM, ag0aep6g wrote:On Wednesday, 18 November 2020 at 22:29:17 UTC, Steven Schveighoffer wrote:Ah, excellent solution! I hadn't thought of that. -SteveHow do I do something really simple for opCmp? I tried this it didn't work: return this == other ? 0 : this.tupleof < other.tupleof ? -1 : 1;std.typecons.Tuple has opCmp. So this works: int opCmp(S other) { import std.typecons: tuple; return tuple(this.tupleof).opCmp(tuple(other.tupleof)); }
Nov 19 2020
On 11/19/20 6:12 AM, Steven Schveighoffer wrote:On 11/18/20 6:06 PM, ag0aep6g wrote:tuple;=C2=A0=C2=A0=C2=A0=C2=A0 int opCmp(S other) =C2=A0=C2=A0=C2=A0=C2=A0 { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 import std.typecons: =leof).opCmp(tuple(other.tupleof));=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 return tuple(this.tup=That's what I use as well. S can be replaced with something like 'typeof(this)' (or perhaps 'ref=20 const(typeof(this))' and throw some inout in there :) ) and the whole=20 thing can be mixed-in whereever needed. Ali=C2=A0=C2=A0=C2=A0=C2=A0 }=20 =20 Ah, excellent solution! I hadn't thought of that. =20 -Steve
Nov 19 2020









Steven Schveighoffer <schveiguy gmail.com> 