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;
    }
}

Reply via email to