]> git.proxmox.com Git - ceph.git/blame - 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
CommitLineData
7c673cae
FG
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
15Almost 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
17function expects. Operators are also functions. For example, `a + b` is just a
18function 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
22what this means in a short while.]
23
24[heading Partial Function Application]
25
26Think of a function as a black box. You pass arguments and it returns something
27back. The figure below depicts the typical scenario.
28
29[$images/fbox.png]
30
31A fully evaluated function is one in which all the arguments are given. All
32functions in plain C++ are fully evaluated. When you call the `sin(x)` function,
33you have to pass a number x. The function will return a result in return: the
34sin of x. When you call the `add(x, y)` function, you have to pass two numbers x
35and y. The function will return the sum of the two numbers. The figure below is
36a fully evaluated `add` function.
37
38[$images/adder.png]
39
40A partially applied function, on the other hand, is one in which not all the
41arguments are supplied. If we are able to partially apply the function `add`
42above, we may pass only the first argument. In doing so, the function does not
43have all the required information it needs to perform its task to compute and
44return a result. What it returns instead is another function, a lambda function.
45Unlike the original `add` function which has an arity of 2, the resulting lambda
46function has an arity of 1. Why? because we already supplied part of the input:
47`2`
48
49[$images/add2.png]
50
51Now, when we shove in a number into our lambda function, it will return 2 plus
52whatever we pass in. The lambda function essentially remembers 1) the original
53function, `add`, and 2) the partial input, 2. The figure below illustrates a
54case where we pass 3 to our lambda function, which then returns 5:
55
56[$images/add2_call.png]
57
58Obviously, partially applying the `add` function, as we see above, cannot be
59done directly in C++ where we are expected to supply all the arguments that a
60function expects. That's where the Phoenix library comes in. The library
61provides the facilities to do partial function application.
62And even more, with Phoenix, these resulting functions won't be black boxes
63anymore.
64
65[heading STL and higher order functions]
66
67So, what's all the fuss? What makes partial function application so useful?
68Recall our original example in the [link phoenix.starter_kit.lazy_operators
69previous section]:
70
71 std::find_if(c.begin(), c.end(), arg1 % 2 == 1)
72
73The expression `arg1 % 2 == 1` evaluates to a lambda function. `arg1` is a
74placeholder for an argument to be supplied later. Hence, since there's only one
75unsupplied 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
80functions as arguments, and may also return functions as results. Higher order
81functions are functions that are treated like any other objects and can be used
82as arguments and return values from functions.]
83
84[heading Lazy Evaluation]
85
86In Phoenix, to put it more accurately, function evaluation has two stages:
87
88# Partial application
89# Final evaluation
90
91The first stage is handled by a set of generator functions. These are your front
92ends (in the client's perspective). These generators create (through partial
93function application), higher order functions that can be passed on just like
94any other function pointer or function object. The second stage, the actual
95function call, can be invoked or executed anytime in the future, or not at all;
96hence /"lazy"/.
97
98If we look more closely, the first step involves partial function application:
99
100 arg1 % 2 == 1
101
102The second step is the actual function invocation (done inside the `find_if`
103function. These are the back-ends (often, the final invocation is never actually
104seen by the client). In our example, the `find_if`, if we take a look inside,
105we'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
116Again, typically, we, as clients, see only the first step. However, in this
117document and in the examples and tests provided, don't be surprised to see the
118first and second steps juxtaposed in order to illustrate the complete semantics
119of 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
130Usually, we, as clients, write the call-back functions while libraries (such as
131STL) provide the callee (e.g. `find_if`). In case the role is reversed, e.g.
132if you have to write an STL algorithm that takes in a predicate, or develop a
133GUI library that accepts event handlers, you have to be aware of a little known
134problem in C++ called the "__forwarding__".
135
136Look again at the code above:
137
138 (arg1 % 2 == 1)(x)
139
140Notice that, in the second-stage (the final evaluation), we used a variable `x`.
141
142In Phoenix we emulated perfect forwarding through preprocessor macros generating
143code to allow const and non-const references.
144
145We 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
149forward arguments an actor can take. By default, `BOOST_PHOENIX_PERFECT_FORWARDLIMIT`
150is set to 3.]
151
152
153[/
154Be aware that the second stage cannot accept non-const temporaries and literal
155constants. Hence, this will fail:
156
157 (arg1 % 2 == 1)(123) // Error!
158
159Disallowing non-const rvalues partially solves the "__forwarding__" but
160prohibits code like above.
161]
162
163[heading Polymorphic Functions]
164
165Unless otherwise noted, Phoenix generated functions are fully polymorphic. For
166instance, the `add` example above can apply to integers, floating points, user
167defined 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
173evaluates to `std::string("Hello World")`. The observant reader might notice
174that this function call in fact takes in heterogeneous arguments where `arg1`
175is of type `std::string` and `arg2` is of type `char const*`. `add` still works
176because 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