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