Variadic templates with aliases

comco void.unsigned at gmail.com
Fri Dec 28 15:26:10 PST 2012


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 at 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!


More information about the Digitalmars-d-learn mailing list