Thread overview
Can you write better code: "alias"ing from member structs
Dec 22, 2004
Russ Lewis
Dec 22, 2004
Regan Heath
Dec 22, 2004
Ben Hinkle
Dec 22, 2004
Regan Heath
Dec 22, 2004
Russ Lewis
mixin a struct bug
Dec 22, 2004
Russ Lewis
Dec 23, 2004
Thomas Kuehne
December 22, 2004
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?

December 22, 2004
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
December 22, 2004
[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]


December 22, 2004
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...

December 22, 2004
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);
> }

December 22, 2004
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
December 23, 2004
Added to DStress as http://svn.kuehne.cn/dstress/nocompile/mixin_04.d

Thomas