A Huge Bummer When Using alias this

Simen Kjaeraas via Digitalmars-d digitalmars-d at puremagic.com
Thu Mar 24 17:22:50 PDT 2016


On Thursday, 24 March 2016 at 21:26:00 UTC, Jack Stouffer wrote:
> alias this is only useful when not using any function that 
> relies on the template constraints in std.traits.
>
> For example
>
> import std.traits;
>
> void someFunc(N)(N val) if (isNumeric!N)
> {
>     int b = val;
> }
>
> void main()
> {
>     import std.typecons;
>     Nullable!int a = 42;
>
>     someFunc(a);
> }
>
> $ dmd test.d
> test.d(13): Error: template someFunc cannot deduce function 
> from argument types !()(Nullable!(int))
>
> Removing the template constraint makes it compile and run just 
> fine. What's a good work around for these types of issues?

Simply put, isNumeric doesn't do what you think it does. It 
checks if the type is *exactly* one of the built-in numeric types 
(ints and floats of various known-at-compile-time sizes). Even 
BigInt, a part of Phobos that is explicitly supposed to work as a 
numeric thing, is not numeric accordion to isNumeric.

IMO, isNumeric should be renamed isBuiltInNumeric or something to 
that effect, as it obviously does not test what it says it does. 
In addtion, there should be a function somewhat like 
numericBehavior below:

template supportsBinOp(string op, T, U = T) {
     enum supportsBinOp = __traits(compiles, (T t, U u) {
         return mixin("t "~op~" u");
     });
}

template supportsUnOp(string op, T) {
     enum supportsUnOp = __traits(compiles, (T t) {
         return mixin(op~" t");
     });
}

template numericBehavior(T) {
     enum numericBehavior =
         supportsBinOp!("+", T) &&
         supportsBinOp!("+", T, int) &&
         supportsBinOp!("-", T) &&
         supportsBinOp!("-", T, int) &&
         supportsBinOp!("*", T) &&
         supportsBinOp!("*", T, int) &&
         supportsBinOp!("*", T) &&
         supportsBinOp!("*", T, int) &&
         supportsBinOp!("+=", T) && // Should opOpAssign be 
required?
         supportsBinOp!("+=", T, int) &&
         supportsBinOp!("-=", T) &&
         supportsBinOp!("-=", T, int) &&
         supportsBinOp!("*=", T) &&
         supportsBinOp!("*=", T, int) &&
         supportsBinOp!("*=", T) &&
         supportsBinOp!("*=", T, int) &&
       //supportsUnOp!("++", T) && // Should these
       //supportsUnOp!("-+", T) && // be included?
         supportsUnOp!("+", T) &&
         supportsUnOp!("-", T);
} unittest {
     import std.typecons : Nullable;
     import std.bigint : BigInt;
     import std.meta : AliasSeq;
     import std.complex : Complex;

     alias goodTypes = AliasSeq!(
         byte, short, int, long,
         ubyte, ushort, uint, ulong,
         float, double, real,
         BigInt, Nullable!int,
         Complex!float
     );

     foreach (e; goodTypes) {
         static assert(numericBehavior!e, "Expected "~e.stringof~" 
to have numeric behavior,");
     }

     alias badTypes = AliasSeq!(
         string, int*, Object, void, Nullable!string,
         immutable int // Should this be on the list?
     );

     foreach (e; badTypes) {
         static assert(!numericBehavior!e, "Did not expect 
"~e.stringof~" to have numeric behavior,");
     }
}

This tests for behavior, not simply type. I'm not saying 
isNumeric is a bad template, it just has a confusing name.


More information about the Digitalmars-d mailing list