July 15, 2021

On Wednesday, 14 July 2021 at 23:44:18 UTC, jfondren wrote:

>

On Wednesday, 14 July 2021 at 22:59:38 UTC, someone wrote:

>

[...]

so, these lines:

    stringUGC32 lugcSequence3 = stringUGC32(cast(char) 'x');
    stringUGC32 lugcSequence4 = stringUGC32(1);

which call stringUGC32, an alias:

alias stringUGC32 = gudtUGC!(stringUTF32);
alias stringUTF32 = dstring; /// same as immutable(dchar)[];

which explicitly instantiates gudtUGC with the type dstring,

enum isTypeSupported(type) = is(type == stringUTF08) || is(type == stringUTF16)
    || is(type == stringUTF32);
...
public struct gudtUGC(typeStringUTF) {
    static assert(isTypeSupported!(typeStringUTF),
            r"ooops … gudtUGC structure requires [string|wstring|dstring] ≠ ["d
            ~ typeStringUTF.stringof ~ r"]"d);

which passes the assert.

You're explicitly instantiating the struct with dstring, not getting an assertion error because dstring passes the test, and are then passing the constructor a different type, which gets you a type error at that time.

I can't believe I completely forgot about my alias; I am a stupid to the say the least, I raised this issue without thinking much :(

>

However, even if you avoid that alias and try either implicit or explicit instantiation of the template with bad types, the template instantiation fails first in random other places, like a default param of " " not making sense as a char:

staticif1.d(369): Error: cannot implicitly convert expression `" "` of type `string` to `const(char)`
staticif1.d(387): Error: cannot implicitly convert expression `" "` of type `string` to `const(char)`
staticif1.d(545): Error: template instance `fw.types.UniCode.gudtUGC!char` error instantiating

So, yeah, even with no static ifs in the file, you're still not getting these static asserts to fail before other parts of the template. Conclusion: you shouldn't rely on static asserts providing a good UI in the case of an error. They still work, the struct still wouldn't instantiate with a char or the like if all the other char-incompatibilities were removed, but for UI purposes they're not great because you can't rely on them firing according to normal control flow.

ACK for the static asserts.

>

You're using aliases though, and not instantiating the struct directly. If you make those function templates you can use template constraints with them, which does result in a nice UI:

A few days ago when I started coding this UDT (and started thinking of actually using it) the first thing that came to mind was that to get a simple, say, strWhatever.left(1) I would end up writing gudtUGC!(dstring)(strWhatever).left(1).encode() or gudtUGC!(dstring)(strWhatever).leftAsUTF(1) or something akin to these ones, so I played a bit with the aliases and ended up with gudtUGC[08|16|32] which seemed a bit more friendly and crystal-clear on their types. The thing with aliases is that they are really useful until you forgot you're using aliases and end up biting yourself on a situation like this one. Again, sorry for being too silly on my part.

>
gudtUGC!stringUTF08 thing8(T)(T arg) if (isTypeSupported!T) { return gudtUGC!T(arg); }
gudtUGC!stringUTF16 thing16(T)(T arg) if (isTypeSupported!T) { return gudtUGC!T(arg); }
gudtUGC!stringUTF32 thing32(T)(T arg) if (isTypeSupported!T) { return gudtUGC!T(arg); }

...

    stringUGC32 lugcSequence3 = thing32(cast(char) 'x');
    stringUGC32 lugcSequence4 = thing32(1);

(introducing new 'thing' functions as stringUGC32 used is elsewhere as a type and not just a function. So you still need the aliases.)

Which fails in this manner:

staticif1.d(548): Error: template `fw.types.UniCode.thing32` cannot deduce function from argument types `!()(char)`, candidates are:
staticif1.d(46):        `thing32(T)(T arg)`
  with `T = char`
  must satisfy the following constraint:
`       isTypeSupported!T`

and would fail more verbosely if the three functions had the same name, instead of numbered names to follow the stringUGC32 pattern.

isTypeSupported isn't great here, but you can pick a more precise name. Or in the case of already numbered functions like this, you could use more precise tests... or just drop the tests entirely and have non-templated functions:

gudtUGC!stringUTF08 thing8(stringUTF08 arg) { return typeof(return)(arg); }
gudtUGC!stringUTF16 thing16(stringUTF16 arg) { return typeof(return)(arg); }
gudtUGC!stringUTF32 thing32(stringUTF32 arg) { return typeof(return)(arg); }

which results in normal type errors:

staticif2.d(548): Error: function `fw.types.UniCode.thing32(dstring arg)` is not callable using argument types `(char)`
staticif2.d(548):        cannot pass argument `'x'` of type `char` to parameter `dstring arg`

I will explore this approach.

Thank you very much for your time and your detailed step-by-step reply. I guess you were LoL when you first saw this one :) !

1 2
Next ›   Last »