On Wednesday, 14 July 2021 at 03:06:06 UTC, someone wrote:
in main() ... so then I went to the D docs and to Ali's book afterward and there I tested his example to same results.

The current behavior seems like it could be taken for a bug, or at least room for improvement in letting static assertions fail faster than other template instantiation, but the workaround seems to be to require all branches of a static if to be valid from the perspective of the rest of the code. Which is very easy to do if you just don't have these branches at all.

case 1:

```d
public struct gudtUGC(typeStringUTF) {
static if (! (is (typeStringUTF == string) || is (typeStringUTF == wstring) || is (typeStringUTF == dstring))) { static assert(false, r"ooops … gudtUGC structure requires [string|wstring|dstring] ≠ ["d ~ typeStringUTF.stringof ~ r"]"d);
   } else {
      // actual structure code
   }
}
```

alternate 1:
- pull tests out into a named enum template, like std.traits
- always static assert enum, rather than conditionally asserting false
- always have the rest of the code

```d
enum isString(T) = is(T == string) || is(T == wstring) || is(T == dstring);
// very similar to std.traits.isSomeString

struct gudtUGC(T) {
    static assert(isString!T, "error message");
    // unconditional structure code
}
```

case 2:

```d
struct MyType(T) {
    static if (is (T == float)) {
        alias ResultType = double;

    } else static if (is (T == double)) {
        alias ResultType = real;

    } else {
        static assert(false, T.stringof ~ " is not supported");
    }
    // code relying on some ResultType
}
```

alternate 2a:
- create a type function with std.meta

```d
enum SmallFloat(T) = is(T == float) || is(T == double);

template largerFloat(T) {
    import std.meta : AliasSeq, staticIndexOf;

    static assert(SmallFloat!T, T.stringof ~ " is not supported");

alias largerFloat = AliasSeq!(void, double, real)[1+staticIndexOf!(T, AliasSeq!(float, double))];
}

struct MyType(T) {
    alias ResultType = largerFloat!T;
    // code relying on some ResultType
}
```

alternate 2b:
- at least make all the branches valid

```d
enum SmallFloat(T) = is(T == float) || is(T == double);

struct MyType(T) {
    static assert(SmallFloat!T, T.stringof ~ " is not supported");
    static if (is(T == float)) {
        alias ResultType = double;
    } else static if (is(T == double)) {
        alias ResultType = real;
    } else {
        alias ResultType = void; // dummy
    }
    // code relying on ResultType
}
```

A benefit of having tests like SmallFloat is that you can use them with template constraints...

```d
void moreWork(T)() if (SmallFloat!T) { }

unittest {
    moreWork!float; // this is fine
    moreWork!real; // !
}
```

... and get readable error messages out of the box, without having to write static assert messages:

```
example.d(23): Error: template instance `example.moreWork!real` does not match template declaration `moreWork(T)()`
  with `T = real`
  must satisfy the following constraint:
`       SmallFloat!T`
```

Reply via email to