[section Examples] Most of these examples are patterned after the examples from Boost.Proto. In part, this was done to underscore where _yap_ can do what Proto can, and where it cannot. Where possible, a Proto-derived example uses syntax in `main()` identical to that in the original Proto example. If you don't know anything about Proto, don't worry. The examples are useful on their own. [section Hello World] Remember how I mentioned earlier that _yap_ does things in a completely lazy way? _yap_ doesn't ever evaluate your expression eagerly. Eager evaluation can be done, but it's a bit of code. [hello_world] [endsect] [section Hello World Redux] That's better! Sort of.... We created a custom expression template with an eager stream operator. This gives us eager evaluation, but gives away all the lazy AST building-then-evaluating that we're using _ets_ for in the first place. In this simple example, we don't really need it. [hello_world_redux] [endsect] [section Minimal] `minimal_expr` below models _ExprTmpl_; since it has no operators, an expression must be built manually. First, the template itself: [minimal_template] This can be used to make a `minimal_expr` plus expression: [minimal_template_manual_construction] You can evaluate, transform, or otherwise operate on `minimal_expr` expressions using the functions in _yap_ that accept an _Expr_: [minimal_template_evaluation] [note Don't use _yap_ this way. Use the operator macros instead. This is an example contrived only to show you the minimum requirements on a _yap_-compatible template.] [endsect] [section Calc1] This is the first of several calculator-building examples derived from Proto. This first one just builds lazy expressions with placeholders, and evaluates them. Here we can first see how much C++14-and-later language features help the end user _emdash_ the Proto version is much, much longer. [calc1] [endsect] [section Calc2] The Proto Calc2 example turns the expressions from Calc1 into callable objects. Using _yap_ you can do this in two ways. You can just use lambdas to wrap the expressions: [calc2a] Or you can use _make_expr_fn_ to make a callable object from your expression: [calc2b] [endsect] [section Calc3] Here, we introduce a _XForm_ used to calculate expression arity, and `static_assert()` that the number of parameters passed by the caller matches the arity. [note The `get_arity` _XForm_ doesn't produce an _Expr_, and it does not have to. _XForms_ may produce _Exprs_ or arbitrary values. They may also have arbitrary side effects, and may be stateful.] [calc3] [endsect] [section Lazy Vector] Finally, it starts to get interesting! This example shows how you can add plus and other operations to sequences of data without creating temporaries and allocating memory. [note In this example, we see a terminal type that owns the storage of its value, a `std::vector`. See the Vector example later on to see a terminal type that does not.] [lazy_vector] [endsect] [section Self-Evaluating Expressions] In most of the examples, we've seen _yap_ expressions captured, transformed, and/or evaluated either manually, or within certain operations that always do certain transformations (as in the `operator[]` in the _lazy_vector_ example). Sometimes, you want the transfrmations to happen just before a _yap_ expression is used by non-_yap_-aware code. At other times, you might want an entire _yap_ expression to be evaluated if it appears by itself in a statement (i.e. as an expression statement). This example uses C++17's `if constexpr ()`, simply because it makes the example shorter and easier to digest. The `if constexpr ()` bits are not strictly necessary. [self_evaluation] [endsect] [section TArray] Proto refers to this as the "mini-library for linear algebra" example. It shows how quite complicated expressions involving sequences can be evaluated elementwise, requiring no temporaries. [note The original Proto example used a terminal that contained an array of three `int`s; _yap_ cannot represent this, and so this example uses a `std::array` instead. _yap_ decays `int[3]` to `int *`, since that is what is done in a C++ expression. See _how_treated_ for details.] [tarray] [endsect] [section Vec3] An example using 3-space vectors, a bit like the tarray example. [vec3] [endsect] [section Vector] So far we've only seen examples with custom terminals that own the values in the expressions we operate on. What happens when you've got types that you want to operate on, non-intrusively? Here's how you might do it with `std::vector<>`s: [vector] [note Though this example only provides overloads for the operations we want to define over `std::vector<>`s, the result of each of those operations is an _expr_, which uses *all* the operator overloads. If we wanted to restrict the operations on the results too, we could have defined a custom expression template with the desired operations, and used that instead of _expr_ in the operator macros.] [endsect] [section Mixed] This is a lot like the previous Vector example, except that it operates on `std::vector<>`s and `std::list<>`s in the same expression. [mixed] [endsect] [section Map Assign] An implementation of `map_list_of()` from Boost.Assign using _yap_. [map_assign] [note `map_list_of_expr` defines a generic call operator that matches any call, including one with the wrong number of arguments. This could be fixed by adding a `static_assert()` to the `map_list_of_expr` template, or by hand-writing the call operator with SFNIAE or concept constraints.] [endsect] [section Future Group] An implementation of Howard Hinnant's design for /future groups/. [future_group] [endsect] [section Autodiff] Here we adapt an [@https://en.wikipedia.org/wiki/Automatic_differentiation automatic differentiation] library to use _yap_ for specifying the equations it operates on. Autodiff is a pretty small library, and doesn't cover every possible input expression. What it covers is simple arithmetic, and the well-known functions `sin`, `cos`, `sqrt`, and `pow`. Here is how you would form an input to the library using its API. This is taken from the test program that comes with the library. [autodiff_original_node_builder] I have a *lot* of trouble understanding what's going on here, and even more verifying that the expression written in the comment is actually what the code produces. Let's see if we can do better. First, we start with a custom expression template, `autodiff_expr`. It supports simple arithmetic, but notice it has no call operator _emdash_ we don't want `(a + b)()` to be a valid expression. [autodiff_expr_template_decl] We're going to be using a lot of placeholders in our Autodiff expressions, and it sure would be nice if they were `autodiff_expr`s and not _exprs_, so that only our desired operators are in play. To do this, we define an operator that produces placeholder literals, using the _literal_op_m_ macro: [autodiff_expr_literals_decl] Now, how about the functions we need to support, and where do we put the call operator? In other examples we created terminal subclasses or templates to get special behavior on terminals. In this case, we want to create a function-terminal template: [autodiff_function_terminals] `OPCODE` is an enumeration in Autodiff. We use it as a non-type template parameter for convenience when declaring `sin_` and friends. All we really need is for the `OPCODE` to be the value of the terminals we produce, and for these function-terminals to have the call operator. [note Using _member_call_m_ is a bit loose here, because it defines a variadic template. We could have written unary call operators to ensure that the user can't write call expressions with the wrong number of arguments.] Now, some tranforms: [autodiff_xform] We need a function to tie everything together, since the transforms cannot fill in the values for the placeholders. [autodiff_to_node] Finally, here is the _yap_ version of the function we started with: [autodiff_yap_node_builder] [endsect] [section Transforming Terminals Only] Sometimes it can be useful only to transform the terminals in an expression. For instance, if you have some type you use for SIMD operations called `simd`, you may want to replace all the `double` terminals with `simd`. Perhaps you just want to change out `double` for `float`, or `int` for `std::size_t`. You get the idea. In this example, we're replacing all the terminals with something essentially arbitrary, the sequence of integer terminals `N, N + 1, N + 2, ...`. This makes it easier to observe the result of the replacement in a simple example. [transform_terminals] [endsect] [section Pipable Algorithms] Let's revisit the pipable standard algorithm example from the intro. Here's how you might implement it. [pipable_algorithms] [endsect] [section Boost.Phoenix-style `let()`] Boost.Phoenix has a thing called _let_. It introduces named reusable values that are usable in subsequent expressions. This example is something simliar, though not exactly like Phoenix's version. In Phoenix, a let placeholder is only evaluated once, whereas the example below does something more like macro substitution; each let-placeholder is replaced with its initializing expression everywhere it is used. This example uses C++17's `if constexpr ()`, simply because it makes the example shorter and easier to digest. The `if constexpr ()` bits are not strictly necessary. [let] [endsect] [endsect]