diff --git a/doc/html/images/important.png b/doc/html/images/important.png new file mode 100644 index 0000000..b4645bc Binary files /dev/null and b/doc/html/images/important.png differ diff --git a/doc/html/index.html b/doc/html/index.html index 01b8e54..afac217 100644 --- a/doc/html/index.html +++ b/doc/html/index.html @@ -1,4 +1,3 @@ -
@@ -27,7 +26,7 @@Copyright © 2002-2005, 2010 Joel de Guzman, Dan Marsden, Thomas Heller
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)
@@ -67,19 +66,60 @@@@ -98,7 +138,7 @@
![]()
- + Description
@@ -116,7 +156,7 @@ library is organized in highly independent modules and layers.
- + How to use this manual
@@ -138,7 +178,7 @@ icons precede some text to indicate:
-Table 1.1. Icons
+Table 1.1. Icons
@@ -227,12 +267,12 @@ - + ...To my dear daughter, Phoenix
- + Last revised: January 24, 2011 at 14:19:02 GMT
Last revised: January 25, 2011 at 00:27:53 GMT
diff --git a/doc/html/phoenix/actor.html b/doc/html/phoenix/actor.html new file mode 100644 index 0000000..db7b79f --- /dev/null +++ b/doc/html/phoenix/actor.html @@ -0,0 +1,90 @@ + + + +Actor + + + + + + + + ++
+ +++++Actor +
+ The
+Actoris the main concept + behind the library. Actors are function objects. An actor can accept 0 to +PHOENIX_LIMITarguments. +++
+ ++ Note ++ + You can set
PHOENIX_LIMIT, + the predefined maximum arity an actor can take. By default,PHOENIX_LIMITis set to 10. ++ Phoenix supplies an
+actorclass + template whose specializations model theActor+ concept.actorhas one template + parameter,Expr, that supplies + the underlying expression to evaluate. +template <typename Expr> +struct actor +{ + 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_LIMITarities + (Don't worry about the details, for now. Note, for example, that we skimp over + the details regardingreturn_type). + The arguments are then forwarded to the actor'sExpr+ for evaluation. ++ + TODO +
++ add some notes about the extension possibilities +
++
+ + +
+ + + diff --git a/doc/html/phoenix/basics.html b/doc/html/phoenix/basics.html index 5900e1a..7d0c996 100644 --- a/doc/html/phoenix/basics.html +++ b/doc/html/phoenix/basics.html @@ -1,4 +1,3 @@ - @@ -43,7 +42,7 @@- + Partial Function Application
@@ -95,7 +94,7 @@ black boxes anymore.- + STL and higher order functions
@@ -127,7 +126,7 @@- + Lazy Evaluation
@@ -182,7 +181,7 @@ std::cout << (arg1 % 2 == 1)(y) << std::endl; // prints 0 or false
- + Forwarding Function Problem
@@ -223,7 +222,7 @@
diff --git a/doc/html/phoenix/ebnf.html b/doc/html/phoenix/ebnf.html index 3ef953e..040ed60 100644 --- a/doc/html/phoenix/ebnf.html +++ b/doc/html/phoenix/ebnf.html @@ -1,4 +1,3 @@ -
diff --git a/doc/html/phoenix/introduction.html b/doc/html/phoenix/introduction.html index 2edce21..bab9152 100644 --- a/doc/html/phoenix/introduction.html +++ b/doc/html/phoenix/introduction.html @@ -1,4 +1,3 @@ - diff --git a/doc/html/phoenix/modules.html b/doc/html/phoenix/modules.html new file mode 100644 index 0000000..d1906f6 --- /dev/null +++ b/doc/html/phoenix/modules.html @@ -0,0 +1,91 @@ + + + +| + | + |
+ Binding is the act of tying together a function to some
+ arguments for deferred (lazy) evaluation. Named lazy
+ functions require a bit of typing. Unlike (unnamed) lambda expressions,
+ we need to write a functor somewhere offline, 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 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.
+
| + | + |
#include <boost/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 composite that can finally be evaluated by another
+ function call invocation. A second function call will invoke the actual
+ foo function. Example:
+
bind(&foo, arg1)(4); ++
+ will print out "4". +
+| + | + |
#include <boost/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) ++
| + | + |
#include <boost/phoenix/bind/bind_member_variable.hpp> ++
+ Member variables can also be bound much like member functions. Member variables
+ are not functions. Yet, like the 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 ++
| + | + |
+ phoenix::bind passes all testcases of the Boost.Bind
+ library. It is therefore completely compatible and interchangeable.
+
+ Given the compatibility with Boost.Bind, we also assume compatibility with + std::tr1::bind and std::bind from the upcoming C++0x standard. +
+| + | + |
+ phoenix::bind passes all testcases of the Boost.Bind
+ library. It is therefore completely compatible and interchangeable.
+
+ Given the compatibility with Boost.Bind, we also assume compatibility with + std::tr1::bind and std::bind from the upcoming C++0x standard. +
+| + | + |
+ 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, these are all defined in the Core + module. +
++ This section shall deal with these preset primitives. +
+| + | + |
#include <boost/phoenix/core/argument.hpp> ++
+ We use an instance of: +
+expression::argument<N>::type ++
+ to represent the Nth function argument. The argument placeholder acts as + an imaginary data-bin where a function argument will be placed. +
+
+ There are a few predefined instances of expression::argument<N>::type
+ named arg1..argN, and its BLL
+ counterpart _1.._N. (where N is a predefined maximum).
+
+ Here are some sample preset definitions of arg1..argN
+
namespace placeholders +{ + expression::argument<0>::type const arg1 = {}; + expression::argument<1>::type const arg2 = {}; + expression::argument<2>::type const arg3 = {}; +} ++
+ and its BLL
+ _1.._N
+ style counterparts:
+
namespace placeholders +{ + expression::argument<0>::type const _1 = {}; + expression::argument<1>::type const _2 = {}; + expression::argument<2>::type const _3 = {}; +} ++
![]() |
+Note | +
|---|---|
+ You can set |
+ When appropriate, you can define your own argument
+ names. For example:
+
expression::argument<0>::type 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) ++
+ 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 ++
+ 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. + |
| + | + |
#include <boost/phoenix/core/reference.hpp> ++
+ Another free function cref(cv)
+ may also be used. cref(cv)
+ creates an expression::reference<T const>::type
+ object. This is similar to expression::value<T>::type
+ 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.
+
| + | + |
#include <boost/phoenix/core/nothing.hpp> ++
+ Finally, the expression::null<mpl::void_>::type
+ does nothing; (a "bum", if you will :-). There's a sole expression::null<mpl::void_>::type instance named "nothing".
+ This actor is actually useful in situations where we don't want to do anything.
+ (See for_ Statement
+ for example).
+
| + | + |
#include <boost/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 Values). To achieve our intent, + we use: +
+expression::reference<T>::type ++
+ This is similar to expression::value<T>::type
+ befor but instead holds a reference to a variable.
+
+ We normally don't instantiate expression::reference<T>::type
+ objects directly. Instead we use:
+
ref(v) ++
+ For example (where i is
+ an int variable):
+
add_assign(ref(i), 2) ++
+ References are actors. Hence, references can be evaluated. Such invocation + gives the references's identity. Example: +
+int i = 3; +char const* s = "Hello World"; +cout << ref(i)() << ref(s)(); ++
+ prints out "3 Hello World" +
++ Another free function +
+cref(cv) ++
+ may also be used. cref(cv)
+ creates an expression::reference<T const>::type
+ object. This is similar to expression::value<T>::type
+ 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.
+
| + | + |
#include <boost/phoenix/core/value.hpp> ++
+ Whenever we see a constant in a partially applied function, an +
+expression::value<T>::type ++
+ (where T is the type of the constant) is automatically created for us. + For instance: +
+add(arg1, 6) ++
+ Passing a second argument, 6,
+ an expression::value<T>::type is implicitly created behind the
+ scenes. This is also equivalent to add(arg1, val(6)).
+
val(v) ++
+ generates an expression::value<T>::type
+ 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.
+
+ 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". +
+| + | + |
#include <boost/phoenix/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 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:
+
operator()
+ that takes N arguments,
+ and implements the function logic. This is also true for ordinary function
+ pointers.
+ result<Signature> or nested typedef result_type,
+ following the Boost.Result
+ Of Protocol
+ + For example, the following type implements the FunctionEval concept, in order + to provide a lazy factorial function: +
+struct factorial_impl +{ + template <typename Sig> +struct result; + +template <typename This, typename Arg> +struct result<This(Arg)> + : typedef boost::remove_reference<Arg> +{}; + + template <typename Arg> + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * this->operator()(n-1); + } +}; ++
+ (See factorial.cpp) +
+![]() |
+Note | +
|---|---|
+ The type of Arg is either a const-reference or non-const-reference (depending + on whether your argument to the actor evaluation is a const-ref or non-const-ref). + |
+ 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 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: +
+std::cout << factorial(arg1)(4); ++
+ 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).
+
![]() |
+Important | +
|---|---|
+ 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. + |
| + | + |
+ TODO +
+| + | + |
+ 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.
+
| + | + |
+ Lazy casts... +
+#include <boost/phoenix/object/static_cast.hpp> +#include <boost/phoenix/object/dynamic_cast.hpp> +#include <boost/phoenix/object/const_cast.hpp> +#include <boost/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*.
+
| + | + |
+ Lazy constructors... +
+#include <boost/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, |
| + | + |
+ Lazy delete... +
+#include <boost/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) ++
| + | + |
+ Lazy new... +
+#include <boost/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 contractor 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 | +
|---|---|
+ 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, |
| + | + |
#include <boost/phoenix/operator.hpp> ++
+ 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 Quick + Start - Lazy Operators). Let's go back and examine them a little bit + further: +
+std::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 Function), + lazy operators are not immediately executed when invoked. Instead, an actor + (see Actor) 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: +
+std::cout << ((arg1 + arg2) * arg3)(4, 5, 6); ++
+ will print out "54". +
++ Operator expressions are lazily evaluated following four simple rules: +
+->*
+ will be lazily evaluated when at least one of its
+ operands is an actor object (see Actor).
+ ->* is lazily
+ evaluated if the left hand argument is an actor object.
+ + For example, to check the following expression is lazily evaluated: +
+-(arg1 + 3 + 6) ++
arg1 + 3 is
+ lazily evaluated since arg1
+ is an actor (see Arguments).
+ arg1 + 3 expression
+ is an actor object, following rule 4.
+ arg1 +
+ 3 + 6 is again lazily evaluated. Rule 2.
+ arg1
+ + 3 + 6 is
+ an actor object.
+ 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 Core).
+ For example:
+
1 << 3; // Immediately evaluated +val(1) << 3; // Lazily evaluated ++
prefix: ~, !, -, +, ++, --, & (reference), * (dereference) +postfix: ++, -- ++
=, [], +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= ++, -, *, /, %, &, |, ^, <<, >> +==, !=, <, >, <=, >= +&&, ||, ->* ++
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.
+
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) ++
|
+ + Operators + + |
+
+ + File + + |
+
|---|---|
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
| + | + |
prefix: ~, !, -, +, ++, --, & (reference), * (dereference) +postfix: ++, -- ++
=, [], +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= ++, -, *, /, %, &, |, ^, <<, >> +==, !=, <, >, <=, >= +&&, ||, ->* ++
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.
+
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) ++
| + | + |
+ 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 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. +
+| + | + |
#include <boost/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 let Visibility 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 |
+ 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: +
+vector<vector<int> >)
+ 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 lambda.cpp) +
+| + | + |
#include <boost/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 |
+ Example: +
+let(_a = 123, _b = 456) +[ + _a + _b +] ++
+ 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). args
+ and refs are L-values.
+ vals are R-values.
+
+ 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 .*/ + ] +] ++
| + | + |
#include <boost/phoenix/scope/local_variable.hpp> ++
+ We use an instance of: +
+expression::local_variable<Key>::type ++
+ 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; +expression::local_variable<size_key>::type size; ++
+ Predefined Local Variables +
+
+ There are a few predefined instances of expression::local_variable<Key>::type
+ 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; ++
| + | + |
+ 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
+ (all_odds.cpp):
+
std::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++!!! + + |
![]() |
+Note | +
|---|---|
+ Unlike lazy functions and lazy operators, lazy statements always return + void. + |
+ Here are more examples with annotations. The code almost speaks for itself. +
+| + | + |
#include <boost/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. +
+std::for_each(c.begin(), c.end(), + ( + do_ + [ + cout << arg1 << ", " + ] + .while_(arg1--), + cout << val("\n") + ) +); ++
| + | + |
#include <boost/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:
+
std::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.
+
| + | + |
+ 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: +
+std::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). +
+| + | + |
#include <boost/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
+ 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; +std::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.
+
| + | + |
#include <boost/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
+ 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; +std::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.
+
| + | + |
#include <boost/phoenix/statement/if.hpp> ++
+ We have seen the if_ statement.
+ The syntax is:
+
if_(conditional_expression) +[ + sequenced_statements +] ++
| + | + |
#include <boost/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:
+
std::for_each(c.begin(), c.end(), + switch_(arg1) + [ + case_<1>(std::cout << val("one") << '\n'), + case_<2>(std::cout << val("two") << '\n'), + default_(std::cout << val("other value") << '\n') + ] +); ++
| + | + |
#include <boost/phoenix/statement/throw.hpp> ++
+ As a natural companion to the try/catch support, the statement module provides + lazy throwing and re-throwing of exceptions. +
++ The syntax to throw an exception is: +
+throw_(exception_expression) ++
+ The syntax to re-throw an exception is: +
+throw_() ++
+ Example: This code extends the try/catch example, re-throwing 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")) +] ++
| + | + |
#include <boost/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") +] ++
| + | + |
#include <boost/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. +
+std::for_each(c.begin(), c.end(), + ( + while_(arg1--) + [ + cout << arg1 << ", " + ], + cout << val("\n") + ) +); ++
| + | + |
#include <boost/phoenix/stl.hpp> ++
+ This section summarizes the lazy equivalents of C++ Standard Library functionality +
+| + | + |
#include <boost/phoenix/stl/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/phoenix/stl/algorithm/iteration.hpp> +#include <boost/phoenix/stl/algorithm/transformation.hpp> +#include <boost/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. +
+ +Table 1.6. Iteration Algorithms
+|
+ + Function + + |
+
+ + stl Semantics + + |
+
|---|---|
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
Table 1.7. Querying Algorithms
+|
+ + Function + + |
+
+ + stl Semantics + + |
+
|---|---|
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
Table 1.8. Transformation Algorithms
+|
+ + Function + + |
+
+ + stl Semantics + + |
+
|---|---|
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
| + | + |
#include <boost/phoenix/stl/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: +
++ 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: +
++ 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 1.4. Sample usage
+|
+ + "Normal" version + + |
+
+ + "Lazy" version + + |
+
|---|---|
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
+ 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 operators.
+
+ The following table describes the container functions and their semantics. +
+ +Table 1.5. Lazy STL Container Functions
+|
+ + Function + + |
+
+ + Semantics + + |
+
|---|---|
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
|
+
+ |
+
+
+ |
+
| + | + |
The library is organized in four layers:
-
-
-
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 @@ -42,7 +52,7 @@ files. There are no object files to link against.
@@ -62,7 +72,7 @@ again be another composite.
Table 1.2. Modules
+Table 1.2. Modules