Operator overloading -- lets collect some use cases

Don nospam at nospam.com
Fri Jan 2 02:50:37 PST 2009


Don wrote:
> Daniel Keep wrote:
>>
>>
>> Andrei Alexandrescu wrote:
>>> [snip]
>>>
>>> All right, let's do this with operator overloading :o). Ideas? I have 
>>> a couple, but I don't want to introduce bias.
>>>
>>> Andrei
>>
>> The only idea I can come up with is to introduce the following:
>>
>> struct Vector
>> {
>>     macro opExpression(ast)
>>     {
>>         ...
>>     }
>> }
>>
>> The compile-time opExpression macro's job is to spit out a function 
>> that takes each leaf of the AST as an argument and returns the final 
>> value of the expression.
>>
>> You could simplify it by ensuring that you don't get any 
>> subexpressions that don't result in the type "Vector".  For example:
>>
>> Vector a, b, c;
>> double g, h;
>>
>> a = b + (g*h) * c;
>>
>> The AST would have "(g*h)" as a single node of type double.
>>
>> Of course, this would probably be exceedingly complex, both on the 
>> compiler and user side of things.  I can't imagine what the contents 
>> of that macro would even look like...
> 
> My BLADE library pretty much does that, so you don't need to guess.
> The ast is a string of the form "A=B+(C*D)", together with a type tuple 
> Tuple!(Vector, Vector, double, Vector), and a values array 
> ["a","b","(g*h)","c"].
> ("g*h" is something like "4.564e+2" if g and h are compile-time constants).
> One approach would be to look at that code and try to simplify it.

It performs the following steps. Note that it uses [5...3, 6, , 2..$]
or [5...3, 6, 0..$, 2..$] syntax for multi-dimensional slicing.

=== (1) Simplify the expression. ===
(A) Remove all duplicate symbols.
(B) Combine all scalars into a single scalar. Combine slicing operations 
into vector variables.
(C) Deal with slices.
     - A[B..C, whatever][D..E] = A[(B+D)..(B+E), whatever] for any 
non-slicable expression A, including something ending with an index.
          Assert(E-D <= C-B).
       (Special case A[B..$, whatever][D..$] = A[(B+D)..$, whatever])
     - A[whatever][D..E] = A[D..E, whatever] if A is slicable.
(D) Rank and arithmetic transformations.
     - Use slicing distributive law for linear algebra:
       Given A[B..C] for expressions A,B,C
       where B and C are non-slicable, and A is slicable, the slice
       can be moved to every vector inside A. Note that this may
       convert matrix multiplications into dot products.
     - Remove unary minus where possible, eg A-(-B) => A+B, abs(-A) => 
abs(A).
     - Use associativity of * in intrinsics:
       sum(A*V) => A*sum(V), abs(A*B) => abs(A)*abs(B)
(E) Expression standardisation
     - Move multiplies to left: Convert A[]*B into B*A[] (assumes * is 
commutative, not valid for quaternions).
     - Convert C-A*B into C+(-A)*B whenever possible.
(F) Remove '$'. Convert x[$] into x[x.length].
(G) Check all of the ranks for each operation. Add each one to the list 
of asserts.

=== (2) Categorize the expression ===
This determines which type of loop it is. For example, with a 
matrix-vector multiply you have nested loops.

=== (3) Generate asserts ===
Spit out all the asserts which were generated during the simplification 
pass.

=== (4) Generate code, based on expression category ===
I only did this for a few cases, but it's generally pretty 
straightforward. It would get much more complicated once you start 
adding cache blocking techniques and sparse matrices...

I completely implemented steps 1 to 3, so this isn't complete fantasy.

> 
>>
>> The one positive to this method is that it's about as general as you 
>> can get; assuming this was implemented, it would hopefully be possible 
>> to implement a simpler scheme on top of it.
>>
>> struct Vector
>> {
>>     mixin FusionOverloadFor!("+","-","*","/",".dot",".cross");
>> }
>>
>> I really hope there's a better way.
>>
>>   -- Daniel



More information about the Digitalmars-d mailing list