• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1[section Examples]
2
3Most of these examples are patterned after the examples from Boost.Proto.  In
4part, this was done to underscore where _yap_ can do what Proto can, and where
5it cannot.
6
7Where possible, a Proto-derived example uses syntax in `main()` identical to
8that in the original Proto example.
9
10If you don't know anything about Proto, don't worry.  The examples are useful
11on their own.
12
13
14[section Hello World]
15
16Remember how I mentioned earlier that _yap_ does things in a completely lazy
17way?  _yap_ doesn't ever evaluate your expression eagerly.  Eager evaluation
18can be done, but it's a bit of code.
19
20[hello_world]
21
22[endsect]
23
24
25[section Hello World Redux]
26
27That's better!  Sort of.... We created a custom expression template with an
28eager stream operator.  This gives us eager evaluation, but gives away all the
29lazy AST building-then-evaluating that we're using _ets_ for in the first
30place.  In this simple example, we don't really need it.
31
32[hello_world_redux]
33
34[endsect]
35
36
37[section Minimal]
38
39`minimal_expr` below models _ExprTmpl_; since it has no operators, an
40expression must be built manually.
41
42First, the template itself:
43
44[minimal_template]
45
46This can be used to make a `minimal_expr` plus expression:
47
48[minimal_template_manual_construction]
49
50You can evaluate, transform, or otherwise operate on `minimal_expr`
51expressions using the functions in _yap_ that accept an _Expr_:
52
53[minimal_template_evaluation]
54
55[note Don't use _yap_ this way.  Use the operator macros instead.  This is an
56example contrived only to show you the minimum requirements on a
57_yap_-compatible template.]
58
59[endsect]
60
61
62[section Calc1]
63
64This is the first of several calculator-building examples derived from Proto.
65This first one just builds lazy expressions with placeholders, and evaluates
66them.  Here we can first see how much C++14-and-later language features help
67the end user _emdash_ the Proto version is much, much longer.
68
69[calc1]
70
71[endsect]
72
73
74[section Calc2]
75
76The Proto Calc2 example turns the expressions from Calc1 into callable
77objects.  Using _yap_ you can do this in two ways.
78
79You can just use lambdas to wrap the expressions:
80
81[calc2a]
82
83Or you can use _make_expr_fn_ to make a callable object from your expression:
84
85[calc2b]
86
87[endsect]
88
89
90[section Calc3]
91
92Here, we introduce a _XForm_ used to calculate expression arity, and
93`static_assert()` that the number of parameters passed by the caller matches
94the arity.
95
96[note The `get_arity` _XForm_ doesn't produce an _Expr_, and it does not have
97to.  _XForms_ may produce _Exprs_ or arbitrary values.  They may also have
98arbitrary side effects, and may be stateful.]
99
100[calc3]
101
102[endsect]
103
104
105[section Lazy Vector]
106
107Finally, it starts to get interesting!  This example shows how you can add
108plus and other operations to sequences of data without creating temporaries
109and allocating memory.
110
111[note In this example, we see a terminal type that owns the storage of its
112value, a `std::vector<double>`.  See the Vector example later on to see a
113terminal type that does not.]
114
115[lazy_vector]
116
117[endsect]
118
119
120[section Self-Evaluating Expressions]
121
122In most of the examples, we've seen _yap_ expressions captured, transformed,
123and/or evaluated either manually, or within certain operations that always do
124certain transformations (as in the `operator[]` in the _lazy_vector_ example).
125
126Sometimes, you want the transfrmations to happen just before a _yap_
127expression is used by non-_yap_-aware code.  At other times, you might want an
128entire _yap_ expression to be evaluated if it appears by itself in a statement
129(i.e. as an expression statement).
130
131This example uses C++17's `if constexpr ()`, simply because it makes the
132example shorter and easier to digest.  The `if constexpr ()` bits are not
133strictly necessary.
134
135[self_evaluation]
136
137[endsect]
138
139
140[section TArray]
141
142Proto refers to this as the "mini-library for linear algebra" example.  It
143shows how quite complicated expressions involving sequences can be evaluated
144elementwise, requiring no temporaries.
145
146[note The original Proto example used a terminal that contained an array of
147three `int`s; _yap_ cannot represent this, and so this example uses a
148`std::array<T, 3>` instead.  _yap_ decays `int[3]` to `int *`, since that is
149what is done in a C++ expression.  See _how_treated_ for details.]
150
151[tarray]
152
153[endsect]
154
155
156[section Vec3]
157
158An example using 3-space vectors, a bit like the tarray example.
159
160[vec3]
161
162[endsect]
163
164
165[section Vector]
166
167So far we've only seen examples with custom terminals that own the values in
168the expressions we operate on.  What happens when you've got types that you
169want to operate on, non-intrusively?  Here's how you might do it with
170`std::vector<>`s:
171
172[vector]
173
174[note Though this example only provides overloads for the operations we want
175to define over `std::vector<>`s, the result of each of those operations is an
176_expr_, which uses *all* the operator overloads.  If we wanted to restrict the
177operations on the results too, we could have defined a custom expression
178template with the desired operations, and used that instead of _expr_ in the
179operator macros.]
180
181[endsect]
182
183
184[section Mixed]
185
186This is a lot like the previous Vector example, except that it operates on
187`std::vector<>`s and `std::list<>`s in the same expression.
188
189[mixed]
190
191[endsect]
192
193
194[section Map Assign]
195
196An implementation of `map_list_of()` from Boost.Assign using _yap_.
197
198[map_assign]
199
200[note `map_list_of_expr` defines a generic call operator that matches any
201call, including one with the wrong number of arguments.  This could be fixed
202by adding a `static_assert()` to the `map_list_of_expr` template, or by
203hand-writing the call operator with SFNIAE or concept constraints.]
204
205[endsect]
206
207
208[section Future Group]
209
210An implementation of Howard Hinnant's design for /future groups/.
211
212[future_group]
213
214[endsect]
215
216
217[section Autodiff]
218
219Here we adapt an [@https://en.wikipedia.org/wiki/Automatic_differentiation
220automatic differentiation] library to use _yap_ for specifying the equations
221it operates on.
222
223Autodiff is a pretty small library, and doesn't cover every possible input
224expression.  What it covers is simple arithmetic, and the well-known functions
225`sin`, `cos`, `sqrt`, and `pow`.
226
227Here is how you would form an input to the library using its API.  This is
228taken from the test program that comes with the library.
229
230[autodiff_original_node_builder]
231
232I have a *lot* of trouble understanding what's going on here, and even more
233verifying that the expression written in the comment is actually what the code
234produces.  Let's see if we can do better.
235
236First, we start with a custom expression template, `autodiff_expr`.  It
237supports simple arithmetic, but notice it has no call operator _emdash_ we
238don't want `(a + b)()` to be a valid expression.
239
240[autodiff_expr_template_decl]
241
242We're going to be using a lot of placeholders in our Autodiff expressions, and
243it sure would be nice if they were `autodiff_expr`s and not _exprs_, so that
244only our desired operators are in play.  To do this, we define an operator
245that produces placeholder literals, using the _literal_op_m_ macro:
246
247[autodiff_expr_literals_decl]
248
249Now, how about the functions we need to support, and where do we put the call
250operator?  In other examples we created terminal subclasses or templates to
251get special behavior on terminals.  In this case, we want to create a
252function-terminal template:
253
254[autodiff_function_terminals]
255
256`OPCODE` is an enumeration in Autodiff.  We use it as a non-type template
257parameter for convenience when declaring `sin_` and friends.  All we really
258need is for the `OPCODE` to be the value of the terminals we produce, and for
259these function-terminals to have the call operator.
260
261[note Using _member_call_m_ is a bit loose here, because it defines a variadic
262template.  We could have written unary call operators to ensure that the user
263can't write call expressions with the wrong number of arguments.]
264
265Now, some tranforms:
266
267[autodiff_xform]
268
269We need a function to tie everything together, since the transforms cannot
270fill in the values for the placeholders.
271
272[autodiff_to_node]
273
274Finally, here is the _yap_ version of the function we started with:
275
276[autodiff_yap_node_builder]
277
278[endsect]
279
280[section Transforming Terminals Only]
281
282Sometimes it can be useful only to transform the terminals in an expression.
283For instance, if you have some type you use for SIMD operations called
284`simd<double>`, you may want to replace all the `double` terminals with
285`simd<double>`.  Perhaps you just want to change out `double` for `float`, or
286`int` for `std::size_t`.  You get the idea.
287
288In this example, we're replacing all the terminals with something essentially
289arbitrary, the sequence of integer terminals `N, N + 1, N + 2, ...`.  This
290makes it easier to observe the result of the replacement in a simple example.
291
292[transform_terminals]
293
294[endsect]
295
296[section Pipable Algorithms]
297
298Let's revisit the pipable standard algorithm example from the intro.  Here's
299how you might implement it.
300
301[pipable_algorithms]
302
303[endsect]
304
305[section Boost.Phoenix-style `let()`]
306
307Boost.Phoenix has a thing called _let_.  It introduces named reusable values
308that are usable in subsequent expressions.  This example is something simliar,
309though not exactly like Phoenix's version.  In Phoenix, a let placeholder is
310only evaluated once, whereas the example below does something more like macro
311substitution; each let-placeholder is replaced with its initializing
312expression everywhere it is used.
313
314This example uses C++17's `if constexpr ()`, simply because it makes the
315example shorter and easier to digest.  The `if constexpr ()` bits are not
316strictly necessary.
317
318[let]
319
320[endsect]
321
322[endsect]
323