]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/phoenix/doc/basics.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / phoenix / doc / basics.qbk
1 [/==============================================================================
2 Copyright (C) 2001-2010 Joel de Guzman
3 Copyright (C) 2001-2005 Dan Marsden
4 Copyright (C) 2001-2010 Thomas Heller
5
6 Distributed under the Boost Software License, Version 1.0. (See accompanying
7 file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 ===============================================================================/]
9
10 [section Basics]
11
12 [def __constant_n__ /n/]
13 [def __argument_n__ a/n/]
14
15 Almost everything is a function in the Phoenix library that can be evaluated as
16 `f(a1, a2, ..., __argument_n__)`, where __constant_n__ is the function's arity, or number of arguments that the
17 function expects. Operators are also functions. For example, `a + b` is just a
18 function with arity == 2 (or binary). `a + b` is the same as `add(a, b)`, `a + b
19 + c` is the same as `add(add(a, b), c)`.
20
21 [note Amusingly, functions may even return functions. We shall see
22 what this means in a short while.]
23
24 [heading Partial Function Application]
25
26 Think of a function as a black box. You pass arguments and it returns something
27 back. The figure below depicts the typical scenario.
28
29 [$images/fbox.png]
30
31 A fully evaluated function is one in which all the arguments are given. All
32 functions in plain C++ are fully evaluated. When you call the `sin(x)` function,
33 you have to pass a number x. The function will return a result in return: the
34 sin of x. When you call the `add(x, y)` function, you have to pass two numbers x
35 and y. The function will return the sum of the two numbers. The figure below is
36 a fully evaluated `add` function.
37
38 [$images/adder.png]
39
40 A partially applied function, on the other hand, is one in which not all the
41 arguments are supplied. If we are able to partially apply the function `add`
42 above, we may pass only the first argument. In doing so, the function does not
43 have all the required information it needs to perform its task to compute and
44 return a result. What it returns instead is another function, a lambda function.
45 Unlike the original `add` function which has an arity of 2, the resulting lambda
46 function has an arity of 1. Why? because we already supplied part of the input:
47 `2`
48
49 [$images/add2.png]
50
51 Now, when we shove in a number into our lambda function, it will return 2 plus
52 whatever we pass in. The lambda function essentially remembers 1) the original
53 function, `add`, and 2) the partial input, 2. The figure below illustrates a
54 case where we pass 3 to our lambda function, which then returns 5:
55
56 [$images/add2_call.png]
57
58 Obviously, partially applying the `add` function, as we see above, cannot be
59 done directly in C++ where we are expected to supply all the arguments that a
60 function expects. That's where the Phoenix library comes in. The library
61 provides the facilities to do partial function application.
62 And even more, with Phoenix, these resulting functions won't be black boxes
63 anymore.
64
65 [heading STL and higher order functions]
66
67 So, what's all the fuss? What makes partial function application so useful?
68 Recall our original example in the [link phoenix.starter_kit.lazy_operators
69 previous section]:
70
71 std::find_if(c.begin(), c.end(), arg1 % 2 == 1)
72
73 The expression `arg1 % 2 == 1` evaluates to a lambda function. `arg1` is a
74 placeholder for an argument to be supplied later. Hence, since there's only one
75 unsupplied argument, the lambda function has an arity 1. It just so happens that
76 `find_if` supplies the unsupplied argument as it loops from `c.begin()` to
77 `c.end()`.
78
79 [note Higher order functions are functions which can take other
80 functions as arguments, and may also return functions as results. Higher order
81 functions are functions that are treated like any other objects and can be used
82 as arguments and return values from functions.]
83
84 [heading Lazy Evaluation]
85
86 In Phoenix, to put it more accurately, function evaluation has two stages:
87
88 # Partial application
89 # Final evaluation
90
91 The first stage is handled by a set of generator functions. These are your front
92 ends (in the client's perspective). These generators create (through partial
93 function application), higher order functions that can be passed on just like
94 any other function pointer or function object. The second stage, the actual
95 function call, can be invoked or executed anytime in the future, or not at all;
96 hence /"lazy"/.
97
98 If we look more closely, the first step involves partial function application:
99
100 arg1 % 2 == 1
101
102 The second step is the actual function invocation (done inside the `find_if`
103 function. These are the back-ends (often, the final invocation is never actually
104 seen by the client). In our example, the `find_if`, if we take a look inside,
105 we'll see something like:
106
107 template <class InputIterator, class Predicate>
108 InputIterator
109 find_if(InputIterator first, InputIterator last, Predicate pred)
110 {
111 while (first != last && !pred(*first)) // <--- The lambda function is called here
112 ++first; // passing in *first
113 return first;
114 }
115
116 Again, typically, we, as clients, see only the first step. However, in this
117 document and in the examples and tests provided, don't be surprised to see the
118 first and second steps juxtaposed in order to illustrate the complete semantics
119 of Phoenix expressions. Examples:
120
121 int x = 1;
122 int y = 2;
123
124 std::cout << (arg1 % 2 == 1)(x) << std::endl; // prints 1 or true
125 std::cout << (arg1 % 2 == 1)(y) << std::endl; // prints 0 or false
126
127
128 [heading Forwarding Function Problem]
129
130 Usually, we, as clients, write the call-back functions while libraries (such as
131 STL) provide the callee (e.g. `find_if`). In case the role is reversed, e.g.
132 if you have to write an STL algorithm that takes in a predicate, or develop a
133 GUI library that accepts event handlers, you have to be aware of a little known
134 problem in C++ called the "__forwarding__".
135
136 Look again at the code above:
137
138 (arg1 % 2 == 1)(x)
139
140 Notice that, in the second-stage (the final evaluation), we used a variable `x`.
141
142 In Phoenix we emulated perfect forwarding through preprocessor macros generating
143 code to allow const and non-const references.
144
145 We generate these second-stage overloads for Phoenix expression up to
146 `BOOST_PHOENIX_PERFECT_FORWARD_LIMIT`
147
148 [note You can set `BOOST_PHOENIX_PERFECT_FORWARD_LIMIT`, the predefined maximum perfect
149 forward arguments an actor can take. By default, `BOOST_PHOENIX_PERFECT_FORWARDLIMIT`
150 is set to 3.]
151
152
153 [/
154 Be aware that the second stage cannot accept non-const temporaries and literal
155 constants. Hence, this will fail:
156
157 (arg1 % 2 == 1)(123) // Error!
158
159 Disallowing non-const rvalues partially solves the "__forwarding__" but
160 prohibits code like above.
161 ]
162
163 [heading Polymorphic Functions]
164
165 Unless otherwise noted, Phoenix generated functions are fully polymorphic. For
166 instance, the `add` example above can apply to integers, floating points, user
167 defined complex numbers or even strings. Example:
168
169 std::string h("Hello");
170 char const* w = " World";
171 std::string r = add(arg1, arg2)(h, w);
172
173 evaluates to `std::string("Hello World")`. The observant reader might notice
174 that this function call in fact takes in heterogeneous arguments where `arg1`
175 is of type `std::string` and `arg2` is of type `char const*`. `add` still works
176 because the C++ standard library allows the expression `a + b` where `a` is a
177 `std::string` and `b` is a `char const*`.
178
179 [endsect]
180