1[section An Expression Template Primer] 2 3What are _ets_ anyway? In short, _ets_ are templates that you write to 4capture expressions so that they can be transformed and/or evaluated lazily. 5 6An example of normal C++ expression is: 7 8 std::sqrt(3.0) + 8.0f 9 10The compiler sees this and creates some representation of that expression 11inside the compiler. This is typically an _ast_ (AST). The AST for the 12expression above might be: 13 14[$yap/img/ast.png] 15 16This tree structure captures all the elements of the original C++ code. The 17expression is a plus operation whose left side is a call to `std::sqrt(3.0)` 18and whose right side is `8.0f`. The call to `std::sqrt(3.0)` is its own 19expression subtree consisting of a call node and its argument node. 20 21A _yap_ version of this same tree is: 22 23[$yap/img/expr.png] 24 25The `operator+()` is represented by a _yap_ expression whose kind is 26`yap::expr_kind::plus` and the call is represented by a _yap_ expression whose 27kind is `yap::expr_kind::call`. Notice that the call expression has two 28terminals, one for the callable, and one for its single argument. 29 30The type that holds this expression is: 31 32[plus_sqrt_yap_type] 33 34That looks like a big mess; let's unpack it. You might notice that the 35overall shape is the same as the expression tree diagram above. We have 36tree-like nesting of `boost::yap::expression` template instantiations. 37 38Here's the top-level `boost::yap::expression` again with 39its noisy guts removed: 40 41[plus_sqrt_yap_top_level_1] 42 43 // Left and right operand expressions ... 44 45[plus_sqrt_yap_top_level_2] 46 47It has an _kind_ of `plus` as its first template parameter (it's a non-type 48parameter); this indicates what kind of "node" it is. In this case, the top 49level expression is analogous to our `operator+()` AST node. Its operands are 50the elements of its _tuple_ data member. 51 52The left operand to the top-level plus operation is itself a _yap_ expression 53representing `std::sqrt(3.0)`: 54 55[plus_sqrt_yap_lhs] 56 57This expression is a call expression. The first operand to the call 58expression is the callable entity, in this case a pointer to `std::sqrt`. The 59remaining operands are the arguments to pass to the callable; in this case, 60there's only one operand after the callable, `3.0`. 61 62The children of the `std::sqrt(3.0)` subexpression are terminals. This means 63that they are leaf nodes in our notional AST. 64 65The right operand to the top-level plus operation is of course also a _yap_ 66expression. It is also a terminal: 67 68[plus_sqrt_yap_rhs] 69 70Notice a couple of things here: 1) non-terminals (the top-level plus operation 71and the call opertion in our example) have tuple elements that are *all* _yap_ 72expressions, and 2) terminals have tuple elements, *none of which* are _yap_ 73expressions (they're just normal types like `float` and `double (*)(double)`). 74 75[note From here on, I'll use the terms "expression" and "node" interchangably, 76and I'll also use the terms "subexpression" and "child" interchangably. Even 77though _ets_ are not identical to tree-based ASTs, they're close enough that 78the terminology is interchangable without loss of meaning.] 79 80[heading Capturing an Expression] 81 82If we want to capture an expression using _yap_ we have to do something to let 83the compiler know not just to eagerly evaulate our expression, as it does when 84it sees `std::sqrt(3.0) + 8.0f`. 85 86To do this, we create _terminal_ expressions out of one or more of the 87terminals in the expression we want to capture and evaluate lazily. Here, 88I've declared a template alias to make that easier to type: 89 90[plus_sqrt_term_alias] 91 92And here is how I might use that alias to create the terminal containing 93`std::sqrt`: 94 95[plus_sqrt_yap_value] 96 97The reason I can then just call the terminal with a `3.0` argument and add 98`8.0f` to the result is that I'm taking a great big shortcut in this example 99by using _yap_'s built-in example _et_, _expr_. _expr_ is a template with all 100the operator overloads defined, including the call operator. Each operator 101overload returns an _expr_, which is why the `+` in `std::sqrt(3.0) + 8.0f` 102also works. 103 104[note _expr_ is great for example code like what you see here, and it's great 105for small _et_ use cases that are essentially implementation details. You 106should write your own _ets_ for anything that is to be used in any other 107context. The reason for this is that most of the time your _et_ system will 108not want to support all combinations of all possible operators and function 109calls. For instance, code like this: 110 111 (a + b) = c; 112 113is at least unusual, if not outright wrong. Where does `c` go? Into `a`, 114`b`, or into an expiring `a + b` temporary? What if `a` is a `std::string` 115and `b` is a `FILE *`? _expr_ doesn't care. You probably want to design 116interfaces that are more carefully considered than the "everything goes" style 117implied by using _expr_. ] 118 119_yap_ comes with a handy _print_ function. Calling it like this: 120 121[print_plus_sqrt_yap_value] 122 123Gives this output: 124 125 expr<+> 126 expr<()> 127 term<double (*)(double)>[=1] 128 term<double>[=3] 129 term<float>[=8] 130 131This is a lot more readable. I show this to you here to give you a more 132concise view of the AST-like structure. 133 134(In case you're wondering why `&std::sqrt` is printed as the value `1`, so was 135I. Apparently, that's just what GCC prints for that. Weird.) 136 137[heading Doing Something Useful With It] 138 139Now we've seen a simple expression both described as a C++ AST and captured as 140a _yap_ expression. This just introduces the _et_ mechanism; what do we do 141with it once we have an _et_? Consider one of the examples from the intro: 142 143 std::vector<int> v1 = {/* ... */}; 144 std::vector<int> v2 = sort(v) | unique; 145 146The rest of the tutorial will explain in greater detail how _yap_ can be used 147in situations like this, but the brief version is this: 148 149* Use _yap_ to capture an expression. In this case, something like `auto expr 150 = sort(v) | unique;`. 151 152* Use the _yap_ _xform_ algorithm to transform the expression into what you 153 want. In this case, something like `auto desired_expr = 154 yap::transform(expr, my_transform);`, which turns the concise form `sort(v) 155 | unique` into the more verbose calls required by the standard algorithm 156 APIs. Note that the resulting expression can be transformed repeatedly if 157 this is desirable. 158 159* Evauate the final expression, either using _eval_ or a call to _xform_ that 160 transforms the final expression into an evaluated result. 161 162[endsect] 163