www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Variadic templates with aliases

reply "comco" <void.unsigned gmail.com> writes:
I wrote a simple template mixin to hold common operator overloads:

mixin template VectorSpaceOpsMixin(Vector, Scalar, alias a, alias 
b)
{
     Vector opUnary(string op)() if (op == "-") {
         return Vector(-a, -b);
     }
     ...
}

This works like this:

struct complex {
     double a, b;
     mixin VectorSpaceOpsMixin!(complex, double, a, b);
     ...
}

Now this is nice, but it only works when the vector has 2 parts. 
Can the mixin template be made more general? What will then be 
its definition? This don't work:
mixin template `VectorSpaceOpsMixin(Vector, Scalar, alias... 
parts) { ... }`

Obviously, this kind of functionality is easy with string mixins. 
Can it be achieved without them?
Dec 27 2012
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Thu, Dec 27, 2012 at 9:01 PM, comco <void.unsigned gmail.com> wrote:

 I wrote a simple template mixin to hold common operator overloads:

 mixin template VectorSpaceOpsMixin(Vector, Scalar, alias a, alias b)
 {
     Vector opUnary(string op)() if (op == "-") {
         return Vector(-a, -b);
     }
     ...
 }

 This works like this:

 struct complex {
     double a, b;
     mixin VectorSpaceOpsMixin!(complex, double, a, b);
     ...
 }

 Now this is nice, but it only works when the vector has 2 parts. Can the
 mixin template be made more general? What will then be its definition? This
 don't work:
 mixin template `VectorSpaceOpsMixin(Vector, Scalar, alias... parts) { ...
 }`
First, the `Vector` part is redundant. You can use `typeof(this)` inside your mixin to have it 'look around` when it's being mixed in and determine the type of the local `this`. So you mixin can become: mixin VectorSpaceOpsMixin!(double, a, b); The `double` part could be deduced also, but it depends what kind of layout you envision for your host structs. I'll let it there. Now, concerning your question, you can make `parts` a template tuple parameter: VectorSpaceOpsMixin(Vector, Scalar, args...) { The trouble is then that mixin VectorSpaceOpsMixin!(complex, double, a, b); will have `args` be the tuple (a,b) (it contains the symbols, not directly the value themselves). Tuples are not arithmetic values in D (that would not make sense for most tuples), so you cannot do `-args` directly.
From there, it depends whether you really want the flexibility that passing
a and b around gives you, or if you can make some simplifying assumptions concerning your host struct. Solution #1: full flexibility: you want to keep the capability to name the fields upon which the mixin will act. That way, even with struct Vector { double a,b,c; } you can have mixin VectorSpaceOpsMixin!(Scalar, a,b); // Look Ma, no c! You can iterate on `args`. By using `.stringof` on its elements, you get "this.a", "this.b", ... From that, you can create the wanted code. Here is a possibility: mixin template VectorSpaceOpsMixin(Scalar, args...) { typeof(this) opUnary(string op)() if (op == "-") { typeof(this) temp; // "temp.a = -this.a;"... foreach(index, arg; args) mixin("temp" ~ args[index].stringof[4..$] ~ " = -" ~ args[index].stringof ~ ";"); return temp; } } It creates a temporary, but then a direct return solution would also have to make one. The only thing I'm not sure is when using floating point types: `temp.a` and `temp.b` are all initialized with NaN. I don't know if it's slow to assign another floating point to a NaN. I guess a one line solution is doable, with a bit of string mixin magic. Btw, of course, there will be many duplication between the different arithmetic operators. You can also write another template to, er, write your mixin for you. But I personally wouldn't bother: it's easier to maintain explicit code. Solution # 2: the mixin will act on all fields inside the struct => no need to pass the names around. In this case, I would even use the first field as the type of Scalar. Everything is greatly simplified: mixin template VectorSpaceOpsMixin2() // no args! { typeof(this) opUnary(string op)() if (op == "-") { typeof(this) temp; foreach(index, ref arg; temp.tupleof) arg = -this.tupleof[index]; return temp; } } struct Complex { double a, b; mixin VectorSpaceOpsMixin2; } Don't worry about the foreach: it's all unrolled at compile-time.
Dec 27 2012
parent reply "comco" <void.unsigned gmail.com> writes:
On Thursday, 27 December 2012 at 21:21:24 UTC, Philippe Sigaud 
wrote:
 On Thu, Dec 27, 2012 at 9:01 PM, comco 
 <void.unsigned gmail.com> wrote:

 I wrote a simple template mixin to hold common operator 
 overloads:

 mixin template VectorSpaceOpsMixin(Vector, Scalar, alias a, 
 alias b)
 {
     Vector opUnary(string op)() if (op == "-") {
         return Vector(-a, -b);
     }
     ...
 }

 This works like this:

 struct complex {
     double a, b;
     mixin VectorSpaceOpsMixin!(complex, double, a, b);
     ...
 }

 Now this is nice, but it only works when the vector has 2 
 parts. Can the
 mixin template be made more general? What will then be its 
 definition? This
 don't work:
 mixin template `VectorSpaceOpsMixin(Vector, Scalar, alias... 
 parts) { ...
 }`
First, the `Vector` part is redundant. You can use `typeof(this)` inside your mixin to have it 'look around` when it's being mixed in and determine the type of the local `this`. So you mixin can become: mixin VectorSpaceOpsMixin!(double, a, b); The `double` part could be deduced also, but it depends what kind of layout you envision for your host structs. I'll let it there. Now, concerning your question, you can make `parts` a template tuple parameter: VectorSpaceOpsMixin(Vector, Scalar, args...) { The trouble is then that mixin VectorSpaceOpsMixin!(complex, double, a, b); will have `args` be the tuple (a,b) (it contains the symbols, not directly the value themselves). Tuples are not arithmetic values in D (that would not make sense for most tuples), so you cannot do `-args` directly.
From there, it depends whether you really want the flexibility 
that passing
a and b around gives you, or if you can make some simplifying assumptions concerning your host struct. Solution #1: full flexibility: you want to keep the capability to name the fields upon which the mixin will act. That way, even with struct Vector { double a,b,c; } you can have mixin VectorSpaceOpsMixin!(Scalar, a,b); // Look Ma, no c! You can iterate on `args`. By using `.stringof` on its elements, you get "this.a", "this.b", ... From that, you can create the wanted code. Here is a possibility: mixin template VectorSpaceOpsMixin(Scalar, args...) { typeof(this) opUnary(string op)() if (op == "-") { typeof(this) temp; // "temp.a = -this.a;"... foreach(index, arg; args) mixin("temp" ~ args[index].stringof[4..$] ~ " = -" ~ args[index].stringof ~ ";"); return temp; } } It creates a temporary, but then a direct return solution would also have to make one. The only thing I'm not sure is when using floating point types: `temp.a` and `temp.b` are all initialized with NaN. I don't know if it's slow to assign another floating point to a NaN. I guess a one line solution is doable, with a bit of string mixin magic. Btw, of course, there will be many duplication between the different arithmetic operators. You can also write another template to, er, write your mixin for you. But I personally wouldn't bother: it's easier to maintain explicit code. Solution # 2: the mixin will act on all fields inside the struct => no need to pass the names around. In this case, I would even use the first field as the type of Scalar. Everything is greatly simplified: mixin template VectorSpaceOpsMixin2() // no args! { typeof(this) opUnary(string op)() if (op == "-") { typeof(this) temp; foreach(index, ref arg; temp.tupleof) arg = -this.tupleof[index]; return temp; } } struct Complex { double a, b; mixin VectorSpaceOpsMixin2; } Don't worry about the foreach: it's all unrolled at compile-time.
I've myself written something like the first, but the second approach suits my needs better. typeof(this) is very nice. I intend to use it (among other things) for multivectors, so the scalar type is not directly obtainable from the parts, but then, I can allow partwise multiplication with whatever type the parts may multiply. The nicest thing about the two approaches is that they are orthogonal - a single template mixin may implement them both!
Dec 28 2012
parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
 I've myself written something like the first, but the second approach
 suits my needs better. typeof(this) is very nice.
It is. For mixin templates, it's a must.
 I intend to use it (among other things) for multivectors, so the scalar
 type is not directly obtainable from the parts, but then, I can allow
 partwise multiplication with whatever type the parts may multiply.
Yes, that can be tested at compile-time.
 The nicest thing about the two approaches is that they are orthogonal - a
 single template mixin may implement them both!
OK, great! Don't hesitate to ask if you're stuck.
Dec 29 2012