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 ;)