blob: e45bcfa926f805b6f53d9fbf193f6e12fa47a7cb [file] [log] [blame]
[library Phoenix
[quickbook 1.3]
[version 2.0]
[authors [de Guzman, Joel], [Marsden, Dan]]
[copyright 2002 2003 2004 2005 Joel de Guzman, Dan Marsden]
[category string-text]
[purpose Lambda Expressions in C++]
[license
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
[@http://www.boost.org/LICENSE_1_0.txt])
]
]
[/ September 2002]
[/ September 2004]
[/ September 2005]
[/ Some links]
[def __note__ [$images/note.png]]
[def __alert__ [$images/alert.png]]
[def __tip__ [$images/tip.png]]
[def __spirit__ [@http://spirit.sourceforge.net Spirit]]
[def __haskell__ [@http://www.haskell.org Haskell]]
[def __mpl__ [@http://www.boost.org/libs/mpl/index.html MPL]]
[def __bll__ [@http://www.boost.org/libs/lambda/doc/index.html BLL]]
[def __fcpp__ [@http://www.cc.gatech.edu/~yannis/fc++/ FC++]]
[def __spirit_repo__ [@http://spirit.sourceforge.net/repository/applications/show_contents.php Spirit Repository]]
[def __spirit_list__ [@https://lists.sourceforge.net/lists/listinfo/spirit-general Spirit Mailing List]]
[def __spirit_general__ [@news://news.gmane.org/gmane.comp.spirit.general Spirit General NNTP news portal]]
[def __gmane__ [@http://www.gmane.org Gmane]]
[def __mlist_archive__ [@http://news.gmane.org/gmane.comp.parsers.spirit.general]]
[def __forwarding__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm Forwarding Function Problem]]
[def __boost_mpl__ [@http://boost.org/libs/mpl/doc/index.html Boost.MPL]]
[def __boost_range__ [@http://boost.org/libs/range/index.html Boost.Range]]
[section Preface]
[:['Functional programming is so called because a program consists entirely of
functions. The main program itself is written as a function which receives the
program's input as its argument and delivers the program's output as its result.
Typically the main function is defined in terms of other functions, which in
turn are defined in terms of still more functions until at the bottom level the
functions are language primitives.]]
[:*John Hughes*-- /Why Functional Programming Matters/]
[$images/lambda_cpp.png]
[h2 Description]
Phoenix enables Functional Programming (FP) in C++. The design and
implementation of Phoenix is highly influenced by __fcpp__ by Yannis Smaragdakis
and Brian McNamara and the __bll__ (Boost Lambda Library) by Jaakko Jaarvi and
Gary Powell. Phoenix is a blend of FC++ and BLL using the implementation
techniques used in the __spirit__ inline parser. Phoenix version 2, this
version, will probably be the last release of the library. Phoenix v2 will be
the basis of the Phoenix and __bll__ merger.
Phoenix is a header only library. It is extremely modular by design. One can
extract and use only a small subset of the full library, literally tearing the
library into small pieces, without fear that the pieces won't work anymore. The
library is organized in highly independent modules and layers.
[h2 How to use this manual]
The Phoenix library is organized in logical modules. This documentation
provides a user's guide and reference for each module in the library. A simple
and clear code example is worth a hundred lines of documentation; therefore, the
user's guide is presented with abundant examples annotated and explained in
step-wise manner. The user's guide is based on examples: lots of them.
As much as possible, forward information (i.e. citing a specific piece of
information that has not yet been discussed) is avoided in the user's manual
portion of each module. In many cases, though, it is unavoidable that advanced
but related topics not be interspersed with the normal flow of discussion. To
alleviate this problem, topics categorized as "advanced" may be skipped at first
reading.
Some icons are used to mark certain topics indicative of their relevance. These
icons precede some text to indicate:
[table Icons
[[Icon] [Name] [Meaning]]
[[__note__] [Note] [Information provided is auxiliary but will
give the reader a deeper insight into a specific
topic. May be skipped.]]
[[__alert__] [Alert] [Information provided is of utmost importance.]]
[[__tip__] [Tip] [A potentially useful and helpful piece of
information.]]
]
This documentation is automatically generated by Spirit QuickBook documentation
tool. QuickBook can be found in the __spirit_repo__.
[h2 Support]
Please direct all questions to Spirit's mailing list. You can subscribe to the
__spirit_list__. The mailing list has a searchable archive. A search link to
this archive is provided in __spirit__'s home page. You may also read and post
messages to the mailing list through __spirit_general__ (thanks to __gmane__).
The news group mirrors the mailing list. Here is a link to the archives:
__mlist_archive__.
[h2 [*/...To my dear daughter, Phoenix/]]
[endsect]
[section Introduction]
[$images/banner.png]
The Phoenix library enables FP techniques such as higher order functions,
/lambda/ (unnamed functions), /currying/ (partial function application) and lazy
evaluation in C++. The focus is more on usefulness and practicality than purity,
elegance and strict adherence to FP principles.
FP is a programming discipline that is not at all tied to a specific language.
FP as a programming discipline can, in fact, be applied to many programming
languages. In the realm of C++ for instance, we are seeing more FP techniques
being applied. C++ is sufficiently rich to support at least some of the most
important facets of FP. C++ is a multi-paradigm programming language. It is not
only procedural. It is not only object oriented. Beneath the core of the
standard C++ library, a closer look into STL gives us a glimpse of FP already in
place. It is obvious that the authors of STL know and practice FP. In the near
future, we shall surely see more FP trickle down into the mainstream.
The truth is, most of the FP techniques can coexist quite well with the standard
object oriented and imperative programming paradigms. When we are using STL
algorithms and functors (function objects) for example, we are already doing FP.
Phoenix is an evolutionary next step.
[endsect]
[section Starter Kit]
Most "quick starts" only get you a few blocks from where you are. From there,
you are on your own. Yet, typically, you'd want to get to the next city. This
starter kit shall be as minimal as possible, yet packed as much power as
possible.
So you are busy and always on the go. You do not wish to spend a lot of time
studying the library. You wish to be spared the details for later when you need
it. For now, all you need to do is to get up to speed as quickly as possible and
start using the library. If this is the case, this is the right place to start.
This chapter is by no means a thorough discourse of the library. For more
information on Phoenix, please take some time to read the rest of the User's
Guide. Yet, if you just want to use the library quickly, now, this chapter will
probably suffice. Rather than taking you to the details of the library, we shall
try to provide you with annotated exemplars instead. Hopefully, this will get
you into high gear quickly.
[h2 Functors everywhere]
Phoenix is built on function objects (functors). The functor is the main
building block. We compose functors to build more complex functors... to build
more complex functors... and so on. Almost everything is a functor.
[note Functors are so ubiquitous in Phoenix that, in the manual, the
words /"functor"/ and /"function"/ are used interchangeably.]
[section Values]
Values are functions! Examples:
val(3)
val("Hello, World")
The first evaluates to a nullary function (a function taking no arguments) that
returns an `int`, `3`. The second evaluates to a nullary function that returns
a `char const(&)[13]`, `"Hello, World"`.
[h2 Lazy Evaluation]
Confused? `val(3)` is a unary function, you say? Yes it is. However, read
carefully: /"evaluates to a nullary function"/. `val(3)` evaluates to (returns) a
nullary function. Aha! `val(3)` returns a function! So, since `val(3)` returns a
function, you can invoke it. Example:
cout << val(3)() << endl;
(See [@../../example/users_manual/values.cpp values.cpp])
[blurb __tip__ Learn more about values [link phoenix.primitives.values here.]]
The second function call (the one with no arguments) calls the nullary function
which then returns `3`. The need for a second function call is the reason why
the function is said to be [*/Lazily Evaluated/]. The first call doesn't do
anything. You need a second call to finally evaluate the thing. The first call
lazily evaluates the function; i.e. doesn't do anything and defers the evaluation
for later.
[h2 Callbacks]
It may not be immediately apparent how lazy evaluation can be useful by just
looking at the example above. Putting the first and second function call in a
single line is really not very useful. However, thinking of `val(3)` as a
callback function (and in most cases they are actually used that way), will make
it clear. Example:
template <typename F>
void print(F f)
{
cout << f() << endl;
}
int
main()
{
print(val(3));
print(val("Hello World"));
return 0;
}
(See [@../../example/users_manual/callback.cpp callback.cpp])
[endsect]
[section References]
References are functions. They hold a reference to a value stored somewhere.
For example, given:
int i = 3;
char const* s = "Hello World";
we create `references` to `i` and `s` this way:
ref(i)
ref(s)
Like `val`, the expressions above evaluates to a nullary function; the first one
returning an `int&`, and the second one returning a `char const*&`.
(See [@../../example/users_manual/references.cpp references.cpp])
[blurb __tip__ Learn more about references [link phoenix.primitives.references here.]]
[endsect]
[section Arguments]
Arguments are also functions? You bet!
Until now, we have been dealing with expressions returning a nullary function.
Arguments, on the other hand, evaluate to an N-ary function. An argument
represents the Nth argument. There are a few predefined arguments arg1,
arg2, arg3, arg4 and so on (and it's __bll__ counterparts: _1, _2, _3, _4 and so
on). Examples:
arg1 // one-or-more argument function that returns its first argument
arg2 // two-or-more argument function that returns its second argument
arg3 // three-or-more argument function that returns its third argument
`argN` returns the Nth argument. Examples:
int i = 3;
char const* s = "Hello World";
cout << arg1(i) << endl; // prints 3
cout << arg2(i, s) << endl; // prints "Hello World"
(See [@../../example/users_manual/arguments.cpp arguments.cpp])
[blurb __tip__ Learn more about arguments [link phoenix.primitives.arguments here.]]
[endsect]
[section Composites]
What we have seen so far, are what are called *primitives*. You can think of
primitives (such as values, references and arguments) as atoms.
Things start to get interesting when we start /composing/ primitives to form
*composites*. The composites can, in turn, be composed to form even more complex
composites.
[endsect]
[section Lazy Operators]
You can use the usual set of operators to form composites. Examples:
arg1 * arg1
ref(x) = arg1 + ref(z)
arg1 = arg2 + (3 * arg3)
ref(x) = arg1[arg2] // assuming arg1 is indexable and arg2 is a valid index
Note the expression: `3 * arg3`. This expression is actually a short-hand
equivalent to: `val(3) * arg3`. In most cases, like above, you can get away with
it. But in some cases, you will have to explicitly wrap your values in `val`.
Rules of thumb:
* In a binary expression (e.g. `3 * arg3`), at least one of the operands must be
a phoenix primitive or composite.
* In a unary expression (e.g. `arg1++`), the single operand must be a phoenix
primitive or composite.
If these basic rules are not followed, the result is either in error, or is
immediately evaluated. Some examples:
ref(x) = 123 // lazy
x = 123 // immediate
ref(x)[0] // lazy
x[0] // immediate
ref(x)[ref(i)] // lazy
ref(x)[i] // lazy (equivalent to ref(x)[val(i)])
x[ref(i)] // illegal (x is not a phoenix primitive or composite)
ref(x[ref(i)]) // illegal (x is not a phoenix primitive or composite)
[blurb __tip__ Learn more about operators [link phoenix.composite.operator here.]]
[h2 First Practical Example]
We've covered enough ground to present a real world example. We want to find the
first odd number in an STL container. Normally we use a functor (function
object) or a function pointer and pass that in to STL's `find_if` generic
function:
Write a function:
bool
is_odd(int arg1)
{
return arg1 % 2 == 1;
}
Pass a pointer to the function to STL's `find_if` algorithm:
find_if(c.begin(), c.end(), &is_odd)
Using Phoenix, the same can be achieved directly with a one-liner:
find_if(c.begin(), c.end(), arg1 % 2 == 1)
The expression `arg1 % 2 == 1` auto-magically creates a functor with the expected
behavior. In FP, this unnamed function is called a lambda function. Unlike the
function pointer version, which is monomorphic (expects and works only with a
fixed type int argument), the Phoenix version is fully polymorphic and works
with any container (of ints, of longs, of bignum, etc.) as long as its elements
can handle the `arg1 % 2 == 1` expression.
(See [@../../example/users_manual/find_if.cpp find_if.cpp])
[blurb __tip__ ...[*That's it, we're done]. Well if you wish to know a little bit
more, read on...]
[endsect]
[section Lazy Statements]
Lazy statements? Sure. There are lazy versions of the C++ statements we all know
and love. For example:
if_(arg1 > 5)
cout << arg1
Say, for example, we wish to print all the elements that are greater than 5
(separated by a comma) in a vector. Here's how we write it:
for_each(v.begin(), v.end(),
if_(arg1 > 5)
[
cout << arg1 << ", "
]
);
(See [@../../example/users_manual/if.cpp if.cpp])
[blurb __tip__ Learn more about statements [link phoenix.composite.statement here.]]
[endsect]
[section Construct, New, Delete, Casts]
You'll probably want to work with objects. There are lazy versions of
constructor calls, `new`, `delete` and the suite of C++ casts. Examples:
construct<std::string>(arg1, arg2) // constructs a std::string from arg1, arg2
new_<std::string>(arg1, arg2) // makes a new std::string from arg1, arg2
delete_(arg1) // deletes arg1 (assumed to be a pointer)
static_cast_<int*>(arg1) // static_cast's arg1 to an int*
[note Take note that, by convention, names that conflict with C++
reserved words are appended with a single trailing underscore `'_'`]
[blurb __tip__ Learn more about this [link phoenix.composite.object here.]]
[endsect]
[section Lazy Functions]
As you write more lambda functions, you'll notice certain patterns that you wish
to refactor as reusable functions. When you reach that point, you'll wish that
ordinary functions can co-exist with phoenix functions. Unfortunately, the
/immediate/ nature of plain C++ functions make them incompatible.
Lazy functions are your friends. The library provides a facility to make lazy
functions. The code below is a rewrite of the `is_odd` function using the
facility:
struct is_odd_impl
{
template <typename Arg>
struct result
{
typedef bool type;
};
template <typename Arg>
bool operator()(Arg arg1) const
{
return arg1 % 2 == 1;
}
};
function<is_odd_impl> is_odd;
[h2 Things to note:]
* `result` is a nested metafunction that reflects the return type of the
function (in this case, bool). This makes the function fully polymorphic:
It can work with arbitrary `Arg` types.
* There are as many Args in the `result` metafunction as in the actual
`operator()`.
* `is_odd_impl` implements the function.
* `is_odd`, an instance of `function<is_odd_impl>`, is the lazy function.
Now, `is_odd` is a truly lazy function that we can use in conjunction with the
rest of phoenix. Example:
find_if(c.begin(), c.end(), is_odd(arg1));
(See [@../../example/users_manual/function.cpp function.cpp])
[h2 Predefined Lazy Functions]
The library is chock full of STL savvy, predefined lazy functions covering the
whole of the STL containers, iterators and algorithms. For example, there are lazy
versions of container related operations such as assign, at, back, begin,
pop_back, pop_front, push_back, push_front, etc. (See [link phoenix.container
Container]).
[endsect]
[section More]
As mentioned earlier, this chapter is not a thorough discourse of the library.
It is meant only to cover enough ground to get you into high gear as quickly as
possible. Some advanced stuff is not discussed here (e.g. [link phoenix.composite.scope
Scopes]); nor are features that provide alternative (short-hand) ways to do the
same things (e.g. [link phoenix.composite.bind Bind] vs. Lazy Functions).
[blurb __tip__ ...*If you still wish to learn more, the read on...*]
[endsect]
[endsect]
[section Basics]
[def __constant_n__ /n/]
[def __argument_n__ a/n/]
Almost everything is a function in the Phoenix library that can be evaluated as
`f(a1, a2, ..., __argument_n__)`, where __constant_n__ is the function's arity, or number of arguments that the
function expects. Operators are also functions. For example, `a + b` is just a
function with arity == 2 (or binary). `a + b` is the same as `add(a, b)`, `a + b
+ c` is the same as `add(add(a, b), c)`.
[note Amusingly, functions may even return functions. We shall see
what this means in a short while.]
[h2 Partial Function Application]
Think of a function as a black box. You pass arguments and it returns something
back. The figure below depicts the typical scenario.
[$images/fbox.png]
A fully evaluated function is one in which all the arguments are given. All
functions in plain C++ are fully evaluated. When you call the `sin(x)` function,
you have to pass a number x. The function will return a result in return: the
sin of x. When you call the `add(x, y)` function, you have to pass two numbers x
and y. The function will return the sum of the two numbers. The figure below is
a fully evaluated `add` function.
[$images/adder.png]
A partially applied function, on the other hand, is one in which not all the
arguments are supplied. If we are able to partially apply the function `add`
above, we may pass only the first argument. In doing so, the function does not
have all the required information it needs to perform its task to compute and
return a result. What it returns instead is another function, a lambda function
--another black box. Unlike the original `add` function which has an arity of 2,
the resulting lambda function has an arity of 1. Why? because we already
supplied part of the input: `2`
[$images/add2.png]
Now, when we shove in a number into our lambda function, it will return 2 plus
whatever we pass in. The lambda function essentially remembers 1) the original
function, `add`, and 2) the partial input, 2. The figure below illustrates a
case where we pass 3 to our lambda function, which then returns 5:
[$images/add2_call.png]
Obviously, partially applying the `add` function, as we see above, cannot be
done directly in C++ where we are expected to supply all the arguments that a
function expects. That's where the Phoenix library comes in. The library
provides the facilities to do partial function application.
[h2 STL and higher order functions]
So, what's all the fuss? What makes partial function application so useful?
Recall our original example in the [link phoenix.starter_kit previous section]:
find_if(c.begin(), c.end(), arg1 % 2 == 1)
The expression `arg1 % 2 == 1` evaluates to a lambda function. `arg1` is a placeholder
for an argument to be supplied later. Hence, since there's only one unsupplied argument, the
lambda function has an arity 1. It just so happens that `find_if` supplies the
unsupplied argument as it loops from `c.begin()` to `c.end()`.
[note Higher order functions are functions which can take other
functions as arguments, and may also return functions as results. Higher order
functions are functions that are treated like any other objects and can be used as
arguments and return values from functions.]
[h2 Lazy Evaluation]
In Phoenix, to put it more accurately, function evaluation has two stages:
# Partial application
# Final evaluation
The first stage is handled by a set of generator functions. These are your front
ends (in the client's perspective). These generators create (through partial
function application), higher order functions that can be passed on just like
any other function pointer or function object. The second stage, the actual
function call, can be invoked or executed anytime in the future, or not at all;
hence /"lazy"/.
If we look more closely, the first step involves partial function application:
arg1 % 2 == 1
The second step is the actual function invocation (done inside the `find_if`
function. These are the back-ends (often, the final invocation is never actually
seen by the client). In our example, the `find_if`, if we take a look inside,
we'll see something like:
template <class InputIterator, class Predicate>
InputIterator
find_if(InputIterator first, InputIterator last, Predicate pred)
{
while (first != last && !pred(*first)) // <--- The lambda function is called here
++first; // passing in *first
return first;
}
Again, typically, we, as clients, see only the first step. However, in this
document and in the examples and tests provided, don't be surprised to see the
first and second steps juxtaposed in order to illustrate the complete semantics
of Phoenix expressions. Examples:
int x = 1;
int y = 2;
cout << (arg1 % 2 == 1)(x) << endl; // prints 1 or true
cout << (arg1 % 2 == 1)(y) << endl; // prints 0 or false
[h2 Forwarding Function Problem]
Usually, we, as clients, write the call-back functions while libraries (such as
STL) provide the callee (e.g. `find_if`). In case the role is reversed, e.g.
if you have to write an STL algorithm that takes in a predicate, or develop a
GUI library that accepts event handlers, you have to be aware of a little known
problem in C++ called the "__forwarding__".
Look again at the code above:
(arg1 % 2 == 1)(x)
Notice that, in the second-stage (the final evaluation), we used a variable `x`.
Be aware that the second stage cannot accept non-const temporaries and literal
constants. Hence, this will fail:
(arg1 % 2 == 1)(123) // Error!
Disallowing non-const rvalues partially solves the "__forwarding__" but
prohibits code like above.
[h2 Polymorphic Functions]
Unless otherwise noted, Phoenix generated functions are fully polymorphic. For
instance, the `add` example above can apply to integers, floating points, user
defined complex numbers or even strings. Example:
std::string h("Hello");
char const* w = " World";
std::string r = add(arg1, arg2)(h, w);
evaluates to `std::string("Hello World")`. The observant reader might notice
that this function call in fact takes in heterogeneous arguments where `arg1` is
of type `std::string` and `arg2` is of type `char const*`. `add` still works
because the C++ standard library allows the expression `a + b` where `a` is a
`std::string` and `b` is a `char const*`.
[endsect]
[section Organization]
Care and attention to detail was given, painstakingly, to the design and
implementation of Phoenix.
The library is organized in four layers:
[$images/organization.png]
The modules are orthogonal, with no cyclic dependencies.
Lower layers do not depend on higher layers. Modules in a layer do not depend on other modules in the same layer.
This means, for example, that Bind can be completely discarded if it is
not required; or one could perhaps take out Operator and Statement and just use Function,
which may be desirable in a pure FP application.
The library has grown from the original Phoenix but still comprises only
header files. There are no object files to link against.
[h2 Core]
The lowest two layers comprise the core.
The `Actor` is the main concept behind the library. Lazy functions are
abstracted as actors. There are only 2
kinds of actors:
# Primitives
# Composites
Primitives provide the basic building blocks of functionality within Phoenix.
Composites are used to combine these primitives together to provide more
powerful functionality.
Composites are composed of zero or more actors. Each actor in a composite can
again be another composite.
[table Modules
[[Module] [Description]]
[[Function] [Lazy functions support (e.g. `add`)]]
[[Operator] [Lazy operators support (e.g. `+`)]]
[[Statement] [Lazy statments (e.g. `if_`, `while_`)]]
[[Object] [Lazy casts (e.g. `static_cast_`),
object creation destruction (e.g.
`new_`, `delete_`)]]
[[Scope] [Support for scopes, local variables and lambda-lambda]]
[[Bind] [Lazy functions from free functions, member functions or member variables.]]
[[Container] [Set of predefined "lazy" functions that work on STL
containers and sequences (e.g. `push_back`).]]
[[Algorithm] [Set of predefined "lazy" versions of the STL algorithms
(e.g. `find_if`).]]
]
Each module is defined in a header file with the same name. For example,
the core module is defined in `<boost/spirit/home/phoenix/core.hpp>`.
[table Includes
[[Module] [File]]
[[Core] [`#include <boost/spirit/home/phoenix/core.hpp>`]]
[[Function] [`#include <boost/spirit/home/phoenix/function.hpp>`]]
[[Operator] [`#include <boost/spirit/home/phoenix/operator.hpp>`]]
[[Statement] [`#include <boost/spirit/home/phoenix/statement.hpp>`]]
[[Object] [`#include <boost/spirit/home/phoenix/object.hpp>`]]
[[Scope] [`#include <boost/spirit/home/phoenix/scope.hpp>`]]
[[Bind] [`#include <boost/spirit/home/phoenix/bind.hpp>`]]
[[Container] [`#include <boost/spirit/home/phoenix/container.hpp>`]]
[[Algorithm] [`#include <boost/spirit/home/phoenix/algorithm.hpp>`]]
]
[blurb __tip__ Finer grained include files are available per feature; see the
succeeding sections.]
[endsect]
[section Actors]
The `Actor` is the main concept behind the library. Actors are function objects.
An actor can accept 0 to `PHOENIX_LIMIT` arguments.
[note You can set `PHOENIX_LIMIT`, the predefined maximum arity an
actor can take. By default, `PHOENIX_LIMIT` is set to 10.]
Phoenix supplies an `actor` class template whose specializations
model the `Actor` concept. `actor` has one template parameter, `Eval`,
that supplies the smarts to evaluate the resulting function.
template <typename Eval>
struct actor : Eval
{
return_type
operator()() const;
template <typename T0>
return_type
operator()(T0& _0) const;
template <typename T0, typename T1>
return_type
operator()(T0& _0, T1& _1) const;
//...
};
The actor class accepts the arguments through a set of function call operators
for 0 to `PHOENIX_LIMIT` arities (Don't worry about the details, for now. Note, for example,
that we skimp over the details regarding `return_type`). The arguments
are then forwarded to the actor's `Eval` for evaluation.
[endsect]
[section Primitives]
Actors are composed to create more complex actors in a tree-like hierarchy. The
primitives are atomic entities that are like the leaves in the tree. Phoenix is
extensible. New primitives can be added anytime. Right out of the box, there are
only a few primitives. This section shall deal with these preset primitives.
[section Arguments]
#include <boost/spirit/home/phoenix/core/argument.hpp>
We use an instance of:
actor<argument<N> >
to represent the Nth function argument. The argument placeholder acts as an
imaginary data-bin where a function argument will be placed.
[h2 Predefined Arguments]
There are a few predefined instances of `actor<argument<N> >` named
`arg1`..`argN`, and its __bll__ counterpart `_1`..`_N`. (where N is a predefined
maximum).
Here are some sample preset definitions of `arg1`..`argN`
actor<argument<0> > const arg1 = argument<0>();
actor<argument<1> > const arg2 = argument<1>();
actor<argument<2> > const arg3 = argument<2>();
and its __bll__ `_1`..`_N` style counterparts:
actor<argument<0> > const _1 = argument<0>();
actor<argument<1> > const _2 = argument<1>();
actor<argument<2> > const _3 = argument<2>();
[note You can set `PHOENIX_ARG_LIMIT`, the predefined maximum
placeholder index. By default, `PHOENIX_ARG_LIMIT` is set to `PHOENIX_LIMIT`
(See [link phoenix.actors Actors]).]
[h2 User Defined Arguments]
When appropriate, you can define your own `argument<N>` names. For example:
actor<argument<0> > x; // note zero based index
`x` may now be used as a parameter to a lazy function:
add(x, 6)
which is equivalent to:
add(arg1, 6)
[h2 Evaluating an Argument]
An argument, when evaluated, selects the Nth argument from the those passed
in by the client.
For example:
char c = 'A';
int i = 123;
const char* s = "Hello World";
cout << arg1(c) << endl; // Get the 1st argument: c
cout << arg1(i, s) << endl; // Get the 1st argument: i
cout << arg2(i, s) << endl; // Get the 2nd argument: s
will print out:
A
123
Hello World
[h2 Extra Arguments]
In C and C++, a function can have extra arguments that are not at all used by
the function body itself. These extra arguments are simply ignored.
Phoenix also allows extra arguments to be passed. For example, recall our
original `add` function:
add(arg1, arg2)
We know now that partially applying this function results to a function that
expects 2 arguments. However, the library is a bit more lenient and allows the
caller to supply more arguments than is actually required. Thus, `add` actually
allows 2 /or more/ arguments. For instance, with:
add(arg1, arg2)(x, y, z)
the third argument `z` is ignored. Taking this further, in-between arguments are
also ignored. Example:
add(arg1, arg5)(a, b, c, d, e)
Here, arguments b, c, and d are ignored. The function `add` takes in the first
argument (`arg1`) and the fifth argument (`arg5`).
[note There are a few reasons why enforcing strict arity is not
desirable. A case in point is the callback function. Typical callback functions
provide more information than is actually needed. Lambda functions are often
used as callbacks.]
[endsect]
[section Values]
#include <boost/spirit/home/phoenix/core/value.hpp>
Whenever we see a constant in a partially applied function, an
actor<value<T> >
(where T is the type of the constant) is automatically created for
us. For instance:
add(arg1, 6)
Passing a second argument, `6`, an `actor<value<int> >` is implicitly created
behind the scenes. This is also equivalent to:
add(arg1, val(6))
`val(x)` generates an `actor<value<T> >` where `T` is the type of `x`. In most
cases, there's no need to explicitly use `val`, but, as we'll see later on,
there are situations where this is unavoidable.
[h2 Evaluating a Value]
Like arguments, values are also actors. As such, values can be evaluated.
Invoking a value gives the value's identity. Example:
cout << val(3)() << val("Hello World")();
prints out "3 Hello World".
[endsect]
[section References]
#include <boost/spirit/home/phoenix/core/reference.hpp>
Values are immutable constants. Attempting to modify a value will result in a
compile time error. When we want the function to modify the parameter, we use a
reference instead. For instance, imagine a lazy function `add_assign`:
void add_assign(T& x, T y) { x += y; } // pseudo code
Here, we want the first function argument, x, to be mutable. Obviously, we
cannot write:
add_assign(1, 2) // error first argument is immutable
In C++, we can pass in a reference to a variable as the first argument in our
example above. Yet, by default, the library forces arguments passed to partially
applied functions functions to be immutable values (see [link phoenix.primitives.values
Values]). To achieve our intent, we use:
actor<reference<T> >
This is similar to `actor<value<T> >` above but instead holds a reference to a
variable.
We normally don't instantiate `actor<reference<T> >` objects directly. Instead we
use `ref`. For example (where `i` is an `int` variable):
add_assign(ref(i), 2)
[h2 Evaluating a Reference]
References are actors. Hence, references can be evaluated. Such invocation gives
the reference's identity. Example:
int i = 3;
char const* s = "Hello World";
cout << ref(i)() << ref(s)();
prints out "3 Hello World"
[endsect]
[section Constant References]
#include <boost/spirit/home/phoenix/core/reference.hpp>
Another free function `cref(cv)` may also be used. `cref(cv)` creates an
`actor<reference<T const&> >` object. This is similar to `actor<value<T> >` but
when the data to be passed as argument to a function is heavy and expensive to
copy by value, the `cref(cv)` offers a lighter alternative.
[endsect]
[section Nothing]
#include <boost/spirit/home/phoenix/core/nothing.hpp>
Finally, the `actor<null_actor>` does nothing; (a "bum", if you will :-).
There's a sole `actor<null_actor>` instance named "nothing". This actor is
actually useful in situations where we don't want to do anything. (See
[link phoenix.composite.statement.for__statement for_ Statement] for example).
[endsect]
[endsect]
[section Composite]
Actors may be combined in a multitude of ways to form composites. Composites are
actors that are composed of zero or more actors. Composition is hierarchical. An
element of the composite can be a primitive or again another composite. The
flexibility to arbitrarily compose hierarchical structures allows us to form
intricate constructions that model complex functions, statements and
expressions.
A composite is-a tuple of 0..N actors. N is the predefined maximum actors a
composite can take.
[note You can set `PHOENIX_COMPOSITE_LIMIT`, the predefined maximum
actors a composite can take. By default, `PHOENIX_COMPOSITE_LIMIT` is set to
`PHOENIX_LIMIT` (See [link phoenix.actors Actors]).]
As mentioned, each of the actors A0..AN can, in turn, be another composite,
since a composite is itself an actor. This makes the composite a recursive
structure. The actual evaluation is handled by a composite specific eval policy.
[section Function]
#include <boost/spirit/home/phoenix/function/function.hpp>
The `function` class template provides a mechanism for implementing lazily
evaluated functions. Syntactically, a lazy function looks like an ordinary C/C++ function.
The function call looks familiar and feels the same as ordinary C++ functions.
However, unlike ordinary functions, the actual function execution is deferred.
Unlike ordinary function pointers or functor objects that need to be explicitly bound through the bind function (see [link phoenix.composite.bind Bind]),
the argument types of these functions are automatically lazily bound.
In order to create a lazy function, we need to implement a model of the
FunctionEval concept. For a function that takes `N` arguments, a model of FunctionEval must
provide:
* An `operator()` that implements that takes `N` arguments, and implements
the function logic.
* A nested metafunction `result<A1, ... AN>` that takes the types of the `N` arguments to
the function and returns the result type of the function. (There is a special case for function
objects that accept no arguments. Such nullary functors are only required to define a typedef
`result_type` that reflects the return type of its `operator()`).
For example, the following type implements the FunctionEval concept, in order to provide a
lazy factorial function:
struct factorial_impl
{
template <typename Arg>
struct result
{
typedef Arg type;
};
template <typename Arg>
Arg operator()(Arg n) const
{
return (n <= 0) ? 1 : n * this->operator()(n-1);
}
};
(See [@../../example/users_manual/factorial.cpp factorial.cpp])
Having implemented the `factorial_impl` type, we can declare and instantiate a lazy
`factorial` function this way:
function<factorial_impl> factorial;
Invoking a lazy function such as `factorial` does not immediately execute the function
object `factorial_impl`. Instead, an [link phoenix.actors actor] object is
created and returned to the caller. Example:
factorial(arg1)
does nothing more than return an actor. A second function call will invoke
the actual factorial function. Example:
int i = 4;
cout << factorial(arg1)(i);
will print out "24".
Take note that in certain cases (e.g. for function objects with state), an
instance of the model of FunctionEval may be passed on to the constructor. Example:
function<factorial_impl> factorial(ftor);
where ftor is an instance of factorial_impl (this is not necessary in this case
as `factorial_impl` does not require any state).
[blurb __alert__ Take care though when using function objects with state because they are
often copied repeatedly, and state may change in one of the copies, rather than the
original.]
[endsect]
[section Operator]
This facility provides a mechanism for lazily evaluating operators.
Syntactically, a lazy operator looks and feels like an ordinary C/C++ infix,
prefix or postfix operator. The operator application looks the same. However,
unlike ordinary operators, the actual operator execution is deferred. Samples:
arg1 + arg2
1 + arg1 * arg2
1 / -arg1
arg1 < 150
We have seen the lazy operators in action (see [link phoenix.starter_kit
Quick Start]). Let's go back and examine them a little bit further:
find_if(c.begin(), c.end(), arg1 % 2 == 1)
Through operator overloading, the expression `arg1 % 2 == 1` actually generates
an actor. This actor object is passed on to STL's `find_if` function. From
the viewpoint of STL, the composite is simply a function object expecting a
single argument of the containers value_type. For each element in `c`,
the element is passed on as an argument `arg1` to the actor (function
object). The actor checks if this is an odd value based on the expression
`arg1 % 2 == 1` where arg1 is replaced by the container's element.
Like lazy functions (see
[link phoenix.composite.function function]), lazy operators are not immediately executed
when invoked. Instead, an actor (see [link phoenix.actors actors])
object is created and returned to the caller. Example:
(arg1 + arg2) * arg3
does nothing more than return an actor. A second function call will evaluate
the actual operators. Example:
int i = 4, j = 5, k = 6;
cout << ((arg1 + arg2) * arg3)(i, j, k);
will print out "54".
Operator expressions are lazily evaluated following four simple rules:
# A binary operator, except `->*` will be lazily evaluated when
/at least/ one of its operands is an actor object
(see [link phoenix.actors actors]).
# Unary operators are lazily evaluated if their argument is an actor object.
# Operator `->*` is lazily evaluated if the left hand argument is an actor object.
# The result of a lazy operator is an actor object that can in turn allow the
applications of rules 1 and 2.
For example, to check the following expression is lazily evaluated:
-(arg1 + 3 + 6)
# Following rule 1, `arg1 + 3` is lazily evaluated since `arg1` is an actor
(see [link phoenix.primitives primitives]).
# The result of this `arg1 + 3` expression is an actor object, following rule 4.
# Continuing, `arg1 + 3 + 6` is again lazily evaluated.
Rule 2.
# By rule 4 again, the result of `arg1 + 3 + 6` is an actor object.
# As `arg1 + 3 + 6` is an actor, `-(arg1 + 3 + 6)` is lazily evaluated. Rule 2.
Lazy-operator application is highly contagious. In most cases, a single `argN`
actor infects all its immediate neighbors within a group (first level or
parenthesized expression).
Note that at least one operand of any operator must be a valid actor
for lazy evaluation to take effect. To force lazy evaluation of an
ordinary expression, we can use `ref(x)`, `val(x)` or `cref(x)` to
transform an operand into a valid actor object (see [link phoenix.primitives primitives].
For example:
1 << 3; // Immediately evaluated
val(1) << 3; // Lazily evaluated
[h2 Supported operators]
[h3 Unary operators]
prefix: ~, !, -, +, ++, --, & (reference), * (dereference)
postfix: ++, --
[h3 Binary operators]
=, [], +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
+, -, *, /, %, &, |, ^, <<, >>
==, !=, <, >, <=, >=
&&, ||, ->*
[h3 Ternary operator]
if_else(c, a, b)
The ternary operator deserves special mention. Since C++ does not allow us to
overload the conditional expression: `c ? a : b`, the if_else pseudo function is
provided for this purpose. The behavior is identical, albeit in a lazy manner.
[h3 Member pointer operator]
a->*member_object_pointer
a->*member_function_pointer
The left hand side of the member pointer operator must be an actor returning a pointer
type. The right hand side of the member pointer operator may be either a pointer to member
object or pointer to member function.
If the right hand side is a member object pointer, the result is an actor which, when evaluated,
returns a reference to that member. For example:
struct A
{
int member;
};
A* a = new A;
...
(arg1->*&A::member)(a); // returns member a->member
If the right hand side is a member function pointer, the result is an actor which, when invoked, calls the specified member function. For example:
struct A
{
int func(int);
};
A* a = new A;
int i = 0;
(arg1->*&A::func)(arg2)(a, i); // returns a->func(i)
[table Include Files
[[Operators] [File]]
[[`-`, `+`, `++`, `--`, `+=`,
`-=`, `*=`, `/=`, `%=`,
`*`, `/`, `%`] [`#include <boost/spirit/home/phoenix/operator/arithmetic.hpp>`]]
[[`&=`, `|=`, `^=`, `<<=`,
`>>=`, `&`, `|`, `^`, `<<`,
`>>`] [`#include <boost/spirit/home/phoenix/operator/bitwise.hpp>`]]
[[`==`, `!=`, `<`,
`<=`, `>`, `>=`] [`#include <boost/spirit/home/phoenix/operator/comparison.hpp>`]]
[[`<<`, `>>`] [`#include <boost/spirit/home/phoenix/operator/io.hpp>`]]
[[`!`, &&, `||`] [`#include <boost/spirit/home/phoenix/operator/logical.hpp>`]]
[[`&x`, `*p`, `=`, `[]`] [`#include <boost/spirit/home/phoenix/operator/self.hpp>`]]
[[`if_else(c, a, b)`] [`#include <boost/spirit/home/phoenix/operator/if_else.hpp>`]]
[[`->*`] [`#include <boost/spirit/home/phoenix/operator/member.hpp>`]]
]
[endsect]
[section Statement]
[*/Lazy statements.../]
The primitives and composite building blocks presented so far are sufficiently
powerful to construct quite elaborate structures. We have presented lazy-
functions and lazy-operators. How about lazy-statements? First, an appetizer:
Print all odd-numbered contents of an STL container using `std::for_each`
([@../../example/users_manual/all_odds.cpp all_odds.cpp]):
for_each(c.begin(), c.end(),
if_(arg1 % 2 == 1)
[
cout << arg1 << ' '
]
);
Huh? Is that valid C++? Read on...
Yes, it is valid C++. The sample code above is as close as you can get to the
syntax of C++. This stylized C++ syntax differs from actual C++ code. First, the
`if` has a trailing underscore. Second, the block uses square brackets instead
of the familiar curly braces {}.
[note *C++ in C++?*
In as much as __spirit__ attempts to mimic EBNF in C++,
Phoenix attempts to mimic C++ in C++!!!
]
Here are more examples with annotations. The code almost speaks for itself.
[section Block Statement]
#include <boost/spirit/home/phoenix/statement/sequence.hpp>
Syntax:
statement,
statement,
....
statement
Basically, these are comma separated statements. Take note that unlike the C/C++
semicolon, the comma is a separator put *in-between* statements. This is like
Pascal's semicolon separator, rather than C/C++'s semicolon terminator. For
example:
statement,
statement,
statement, // ERROR!
Is an error. The last statement should not have a comma. Block statements can be
grouped using the parentheses. Again, the last statement in a group should not
have a trailing comma.
statement,
statement,
(
statement,
statement
),
statement
Outside the square brackets, block statements should be grouped. For example:
for_each(c.begin(), c.end(),
(
do_this(arg1),
do_that(arg1)
)
);
Wrapping a comma operator chain around a parentheses pair blocks the
interpretation as an argument separator. The reason for the exception for
the square bracket operator is that the operator always takes exactly one
argument, so it "transforms" any attempt at multiple arguments with a comma
operator chain (and spits out an error for zero arguments).
[endsect]
[section if_ Statement]
#include <boost/spirit/home/phoenix/statement/if.hpp>
We have seen the `if_` statement. The syntax is:
if_(conditional_expression)
[
sequenced_statements
]
[endsect]
[section if\_else\_ statement]
#include <boost/spirit/home/phoenix/statement/if.hpp>
The syntax is
if_(conditional_expression)
[
sequenced_statements
]
.else_
[
sequenced_statements
]
Take note that `else` has a leading dot and a trailing underscore: `.else_`
Example: This code prints out all the elements and appends `" > 5"`, `" == 5"`
or `" < 5"` depending on the element's actual value:
for_each(c.begin(), c.end(),
if_(arg1 > 5)
[
cout << arg1 << " > 5\n"
]
.else_
[
if_(arg1 == 5)
[
cout << arg1 << " == 5\n"
]
.else_
[
cout << arg1 << " < 5\n"
]
]
);
Notice how the `if_else_` statement is nested.
[endsect]
[section switch_ statement]
#include <boost/spirit/home/phoenix/statement/switch.hpp>
The syntax is:
switch_(integral_expression)
[
case_<integral_value>(sequenced_statements),
...
default_<integral_value>(sequenced_statements)
]
A comma separated list of cases, and an optional default can be provided. Note unlike
a normal switch statement, cases do not fall through.
Example: This code prints out `"one"`, `"two"` or `"other value"` depending on the
element's actual value:
for_each(c.begin(), c.end(),
switch_(arg1)
[
case_<1>(cout << val("one") << '\n'),
case_<2>(cout << val("two") << '\n'),
default_(cout << val("other value") << '\n')
]
);
[endsect]
[section while_ Statement]
#include <boost/spirit/home/phoenix/statement/while.hpp>
The syntax is:
while_(conditional_expression)
[
sequenced_statements
]
Example: This code decrements each element until it reaches zero and prints out
the number at each step. A newline terminates the printout of each value.
for_each(c.begin(), c.end(),
(
while_(arg1--)
[
cout << arg1 << ", "
],
cout << val("\n")
)
);
[endsect]
[section do\_while\_ Statement]
#include <boost/spirit/home/phoenix/statement/do_while.hpp>
The syntax is:
do_
[
sequenced_statements
]
.while_(conditional_expression)
Again, take note that `while` has a leading dot and a trailing underscore:
`.while_`
Example: This code is almost the same as the previous example above with a
slight twist in logic.
for_each(c.begin(), c.end(),
(
do_
[
cout << arg1 << ", "
]
.while_(arg1--),
cout << val("\n")
)
);
[endsect]
[section for_ Statement]
#include <boost/spirit/home/phoenix/statement/for.hpp>
The syntax is:
for_(init_statement, conditional_expression, step_statement)
[
sequenced_statements
]
It is again very similar to the C++ for statement. Take note that the
init\_statement, conditional\_expression and step\_statement are separated by the
comma instead of the semi-colon and each must be present (i.e. `for_(,,)` is
invalid). This is a case where the [link phoenix.primitives.nothing nothing]
actor can be useful.
Example: This code prints each element N times where N is the element's value. A
newline terminates the printout of each value.
int iii;
for_each(c.begin(), c.end(),
(
for_(ref(iii) = 0, ref(iii) < arg1, ++ref(iii))
[
cout << arg1 << ", "
],
cout << val("\n")
)
);
As before, all these are lazily evaluated. The result of such statements are in
fact composites that are passed on to STL's for_each function. In the viewpoint
of `for_each`, what was passed is just a functor, no more, no less.
[note Unlike lazy functions and lazy operators, lazy statements always
return void.]
[endsect]
[section try_ catch_ Statement]
#include <boost/spirit/home/phoenix/statement/try_catch.hpp>
The syntax is:
try_
[
sequenced_statements
]
.catch_<exception_type>()
[
sequenced_statements
]
...
.catch_all
[
sequenced_statement
]
Note the usual underscore after try and catch, and the extra parentheses required
after the catch.
Example: The following code calls the (lazy) function `f` for each element, and
prints messages about different exception types it catches.
try_
[
f(arg1)
]
.catch_<runtime_error>()
[
cout << val("caught runtime error or derived\n")
]
.catch_<exception>()
[
cout << val("caught exception or derived\n")
]
.catch_all
[
cout << val("caught some other type of exception\n")
]
[endsect]
[section throw_]
#include <boost/spirit/home/phoenix/statement/throw.hpp>
As a natural companion to the try/catch support, the statement module provides
lazy throwing and rethrowing of exceptions.
The syntax to throw an exception is:
throw_(exception_expression)
The syntax to rethrow an exception is:
throw_()
Example: This code extends the try/catch example, rethrowing exceptions derived from
runtime_error or exception, and translating other exception types to runtime_errors.
try_
[
f(arg1)
]
.catch_<runtime_error>()
[
cout << val("caught runtime error or derived\n"),
throw_()
]
.catch_<exception>()
[
cout << val("caught exception or derived\n"),
throw_()
]
.catch_all
[
cout << val("caught some other type of exception\n"),
throw_(runtime_error("translated exception"))
]
[endsect]
[endsect]
[section Object]
The Object module deals with object construction, destruction and conversion.
The module provides /"lazy"/ versions of C++'s object constructor, `new`,
`delete`, `static_cast`, `dynamic_cast`, `const_cast` and `reinterpret_cast`.
[h2 Construction]
[*/Lazy constructors.../]
#include <boost/spirit/home/phoenix/object/construct.hpp>
Lazily construct an object from an arbitrary set of arguments:
construct<T>(ctor_arg1, ctor_arg2, ..., ctor_argN);
where the given parameters are the parameters to the constructor of the object of
type T (This implies, that type T is expected to have a constructor with a
corresponding set of parameter types.).
Example:
construct<std::string>(arg1, arg2)
Constructs a `std::string` from `arg1` and `arg2`.
[note The maximum number of actual parameters is limited by the
preprocessor constant PHOENIX_COMPOSITE_LIMIT. Note though, that this limit
should not be greater than PHOENIX_LIMIT. By default, `PHOENIX_COMPOSITE_LIMIT`
is set to `PHOENIX_LIMIT` (See [link phoenix.actors Actors]).]
[h2 New]
[*/Lazy new.../]
#include <boost/spirit/home/phoenix/object/new.hpp>
Lazily construct an object, on the heap, from an arbitrary set of arguments:
new_<T>(ctor_arg1, ctor_arg2, ..., ctor_argN);
where the given parameters are the parameters to the constructor of the object of
type T (This implies, that type T is expected to have a constructor with a
corresponding set of parameter types.).
Example:
new_<std::string>(arg1, arg2) // note the spelling of new_ (with trailing underscore)
Creates a `std::string` from `arg1` and `arg2` on the heap.
[note Again, the maximum number of actual parameters is limited by the
preprocessor constant PHOENIX_COMPOSITE_LIMIT. See the note above.]
[h2 Delete]
[*/Lazy delete.../]
#include <boost/spirit/home/phoenix/object/delete.hpp>
Lazily delete an object, from the heap:
delete_(arg);
where arg is assumed to be a pointer to an object.
Example:
delete_<std::string>(arg1) // note the spelling of delete_ (with trailing underscore)
[h2 Casts]
[*/Lazy casts.../]
#include <boost/spirit/home/phoenix/object/static_cast.hpp>
#include <boost/spirit/home/phoenix/object/dynamic_cast.hpp>
#include <boost/spirit/home/phoenix/object/const_cast.hpp>
#include <boost/spirit/home/phoenix/object/reinterpret_cast.hpp>
The set of lazy C++ cast template functions provide a way of lazily casting an
object of a certain type to another type. The syntax resembles the well known
C++ casts. Take note however that the lazy versions have a trailing underscore.
static_cast_<T>(lambda_expression)
dynamic_cast_<T>(lambda_expression)
const_cast_<T>(lambda_expression)
reinterpret_cast_<T>(lambda_expression)
Example:
static_cast_<Base*>(&arg1)
Static-casts the address of `arg1` to a `Base*`.
[endsect]
[section Scope]
Up until now, the most basic ingredient is missing: creation of and access to
local variables in the stack. When recursion comes into play, you will soon
realize the need to have true local variables. It may seem that we do not need
this at all since an unnamed lambda function cannot call itself anyway; at least
not directly. With some sort of arrangement, situations will arise where a
lambda function becomes recursive. A typical situation occurs when we store a
lambda function in a [@http://www.boost.org/libs/function Boost.Function],
essentially naming the unnamed lambda.
There will also be situations where a lambda function gets passed as an argument
to another function. This is a more common situation. In this case, the lambda
function assumes a new scope; new arguments and possibly new local variables.
This section deals with local variables and nested lambda scopes.
[h2 Local Variables]
#include <boost/spirit/home/phoenix/scope/local_variable.hpp>
We use an instance of:
actor<local_variable<Key> >
to represent a local variable. The local variable acts as an imaginary data-bin
where a local, stack based data will be placed. `Key` is an arbitrary type that
is used to identify the local variable. Example:
struct size_key;
actor<local_variable<size_key> > size;
[h2 Predefined Local Variables]
There are a few predefined instances of `actor<local_variable<Key> >`
named `_a`..`_z` that you can already use. To make use of them, simply use the
`namespace boost::phoenix::local_names`:
using namespace boost::phoenix::local_names;
[h2 let]
#include <boost/spirit/home/phoenix/scope/let.hpp>
You declare local variables using the syntax:
let(local-declarations)
[
let-body
]
`let` allows 1..N local variable declarations (where N ==
`PHOENIX_LOCAL_LIMIT`). Each declaration follows the form:
local-id = lambda-expression
[note You can set `PHOENIX_LOCAL_LIMIT`, the predefined maximum local
variable declarations in a let expression. By default, `PHOENIX_LOCAL_LIMIT` is
set to `PHOENIX_LIMIT`.]
Example:
let(_a = 123, _b = 456)
[
_a + _b
]
[h2 Reference Preservation]
The type of the local variable assumes the type of the lambda- expression. Type
deduction is reference preserving. For example:
let(_a = arg1, _b = 456)
`_a` assumes the type of `arg1`: a reference to an argument, while `_b` has type
`int`.
Consider this:
int i = 1;
let(_a = arg1)
[
cout << --_a << ' '
]
(i);
cout << i << endl;
the output of above is : 0 0
While with this:
int i = 1;
let(_a = val(arg1))
[
cout << --_a << ' '
]
(i);
cout << i << endl;
the output is : 0 1
Reference preservation is necessary because we need to have L-value access to
outer lambda-scopes (especially the arguments). `arg`s and `ref`s are L-values.
`val`s are R-values.
[h2 Visibility]
The scope and lifetimes of the local variables is limited within the let-body.
`let` blocks can be nested. A local variable may hide an outer local variable.
For example:
let(_x = 1, _y = ", World")
[
// _x here is an int: 1
let(_x = "Hello") // hides the outer _x
[
cout << _x << _y // prints "Hello, World"
]
]
The RHS (right hand side lambda-expression) of each local-declaration cannot
refer to any LHS local-id. At this point, the local-ids are not in scope yet;
they will only be in scope in the let-body. The code below is in error:
let(
_a = 1
, _b = _a // Error: _a is not in scope yet
)
[
// _a and _b's scope starts here
/*. body .*/
]
However, if an outer let scope is available, this will be searched. Since
the scope of the RHS of a local-declaration is the outer scope enclosing
the let, the RHS of a local-declaration can refer to a local variable of
an outer scope:
let(_a = 1)
[
let(
_a = 1
, _b = _a // Ok. _a refers to the outer _a
)
[
/*. body .*/
]
]
[h2 lambda]
#include <boost/spirit/home/phoenix/scope/lambda.hpp>
A lot of times, you'd want to write a lazy function that accepts one or more
functions (higher order functions). STL algorithms come to mind, for example.
Consider a lazy version of `stl::for_each`:
struct for_each_impl
{
template <typename C, typename F>
struct result
{
typedef void type;
};
template <typename C, typename F>
void operator()(C& c, F f) const
{
std::for_each(c.begin(), c.end(), f);
}
};
function<for_each_impl> const for_each = for_each_impl();
Notice that the function accepts another function, `f` as an argument. The scope
of this function, `f`, is limited within the `operator()`. When `f` is called
inside `std::for_each`, it exists in a new scope, along with new arguments and,
possibly, local variables. This new scope is not at all related to the outer
scopes beyond the `operator()`.
Simple syntax:
lambda
[
lambda-body
]
Like `let`, local variables may be declared, allowing 1..N local variable
declarations (where N == `PHOENIX_LOCAL_LIMIT`):
lambda(local-declarations)
[
lambda-body
]
The same restrictions apply with regard to scope and visibility. The RHS
(right hand side lambda-expression) of each local-declaration cannot refer
to any LHS local-id. The local-ids are not in scope yet; they will be in
scope only in the lambda-body:
lambda(
_a = 1
, _b = _a // Error: _a is not in scope yet
)
See [link phoenix.composite.scope.visibility `let` Visibility] above for more information.
Example: Using our lazy `for_each` let's print all the elements in a container:
for_each(arg1, lambda[cout << arg1])
As far as the arguments are concerned (arg1..argN), the scope in which the
lambda-body exists is totally new. The left `arg1` refers to the argument passed
to `for_each` (a container). The right `arg1` refers to the argument passed by
`std::for_each` when we finally get to call `operator()` in our `for_each_impl`
above (a container element).
Yet, we may wish to get information from outer scopes. While we do not have
access to arguments in outer scopes, what we still have is access to local
variables from outer scopes. We may only be able to pass argument related
information from outer `lambda` scopes through the local variables.
[note This is a crucial difference between `let` and `lambda`: `let`
does not introduce new arguments; `lambda` does.]
Another example: Using our lazy `for_each`, and a lazy `push_back`:
struct push_back_impl
{
template <typename C, typename T>
struct result
{
typedef void type;
};
template <typename C, typename T>
void operator()(C& c, T& x) const
{
c.push_back(x);
}
};
function<push_back_impl> const push_back = push_back_impl();
write a lambda expression that accepts:
# a 2-dimensional container (e.g. `vector<vector<int> >`)
# a container element (e.g. `int`)
and pushes-back the element to each of the `vector<int>`.
Solution:
for_each(arg1,
lambda(_a = arg2)
[
push_back(arg1, _a)
]
)
Since we do not have access to the arguments of the outer scopes beyond the
lambda-body, we introduce a local variable `_a` that captures the second outer
argument: `arg2`. Hence: _a = arg2. This local variable is visible inside the
lambda scope.
(See [@../../example/users_manual/lambda.cpp lambda.cpp])
[endsect]
[section Bind]
['Binding] is the act of tying together a function to some arguments for
deferred (lazy) evaluation. Named [link phoenix.composite.function Lazy functions]
require a bit of typing. Unlike (unnamed) lambda expressions, we need to write a
functor somewhere off-line, detached from the call site. If you wish to transform a
plain function, member function or member variable to a lambda expression, `bind`
is your friend.
[note Take note that binders are monomorphic. Rather than binding
functions, the preferred way is to write true generic and polymorphic [link
phoenix.composite.function lazy-functions]. However, since most of the time we
are dealing with adaptation of existing code, binders get the job done faster.]
There is a set of overloaded `bind` template functions. Each `bind(x)`
function generates a suitable binder object, a [link phoenix.composite
composite].
[h2 Binding Functions]
#include <boost/spirit/home/phoenix/bind/bind_function.hpp>
Example, given a function `foo`:
void foo(int n)
{
std::cout << n << std::endl;
}
Here's how the function `foo` may be bound:
bind(&foo, arg1)
This is now a full-fledged [link phoenix.composite composite] that can finally
be evaluated by another function call invocation. A second function call will
invoke the actual `foo` function. Example:
int i = 4;
bind(&foo, arg1)(i);
will print out "4".
[h2 Binding Member Functions]
#include <boost/spirit/home/phoenix/bind/bind_member_function.hpp>
Binding member functions can be done similarly. A bound member function takes in
a pointer or reference to an object as the first argument. For instance, given:
struct xyz
{
void foo(int) const;
};
`xyz`'s `foo` member function can be bound as:
bind(&xyz::foo, obj, arg1) // obj is an xyz object
Take note that a lazy-member functions expects the first argument to be a
pointer or reference to an object. Both the object (reference or pointer) and
the arguments can be lazily bound. Examples:
xyz obj;
bind(&xyz::foo, arg1, arg2) // arg1.foo(arg2)
bind(&xyz::foo, obj, arg1) // obj.foo(arg1)
bind(&xyz::foo, obj, 100) // obj.foo(100)
[h2 Binding Member Variables]
#include <boost/spirit/home/phoenix/bind/bind_member_variable.hpp>
Member variables can also be bound much like member functions. Member variables
are not functions. Yet, like the [link phoenix.primitives.references `ref(x)`] that
acts like a nullary function returning a reference to the data, member variables,
when bound, act like a unary function, taking in a pointer or reference to an
object as its argument and returning a reference to the bound member variable.
For instance, given:
struct xyz
{
int v;
};
`xyz::v` can be bound as:
bind(&xyz::v, obj) // obj is an xyz object
As noted, just like the bound member function, a bound member variable also
expects the first (and only) argument to be a pointer or reference to an object.
The object (reference or pointer) can be lazily bound. Examples:
xyz obj;
bind(&xyz::v, arg1) // arg1.v
bind(&xyz::v, obj) // obj.v
bind(&xyz::v, arg1)(obj) = 4 // obj.v = 4
[endsect]
[endsect]
[section Container]
#include <boost/spirit/home/phoenix/container.hpp>
The container module predefines a set of lazy functions that work on STL
containers. These functions provide a mechanism for the lazy evaluation of the
public member functions of the STL containers. The lazy functions are thin
wrappers that simply forward to their respective counterparts in the STL
library.
Lazy functions are provided for all of the member functions of the following
containers:
* deque
* list
* map
* multimap
* vector
Indeed, should your class have member functions with the same names and
signatures as those listed below, then it will automatically be supported. To
summarize, lazy functions are provided for member functions:
* assign
* at
* back
* begin
* capacity
* clear
* empty
* end
* erase
* front
* get_allocator
* insert
* key_comp
* max_size
* pop_back
* pop_front
* push_back
* push_front
* rbegin
* rend
* reserve
* resize
* size
* splice
* value_comp
The lazy functions' names are the same as the corresponding member function. The
difference is that the lazy functions are free functions and therefore does not
use the member "dot" syntax.
[table Sample usage
[["Normal" version] ["Lazy" version]]
[[`my_vector.at(5)`] [`at(arg1, 5)`]]
[[`my_list.size()`] [`size(arg1)`]]
[[`my_vector1.swap(my_vector2)`] [`swap(arg1, arg2)`]]
]
Notice that member functions with names that clash with stl algorithms are
absent. This will be provided in Phoenix's algorithm module.
No support is provided here for lazy versions of `operator+=`, `operator[]` etc.
Such operators are not specific to STL containers and lazy versions can
therefore be found in [link phoenix.composite.operator operators].
The following table describes the container functions and their semantics.
[blurb __tip__ Arguments in brackets denote optional parameters.]
[table Lazy STL Container Functions
[[Function] [Semantics]]
[[`assign(c, a[, b, c])`] [`c.assign(a[, b, c])`]]
[[`at(c, i)`] [`c.at(i)`]]
[[`back(c)`] [`c.back()`]]
[[`begin(c)`] [`c.begin()`]]
[[`capacity(c)`] [`c.capacity()`]]
[[`clear(c)`] [`c.clear()`]]
[[`empty(c)`] [`c.empty()`]]
[[`end(c)`] [`c.end()`]]
[[`erase(c, a[, b])`] [`c.erase(a[, b])`]]
[[`front(c)`] [`c.front()`]]
[[`get_allocator(c)`] [`c.get_allocator()`]]
[[`insert(c, a[, b, c])`] [`c.insert(a[, b, c])`]]
[[`key_comp(c)`] [`c.key_comp()`]]
[[`max_size(c)`] [`c.max_size()`]]
[[`pop_back(c)`] [`c.pop_back()`]]
[[`pop_front(c)`] [`c.pop_front()`]]
[[`push_back(c, d)`] [`c.push_back(d)`]]
[[`push_front(c, d)`] [`c.push_front(d)`]]
[[`pop_front(c)`] [`c.pop_front()`]]
[[`rbegin(c)`] [`c.rbegin()`]]
[[`rend(c)`] [`c.rend()`]]
[[`reserve(c, n)`] [`c.reserve(n)`]]
[[`resize(c, a[, b])`] [`c.resize(a[, b])`]]
[[`size(c)`] [`c.size()`]]
[[`splice(c, a[, b, c, d])`] [`c.splice(a[, b, c, d])`]]
[[`value_comp(c)`] [`c.value_comp()`]]
]
[endsect]
[section Algorithm]
#include <boost/spirit/home/phoenix/algorithm.hpp>
The algorithm module provides wrappers for the standard algorithms in the
`<algorithm>` and `<numeric>` headers.
The algorithms are divided into the categories iteration, transformation and querying,
modelling the __boost_mpl__ library. The different algorithm classes can be
included using the headers:
#include <boost/spirit/home/phoenix/stl/algorithm/iteration.hpp>
#include <boost/spirit/home/phoenix/stl/algorithm/transformation.hpp>
#include <boost/spirit/home/phoenix/stl/algorithm/querying.hpp>
The functions of the algorithm module take ranges as arguments where
appropriate. This is different to the standard
library, but easy enough to pick up. Ranges are described in detail in the
__boost_range__ library.
For example, using the standard copy algorithm to copy between 2 arrays:
int array[] = {1, 2, 3};
int output[3];
std::copy(array, array + 3, output); // We have to provide iterators
// to both the start and end of array
The analogous code using the phoenix algorithm module is:
int array[] = {1, 2, 3};
int output[3];
copy(arg1, arg2)(array, output); // Notice only 2 arguments, the end of
// array is established automatically
The __boost_range__ library provides support for standard containers, strings and
arrays, and can be extended to support additional types.
The following tables describe the different categories of algorithms, and their
semantics.
[blurb __tip__ Arguments in brackets denote optional parameters.]
[table Iteration Algorithms
[[Function] [stl Semantics]]
[[`for_each(r, c)`] [`for_each(begin(r), end(r), f)`]]
[[`accumulate(r, o[, f])`] [`accumulate(begin(r), end(r), o[, f])`]]
]
[table Querying Algorithms
[[Function] [stl Semantics]]
[[`find(r, a)`] [`find(begin(r), end(r), a)`]]
[[`find_if(r, f)`] [`find_if(begin(r), end(r), f)`]]
[[`find_end(r1, r2[, f])`] [`find_end(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`find_first_of(r1, r2[, f])`] [`find_first_of(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`adjacent_find(r[, f])`] [`adjacent_find(begin(r), end(r)[, f])`]]
[[`count(r, a)`] [`count(begin(r), end(r), a)`]]
[[`count_if(r, f)`] [`count_if(begin(r), end(r), f)`]]
[[`distance(r)`] [`distance(begin(r), end(r))`]]
[[`mismatch(r, i[, f])`] [`mismatch(begin(r), end(r), i[, f])`]]
[[`equal(r, i[, f])`] [`equal(begin(r), end(r), i[, f])`]]
[[`search(r1, r2[, f])`] [`search(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`lower_bound(r, a[, f])`] [`lower_bound(begin(r), end(r), a[, f])`]]
[[`upper_bound(r, a[, f])`] [`upper_bound(begin(r), end(r), a[, f])`]]
[[`equal_range(r, a[, f])`] [`equal_range(begin(r), end(r), a[, f])`]]
[[`binary_search(r, a[, f])`] [`binary_search(begin(r), end(r), a[, f])`]]
[[`includes(r1, r2[, f])`] [`includes(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`min_element(r[, f])`] [`min_element(begin(r), end(r)[, f])`]]
[[`max_element(r[, f])`] [`max_element(begin(r), end(r)[, f])`]]
[[`lexicographical_compare(r1, r2[, f])`] [`lexicographical_compare(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
]
[table Transformation Algorithms
[[Function] [stl Semantics]]
[[`copy(r, o)`] [`copy(begin(r), end(r), o)`]]
[[`copy_backward(r, o)`] [`copy_backward(begin(r), end(r), o)`]]
[[`transform(r, o, f)`] [`transform(begin(r), end(r), o, f)`]]
[[`transform(r, i, o, f)`] [`transform(begin(r), end(r), i, o, f)`]]
[[`replace(r, a, b)`] [`replace(begin(r), end(r), a, b)`]]
[[`replace_if(r, f, a)`] [`replace(begin(r), end(r), f, a)`]]
[[`replace_copy(r, o, a, b)`] [`replace_copy(begin(r), end(r), o, a, b)`]]
[[`replace_copy_if(r, o, f, a)`] [`replace_copy_if(begin(r), end(r), o, f, a)`]]
[[`fill(r, a)`] [`fill(begin(r), end(r), a)`]]
[[`fill_n(r, n, a)`] [`fill_n(begin(r), n, a)`]]
[[`generate(r, f)`] [`generate(begin(r), end(r), f)`]]
[[`generate_n(r, n, f)`] [`generate_n(begin(r), n, f)`]]
[[`remove(r, a)`] [`remove(begin(r), end(r), a)`]]
[[`remove_if(r, f)`] [`remove_if(begin(r), end(r), f)`]]
[[`remove_copy(r, o, a)`] [`remove_copy(begin(r), end(r), o, a)`]]
[[`remove_copy_if(r, o, f)`] [`remove_copy_if(begin(r), end(r), o, f)`]]
[[`unique(r[, f])`] [`unique(begin(r), end(r)[, f])`]]
[[`unique_copy(r, o[, f])`] [`unique_copy(begin(r), end(r), o[, f])`]]
[[`reverse(r)`] [`reverse(begin(r), end(r))`]]
[[`reverse_copy(r, o)`] [`reverse_copy(begin(r), end(r), o)`]]
[[`rotate(r, m)`] [`rotate(begin(r), m, end(r))`]]
[[`rotate_copy(r, m, o)`] [`rotate_copy(begin(r), m, end(r), o)`]]
[[`random_shuffle(r[, f])`] [`random_shuffle(begin(r), end(r), f)`]]
[[`partition(r, f)`] [`partition(begin(r), end(r), f)`]]
[[`stable_partition(r, f)`] [`stable_partition(begin(r), end(r), f)`]]
[[`sort(r[, f])`] [`sort(begin(r), end(r)[, f])`]]
[[`stable_sort(r[, f])`] [`stable_sort(begin(r), end(r)[, f])`]]
[[`partial_sort(r, m[, f])`] [`partial_sort(begin(r), m, end(r)[, f])`]]
[[`partial_sort_copy(r1, r2[, f])`] [`partial_sort_copy(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`nth_element(r, n[, f])`] [`nth_element(begin(r), n, end(r)[, f])`]]
[[`merge(r1, r2, o[, f])`] [`merge(begin(r1), end(r1), begin(r2), end(r2), o[, f])`]]
[[`inplace_merge(r, m[, f])`] [`inplace_merge(begin(r), m, end(r)[, f])`]]
[[`set_union(r1, r2, o[, f])`] [`set_union(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`set_intersection(r1, r2, o[, f])`] [`set_intersection(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`set_difference(r1, r2, o[, f])`] [`set_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`set_symmetric_difference(r1, r2, o[, f])`] [`set_symmetric_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])`]]
[[`push_heap(r[, f])`] [`push_heap(begin(r), end(r)[, f])`]]
[[`pop_heap(r[, f])`] [`pop_heap(begin(r), end(r)[, f])`]]
[[`make_heap(r[, f])`] [`make_heap(begin(r), end(r)[, f])`]]
[[`sort_heap(r[, f])`] [`sort_heap(begin(r), end(r)[, f])`]]
[[`next_permutation(r[, f])`] [`next_permutation(begin(r), end(r)[, f])`]]
[[`prev_permutation(r[, f])`] [`prev_permutation(begin(r), end(r)[, f])`]]
[[`inner_product(r, o, a[, f1, f2])`] [`inner_product(begin(r), end(r), o[, f1, f2])`]]
[[`partial_sum(r, o[, f])`] [`partial_sum(begin(r), end(r), o[, f])`]]
[[`adjacent_difference(r, o[, f])`] [`adjacent_difference(begin(r), end(r), o[, f])`]]
]
[endsect]
[section Inside Phoenix]
[def __eval_policy__ [link phoenix.inside_phoenix.composites_in_detail.evalpolicy `EvalPolicy`]]
[def __eval__ [link phoenix.inside_phoenix.actors_in_detail.eval_concept `Eval`]]
[def __eval_tuple__ [link phoenix.inside_phoenix.composites_in_detail.evaltuple `EvalTuple`]]
[def __environment__ [link phoenix.inside_phoenix.actors_in_detail.environment `Environment`]]
This chapter explains in more detail how the library operates. The information
henceforth should not be necessary to those who are interested in just using the
library. However, a microscopic view might prove to be beneficial to moderate
to advanced programmers who wish to extend the library.
[section Actors In Detail]
[h3 Actor Concept]
The main concept is the `Actor`. Actors are function objects (that can accept 0
to N arguments (where N is a predefined maximum).
[note You can set `PHOENIX_LIMIT`, the predefined maximum arity an
actor can take. By default, `PHOENIX_LIMIT` is set to 10.]
[h3 actor template class]
The `actor` template class models the `Actor` concept:
template <typename Eval>
struct actor : Eval
{
typedef Eval eval_type;
actor();
actor(Eval const& base);
template <typename T0>
explicit actor(T0 const& _0);
template <typename T0, typename T1>
actor(T0 const& _0, T1 const& _1);
// more constructors
typename apply_actor<eval_type, basic_environment<> >::type
operator()() const;
template <typename T0>
typename apply_actor<eval_type, basic_environment<T0> >::type
operator()(T0& _0) const;
template <typename T0, typename T1>
typename apply_actor<eval_type, basic_environment<T0, T1> >::type
operator()(T0& _0, T1& _1) const;
// function call operators
};
[table Actor Concept Requirements
[ [Expression] [Result/Semantics] ]
[ [`T::eval_type`] [The actor's Eval type] ]
[ [`T()`] [Default Constructor] ]
[ [`T(base)`] [Constructor from Eval] ]
[ [`T(arg0, arg1, ..., argN)`] [Pass through constructors] ]
[ [`x(arg0, arg1, ..., argN)`] [Function call operators] ]
]
[h3 Eval Concept]
The `actor` template class has a single template parameter, `Eval`, from which
it derives from. While the `Actor` concept represents a function, the `Eval`
concept represents the function body. The requirements for `Eval` are
intentionally kept simple, to make it easy to write models of the concept. We
shall see an example in the [link phoenix.inside_phoenix.actor_example next section].
[table Eval Concept Requirements
[ [Expression] [Result/Semantics] ]
[ [`return x.eval(env)`] [Evaluates the function (see Environment below)] ]
[ [`T::result<Env>::type`] [The return type of eval (see Environment below)] ]
]
[h3 Constructors]
In addition to a default constructor and an constructor from a Eval object,
there are templated (pass through) constructors for 1 to N arguments (N ==
`PHOENIX_LIMIT`). These constructors simply forward the arguments to the `base`.
[note *Parametric Base Class Pattern*
Notice that actor derives from its template argument Eval. This is the
inverse of the curiously recurring template pattern (CRTP). With the CRTP, a
class, T, has a Derived template parameter that is assumed to be its
subclass. The "parametric base class pattern" (PBCP), on the other hand,
inverses the inheritance and makes a class, T, the derived class. Both CRTP
and PBCP techniques have its pros and cons, which is outside the scope of
this document. CRTP should really be renamed "parametric subclass pattern
(PSCP), but again, that's another story.
]
[h3 Function Call Operators]
There are N function call operators for 0 to N arguments (N == `PHOENIX_LIMIT`).
The actor class accepts the arguments and forwards the arguments to the actor's
base `Eval` for evaluation.
[def [$http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm]
[note *Forwarding Function Problem*
The function call operators cannot accept non-const temporaries and literal
constants. There is a known issue with current C++ called the
"__forwarding__". The problem is that given an arbitrary function `F`, using
current C++ language rules, one cannot create a forwarding function `FF`
that transparently assumes the arguments of `F`. Disallowing non-const
rvalues arguments partially solves the problem but prohibits code such as
`f(1, 2, 3);`.
]
[h3 Environment]
On an actor function call, before calling the actor's `Eval::eval` for
evaluation, the actor creates an ['*environment*]. Basically, the environment
packages the arguments in a tuple. The `Environment` is a concept, of which, the
`basic_environment` template class is a model of.
[table Environment Concept Requirements
[ [Expression] [Result/Semantics] ]
[ [`x.args()`] [The arguments in a tie (a tuple of references)] ]
[ [`T::args_type`] [The arguments' types in an MPL sequence] ]
[ [`T::tie_type`] [The tie (tuple of references) type] ]
]
Schematically:
[$images/funnel_in.png]
Other parts of the library (e.g. the scope module) extends the `Environment`
concept to hold other information such as local variables, etc.
[h3 apply_actor]
`apply_actor` is a standard MPL style metafunction that simply calls the
Action's `result` nested metafunction:
template <typename Action, typename Env>
struct apply_actor
{
typedef typename Action::template result<Env>::type type;
};
After evaluating the arguments and doing some computation, the `eval` member
function returns something back to the client. To do this, the forwarding
function (the actor's `operator()`) needs to know the return type of the eval
member function that it is calling. For this purpose, models of `Eval` are
required to provide a nested template class:
template <typename Env>
struct result;
This nested class provides the result type information returned by the `Eval`'s
`eval` member function. The nested template class `result` should have a typedef
`type` that reflects the return type of its member function `eval`.
For reference, here's a typical `actor::operator()` that accepts two arguments:
template <typename T0, typename T1>
typename apply_actor<eval_type, basic_environment<T0, T1> >::type
operator()(T0& _0, T1& _1) const
{
return eval_type::eval(basic_environment<T0, T1>(_0, _1));
}
[h3 actor_result]
For reasons of symmetry to the family of `actor::operator()` there is a special
metafunction usable for actor result type calculation named `actor_result`. This
metafunction allows us to directly to specify the types of the parameters to be
passed to the `actor::operator()` function. Here's a typical `actor_result` that
accepts two arguments:
template <typename Action, typename T0, typename T1>
struct actor_result
{
typedef basic_environment<T0, T1> env_type;
typedef typename Action::template result<env_type>::type type;
};
[endsect]
[section Actor Example]
Let us see a very simple prototypical example of an actor. This is not a toy
example. This is actually part of the library. Remember the [link
phoenix.primitives.references `reference`]?.
First, we have a model of the `Eval` concept: the `reference`:
template <typename T>
struct reference
{
template <typename Env>
struct result
{
typedef T& type;
};
reference(T& arg)
: ref(arg) {}
template <typename Env>
T& eval(Env const&) const
{
return ref;
}
T& ref;
};
Models of `Eval` are never created directly and its instances never exist alone.
We have to wrap it inside the `actor` template class to be useful. The `ref`
template function does this for us:
template <typename T>
actor<reference<T> > const
ref(T& v)
{
return reference<T>(v);
}
The `reference` template class conforms to the __eval__ concept. It has a nested
`result` metafunction that reflects the return type of its `eval` member
function, which performs the actual function. `reference<T>` stores a reference to
a `T`. Its `eval` member function simply returns the reference. It does not make
use of the environment `Env`.
/Pretty simple.../
[endsect]
[section Composites In Detail]
We stated before that composites are actors that are composed of zero or more
actors (see [link phoenix.composite Composite]). This is not quite accurate. The
definition was sufficient at that point where we opted to keep things simple and
not bury the reader with details which she might not need anyway.
Actually, a composite is a model of the __eval__ concept (more on this later).
At the same time, it is also composed of 0..N (where N is a predefined maximum)
__eval__ instances and an eval policy. The individual __eval__ instances are
stored in a tuple.
[note In a sense, the original definition of "composite", more or
less, will do just fine because __eval__ instances never exist alone and are
always wrapped in an `actor` template class which inherits from it anyway. The
resulting actor IS-AN __eval__.]
[note You can set `PHOENIX_COMPOSITE_LIMIT`, the predefined maximum
`Eval`s (actors) a composite can take. By default, `PHOENIX_COMPOSITE_LIMIT` is
set to `PHOENIX_LIMIT` (See [link phoenix.actors Actors]).]
[h2 composite template class]
template <typename EvalPolicy, typename EvalTuple>
struct composite : EvalTuple
{
typedef EvalTuple base_type;
typedef EvalPolicy eval_policy_type;
template <typename Env>
struct result
{
typedef implementation-defined type;
};
composite();
composite(base_type const& actors);
template <typename U0>
composite(U0 const& _0);
template <typename U0, typename U1>
composite(U0 const& _0, U1 const& _1);
// more constructors
template <typename Env>
typename result<Env>::type
eval(Env const& env) const;
};
[h2 EvalTuple]
`EvalTuple`, holds all the __eval__ instances. The `composite` template class
inherits from it. In addition to a default constructor and a constructor from an
`EvalTuple` object, there are templated (pass through) constructors for 1 to N
arguments (again, where N == `PHOENIX_COMPOSITE_LIMIT`). These constructors
simply forward the arguments to the `EvalTuple` base class.
[h2 EvalPolicy]
The composite's `eval` member function calls its `EvalPolicy`'s `eval` member
function (a static member function) passing in the [link
phoenix.inside_phoenix.actors_in_detail.environment environment] and each of
its actors, in parallel. The following diagram illustrates what's happening:
[$images/funnel_out.png]
[table EvalPolicy Requirements
[ [Expression] [Result/Semantics] ]
[ [`x.eval<RT>(env, eval0, eval1, ..., evalN)`] [Evaluate the composite] ]
[ [`T::result<Env, Eval0, Eval1, Eval2, ..., EvalN>::type`] [The return type of eval] ]
]
The `EvalPolicy` is expected to have a nested template class `result` which has a
typedef `type` that reflects the return type of its member function `eval`.
Here's a typical example of the composite's eval member function for a 2-actor
composite:
template <typename Env>
typename result<Env>::type
eval(Env const& env) const
{
typedef typename result<Env>::type return_type;
return EvalPolicy::template
eval<return_type>(
env
, get<0>(*this) // gets the 0th element from EvalTuple
, get<1>(*this)); // gets the 1st element from EvalTuple
}
[endsect]
[section Composing]
Composites are never instantiated directly. Front end expression templates are
used to generate the composites. Using expression templates, we implement a DSEL
(Domain Specific Embedded Language) that mimics native C++. You've seen this
DSEL in action in the preceding sections. It is most evident in the
[link phoenix.composite.statement Statement] section.
There are some facilities in the library to make composition of composites
easier. We have a set of overloaded `compose` functions and an `as_composite`
metafunction. Together, these helpers make composing a breeze. We'll provide an
[link phoenix.inside_phoenix.composing.composite_example example of a
composite] later to see why.
[section compose]
compose<EvalPolicy>(arg0, arg1, arg2, ..., argN);
Given an __eval_policy__ and some arguments `arg0`...argN, returns a proper
`composite`. The arguments may or may not be phoenix actors (primitives of
composites). If not, the arguments are converted to actors appropriately. For
example:
compose<X>(3)
converts the argument `3` to an `actor<value<int> >(3)`.
[endsect]
[section as_composite]
as_composite<EvalPolicy, Arg0, Arg1, Arg2, ..., ArgN>::type
This is the metafunction counterpart of `compose`. Given an __eval_policy__ and
some argument types `Arg0`...ArgN, returns a proper `composite` type. For
example:
as_composite<X, int>::type
is the composite type of the `compose<X>(3)` expression above.
[endsect]
[section Composite Example]
Now, let's examine an example. Again, this is not a toy example. This is actually
part of the library. Remember the [link phoenix.composite.statement.while__statement
`while_`] lazy statement? Putting together everything we've learned so far, we
will present it here in its entirety (verbatim):
struct while_eval
{
template <typename Env, typename Cond, typename Do>
struct result
{
typedef void type;
};
template <typename RT, typename Env, typename Cond, typename Do>
static void
eval(Env const& env, Cond& cond, Do& do_)
{
while (cond.eval(env))
do_.eval(env);
}
};
template <typename Cond>
struct while_gen
{
while_gen(Cond const& cond)
: cond(cond) {}
template <typename Do>
actor<typename as_composite<while_eval, Cond, Do>::type>
operator[](Do const& do_) const
{
return compose<while_eval>(cond, do_);
}
Cond cond;
};
template <typename Cond>
while_gen<Cond>
while_(Cond const& cond)
{
return while_gen<Cond>(cond);
}
`while_eval` is an example of an __eval_policy__. `while_gen` and `while_` are
the expression template front ends. Let's break this apart to understand what's
happening. Let's start at the bottom. It's easier that way.
When you write:
while_(cond)
we generate an instance of `while_gen<Cond>`, where `Cond` is the type of
`cond`. `cond` can be an arbitrarily complex actor expression. The `while_gen`
template class has an `operator[]` accepting another expression. If we write:
while_(cond)
[
do_
]
it will generate a proper composite with the type:
as_composite<while_eval, Cond, Do>::type
where `Cond` is the type of `cond` and `Do` is the type of `do_`. Notice how we
are using phoenix's [link phoenix.inside_phoenix.composing composition] (`compose`
and `as_composite`) mechanisms here
template <typename Do>
actor<typename as_composite<while_eval, Cond, Do>::type>
operator[](Do const& do_) const
{
return compose<while_eval>(cond, do_);
}
Finally, the `while_eval` does its thing:
while (cond.eval(env))
do_.eval(env);
`cond` and `do_`, at this point, are instances of __eval__. `cond` and `do_` are
the __eval__ elements held by the composite's __eval_tuple__. `env` is the
__environment__.
[endsect]
[endsect]
[section Extending]
We've shown how it is very easy to extend phoenix by writing new primitives and
composites. The modular design of Phoenix makes it extremely extensible. We have
seen that layer upon layer, the whole library is built on a solid foundation.
There are only a few simple well designed concepts that are laid out like
bricks. Overall, the library is designed to be extended. Everything above the
core layer can in fact be considered just as extensions to the library. This
modular design was inherited from the __spirit__ inline parser library.
Extension is non-intrusive. And, whenever a component or module is extended, the
new extension automatically becomes a first class citizen and is automatically
recognized by all modules and components in the library.
[endsect]
[endsect]
[section Wrap Up]
Sooner or later more FP techniques become standard practice as people find the
true value of this programming discipline outside the academy and into the
mainstream. In as much as structured programming of the 70s and object oriented
programming in the 80s and generic programming in the 90s shaped our thoughts
towards a more robust sense of software engineering, FP will certainly be a
paradigm that will catapult us towards more powerful software design and
engineering onward into the new millennium.
Let me quote Doug Gregor of Boost.org. About functional style programming
libraries:
[:['They're gaining acceptance, but are somewhat stunted by the ubiquitousness
of broken compilers. The C++ community is moving deeper into the so-called "STL-
style" programming paradigm, which brings many aspects of functional programming
into the fold. Look at, for instance, the Spirit parser to see how such function
objects can be used to build Yacc-like grammars with semantic actions that can
build abstract syntax trees on the fly. This type of functional composition is
gaining momentum.]]
Indeed. Phoenix is another attempt to introduce more FP techniques into the
mainstream. Not only is it a tool that will make life easier for the programmer.
In its own right, the actual design of the library itself is a model of true C++
FP in action. The library is designed and structured in a strict but clear and
well mannered FP sense. By all means, use the library as a tool. But for those
who want to learn more about FP in C++, don't stop there, I invite you to take a
closer look at the design of the library itself.
So there you have it. Have fun! See you in the FP world.
[endsect]
[section Acknowledgement]
# Hartmut Kaiser implemented the original lazy casts and constructors based on
his original work on Spirit SE "semantic expressions" (the precursor to
Phoenix).
# Angus Leeming implemented the container functions on Phoenix-1 which I then
ported to Phoenix-2.
# Daniel Wallin helped with the scope module, local variables, let and lambda
and the algorithms. I frequently discuss design issues with Daniel on Yahoo Messenger.
# Jaakko Jarvi. DA Lambda MAN!
# Dave Abrahams, for his constant presence, wherever, whenever.
# Aleksey Gurtovoy, DA MPL MAN!
# Doug Gregor, always a source of inspiration.
# Dan Marsden, did almost all the work in bringing Phoenix-2 out the door.
# Eric Niebler did a 2.0 pre-release review and wrote some range related code
that Phoenix stole and used in the algorithms.
# Thorsten Ottosen; Eric's range_ex code began life as "container_algo" in the
old boost sandbox, by Thorsten in 2002-2003.
# Jeremy Siek, even prior to Thorsten, in 2001, started the "container_algo".
# Vladimir Prus wrote the mutating algorithms code from the Boost Wiki.
# Daryle Walker did a 2.0 pre-release review.
[endsect]
[section References]
# Why Functional Programming Matters, John Hughes, 1989.
Available online at [@http://www.math.chalmers.se/~rjmh/Papers/whyfp.html].
# Boost.Lambda library, Jaakko Jarvi, 1999-2004 Jaakko Jarvi, Gary Powell.
Available online at [@http://www.boost.org/libs/lambda/].
# Functional Programming in C++ using the FC++ Library: a short article
introducing FC++, Brian McNamara and Yannis Smaragdakis, August 2003. Available
online at [@http://www.cc.gatech.edu/~yannis/fc++/].
# Side-effects and partial function application in C++, Jaakko Jarvi and Gary
Powell, 2001. Available online at
[@http://osl.iu.edu/~jajarvi/publications/papers/mpool01.pdf].
# Spirit Version 1.8.1, Joel de Guzman, Nov 2004. Available online at
[@http://www.boost.org/libs/spirit/].
# The Boost MPL Library, Aleksey Gurtovoy and David Abrahams, 2002-2004.
Available online at [@http://www.boost.org/libs/mpl/].
# Generic Programming Redesign of Patterns, Proceedings of the 5th European
Conference on Pattern Languages of Programs, (EuroPLoP'2000) Irsee, Germany,
July 2000. Available online at
[@http://www.coldewey.com/europlop2000/papers/geraud%2Bduret.zip].
# A Gentle Introduction to Haskell, Paul Hudak, John Peterson and Joseph Fasel,
1999. Available online at [@http://www.haskell.org/tutorial/].
# Large scale software design, John Lackos, ISBN 0201633620, Addison-Wesley, July
1996.
# Design Patterns, Elements of Reusable Object-Oriented Software, Erich Gamma,
Richard Helm, Ralph Jhonson, and John Vlissides, Addison-Wesley, 1995.
# The Forwarding Problem: Arguments Peter Dimov, Howard E. Hinnant, Dave
Abrahams, September 09, 2002. Available online: __forwarding__.
[endsect]