Expression templates

Don Clugston dac at nospam.com.au
Tue Dec 12 09:09:09 PST 2006


The recent language improvements (especially, opAssign) have made 
expression templates eminently feasible. Here's a proof-of-concept which
implements parsing of the basic vector operations of addition and scalar 
multiplication.
It includes constant folding of scalar multiplication, and does some 
ordering of the parameters.
At compile time, it prints the expression in postfix notation, and at 
runtime it prints the calculation to be performed.
Enjoy.

-------------
import std.stdio;
import std.string;

// This would be a vector. For now, just store a name.
class DVec {
     char [] data;
public:
     const bool isDVec=true; // workaround because is() doesn't work 
properly
     this(char [] name) { data = name; }
     void opAssign(A)(A expr) {
         pragma(msg, "In postfix: " ~ A.expressionString);
         // Do the actual work here. For now, just print a message
         // to prove we have access to the actual variables.
         printf((data ~ " = " ~ expr.getStr ~ \n).ptr);

     }
     Expr!('+', DVec, A) opAdd(A)(A w) {
         Expr!('+', DVec, A) q;
         q.left = this;
         q.right = w;
         return q;
     }
     Expr!('*', real, DVec) opMul(A)(A w) {
         static assert(is(A : real), "Can only multiply by scalars");
         Expr!('*', real, DVec) q;
         q.left = w;
         q.right = this;
         return q;
     }
}

/// For compile-time debugging
template ExprToText(X)
{
     static if (is (X: real))
        const char [] ExprToText = "scalar";
     else static if (is (X.isExpr))
         const char [] ExprToText = X.expressionString;
     else static if (is (X: DVec))
        const char [] ExprToText = "Vec";
}

// Stores all the data from the expression.
// 'dummy' is a workaround to avoid a 'recursive template' error message.
struct Expr(char operation, LeftExpr, RightExpr, int dummy=0) {
     typedef int isExpr; // workaround because is() doesn't work properly
     const char opType = operation;
     const char [] expressionString = ExprToText!(LeftExpr)  ~ " " ~ 
ExprToText!(RightExpr) ~ " " ~  operation ~ " ";

     LeftExpr left;
     RightExpr right;

    Expr!('+', Expr!(operation, LeftExpr, RightExpr, 1), A) opAdd(A)(A w) {
        Expr!('+', Expr!(operation, LeftExpr, RightExpr, 1), A) q;
        q.left.left = this.left;
        q.left.right = this.right;
        q.right = w;
        return q;
    }
    static if ( operation=='*' && is(LeftExpr: real)) {
        // Constant fold scalars
        Expr!('*', real, RightExpr, 1) opMul(real w) {
            Expr!('*', real, RightExpr, 1) q;
            q.right = this.right;
            q.left = this.left * w;
            return q;
        }
   } else {
    Expr!('*', real, Expr!(operation, LeftExpr, RightExpr, 1)) 
opMul(A)(A w) {
        static assert(is(A : real), "Can only multiply by scalars");
        Expr!('*', real, Expr!(operation, LeftExpr, RightExpr, 1)) q;
        q.right.left = this.left;
        q.right.right = this.right;
        q.left = w;
        return q;
    }
   }

     // Just for debugging.
     char [] getStr() {
         char [] s;
         static if (is (left.isExpr)) {
             s = "(" ~ left.getStr() ~ ")";
         } else static if (is (LeftExpr : DVec)) {
             s =  left.data;
         } else s ~= format("%g", left);
         s ~= operation;
         static if (is (right.isExpr)) {
             s ~= "(" ~ right.getStr() ~ ")";
         } else static if (is (RightExpr: DVec)) {
             s ~= right.data;
         } else s ~= format("%g", right);
         return s;
     }

}

void main()
{
     DVec a = new DVec("a");
     DVec b = new DVec("b");
     DVec c = new DVec("c");

     b = a + c;
     b = c*2;
     a = 27.4*a + 2*((b+c) + 3.2*c)*1.234*2;
}



More information about the Digitalmars-d mailing list