Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

Primitives

Arguments
Values
References
Constant References
Nothing

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.

#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.

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] Note

You can set PHOENIX_ARG_LIMIT, the predefined maximum placeholder index. By default, PHOENIX_ARG_LIMIT is set to PHOENIX_LIMIT (See Actors).

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)

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

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] Note

There are a few reasons why enforcing strict arity is not desireable. 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/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.

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".

#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 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)

Evaluating a Reference

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"

#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.

#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 for_ Statement for example).


PrevUpHomeNext