Thread overview
Generic comparison
Nov 10, 2020
Paul Backus
Nov 10, 2020
Paul Backus
Nov 10, 2020
Paul Backus
November 10, 2020
I want to implement a generic function for "a < f(x) < b" that will give the same result as "(a < f(x)) && (f(x) < b)" for any conceivable mix of types. Except if that "f(x)" should only be evaluated once.

Is it sufficient to use "scope ref" in parameters?

I don't want to assume _anything_ about the definition of the types and the implementation of the comparison operator (can be overloaded).

November 10, 2020
On Tuesday, 10 November 2020 at 16:55:11 UTC, Ola Fosheim Grøstad wrote:
> I want to implement a generic function for "a < f(x) < b" that will give the same result as "(a < f(x)) && (f(x) < b)" for any conceivable mix of types. Except if that "f(x)" should only be evaluated once.
>
> Is it sufficient to use "scope ref" in parameters?
>
> I don't want to assume _anything_ about the definition of the types and the implementation of the comparison operator (can be overloaded).

bool between(Value, Bound)(auto ref Value value, auto ref Bound low, auto ref Bound high)
{
    return (low < value) && (value < high);
}

You need `auto ref` because either Bound or Value may have copying disabled. Because the function is a template, attributes like `scope` will be inferred when applicable (modulo compiler bugs).
November 10, 2020
On Tuesday, 10 November 2020 at 17:09:00 UTC, Paul Backus wrote:
> bool between(Value, Bound)(auto ref Value value, auto ref Bound low, auto ref Bound high)
> {
>     return (low < value) && (value < high);
> }
>
> You need `auto ref` because either Bound or Value may have copying disabled. Because the function is a template, attributes like `scope` will be inferred when applicable (modulo compiler bugs).

Interesting, so "auto ref T" is the go-to type specifier for generic code then?  I guess I also should conditionally add things like pure, nogc, nothrow... I assume I would have to test the comparison operator. I actually want to implement

(low <= value) && (value < high)

So I guess I need to test both. But how...? compiles-trait?

November 10, 2020
On Tuesday, 10 November 2020 at 17:19:09 UTC, Ola Fosheim Grøstad wrote:
>
> Interesting, so "auto ref T" is the go-to type specifier for generic code then?  I guess I also should conditionally add things like pure, nogc, nothrow... I assume I would have to test the comparison operator. I actually want to implement

The compiler infers pure, @nogc, nothrow etc. for template functions automatically. It's actually better if you don't add them by hand.

> (low <= value) && (value < high)
>
> So I guess I need to test both. But how...? compiles-trait?

You could add a template constraint, if you wanted. Something like:

    alias isOrderingComparableWith(T, U) = __traits(compiles, (T t, U u) => t < u);
    bool between(Value, Bound)(...)
        if (isOrderingComparaibleWith!(Value, Bound))

For a function this short, though, I don't think it's really necessary.
November 10, 2020
On Tuesday, 10 November 2020 at 20:32:40 UTC, Paul Backus wrote:
>     alias isOrderingComparableWith(T, U) = __traits(compiles, (T t, U u) => t < u);

My bad, should be `enum`, not `alias`.


November 10, 2020
On Tuesday, 10 November 2020 at 20:32:40 UTC, Paul Backus wrote:
> The compiler infers pure, @nogc, nothrow etc. for template functions automatically. It's actually better if you don't add them by hand.

Ok, thanks :-).