Thread overview
Questions about opAssign alternate and template quality
Aug 10, 2006
nobody
Aug 10, 2006
Oskar Linde
Aug 11, 2006
nobody
August 10, 2006
I wanted to try out D's template system so I decided to define data structures that represent all the permutations of RGB, HLS, RGBA and HLSA. I decided to convert between the various types by overriding opAssign under the mistaken impression that D supported it. Impressive when you do the math:

    3! + 3! + 4! + 4! = 60 types
   (3! + 3! + 4! + 4!)^2 = 3600 opAssigns

So my first question is whether anyone has an elegant alternative to using opAssign?

My second question is mostly an open invitation to make suggestions for improving my template writing style. In particular I am suspicious that there is a way to take better advantage of templates and not write out all 60 permutations.

So here is the actual code. I only included the RGB permutations to keep it as short as possible.


union PixArray
{
  PixBGR[] bgr;
  PixBRG[] brg;
  PixGBR[] gbr;
  PixGRB[] grb;
  PixRBG[] rbg;
  PixRGB[] rgb;
}


template PixBGR_Perms_opAssignBGR(T,U)
{

  T opAssign(U that)
  {
    ubyte tempB = that.b;
    ubyte tempG = that.g;
    ubyte tempR = that.r;
    this.b = tempB;
    this.g = tempG;
    this.r = tempR;
    return *this;
  }

}


template PixBGR_Perms(T)
{
  alias b blue;
  alias g green;
  alias r red;

  // opAssign defined for all perms
  mixin PixBGR_Perms_opAssignBGR!(T,PixBGR);
  mixin PixBGR_Perms_opAssignBGR!(T,PixBRG);
  mixin PixBGR_Perms_opAssignBGR!(T,PixGBR);
  mixin PixBGR_Perms_opAssignBGR!(T,PixGRB);
  mixin PixBGR_Perms_opAssignBGR!(T,PixRBG);
  mixin PixBGR_Perms_opAssignBGR!(T,PixRGB);
}


align(1)
{
  struct PixBGR { ubyte b; ubyte g; ubyte r; mixin PixBGR_Perms!(PixBGR); }
  struct PixBRG { ubyte b; ubyte r; ubyte g; mixin PixBGR_Perms!(PixBRG); }
  struct PixGBR { ubyte g; ubyte b; ubyte r; mixin PixBGR_Perms!(PixGBR); }
  struct PixGRB { ubyte g; ubyte r; ubyte b; mixin PixBGR_Perms!(PixGRB); }
  struct PixRBG { ubyte r; ubyte b; ubyte g; mixin PixBGR_Perms!(PixRBG); }
  struct PixRGB { ubyte r; ubyte g; ubyte b; mixin PixBGR_Perms!(PixRGB); }
}
August 10, 2006
nobody wrote:
> I wanted to try out D's template system so I decided to define data structures that represent all the permutations of RGB, HLS, RGBA and HLSA. I decided to convert between the various types by overriding opAssign under the mistaken impression that D supported it. Impressive when you do the math:
> 
>     3! + 3! + 4! + 4! = 60 types
>    (3! + 3! + 4! + 4!)^2 = 3600 opAssigns
> 
> So my first question is whether anyone has an elegant alternative to using opAssign?

I don't know of any elegant alternative. I also find it very unfortunate that opAssign isn't overloadable for structs.

> My second question is mostly an open invitation to make suggestions for improving my template writing style. In particular I am suspicious that there is a way to take better advantage of templates and not write out all 60 permutations.
> 
> So here is the actual code. I only included the RGB permutations to keep it as short as possible.

Implicit function template instantiation and some static ifs can help you. Unfortunately, IFTI isn't yet implemented for member functions, but you can make a free function like:

void pixAssign(A,B)(out A A, in B b) {
        static if (is(a.a) && is(b.a)) {
                // Alpha
                a.a = b.a;
        } else static if(is(a.a)) {
                a.a = typeof(a.a).max;
        }
        static if (is(a.r) && is(b.r)) {
                // RGB->RGB
                auto r = b.r;
                auto g = b.g;
                auto b = b.b;
                a.r = r;
                a.g = g;
                a.b = b;
        } else static if (is(a.h) && is(b.h)) {
                // HLS -> HLS
                auto h = b.h;
                auto l = b.l;
                auto s = b.s;
                a.h = h;
                a.l = l;
                a.s = s;
        } else static if(is(a.r)) {
                // HLS -> RGB
                assert(0,"not implemented.");
        } else static if(is(b.r)) {
                // RGB -> HLS
                assert(0,"not implemented.");
        }
}

Which should take care of all 3600 combinations of opAssign. is(x.y) is there just to check for the existence of a field y in x.

Which can be called:

pixAssign(dst,src);

You can also add the following to your PixColor structs:

	void opAssign(B)(B b) {
		pixAssign(*this,b);
	}

Bur, until IFTI is implemented for member functions, you would have to call it:

c1.opAssign!(typeof(c2))(c2);

And one can always hope, that in the future it will be:

c1 = c2;

> align(1)
> {
>   struct PixBGR { ubyte b; ubyte g; ubyte r; mixin PixBGR_Perms!(PixBGR); }
>   struct PixBRG { ubyte b; ubyte r; ubyte g; mixin PixBGR_Perms!(PixBRG); }
>   struct PixGBR { ubyte g; ubyte b; ubyte r; mixin PixBGR_Perms!(PixGBR); }
>   struct PixGRB { ubyte g; ubyte r; ubyte b; mixin PixBGR_Perms!(PixGRB); }
>   struct PixRBG { ubyte r; ubyte b; ubyte g; mixin PixBGR_Perms!(PixRBG); }
>   struct PixRGB { ubyte r; ubyte g; ubyte b; mixin PixBGR_Perms!(PixRGB); }
> }

You can also make a template that automatically defines your Pix??? types, but I am not sure it is worth it.

alias Pix!(R,G,B) PixRGB;
alias Pix!(R,G,B,A) PixRGBA;
alias Pix!(A,R,G,B) PixARGB;
alias Pix!(H,L,S) PixHLS;
alias Pix!(B,G,R) PixBGR;
alias Pix!(R,B,G) PixRBG;
alias Pix!(B,R,G) PixBRG;
...


/Oskar
August 11, 2006
Oskar Linde wrote:

>> My second question is mostly an open invitation to make suggestions for improving my template writing style. In particular I am suspicious that there is a way to take better advantage of templates and not write out all 60 permutations.
>>
>> So here is the actual code. I only included the RGB permutations to keep it as short as possible.
> 
> Implicit function template instantiation and some static ifs can help you. Unfortunately, IFTI isn't yet implemented for member functions, but you can make a free function like:
> void pixAssign(A,B)(out A A, in B b) {
>         static if (is(a.a) && is(b.a)) {
>                 // Alpha
>                 a.a = b.a;
>         } else static if(is(a.a)) {
>                 a.a = typeof(a.a).max;
>         }
>         static if (is(a.r) && is(b.r)) {
>                 // RGB->RGB
>                 auto r = b.r;
>                 auto g = b.g;
>                 auto b = b.b;
>                 a.r = r;
>                 a.g = g;
>                 a.b = b;
>         } else static if (is(a.h) && is(b.h)) {
>                 // HLS -> HLS
>                 auto h = b.h;
>                 auto l = b.l;
>                 auto s = b.s;
>                 a.h = h;
>                 a.l = l;
>                 a.s = s;
>         } else static if(is(a.r)) {
>                 // HLS -> RGB
>                 assert(0,"not implemented.");
>         } else static if(is(b.r)) {
>                 // RGB -> HLS
>                 assert(0,"not implemented.");
>         }
> }
> 
> Which should take care of all 3600 combinations of opAssign. is(x.y) is there just to check for the existence of a field y in x.

Thanks for the great post! Your pixAssign function is a great example and helped
me better understand static if, is and auto. It might take awhile before I can see why there would be trouble with trying to use a mixin to avoid paramatized calls to opAssign.