On Mon, Oct 15, 2012 at 07:19:42PM +0200, so wrote: > On Monday, 15 October 2012 at 16:37:08 UTC, Peter Alexander wrote: > >... > >- Simple(r) templates > > I keep seeing things like this and probably i am failing to understand > it. This is a vast understatement for D templates. Yes, easier to > use, i agree. But C++ templates are stone age comparing to D and i > don't see this mentioned enough where it matters most. [...] [...]
One of the major advances of D templates over C++ is its potent combination with other D features like aliases/enums, static if, compile-time introspection, and signature constraints. For example, consider this: // This template checks if T satisfies certain properties, like // having a .NodeType type, parseAtom and parsePrimary methods, // etc. template isExprType(T) { static if (is(T.NodeType) && is(isNodeType!(T.NodeType) && is(T.parseAtom(Lexer.init)) && is(T.parsePrimary(Lexer.init)) && ...) { enum isExprType = true; } else enum isExprType = false; } // This template checks that T is a valid node type. That is, // the previous template uses this one to enforce T.NodeType // being a type that satisfies certain properties. template isNodeType(T) { static if (is(T.precedence : int)) enum isNodeType = true; else enum isNodeType = false; } // By using the above template as a signature constraint, we're // free to make use of properties we assumed about T, such as // parametrizing the return type on .NodeType as defined in T T.NodeType parseExpr(T)(Lexer lex) if (isExprType!T) { ... // Or calling functions we verified to exist in T auto node1 = T.parseAtom(lex); auto node2 = T.parseAtom(lex); ... // Or using properties of T.NodeType that we verified // exists, even though the template itself is // independent of how T.NodeType is even defined by T! if (node1.precedence < node2.precedence) ... } This is a ducktyping system where any type that satisfies isExprType will work with parseExpr. This isn't just C++'s "conform to conventions described in the missing accompanying documentation or get 10 pages of template errors". This is *compile-time* verification that any type T you instantiate parseExpr with, conforms to the requirements defined by isExprType. And furthermore, even T.NodeType itself has been checked to have certain properties -- such as a .precedence property that is convertible to int (which means it can be an actual int field, or a @property function that returns an int, or something that returns an object convertible to int, etc.). This allows you to implement an expression parser that is completely independent of the concrete types of the expression being parsed. AND this is done without requiring 100 template parameters that specify every configurable parameter: you just specify them in the type used to instantiate parseExpr. Better yet, if you designed the isExprType template correctly, you can even have a derived class of an expression node that stores a value, and specifies parsing functions that computes the value on the expression on-the-fly. So instantiating parseExpr with the base class gives you an expression tree, and instantiating it with the derived class computes the value of the expression at parse-time. In the latter case, even the return type is the correct derived class so you don't even need to down-cast a base class reference to get at the value. (I actually have code that does this. You cannot imagine the sense of power when code like this can be written _cleanly_, without bending over backwards and running a 100-meter dash with your left leg tied to your neck.) Now try doing this in C++. It is in all likelihood plain impossible, or so extremely painful that it's not worth the suffering to implement. T -- Lottery: tax on the stupid. -- Slashdotter