opDispatch + alias this intervene compilation?
Adam D. Ruppe
destructionator at gmail.com
Mon Dec 2 07:48:37 PST 2013
On Monday, 2 December 2013 at 15:25:13 UTC, Namespace wrote:
> I'm sure it is a bug, but I've no idea how to name it.
Not really a bug, but surprising if you've never seen it before.
The problem is that writeln() has different specializations if
the argument is an input range.
writeln(my_input_range); // prints the contents of the range
How does it check if it is an input range?
std.range.isInputRange!T. What's that implementation?
enum bool isInputRange = is(typeof(
(inout int = 0)
{
R r = void; // can define a range object
if (r.empty) {} // can test for empty
r.popFront(); // can invoke popFront()
auto h = r.front; // can get the front of the range
}));
It tries to see if the three functions, empty, popFront, and
front will compile, and that front returns something. Let's go
back to your code. What happens if you replace R with a vec2f?
vec2f r = void; // ok, can be declared
if(r.empty) {} // ok, calls r.opDispatch!"empty"
r.popFront(); // ok, calls r.opDispatch!"popFront"
auto h = r.front; // ok, calls r.opDispatch!"front", which
returns a float
writeln thinks your vector is an input range of floats! Since
input ranges of floats can't be printed as hex, it throws the
exception with %x. If you tried %s, it would print out an endless
amount of zeros, because r.empty() is returning zero, which
converts to false in if(empty).
Put a pragma(msg) in your opDispatch and you can see it being
instantiated for this.
Why then does putting in the writeln() prevent this? Because you
didn't import std.stdio! So opDispatch fails to compile with
"undefined identifier writeln", but the failure is silenced by
the is(typeof()) check in isInputRange. So it doesn't pass as a
range and doesn't tell you that either. (If it gave an error for
every template constraint it failed, you'd be spammed out of
control.)
hmm though, why is arr.ptr doing this? I can only imagine writeln
is trying to dereference it - *arr.ptr has type of vec2f, so then
it tries to print that and triggers the input range check. I have
to admit that's a wee bit surprising to me too, but again, hard
to say it is technically a bug since referencing struct pointers
is fairly useful when writing them.
So, a few fixes here: to just print the pointer without writeln
attempting to print the contents, cast it to void*:
writefln("ptr = %x", cast(void*) arr.ptr); // always
prints address
To prevent your opDispatch from incorrectly triggering the
duck-typing checks for isInputRange, put a constraint on it:
T opDispatch(string str)() const pure nothrow if(str !=
"popFront") {
Having been bit by this more than once, every time I write
opDispatch, I put that != "popFront" constraint on it. It is
usually needed.
Alternatively, you could write a helper function that does:
foreach(s; str)
if(s < 'x' || s > 'v') return false; // not a valid vector
component
return true; // checks out
to be a bit more strict. This is probably ideal, perhaps you can
statically make sure it is in range too; e.g. use s > 'y' instead
of v if there's only two components.
And, of course, if you want the writeln to actually compile in
there, don't forget to import std.stdio in that function too.
More information about the Digitalmars-d-learn
mailing list