Rebind template(bug?)

Engine Machine via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Aug 21 15:06:17 PDT 2016


On Sunday, 21 August 2016 at 21:11:37 UTC, ag0aep6g wrote:
> 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 ;)

So, the point is, I don't like putting a lot of work in to 
something unless I know it works. D is horrible in showing what 
is actually going on most of the time and I have to figure out 
and try many different things. If it has a bug or error msg that 
isn't informative then I have to try something again, and keep 
trying until the problem is solved. I've never had these problems 
with modern compilers. I assume it is due to D's meta complexity. 
It is amazing on one hand but sucks on the other.

Anyways, This is what I finally have:

import std.meta, std.traits;  	

template Rebind(alias instance, newArgs...)
{
     import std.traits: TemplateOf;
	alias Seq(T...)=T;
     alias tmpl = TemplateOf!instance;
	static if (newArgs.length > 0)
	    alias Rebind = tmpl!newArgs;
	else
		alias Rebind = Seq!();
}



template EraseLast(A...)
{
	alias Seq(T...)=T;
	static if (A.length > 0)
		alias EraseLast = Erase!(A[$-1], A);
	else
		alias EraseLast = Seq!();
}



class T(A...) : Rebind!(T!A, EraseLast!A)
{	
	pragma(msg, A);
	static if (A.length == 0)
	{
		pragma(msg, "BASE!!");
	} else
	static if (A[$-1] == "Animal")
	{
		int x;
		int y;
	} else
	static if (A[$-1] == "Dog")
	{
		int z;
	} else
	static if (A[$-1] == "Pug")
	{
		int s;
	} else pragma(msg, A[$-1], " not a defined class of ", 
this.stringof);
}



This is the problem. Rebind(or even if your modification of 
InstantiateOrBase, which requires a base), does not provide the 
base class.

I want the above code to simply provide itself as a base when it 
is at the "top" of the hierarchy.

The idea is suppose to be simple:

One has a hierarchy a super b super c. (e.g. Animal <= Dog <= Pug)

There is a base to A, which contains all the base stuff, It is 
taken to 0 template parameters: T!() <= T!"Animal" <= 
T!("Animal", "Dog") <= T!("Animal", "Dog", "Pug")

T!()'s "data" is specified in the class just like all the other 
derivations. I don't want to have to specify an external base 
class as in your InstaniateOrBase. Why? Because!!! (There should 
be no need to, and if one goes this route of creating classes, it 
should be all or nothing, else there is no real benefit)


Now, I can do somethign like T!("Base", "Animal", etc) but then 
this requires specifying "Base" in every construction, which is 
unnecessary since it always exists.

So, I basically want to conditionally inherit. If A... has length 
0(in T), i.e., T!() then we get the base class. I've tried using 
Object as a substitute and all that mess but nothing works 
properly. Usually results in a circular definition.

It seems that when one uses `class T(A...) : X`, one can't, for 
some X, not have inheritance. The compiler expects X to be 
something inheritable from no matter what. I could be wrong, but 
this seems to be the case. i.e., there is no such thing as 
conditional inheritance in D, e.g.,

class T(A...) if (A.length > 0) : X // (inherits from X only if 
A.length > 0)
{

}

Or, say, X is "null" or an empty sequence.

Hopefully I am truly wrong and D can do this.

Again, all I want to do is specify the "base class" inside the 
class rather than create a new class outside. I can do this for 
all the derivations now but not for base.











More information about the Digitalmars-d-learn mailing list