examples.qbk 9.4 KB

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