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