Rebind template(bug?)
ag0aep6g via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Sun Aug 21 14:11:37 PDT 2016
On 08/21/2016 09:29 PM, Engine Machine wrote:
> I know you like to play the right or wrong game, but did you ever learn
> that a single example does not prove the truth of something?
But you can show in a single example that something doesn't work. You
tried to do that, and you did it with a simple example, which is always
appreciated. But apparently, you were wrong. Happens to the best. And
when the compiler prints "tmpl!a" that's indeed a little misleading.
You being wrong there doesn't mean that the Rebind template as I posted
it works correctly, of course. But Jack didn't claim that. He just said
that your example for how it doesn't work is wrong. And as far as I see,
Rebind does work as expected. But I wouldn't be surprised if there is
some edge case where things fall apart.
Also, if you're looking for help or civil discussion, I suggest you try
a less antagonistic approach. Things like "I know you like to [play
games]" and "did you ever learn [whatever]" do not set the tone for that.
> How about something more complex?
>
> import std.stdio;
> import std.meta, std.traits;
>
> class base { }
Style nitpick: Class names in PascalCase, please.
> template Rebind(alias instance, newArgs...)
> {
> import std.traits: TemplateOf;
> alias tmpl = TemplateOf!instance;
> static if (newArgs.length > 0)
> alias Rebind = tmpl!newArgs;
> else
> alias Rebind = base;
> }
>
> template EraseLast(A...)
> {
> static if (A.length > 0)
> alias EraseLast = Erase!(A[$-1], A);
This line can be simplified: alias EraseLast = A[0 .. $ - 1];
> else
> alias EraseLast = base;
This is quite surprising for a template that's called "EraseLast". I
renamed it to "EraseLastOrBase" for myself when looking at this.
> }
>
>
>
> class T(A...) : Rebind!(T!A, EraseLast!A)
The core idea of Rebind isn't needed here, is it? You've got T, so you
can just pass it directly. No need to extract it from T!A.
So, instead of using some form of Rebind, you can use a simpler template:
----
/* Instantiates tmpl with args, or if no args given, returns base. */
template InstantiateOrBase(alias tmpl, args ...)
{
static if (args.length == 0) alias InstantiateOrBase = base;
else alias InstantiateOrBase = tmpl!args;
}
class T(A...) : InstantiateOrBase!(T, EraseLast!A)
----
> {
> int x;
Note that this means that every instantiation of T brings its own x.
That is, T!foo declares an x, and T!(foo, bar) which inherits from T!foo
adds another x. The same happens with y, z, and s, of course.
> static if (A.length > 0 && A[0] == "Animal")
> {
> int y;
> static if (A.length > 1 && A[1] == "Dog")
> {
> int z;
> static if (A.length > 2 && A[2] == "Pug")
> int s;
> }
>
> }
> }
>
>
> pragma(msg, is(T!("Animal", "Dog", "Pug") : T!("Animal", "Dog")));
> pragma(msg, is(T!("Animal", "Dog", "Pug") : T!("Animal")));
> pragma(msg, is(T!("Animal", "Dog", "Pug") : base));
> pragma(msg, is(T!("Animal", "Dog") : T!("Animal")));
> pragma(msg, is(T!("Animal", "Dog") : base));
> pragma(msg, is(T!("Animal") : base));
Asserts are great to show that something holds. When someone like me
makes changes to your code in order to understand it, the complier
throws a nice loud error in their face when they mess up. So I changed
these to static asserts for myself.
> // all true
>
>
>
> void main()
> {
> auto t = new T!("Animal", "Dog", "Pug")();
> T!("Animal", "Dog") q = t;
> //T!("Animal", "Dog", "Pug") q = t; works but then q is not the
> super of t
> t.x = 3;
> t.y = 4;
> t.z = 6;
> t.s = 123;
>
> q.y = 1;
> writeln(t, ", ", typeof(t).stringof);
> writeln(q, ", ", typeof(q).stringof);
> writeln("---");
> writeln(t.x);
> writeln(t.y);
> writeln(t.z);
> writeln(t.s);
> writeln("---");
> writeln(q.x);
> writeln(q.y);
> writeln(q.z);
> writeln("---");
> writeln(t.y);
> writeln("---");
> writeln(&t);
> writeln(&q);
>
> }
>
> Since the pragma's are true, it is obvious that the inheritance should
> work chain works. Yet q is not a reference to t as it should be, why is
> this?
It would help a lot if you would point out what parts of the output are
surprising to you. If you let the reader figure it out themselves,
chances are 1) people are not going to bother, and 2) if they do bother,
they might miss your point.
Labeling the output also helps. By that I mean, if you write
`writeln("t.x: ", t.x);`, the output can be read much more easily. I
also think that you could have made your point with less output, which
would again make it easier to follow. That is, just looking t.x/q.x
would have been enough, no?
I guess you expect q.x and friends to be the same as t.x and friends.
And since you put it there, you may also expect &t to be the same as &q.
About the members being different: That's because every instantiation
brings its own x/y/z, as mentioned above. Lodovico has shown what
happens here in his Child/Parent example.
About &t and &t: Those are the addresses of the class references
themselves. To print the addresses of the objects, cast to void*:
----
writeln(cast(void*) t);
writeln(cast(void*) q); /* prints the same as the line above */
----
So, you're wrong again ;)
More information about the Digitalmars-d-learn
mailing list