www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Can you write better code: "alias"ing from member structs

reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I have a set of template structs, where I overload the template for many 
different numbers of parameters.  To simplify code, each struct includes 
the previous struct.

Each struct overloads opCatAssign(), taking delegates as arguments; the 
arguments of the delegates vary from no arguments to all of the template 
arguments.

My problem is that I can't use 'alias' to bring in overloaded 
opCatAssign()s, so I have to write an increasing number of wrappers in 
each struct.  I'm looking for a better way.

Yes, 'alias' could bring in the overloaded opCatAssign()s if I used 
classes, but I don't want to do that; I want these to be structs, 
because I am going to use them by value and because I don't want to have 
to call constructors whenever I use them.

My code looks something like this:



struct Foo() {
   alias bool delegate() dg;
   dg[] opCatAssign(dg d) { ... }
}

struct Foo(T) {
   .Foo!() base;

   /* syntax error */
   alias base.opCatAssign opCatAssign;

   /* what I have to do */
   base.dg[] opCatAssign(base.dg d) { return base.opCatAssign(dg); }

   alias bool delegate(T) dg;
   dg[] opCatAssign(dg d) { ... }
}

struct Foo(T1,T2) {
   .Foo!(T1) base;

   base.base.dg[] opCatAssign(base.base.dg d) { return 
base.base.opCatAssign(d); }
   base.     dg[] opCatAssign(base.     dg d) { return 
base.base.opCatAssign(d); }

   alias bool delegate(T1,T2) dg;
   dg[] opCatAssign(dg d) { ... }
}



As you can see, with each parameter I add, I have more and more 
overloading to do.  Ick.  Anybody have any thoughts about a better way 
to do it?
Dec 22 2004
parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 22 Dec 2004 11:21:08 -0700, Russ Lewis  
<spamhole-2001-07-16 deming-os.org> wrote:
 I have a set of template structs, where I overload the template for many  
 different numbers of parameters.  To simplify code, each struct includes  
 the previous struct.

 Each struct overloads opCatAssign(), taking delegates as arguments; the  
 arguments of the delegates vary from no arguments to all of the template  
 arguments.

 My problem is that I can't use 'alias' to bring in overloaded  
 opCatAssign()s, so I have to write an increasing number of wrappers in  
 each struct.  I'm looking for a better way.

 Yes, 'alias' could bring in the overloaded opCatAssign()s if I used  
 classes, but I don't want to do that; I want these to be structs,  
 because I am going to use them by value and because I don't want to have  
 to call constructors whenever I use them.

 My code looks something like this:



 struct Foo() {
    alias bool delegate() dg;
    dg[] opCatAssign(dg d) { ... }
 }

 struct Foo(T) {
    .Foo!() base;

    /* syntax error */
    alias base.opCatAssign opCatAssign;

    /* what I have to do */
    base.dg[] opCatAssign(base.dg d) { return base.opCatAssign(dg); }

    alias bool delegate(T) dg;
    dg[] opCatAssign(dg d) { ... }
 }

 struct Foo(T1,T2) {
    .Foo!(T1) base;

    base.base.dg[] opCatAssign(base.base.dg d) { return  
 base.base.opCatAssign(d); }
    base.     dg[] opCatAssign(base.     dg d) { return  
 base.base.opCatAssign(d); }

    alias bool delegate(T1,T2) dg;
    dg[] opCatAssign(dg d) { ... }
 }



 As you can see, with each parameter I add, I have more and more  
 overloading to do.  Ick.  Anybody have any thoughts about a better way  
 to do it?

Mixins. I tried the code down below but I'm getting some odd results, as in, this output: ----------------------------- a catAssignC catAssignB catAssignA executeA() on 3 items abar: 8855520,1 bbar: 0 cbar executeB(1) on 3 items abar: 1244968,1 bbar: 1 cbar executeC(2,3) on 3 items abar: 2,3 bbar: 3 cbar b Error: Access Violation Tool completed with exit code 1 ----------------------------- The thing that seems odd to me is that when I call 'execute' I am expecting only 1 item in each list, not 3 items, each call to catAssignC adds an item to listC, catAssignB to listB, catAssignA to listA, yet executeA finds 3 items in listA. Anyone know why? ----------------------------- import std.stdio; struct FooA() { alias bool delegate() dgA; dgA[] listA; dgA[] opCatAssign(dgA d) { writef("catAssignA\n"); listA ~= d; return listA; } void execute() { writef("executeA() on ",listA.length," items\n"); foreach(dgA d; listA) d(); } } struct FooB(T) { mixin FooA; alias FooA.opCatAssign opCatAssign; alias FooA.execute execute; alias bool delegate(T) dgB; dgB[] listB; dgB[] opCatAssign(dgB d) { writef("catAssignB\n"); listB ~= d; return listB; } void execute(T p1) { writef("executeB(",p1,") on ",listB.length," items\n"); foreach(dgB d; listB) d(p1); } } struct FooC(T1,T2) { mixin FooB!(T1); alias FooB.opCatAssign opCatAssign; alias FooB.execute execute; alias bool delegate(T1,T2) dgC; dgC[] listC; dgC[] opCatAssign(dgC d) { writef("catAssignC\n"); listC ~= d; return listC; } void execute(T1 p1, T2 p2) { writef("executeC(",p1,",",p2,") on ",listC.length," items\n"); foreach(dgC d; listC) d(p1,p2); } } class A { bool abar(int a, int b) { writef(" abar: ",a,",",b,"\n"); return true; } bool bbar(int a) { writef(" bbar: ",a,"\n"); return true; } bool cbar() { writef(" cbar\n"); return true; } } void main() { A c = new A(); FooC!(int,int) a; FooB!(int) b; writef("\na\n"); a ~= &c.abar; a ~= &c.bbar; a ~= &c.cbar; a.execute(); a.execute(1); a.execute(2,3); writef("\nb\n"); b ~= &c.bbar; b ~= &c.cbar; b.execute(); b.execute(4); } ----------------------------- Regan
Dec 22 2004
next sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
[snip]
 The thing that seems odd to me is that when I call 'execute' I am
 expecting only 1 item in each list, not 3 items, each call to catAssignC
 adds an item to listC, catAssignB to listB, catAssignA to listA, yet
 executeA finds 3 items in listA.

 Anyone know why?

It works better when the first two struct FooA and struct FooB are replaced with template FooA and template FooB. With the struct it was mixing in a single declaration of the struct instead of all the declarations from the template since struct FooA() is treated like template FooA(){struct FooA {...}} Then when you mix that in you are doing the same thing as: struct A { struct B { int x; } int y; // note: no variable of type B. it just defines B, it does not use B void oops() { printf("%d",B.x); } } viod main(){ A a; a.y = 7; a.oops(); } [snip]
Dec 22 2004
parent "Regan Heath" <regan netwin.co.nz> writes:
On Wed, 22 Dec 2004 16:21:47 -0500, Ben Hinkle <bhinkle mathworks.com>  
wrote:
 [snip]
 The thing that seems odd to me is that when I call 'execute' I am
 expecting only 1 item in each list, not 3 items, each call to catAssignC
 adds an item to listC, catAssignB to listB, catAssignA to listA, yet
 executeA finds 3 items in listA.

 Anyone know why?

It works better when the first two struct FooA and struct FooB are replaced with template FooA and template FooB. With the struct it was mixing in a single declaration of the struct instead of all the declarations from the template since struct FooA() is treated like template FooA(){struct FooA {...}} Then when you mix that in you are doing the same thing as: struct A { struct B { int x; } int y; // note: no variable of type B. it just defines B, it does not use B void oops() { printf("%d",B.x); } } viod main(){ A a; a.y = 7; a.oops(); } [snip]

Ahh.. of course.. thanks. So the mixin solution for this problem is something like: import std.stdio; template FooA() { alias bool delegate() dgA; dgA[] listA; dgA[] opCatAssign(dgA d) { writef("catAssignA\n"); listA ~= d; return listA; } void execute() { writef("executeA() on ",listA.length," items\n"); foreach(dgA d; listA) d(); } } template FooB(T) { alias bool delegate(T) dgB; dgB[] listB; dgB[] opCatAssign(dgB d) { writef("catAssignB\n"); listB ~= d; return listB; } void execute(T p1) { writef("executeB(",p1,") on ",listB.length," items\n"); foreach(dgB d; listB) d(p1); } } template FooC(T1,T2) { alias bool delegate(T1,T2) dgC; dgC[] listC; dgC[] opCatAssign(dgC d) { writef("catAssignC\n"); listC ~= d; return listC; } void execute(T1 p1, T2 p2) { writef("executeC(",p1,",",p2,") on ",listC.length," items\n"); foreach(dgC d; listC) d(p1,p2); } } struct Foo(T1,T2) { mixin FooC!(T1,T2); alias FooC!(T1,T2).opCatAssign opCatAssign; alias FooC!(T1,T2).execute execute; mixin FooB!(T1); alias FooB!(T1).opCatAssign opCatAssign; alias FooB!(T1).execute execute; mixin FooA!(); alias FooA!().opCatAssign opCatAssign; alias FooA!().execute execute; } class A { bool abar(int a, int b) { writef(" abar: ",a,",",b,"\n"); return true; } bool bbar(int a) { writef(" bbar: ",a,"\n"); return true; } bool cbar() { writef(" cbar\n"); return true; } } int main() { A c = new A(); Foo!(int,int) a; a ~= &c.abar; a ~= &c.bbar; a ~= &c.cbar; a.execute(); a.execute(1); a.execute(2,3); return 0; } Which doesn't really look any nicer than the original example. Regan
Dec 22 2004
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Looks like we found a bug.  I modified your code some to put out more 
debugging info and found that the listA and listB variables have the 
same location.  It's not just the arrays - the address of the two 
variables is identical.  That sounds like a compiler bug to me.

However, I also think that your code is faulty, because I don't think 
you can mixin another struct like that.  If things work the way I think 
they do, that would simply mixin the struct declaration, and not 
actually allocate any variable space.

I did play around with using mixins to do variable declarations and 
aliasing, then wrapping them in structs, but that seemed even less 
readable than my original post:



template Contents() {
   ... stuff ...
}
struct MyStruct() {
   mixin Contents!();
}

template Contents(T) {
   mixin Contents!() base;
   alias base.opCatAssign opCatAssign;

   ... stuff ...
}
struct MyStruct(T) {
   mixin Contents!(T);
}



I never got it to compile, anyhow, so maybe it wouldn't have worked at 
all...
Dec 22 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I'm pretty sure that this code shouldn't even compile, because (if I 
understand correctly) the 'mixin Foo' line is mixing in a struct 
declaration, not member variables.  Thus, the code should not be able to 
alias 'Foo.var'.

Further, if you run it, it shows that the addresses of the two variables 
are identical, and that the size of the 'Bar!(int)' variable is 4.

 struct Foo() {
   int var;
 }
 struct Bar(T) {
   mixin Foo;
   alias Foo.var var;
   T var2;
 }
                                                                               
 
 import std.stdio;
 void main() {
   Bar!(int) x;
   x.var = 1;
   x.var2 = 2;
   writef("address of x.var=%08x x.var2=%08x\n",
          cast(int)&x.var, cast(int)&x.var2);
   writef("size of x = %d\n", x.sizeof);
 }

Dec 22 2004
parent "Thomas Kuehne" <thomas-dloop kuehne.cn> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Added to DStress as
http://svn.kuehne.cn/dstress/nocompile/mixin_04.d

Thomas

-----BEGIN PGP SIGNATURE-----

iD8DBQFByy5D3w+/yD4P9tIRAp8nAKCbyZ/vnA7YRIXYeBPKA5l4h/KkHgCcCxW2
28dwoKFdwMj9UwB7UYksfVw=
=hpYk
-----END PGP SIGNATURE-----
Dec 23 2004