You are on page 1of 57

The Lambda Library

Lambda Abstra tion in C++

Jaakko Järvi
jaakko.jarvi s.utu.

Gary Powell

Sierra On-Line Ltd.
gary.powellsierra. om

Turku Centre for Computer S ien e
TUCS Te hni al Report No 378
November 2000
ISBN 952-12-0758-2
ISSN 1239-1891

Abstra t

The Lambda Library (LL) adds a form of lambda abstra tion to C++.
The LL is implemented as a template library using standard C++; thus
no language extensions or prepro essing is required. The LL onsists of a
ri h set of tools for dening unnamed fun tions. Parti ularly, these unnamed
fun tions work seamlessly with the STL algorithms. The LL oers signi ant
improvements, in terms of generality and ease of use, ompared to the binders
and fun tors in the C++ standard library.

Keywords:

lambda abstra tion, generi programming, C++ templates

The Lambda Library

Lambda Abstra tion in C++

Jaakko Järvi
jaakko.jarvi s.utu.

Gary Powell

Sierra On-Line Ltd.
gary.powellsierra. om

Turku Centre for Computer S ien e
TUCS Te hni al Report No 378
November 2000
ISBN 952-12-0758-2
ISSN 1239-1891

1

Introdu tion

The ability to dene lambda fun tions, i.e. unnamed fun tions, is a standard
feature in fun tional programming languages. For some reason, this feature
does not appear in any of the mainstream pro edural or obje t-oriented languages. This arti le introdu es the Lambda Library whi h xes this 'omission'
for C++.
The Lambda Library (LL) is a C++ template library implementing a
form of lambda abstra tion for C++. The library is designed to work with
the Standard Template Library (STL) [SL94℄, now part of the C++ Standard
Library [C++98℄. The paper assumes some familiarity with the STL.
The arti le onsists of two parts. In the rst part we explain how to use
the library, in the se ond part we go over the implementation details in all
their glory.

1.1 Motivation
Typi ally STL algorithms operate on ontainer elements via fun tions and
fun tion obje ts passed as arguments to the algorithms. An obje t of a lass
with a fun tion all operator is a fun tion obje t. Fun tion obje ts are
sometimes alled fun tors as well.
The STL ontains predened fun tion obje ts for some ommon ases
(su h as plus, less and negate). In addition, it ontains adaptors for reating fun tion obje ts from fun tion pointers et . Further, it ontains two
binder templates bind1st and bind2nd. With binders, you an reate a
unary fun tion obje t from a binary fun tion obje t by binding one of the
arguments to a onstant value. Some STL implementations ontain fun tion
omposition operations as extensions to the standard [STL00℄.
The binder templates an be seen as kind of urrying operators, but
together with negaters and fun tion omposition operators the goal of all
these tools is lear: to make it possible to spe ify unnamed fun tions in a all
to an STL algorithm. Still in other words, the goal is to be able to pass ode
fragments as arguments to fun tions. However, the tools in the Standard
Library provide only a small step towards this goal. Unnamed fun tors
built as ompositions of standard fun tion obje ts, binders, adaptors et .
are very hard to read in all but the simplest ases. Moreover, the 'lambda
abstra tion' with the standard tools is full of restri tions. For example, the
standard binders allow only one argument of a binary fun tion to be bound;
there are no binders for 3-ary, 4-ary et . fun tions. See [Jär00℄ for a more
in-depth dis ussion about these restri tions.
The Lambda Library solves these problems. The syntax is intuitive and
there are no (well, fewer) arbitrary restri tions. The on rete onsequen es
of the LL on the tools in the Standard Library are:
1

 The standard fun tors plus, minus, less et . be ome unne essary.
Instead, the orresponding operators are used dire tly. 
The binders bind1st and bind2nd are repla ed by a more general
bind fun tion template. Using bind, arbitrary arguments of pra ti ally
any C++ fun tion an be bound. Furthermore, bind makes ptr_fun,
mem_fun and mem_fun_ref adaptors unne essary. 
No expli it fun tion omposition operators are needed.
We'll take an example to demonstrate what LL's impa t an be on ode
using STL algorithms. The following example is an extra t from the do umentation of one STL implementation:

... al ulates the negative of the sines of the elements in a ve tor, where
the elements are angles measured in degrees. Sin e the C library fun tion
sin takes its arguments in radians, this operation is the omposition of three
operations: negation, sin, and the onversion of degrees to radians.
ve tor<double> angles;
ve tor<double> sines;
onst double pi = 3.14159265358979323846;
...
assert(sines.size() >= angles.size());
transform(angles.begin(), angles.end(), sines.begin(),
ompose1(negate<double>(),
ompose1(ptr_fun(sin),
bind2nd(multiplies<double>(), pi / 180.0))));
Using LL onstru ts, the transform fun tion all an be written as:

transform(angles.begin(), angles.end(), sines.begin(),
- bind(sin, free1 * pi / 180.0));
The operator- repla es the all to negate, bind repla es ompose1, ptr_fun
be omes unne essary, along with bind2nd, and the all to multiplies is
repla ed with the operator*. The argument free1 is a pla eholder representing the parameter of the fun tion obje t. At ea h iteration, it will be
substituted for the element from the ontainer of angles. Noti e how the
ode using the Lambda Library is mu h easier to read.

1.2 About expression templates
The LL implementation is based on expression templates. The basi idea
behind expression templates is to overload operators to reate expression
obje ts to represent the expression and its arguments instead of evaluating
the operator instantly. As a result, the type of an expression obje t an
2

to emphasize the analogy to fun tional programming we all this a lambda expression. Chapter 10℄ as well as in [HCKS99℄. 3 . In part I we are going to try and show you what you an do with them.0) shown in se tion 1. or more pre isely. There are s ar ely any introdu tory arti les on expression templates. free1 * pi / 180. This expression obje t an be manipulated in various ways (also at ompile time) prior to a tually evaluating it. 1. The arti le on the Expression Template Library (ET) [PH00℄ explains expression templates whi h are loser to the LL implementation.bind(sin. However. Some operators in C++ have spe ial semanti s. To relate our terminology with existing on epts. The original arti le by Veldhuizen [Vel95℄ des ribed expression templates tailored for ve tor and matrix expressions. We need a ner segregation between on epts. for example to yield better performan e. Expression templates are a omplex topi .des ribe a parse tree of the underlying expression. not the internals of how they work.1 is an expression template. We will spend more time on LL expression templates in the se ond part of this arti le. the term expression template overs the whole ma hinery for reating expression obje ts and alling them. an unnamed fun tion obje t. this manipulation means passing the expression obje t as a parameter to a fun tion. Note that there are ex eptions to these basi rules. Part I Using the Lambda Library 2 Basi usage This se tion des ribes the basi rules for writing lambda expressions onsisting of operator invo ations and dierent types of fun tions and fun tion-like onstru ts. We over these spe ial ases in se tion 3. so don't be dis ouraged if you don't fully understand them. as it in ee t denes an unnamed fun tion.3 Terminology The expression . In the LL ase. Su h expression templates are dis ussed also in [CE00. hen e we make a distin tion between lambda expressions and lambda fun tors. whi h is ree ted in the semanti s of the orresponding lambda expressions as well. Just like the I/O streams library you an use them anyway. as we understand them. This is a ording to ommonly established C++ terminology. and substituting the a tual arguments for the pla eholder obje ts. We use the term lambda fun tor to spe i ally refer to the fun tion obje t resulting from a lambda expression.

a list) to the value 1: list<int> v(10). Next we'll sort the elements of vp: sort(vp. First. Here we take the address of ea h element in v (with &free1) and assign it to the orresponding element in vp.g.end(). out << *free1 << endl). *free1 > *free2). Now lets hange the values in v. whi h will be lled with a value at ea h iteration.begin(). the rst argument is substituted for free1 and the se ond argument for free2. free1 = bind(foo. We must admit that there are some traps and we'll try and point them out as we go along. &free1). vp. vp. we reate a ontainer of pointers and make them point to the elements in the rst ontainer v: list<int*> vp(10). remember that free1 is a pla eholder.end(). for_ea h(v. v. // a ontainer of pointers transform(v. vp. for_ea h(v.begin(). Consequently. Next.begin().end(). For ea h element we all some fun tion foo passing the original value of the element as an argument to foo: int foo(int). When this fun tor is alled.end(). 4 . free1 = 1). After seeing these examples it should be obvious how to write the lambda expression to. In this all to sort. Finally we'll output the sorted ontent of vp separated by line breaks: for_ea h(vp. say. we'll initialize the elements of a ontainer (e.begin(). it reates a binary lambda fun tor.end(). free1)). So let's start with some simple expressions and work up.begin(). In this example free1 = 1 reates a lambda fun tion whi h assigns the value 1 to every element in v. v. See this is easy.1 Introdu tory examples It is easiest to understand how to use the LL by looking at some examples.end(). we are sorting the elements by their ontents in des ending order. But in general you will have no trouble using this new library. v. free1 += 6 * bind(rand)). v. in rement ea h element in v by 6 times the result of the all to a zero-argument fun tion named rand: for_ea h(v.2.begin(). free1 and free2. an empty slot. Note that the lambda expression *free1 > *free2 ontains two dierent pla eholders.begin().

so it is easy to dene the pla eholder names to your own liking.1 So far we have used the pla eholders free1 and free2. No STL algorithm a epts a fun tor with the number of arguments greater than two. free2 and free3. whi h we will explain in the sequel.2. The types serve as tags. another in arnation of free3).e. the third pla eholder is a ne essity in order to implement all the features of the urrent library (see se tion 3. arg2 and arg3 instead of free1. you an write: free1_type arg1.For example. zero parameters is possible too. two or three arguments passed in by the STL algorithm. have predened names. i. 5 .8. This means that the lambda 1 This doesn't hold for all expressions. This means that lambda fun tors an take one. free2_type arg2.2 Pla eholders In lambda fun tions o urring in lambda al ulus and in fun tional programming languages the formal parameters are ommonly named within the lambda expression with some expli it syntax. The LL provides typedefs for the pla eholder types. A pla eholder leaves the argument totally open. You may have noti ed from the previous examples that we do not spe ify any types for the arguments that the pla eholders stand for. to use arg1. the following denition gives the names x and y to the formal parameters of an addition fun tion: xy:x + y The C++ ounterpart of the above expression is written as: free1 + free2 In the C++ version the formal parameters. but they are just normal variables. free3_type arg3. and later repla e them with the a tual arguments that are passed in the all to the lambda fun tor. in luding the type. The variables themselves are not important but rather their types are. In fa t. For example. Veldhuizen [Vel95℄ was the rst to introdu e the on ept of using pla eholders in expression templates. whi h introdu es freeE. whi h allow us to spot the pla eholders from the arguments stored in a lambda fun tor. so the three pla eholders should be enough. the pla eholder variables. The pla eholders may seem somehow magi al. There is no expli it synta ti onstru t for C++ lambda expressions. the use of a pla eholder variable in an expression impli itly turns the expression into a lambda expression. The LL supports one more: free3. The LL pla eholders are somewhat dierent.

Moreover.end(). B. or more generally. The syntax of a lambda expression reated with the bind fun tion is: bind(target-fun tion. or again more generally. The target fun tion an be a pointer to fun tion. 2. it is obvious that the return type of the lambda fun tor is not known either. v. the bind-argument-list must be a valid argument list for target-fun tion. 2. thus the lambda fun tor is alled with an argument of type int. Sin e the type of the pla eholder argument is left open. and for your user dened operators with unorthodox return types. Where a pla eholder is used in pla e of an a tual argument.1 Fun tion pointers as targets The target fun tion an be a pointer or a referen e to a non-member fun tion and it an be either bound or unbound. always to denote the underlying operation of the lambda expression. the use of a pla eholder in a fun tion all does not turn the invo ation into a lambda expression. In a bind expression. We will over this aspe t of the LL in the latter part of the paper. the LL has a type dedu tion system that gures out the return type when the lambda fun tor is alled. a referen e to fun tion or a fun tion obje t. the bind fun tion template serves for this purpose. whi h an be alled with any obje t x. Not to worry. we say that the argument is unbound or free. C and X are some types: 6 . bind-argument-list) We use the term bind expression to refer to this type of lambda expressions. with another lambda expression. free1 = 1). suppose A. for whi h x = 1 is a valid expression. Note that we use the term target fun tion with other types of lambda expressions as well. For example. As the examples above show. it an be a pointer to a member fun tion or even a pla eholder. The for_ea h algorithm iterates over a ontainer of integers.3. a lambda expression. the dedu tion system is easy to extend.fun tor an be alled with arguments of any type for whi h the underlying fun tion makes sense. The lambda expression free1 = 1 reates a unary lambda fun tor. ex ept that any argument an be repla ed with a pla eholder.begin(). It overs operators of built-in types and 'wellbehaved' operators of user dened types.3 Fun tions as lambda expressions Obviously. Consider again the rst example we showed in se tion 2: for_ea h(v.

In C++. free2.3. bind(pf1. B and C. the fun tion to be alled with the arguments a. bind(&foo.. free1. free2. ) bind(&foo. int = 10). 7 . the argument to the resulting unary lambda fun tor must be impli itly onvertible to A. . bind(goo. int i. This means that overloaded fun tions annot be used in bind expressions dire tly: void foo(int). // ok Also. The return type dedu tion system requires that the fun tion obje t lass denes the return type of the fun tion all operator as the typedef result_type. it just uses a fun tion pointer instead of a referen e. free1. ) bind(foo.X foo(A. b. The third bind expression demonstrates that a ertain pla eholder an be used multiple times in a lambda expression. Z). This is the onvention used with the fun tion obje ts in the Standard Library. Y.. B b. void (*pf1)(int) = &foo.2 Fun tion ob je ts as targets Fun tion obje ts an also be used as target fun tions. }.. For this bind expression to make sense. The se ond bind expression has an equivalent fun tionality. free1)(i). free1)(i). b and . the resulting lambda fun tor takes one parameter. // error .. For example: stru t A { typedef B result_type. void foo(float). and to ompile. a. free1. The argument will be dupli ated in ea h pla e that the pla eholder is used. B operator()(X.. ) The rst bind expression returns a binary lambda fun tor. it is possible to take the address of an overloaded fun tion only if the address is assigned to or used to initialize a properly typed variable. C). // error: goo takes two arguments 2. The fourth bind expression shows that even the target fun tion an be left unbound. C . free1. bind(foo. free1)(i). B. . free1) bind(free1. there is no way to gather the default arguments to a fun tion: void goo(int.. A a.

begin(). bind(&A::foo. // pointer is ok: find_if(ints. if you want to be able to use them as target fun tions (there is a way around this. A* ap. see se tion 6..end(). a. the resulting fun tor an be alled both via a pointer or a referen e: list<A> refs.. 1)). bind(&A::foo. for_ea h(lz. ints.befin().3 Member fun tions as targets The form of the bind expression with member fun tion targets is slightly dierent. 1)). 2.begin(). the se ond argument is the target obje t. free1)). . ints.end(). the return type dedu tion system an handle only one return type per fun tion obje t lass. Similar to other types of bind expressions.end(). list<A> la.. x. Consequently. However. if the obje t argument is free.begin(). lz. we have hosen to de lare the bind fun tions with the following format: bind(target-member-fun tion. The fun tion all operator an be overloaded within a lass.end(). target-obje t.end(). that is. all the overloaded fun tion all operators within one lass must have the same return type. z)). 8 . A a. an obje t of type A for whi h the member fun tion is to be alled. list<Z> lz.. // referen e is ok: find_if(ints. y. bind(a. Z z. the target-member-fun tion an be left open as well. free1)). Y y. The fun tionality is identi al in both ases. free1)). find_if(ptrs. for_ea h(la. By onvention. X x. find_if(refs. ve tor<int> ints. bind-argument-list) If the rst argument is a pointer to a member fun tion of some lass A. bind(free1. free1. ptrs. list<A*> ptrs. the LL supports both ases with the same interfa e: bool A::foo(int). Similarly.7. free1. . bind(&A::foo. a bound obje t argument an be either a referen e or pointer to the obje t. x. refs.3.The above fun tion obje t an be used like this: A a.begin().1). la.begin(). y. bind(&A::foo. A tually. ap.end().

begin().7). the operator<< all with out and a lambda fun tor builds another lambda fun tor. no assignment from free1_type to int A workaround for this situation is explained in se tion 2. This lambda fun tor will be evaluated later at ea h iteration and we get the desired result. out << " " << free1). the basi rule is that any operand of any operator an be repla ed with a pla eholder. delete.4 Operators as lambda expressions We have overloaded almost every operator for lambda expressions. whi h reates some asymmetry to lambda expressions. For example. All the pre eding ode examples follow this rule. // a valid lambda expression i = free1. and reate a lambda fun tor out of the variable. or temporarily overridden by expli it type information (see se tion 6. In su h ases the dedu tion system an either be extended. the following line outputs a spa e followed by the elements of the ontainer a: for_ea h(a. a. free1 = 1.end().5 Delayed onstants and variables Lambda fun tors are built with fun tion alling rules: arguments that an be evaluated before the lambda fun tor reation fun tion is alled. We ommonly need to prevent the evaluation of a variable. For example: int i.  The assignment and subs ript operators must be dened as member fun tions. there are some spe ial ases and restri tions:  The return types annot be hosen freely while overloading operators ->.  As stated in se tion 2.  It is not possible to overload the .. new. 9 . out << onstant(" ") << free1). Hen e. and ?: operators in C++. Consequently. will be. This may not be what we want. the return type dedu tion system may not handle all user-dened operators. Now rather than writing to the stream immediately. or with a lambda expression. a. or delayed onstants. We all su h lambda fun tors delayed variables. we an't overload them dire tly for lambda expressions.end(). we need to turn the onstant " " into a lambda expression with the onstant fun tion: for_ea h(a. // error.5.*.begin(). or onstant.2. new[℄ and delete[℄. instead.2. To output a spa e separated list of elements. or sometimes even a onstant. . 2. However. respe tively.

A delayed variable. delaying the evaluation of a variable with var is a solution to this problem: int i. onsider the lambda 10 . for_ea h(a. no assignment from free1_type to int var(i) = free1. an be reated outside the lambda expression as well. (free1 + i)(i = 10). As be omes lear from the above examples. A all var(i) turns some variable i into a lambda expression. there are a few alternative ways of doing it. A somewhat arti ial. For example. for_ea h(a. but hopefully illustrative example is to ompute the number of elements in a ontainer using the for_ea h algorithm: int ount = 0. and for some expressions it makes more sense to store the arguments as referen es. in se tion 2. As an example.A delayed variable is simply a lambda fun tor ontaining a referen e to a regular C++ variable and is reated with the fun tion template var. In general. var( ount)++). However.end().begin(). the result of the lambda fun tor invo ation below is 11. not 20: int i = 1. var_type<int>::type v ount(var( ount)). see se tion 3.begin(). This feature is useful if the same variable appears repeatedly in a lambda expression. The template lasses var_type and onstant_type serve for this purpose. or a onstant. Basi ally the lambda fun tors an store temporary opies of the arguments. The default is temporary opies. i = free1.end().6 Controlling how bound arguments are stored We have stated that bound arguments are stored in the lambda fun tor. // ok 2.4 we brought up the asymmetry within lambda assignment and subs ript operators. The variable ount is delayed. this is the default. This is relatively ommon with some of the ontrol lambda expressions. This means that the value of a bound argument is xed when the lambda fun tor is reated and remains onstant during its lifetime. As said. a. Above. v ount++). a.5. it is evaluated at ea h iteration within the body of the for_ea h fun tion. // error. delayed variables are in frequent use within ontrol lambda expressions. Using var_type the previous example be omes: int ount = 0. or hold onst or non onst referen es to them.

These fun tions reate wrapper obje ts that hold a referen e to the wrapped argument. 11 . Finally.. the stream argument is stored as a referen e. to make the streaming operators (<< and >>) intuitive. but rather we des ribe the basi idea here briey (for more details. how to write ontrol stru tures as lambda expressions and how to onstru t and destru t obje ts in lambda expressions. as array types annot be opied. we may have argument types that annot be opied. the ref fun tion states that a onst referen e should be used. rather than some temporary opy of it.5. This se tion des ribes how to use some of the C++ spe i operators in lambda expressions. This is a reasonable default. the default is again to store temporary opies. ref(i)) // store a referen e to i The implementation of ref and ref is not dis ussed in part II. We believe that in addition to the 'typeless' pla eholders and the unique return type dedu tion system. see [Jär00℄). we even show how to do ex eption handling in lambda expressions. For example: A a. and are thus dierent from var and onstant explained in se tion 2. } . The obvious intention is that alls to the lambda fun tor ae t the value of the variable i. For these reasons the LL provides two simple fun tions for overriding the default storing me hanism: wrapping a bound argument with the ref fun tion states that the lambda fun tor should store a non- onst referen e to the argument. In lambda fun tors reated with bind expressions. the wrapper obje t has a onversion operator to the wrapped referen e type. we may have target fun tions where the side-ee ts are deliberate. Further. note that ref and ref do not reate lambda fun tors. Further. int i. Further. store a onst referen e instead bind(rotate. The LL has this behavior: the left argument of the ombined assignment operators (+=. and respe tively.. void rotate(int& j) { j < 10 ? ++j : j = 0. *=. lambda fun tors store referen es to arguments that are of array types. 3 Advan ed te hniques (templates gone wild) Our goal has been to make the LL as omplete as possible in the sense that any C++ expression ould be turned into a lambda expression. // suppose A is a lass that an't be opied void foo( onst A& a). hen e the obje t an be used transparently in pla e of the wrapped argument. as it prevents sideee ts via bound arguments. Also. However.) are stored as referen es to non- onst. ref(a)) // annot opy a. et . or are very expensive to opy.expression i += free1. The fun tions and operators that reate lambda fun tors re ognize su h wrapper types and sele t the storing method appropriately. these extra features really set the LL apart from its prede essors. bind(foo.

v. the following ode sets all negative elements of some ontainer a to zero: for_ea h(a. as opposed to trying to all the for_ea h fun tion with four arguments. this overloaded operator has dupli ate fun tionality to using bind. 'end of this lambda expression'. it may make the ode easier to read. this means that ea h element of a is guaranteed to be rst written to out and then to log. For example. In the above example. log << free1 << endl)).1 Comma operator The LL overloads the omma operator for sequen ing lambda expressions together. free1 < 0 && free1 = 0).3. 3. See [Mey96.end().begin(). ve tor<data*> v. as did the ET library [PH00℄. a.begin(). The hara ter ". The LL follows the C++ rule for always evaluating the left-hand operand of a omma expression before the right-hand operand. For this library we would like to have it mean.end().. free1->*(&data::x) == 0). Note that the short ir uiting rules for the operators &&. Sin e omma is also the separator between fun tion arguments. and || are respe ted as well. Nevertheless. It an refer to either a member obje t. }.begin(). Unfortunately it just isn't possible. 12 . Here the parenthesis are used to group the two lambda expressions into one expression. ( out << free1 << endl. so we are left with operator. // ount all elements. where the member x equals to zero: ount_if(v. For example: stru t data { int x. 3538℄ for a full dis ussion about the pitfalls when overloading omma and the short ir uited logi al operators." is reserved by the C++ language to mean 'end of statement'. extra parenthesis are sometimes ne essary to write synta ti ally orre t lambda expressions: for_ea h(a. a. int foo(int y). pp. or a member fun tion. In the ase of a member fun tion.2 Pointer to member operator The operator->* is another spe ial ase.end().

else set bit 0x010 for_ea h(a. 5)). Here is an example using operator(): stru t data { // typedef required for return type dedu tion typedef int result_type. v. v. out << bind(&data::foo.// stream out the results of alls to data::foo(5): for_ea h(v. }. a.begin(). The same fun tionality an be a hieved using bind. 2 Sin e we have overloaded operator|=. whi h dupli ates its fun tionality. free1.end(). 13 . set bit 0x100. free1(y) == 0). ount_if(v. We have not overloaded operator() for arbitrary lambda fun tors. int operator()(int y). 0x100. For example:2 // if bit 0x001 is set. operators whi h take advantage of not having to reate a new obje t on the sta k and then all the assignment operator.begin(). ..end()..3 Fun tion all operator We have also overloaded operator() for the pla eholders..begin(). free1 |= if_then_else_return( (free1 & 0x001) == 0x001..end(). we have reated a fun tion alled if_then_else_return. We provide the zero through nine argument ases of operator()(arg1. the for_ea h loop is as e ient as a hand oded for loop. // the same example using bind: for_ea h(v. ve tor<data> v. -= et . 0x010)). Unlike using transform whi h only uses operator=. .end(). arg9). 3. the LL an use the +=. The operator() has been overloaded be ause it may make your ode easier to read and understand. out << (free1->*(&data::foo))(5)). int y. sin e this would oni t with the operator() already dened in the lambda fun tor. whi h is what the LL is all about. 3. v. The result type rules for the ?: operator are omplex but we believe we have them en oded to the extent that it is possible.begin().4 Conditional expression Sin e it is not possible to overload the ?: operator..

In addition to if_then. if_then_else. a. we an avoid the repeated wrapping of a variable with var. a+5. body) Again. if_then(free1 % 2 == 0.end(). ++var(i). test. out << free1)). the pseudo ode denition of for_loop is: for_loop(initialization. the following ode outputs all even elements of some ontainer a: for_ea h(a. The LL implements the ontrol lambda expressions of the ET library and adds more. vi < 10. Control lambda expressions are fun tion templates that reate lambda fun tors that implement the behavior of some ontrol stru ture. in rement. for_ea h(a. int i. free1[var(i)℄ += 6)). The arguments to these fun tion templates are lambda fun tors. if we reate the delayed variable beforehand using the var_type template. Other loop stru tures are analogous to for_loop. the arguments to the for_loop fun tion are lambda fun tors. and do_while_loop. for_ea h(a.3. var_type<int>::type vi(var(i)). ++vi.5. Using var_type the above example be omes: int i. for_loop. Let's take a on rete example. the LL also has swit h and do_on e. As an example of a loop ontrol lambda expression. while_loop. free1[vi℄ += 6)). a+5. For example. for_loop(var(i) = 0. for_loop(vi = 0. The following ode adds 6 to ea h element of a two-dimensional array: int a[5℄[10℄. The return type of all ontrol lambda fun tors is void. 14 .begin(). var(i) < 10. Note the use of delayed variables to turn the for_loop arguments into lambda expressions. As stated in se tion 2.5 Control lambda expressions The idea of lambda expressions for ontrol stru tures originates from the ET Library [PH00℄.

are more omplex sin e the number of ases may vary. ase_statement<label >(lambda expression ).. The general form of a swit h lambda expression is: swit h_statement( ondition . as well as for do_on e. The ase labels are given as expli itly spe ied template arguments to ase_statement fun tions. ode . do_statement(lambda expression . if ( ondition ) break. . ase_statement<label >(lambda expression ). In ase you do not re ognize the do_on e onstru t. break_if( ondition )). ase_statement<1>(lambda_fun tor<A>& a) generates the ode: ase 1: evaluate lambda fun tor a . . The lambda expression equivalent to this is written as: do_on e ( do_statement(lambda expression . .3.6 Swit h and do_on e The lambda expressions for swit h ontrol stru tures. and the optional default ase with the default_statement fun tion. if ( ondition ) break.. } while(false). . it is a spe ialization of the ode: do { ode . break_if( ondition )). The dierent ases are reated with ase_statement fun tions. The break statements are impli itly part of ea h ase: for example... We have spe ialized the swit h_statement fun tion for up to 9 ase statements. ode .. ) lambda expression 15 . default_statement(lambda expression ) ) The ondition is a lambda expression reating a lambda fun tor with an integral return type.. break.

int> > v. int> >(free1. The fun tion template new_ptr reates a lambda fun tor that wraps a new invo ation.The do_on e onstru t is similar in stru ture to the swit h lambda expression: do_statement fun tions group the ode into blo ks. free2)). The last argument is the end of the do on e stru ture. but their return types are xed.7 Constru tors and destru tors as lambda expressions Operators new and delete an be overloaded. ba k_inserter(v).8 Ex eption handling in lambda expressions The LL allows you to reate lambda fun tors that throw and at h ex eptions. just as the ase_statement fun tions in the swit h stru ture. sin e it is not possible to take the address of a onstru tor. The omplementary fun tion destru tor exists as well. we have dened a set of onstru tor fun tions whi h reate lambda fun tors for onstru ting obje ts. there is no need for another test/break blo k. For example: int* a[10℄. x. 3. transform(x. As in the ase of the onditional operator. this may seem a straightforward addition to the library 16 . Note that new_ptr an take arguments as well. At rst glan e. for_ea h(a. free1 = new_ptr<int>()). The lambda fun tor reated with delete_ptr rst evaluates its argument (whi h is a lambda fun tor as well) and then alls delete on the result of this evaluation. To be able to write onstru tors as lambda expressions. for_ea h(a. a+10.begin(). We have also dened new_array and delete_array for new[℄ and delete[℄. They are passed dire tly to the onstru tor invo ation and thus allow alls to onstru tors whi h take arguments.begin(). Parti ularly. delete_ptr respe tively a lambda fun tor that wraps a deletion. We annot use bind. and thus a plain lambda fun tor. we have dened fun tion templates to ir umvent this restri tion. Instead.end(). 3. y. free1 = delete_ptr(free1)). The new_ptr fun tion takes the type of the obje t to be onstru ted as an expli itly spe ied template argument. the return types annot be lambda fun tors. onstru t<pair<int. The lambda expression onstru tor<type >(args ) reates a lambda fun tion whi h wraps the onstru tor all type (args ) and returns the resulting obje t. The following example reads integers from two ontainers. we have to resort to a set of fun tion templates again. a+10. onstru ts pairs out of them and inserts them into a third ontainer: ve tor<pair<int.

A lambda fun tor for rethrowing ex eptions is reated with the nullary rethrow fun tion. freeE annot be used outside of an ex eption handler lambda expression. in addition to freeE. at h_ex eption<type >(lambda expression ).. We will explain more in the se ond part of the arti le (see se tion 9). The std::ex eption arries a string explaining the ause of the ex eption.. but rather a spe ial ase of free3.). at h_ex eption<type >(lambda expression ). The resulting lambda fun tors at h the ex eptions as referen es. The se ond handler at hes ex eptions from the standard library. The argument to this fun tion is the ex eption to be thrown. writes an informative message to out and onstru ts and throws another type of ex eption (bar_ex eption). The lambda expression within the at h_ex eption denes the a tions to take if the ex eption is aught. we have used the fun tion at h_all to mean at h(. or to at h_all. the type of the ex eption to at h is spe ied with the expli it template argument.. freeE is not a fulledged pla eholder... Sin e it is not possible to write at h_ex eption<. whi h refers to the aught ex eption obje t within the handler body. if you an implement swit h statements with an arbitrary number of ase blo ks. Lambda fun tors for throwing ex eptions are reated with the unary fun tion throw_ex eption.3 As free1 and free2 an be used inside a handler. Note the use of the free1 pla eholder in the lambda expression that denes the body of the handler. The rst at h blo k is for handling ex eptions of type foo_ex eption. 17 . we do not see 3 Fair enough. after all. The form of a lambda expression for try at h blo ks is as follows: try_ at h( lambda expression . Ea h at h_ex eption denes a at h blo k.. ) . bind(&std::ex eption::what. The gure 1. As a onsequen e. the usage is fairly easy. and free3 annot be used inside of an ex eption handler lambda expression. It is a spe ial pla eholder. The last at h blo k an be either a all to at h_ex eption<type >. it is not that easy. why not try and at h blo ks? Trust us. freeE) reates a lambda fun tor for alling the what fun tion.>. Nevertheless. The explanation an be queried with the zeroargument what member fun tion. at h_all(lambda expression ) The rst lambda expression is the try blo k. Note the use of freeE as the argument. or a lambda fun tor whi h reates the ex eption to be thrown. demonstrates the use of the LL ex eption handling tools.

//foo may throw at h_ex eption<foo_ex eption>( out << onstant("Caught foo_ex eption. rethrow() ) ) ).begin(). at h_all( out << onstant("Unre ognized ex eption"). Figure 1: Throwing and handling ex eptions. 18 . a.end(). freeE). try_ at h( bind(foo. at h_ex eption<std::ex eption>( out << onstant("Caught std::ex eption: ") << bind(&std::ex eption::what. foo argument = ") << free1 ).for_ea h( a. free1). throw_ex eption( onstru tor<bar_ex eption>(free1)) ).

We'll repeat the example here: int a[5℄[10℄. where the names of the standard algorithms refer to our own lambda fun tors. In the third 19 .8. We ould do better: the inner loop should really be another for_ea h invo ation. it was worth the eort. we are extending the library to allow nesting of STL algorithms. a+5. be ause we thought it was not that important. x things up and maybe try again. for_ea h is a fun tion template. free1[var(i)℄ += 6)). Noti e the reuse of free1. free1 += 6)). and be ause it was not that easy. you an't restart the algorithm where it was when the ex eption was thrown. and we haven't armatively de ided on all the details yet. However. But the standard fun tors with the standard algorithms don't give that han e. and thus annot be passed as a parameter. for_loop(var(i) = 0. In other words. a+5. 3. In any ase. Pla ing a try/ at h blo k inside the algorithm allows you to de ide whether to ontinue or bail. Consequently. The last handler ( at h_all) demonstrates rethrowing ex eptions. we have opied the interfa e of all the STL algorithms into a subnamespa e LL. if there is a possibility that the fun tor throws. There are some issues whi h makes this ompli ated. The LL gives you an easy way to write ex eption safe ode.the immediate threat of this being a true restri tion in pra ti e. the above example be omes: for_ea h(a. Looking ba k. LL::for_ea h(free1. you an't make ex eption safe alls to STL algorithms  unless of ourse you rewrite the fun tor to handle the ex eptions.5 we showed an example using for_loop as the fun tion obje t passed to the for_ea h algorithm. this se tion des ribes work in progress and the exa t syntax for nesting STL algorithms may hange in the future versions of the library. var(i) < 10.9 Nesting STL algorithms with lambda expressions At the time of writing this. Using a nested for_ea h. In se tion 3. int i. free1 + 10. 3. In the rst two arguments of the inner for_ea h it refers to the elements over whi h the outer for_ea h iterates. illegal use of pla eholders is aught by the ompiler. for_ea h(a. whi h implement the fun tionality of the orresponding algorithms (by alling the standard fun tion template). To ir umvent this. If the fun tor passed to an algorithm throws.1 Why do ex eption handling at all? Lambda expressions for ex eption handling were one of the last things we added to the Lambda Library. Ex eption handling made the promise of giving you another han e. ++var(i).

the purpose of ea h layer be omes lear. and either as opies or referen es. another to remove namespa ing all together.hpp has everything in it for users who want it all. the basi in lude le ll. but rather will look at the hain of template instantiations taking pla e while ompiling a simple lambda expression. In order to keep the ore of the library lean.fi. To start using the library. the arguments are stored either as onst or non- onst. the same pla eholder refers to the elements in the inner for_ea h. The third argument is a lambda fun tor as well. Within ea h layer. There are a ouple of denes to help manage the namespa e issues. bind fun tions and the basi operators. installing and using the library To get the library.hpp just in ludes the lambda fun tors. s. This orthogonal layering is the key to manage the variation between dierent types of lambda expressions.argument. This variability is a hieved with a set of traits lasses whi h perform the ne essary type onversions. Parti ularly. Therefore. The library is urrently in the namespa e "boost". There is no installation pro edure. The le ll_all. The presented ode is somewhat simplied for larity. but it is not evaluated. 4 Getting. The rst two arguments to LL::for_ea h are just plain old lambda fun tors. whi h the LL::for_ea h fun tor evaluates at ea h iteration and passes the results forward to std::for_ea h. We will not try to des ribe the library ode entirely. Part II Implementing the Lambda Library 5 General library design The LL onsists of a number of layers.2) in the lambda fun tor. In this way. Even with the stri t layering. What is going on? Well. and to allow users to sele tively take on the features of the LL. Depending on the lambda expression and the type of the arguments.utu. Control onstru ts and other extras an be in luded sele tively. it is enough to write template spe ializations with respe t to a single varying fa tor. free1 is just a pla eholder so we an reuse it wherever a pla eholder makes sense. but rather passed on to std::for_ea h as su h. visit the LL web-site http://lambda. the details vary. one whi h allows you to hange the LL namespa e. the ar hite ture of the LL is omplex. you just in lude some les. while the arguments of the lambda expressions are always stored inside a tuple obje t (see se tion 5. in the library ode ea h argument 20 . Ea h layer implements a ertain task orthogonal to the tasks of the other layers.

in the expression -(free1 * free2) the argument of the unary operator. Furthermore. When this new fun tion is alled with the arguments that were left open. 1. we ommonly use the make_tuple fun tions (see se tion 5. in the LL a zero-argument (see the example in se tion 2. an argument an be bound to another partially applied fun tion. the types of the results of dierent lambda expressions. Pla eholder types.fun tion is bound to a partially applied fun tion free1 * free2.1 Lambda expressions as partial fun tion appli ations The LL implementation is best understood by viewing lambda expressions as delayed and partial fun tion appli ations. su h as free1 + 1 and bind(foo. are all instan es of the lambda_fun tor template. the original fun tion is alled with an argument list where these arguments have been substituted for the pla eholders.2) to reate the argument tuples. while some parameters are left open. Note that in this example. When this unary partially applied fun tion is alled with some argument x. whi h is a ommon umbrella for all dierent types of lambda fun tors. the expression free1 = 1 is a partial appli ation of the binary assignment fun tion. see below) arguments. In a partial fun tion appli ation. ex eption handling lambda fun tors et . a fun tion is alled with empty slots. while in the library ode we are alling the tuple onstru tors dire tly and spe ify the full type of the tuples. This means that the fun tion all is simply delayed. It is our expression template lass. The model of partial fun tion appli ation must be generalized a bit in order to ompletely des ribe the fun tionality of the LL: in addition to a onstant value. For example. the entral template in the implementation is the template lass lambda_fun tor. some formal parameters of the fun tion are bound normally to the a tual arguments.1) fun tion an be a target of a partial appli ation. we believe that omitting them here is bene ial: the ode is mu h easier to read and the general library design does not get lost as easily. 5. Moreover. The result of su h a partial fun tion all is another fun tion with possibly fewer (or more. We do not show these wrappings here. the partial appli ation of the unary operatorresults in a binary fun tion. 5. free2.type is wrapped inside one of these traits lass instantiations. Although these details are ru ial for a fully fun tional library. where the rst argument is left open and the se ond argument is bound to 1. free1). For example. To start with. pla eholders. free1 gets repla ed and the original operator= fun tion is alled as x = 1.2 The role of tuples The parameters of the lambda_fun tor template des ribe the type of the delayed operator/fun tion and the types of the bound arguments and pla e21 . in the argument list. Hen e.

thanks to tuples this ode repetition an be kept to a minimum. an be a essed with no runtime overhead using the get fun tion as get<N>(x) or x. you do not have to know anything about tuples. However.14. C&> Hen e. B>. For an interested reader.holders. In short. onst double*> tuple<int (*) (int.get<N>(). 22 . 4 Our urrent implementation does not support volatile-qualied types. double). With the ex eption of some unavoidable restri tions (e. double. any C++ type an be used as a tuple element type.g. A>(1. where N is an integral onstant. Jär99a. 3. An arbitrary number of obje ts of arbitrary types an be grouped into a tuple. A()) The Nth element of a tuple. For example. tuples are des ribed in greater depth in [Jär01. a tuple is a generalization of the standard pair template. are valid element types. Note that our urrent implementation of tuples has an upper limit of 10 arguments. Jär99b℄. this is where the limitation originates. To understand the implementation. the above should be enough. other tuples. referen e types. fun tion pointers et . value types. onst and non- onst types. To use the LL. T>::type retrieves the type of the Nth element of a tuple of type T. onst har&. regardless of the true number of parameters. the lambda_fun tor parameterization overs all ases (there are a few spe ializations for lambda_fun tor though). at some point we must break up the tuples and repeat some ode for dierent argument list lengths in order to be able to all the underlying fun tions. We ould easily in rease this number but for now it has been a reasonable upper limit. std::make_pair): make_tuple(1. the following denitions are valid tuple types: tuple<int. Obviously. The two alternatives are inter hangeable. We wrap the parameters inside a tuple obje t and an thus treat the parameter list as one entity. A()) or with the make_tuple fun tion ( f. The type expression: tuple_element_type<N. The type of a tuple element an be queried with the traits lass tuple_element_type.14. When we mention a limit on the number of ases in the LL as being 10 or less. non- opyable types annot be stored in a tuple ex ept as referen es). onst float. Although the number of bound and unbound arguments an vary. 3.4 Tuples an be onstru ted expli itly: tuple<int. tuple<A. string. This is possible be ause of tuples.

e. typedef onst lambda_fun tor<pla eholder<THIRD> > free3_type. provided that this results in a valid all to the underlying fun tion after the argument substitutions have been performed.3. the instantiation be omes operator=(int&. of the lambda fun tor together with the types of the arguments this operator will be alled with. We'll rst demonstrate a simple non-re ursive ase: int x. the return type dedu tion system takes the underlying operator. First. As we explained in se tion 2. Consequently. i. This is possible. The onstants FIRST. the ompiler dedu es the argument types to the underlying assignment operator. the target fun tion. two re ursively alternating dedu tion hains ommen e. pla eholder<SECOND> the se ond argument and so forth. a lambda fun tor an be alled with arguments of any types.1).3 Pla eholders In the rst part of the arti le we went over how to use pla eholders. the return type is dedu ed based on the information that we are performing an assignment operator between arguments of type int. Noti e that the pla eholder has no knowledge of the type of the argument it stands for. 23 . we all them arity odes (see se tion 5. In order to gure out the argument types.. sin e lambda fun tor templates dene the operator() fun tions as member templates. We have dened pla eholder<FIRST> to be the pla eholder for the rst argument to the lambda fun tor.. . SECOND and THIRD are propagated from pla eholder types to more omplex lambda fun tors and arry information about the arity of the lambda fun tors. The argument types are not known until the fun tor is a tually alled and impli it instantiation of the operator() member template is performed. Here we explain the internals of what they are.5. onst int&). This is performed with the return type dedu tion templates of the LL. As input. When the ompiler has dedu ed these argument types. Next. Pla eholders in the LL are just instan es of a templated lass pla eholder wrapped in a lambda_fun tor: template <int I> lass pla eholder {}. the ompiler may have to resort to the return type dedu tion system re ursively. typedef onst lambda_fun tor<pla eholder<SECOND> > free2_type.2. The ompiler must dedu e the return type of the operator(). typedef onst lambda_fun tor<pla eholder<FIRST> > free1_type. (free1 = 1)(x).

not for the user). This expression reates a unary lambda fun tor.Fun tion ompositions ause a bit more of a heada he (for the implementers of the library.3.5 6 A walk through the layers We have now overed the basi s and an guide you through the dierent layers of the LL  with the simple lambda expression we promised. and an pro eed with the type dedu tion for that operation. whi h ompares its argument to zero.free2 . free1 + (free1 * free2). When a lambda expression is evaluated. Note parti ularly. it must perform the return type dedu tion for multipli ation of types A and B. For example: -free1 // arity ode = FIRST free1 . 5 There is still one more onstant (RETHROW). whi h means that the lambda fun tor is nullary. whi h an be bitwise ored with other arity odes. whi h indi ates that the freeE pla eholder was used in a lambda expression. onsider the lambda expression.1 Arity odes Ea h lambda fun tor has an arity ode as one of its template arguments. For example.7. the pla eholder with the highest arity ode determines the arity of the lambda fun tor. As omplex as this is. When alled. 5. Note that arity ode an have the value NONE as well. the resulting lambda fun tor gets the highest arity ode from its subexpressions. the lambda expression &free3 reates a 3-argument lambda fun tor. Suppose this binary fun tion is alled with obje ts of type A and B.free2 // arity ode = SECOND free1 . SECOND a binary fun tor et . that even in the absen e of pla eholders with a lower arity ode. The return type dedu tion system is explained in more details in se tion 6. It is used internally to guarantee that a rethrow lambda expression is only used within a at h blo k. and the value EXCEPTION. Our example lambda expression is free1 < 0. the arity ode of a lambda fun tor states the highest pla heholder index that is present in any of the subexpressions.free3 // arity ode = THIRD There is a dire t mapping from the arity odes to the a tual arities of the lambda fun tors: FIRST means a unary fun tor. Then the ompiler knows that the addition is for types A and C. For example. Say it results in a type C. the LL orre tly dedu es return types for su h ompositions. 24 . Before the ompiler knows the se ond argument type for the operator+. In other words. this lambda fun tor dis ards the rst two arguments and returns the address of the third argument.

is sele ted as a result of the overload resolution.4 about a tion lasses).1 Overloaded operators for lambda expressions For ea h binary operator op. relational_a tion<less_a tion> > means that the lambda fun tor stands for the binary less than operator (see se tion 6. 6. whi h makes overloading operators and fun tions for lambda fun tors simpler.2 The lambda_fun tor template The lambda_fun tor templates dene the fun tion all operators that are alled from STL algorithms. Due to this arrangement the lambda_fun tor template has only one template parameter. onst int>. The arguments free1 and 0 are stored in a tuple. onst int>.1). There are four lambda_fun tor spe ializations for dierent arities (0-3 arguments). This operator takes a lambda_fun tor as the left-hand argument and onstru ts another lambda_fun tor. Note that we need three template arguments to store the above pie es of information. the fun tion template shown in gure 2. The type a tion<2. one overloaded denition is enough. stating that its fun tion all operator is unary. The dig_arity is a template whi h extra ts the arity ode of a lambda fun tor. The template parameters Arg and B get the values pla eholder<FIRST> and int.3. and the type of the onstru ted lambda fun tor be omes: lambda_fun tor< lambda_fun tor_args< a tion<2. whi h gets the type tuple<lambda_fun tor<pla eholder<FIRST> >. the types of the arguments to the operator.6. As the onstant 0 has no arity ode. relational_a tion<less_a tion> >. The lambda_fun tor_args is an intermediate template that groups these three parameters into a single lass. there are three overloaded denitions: lambda_fun tor op any_type any_type op lambda_fun tor lambda_fun tor op lambda_fun tor For ea h unary operator. In addition to this. lambda_fun tor has a ommon spe ialization for the three pla eholder types and one more 25 . The arity ode of free1 is FIRST (see se tion 5. FIRST > > This type arries information about the invoked operator. tuple<lambda_fun tor<pla eholder<FIRST> >. and the arity of the lambda fun tor. When the ompiler en ounters our example expression. the value FIRST is propagated to the resulting lambda fun tor.

26 . dig_arity<Arg>::value > > operator< ( onst lambda_fun tor<Arg>& a. b) ). onst B>. lass B> inline onst lambda_fun tor< lambda_fun tor_args< a tion<2. tuple<lambda_fun tor<Arg>. tuple<lambda_fun tor<Arg>. relational_a tion<less_a tion> >. } Figure 2: The operator< overloaded for a lambda expression with a lambda fun tor as the left-hand argument. onst B& b) { return lambda_fun tor< lambda_fun tor_args< a tion<2.template< lass Arg. relational_a tion<less_a tion> >. onst B> dig_arity<Arg>::value > > ( make_tuple(a.

} template< lass RET.template < lass A tion. Args. (free1 < 0)(i). onst_nil(). nil. Args> { publi : expli it lambda_fun tor( onst Args& args) : lambda_fun tor_base<A tion. onst_nil()). in this unary ase the se ond and third arguments are lled with spe ial nil-obje ts. . nil>::type > (a. . For example: int i. A&. Sin e our example lambda expression has one unbound parameter. whi h is used with delayed ex eption handling for a ombined total of 6 partial spe ializations. as well as the fun tions that substitute the pla eholders for the a tual arguments are only implemented for the ase of 3-argument lambda fun tors. The return type dedu tion traits. nil.). 27 . } }. FIRST> > : publi lambda_fun tor_base<A tion.. a lambda_fun tor spe ialization with a unary operator() template is instantiated (gure 3. and the arity ode is thus FIRST. Suppose that the lambda fun tor in gure 3. is alled with an argument of type int. Args>(args) {} template< lass A> typename return_type<lambda_fun tor. lass A> RET ret_ all(A& a) onst { return this->template all<RET>(a. nil>::type operator()(A& a) onst { return this->template all< typename return_type<lambda_fun tor. it helps the parser to gure out that all is a templated member fun tion rather than a data member. A&.. The task of the operator() fun tion template is to initiate the return type dedu tion hain and forward the all further. onst_nil(). Hen e. lass Args> lass lambda_fun tor<lambda_fun tor_args<A tion. onst_nil()). The template keyword before the all invo ation is required by the C++ standard. Figure 3: The spe ialization for unary lambda fun tors.

is sele ted. or a tually to onst bool.). There is one for ea h overloaded operator (less_a tion. This ret_ all fun tion has the same fun tionality as the fun tion all operator. nil.5) and forwards the substituted argument list to A t::apply fun tion. This arrangement lessens the load of the ompiler signi antly. the all member template gets instantiated as: onst bool all(int& a. is binary. Our example target fun tion.7. the lambda_fun tor templates dene another member fun tion.4 A tion lasses A tion lasses are wrappers for the target fun tions.3 The lambda_fun tor_base template The lambda_fun tor_base templates store the argument tuple and dene the all fun tions whi h perform the argument substitution. The dedu ed return type is then forwarded as an expli itly spe ied template parameter both to the lambda_fun tor_base:: all and further to the A t::apply fun tion. Therefore the spe ialization shown in gure 4. operator<. This allows us to pass the result of one lambda fun tor to another lambda fun tor as a referen e argument. in this ase to relational_a tion<less_a tion>::apply. all arguments are forwarded to the all fun tion dened in the base lass lambda_fun tor_base. onst nil& b. here it is the arity of the target fun tion. int&. 28 . rather than dedu ed with the return type dedu tion traits. The LL uses ret_ all in internal alls to lambda fun tors to prevent the ompiler from doing the same return type dedu tions repeatedly. onst nil& ) This fun tion delegates the argument substitution to the sele t fun tions (see se tion 6. pointers to 6 In order ompose lambda fun tors we return temporaries as onst types. ex ept for the return type whi h is given as an expli itly spe ied template argument. 7 Note that the return type dedu tion is only performed in the lambda fun tor's operator(). There are also a tion lasses for dierent types of fun tions: fun tion pointers. We'll onsider the return type dedu tion traits later in se tion 6. In addition to the fun tion all operator. nil>::type an be resolved.7 6. minus_a tion et . As a result of the all from the lambda_fun tor::operator() fun tion. The template is spe ialized for ea h dierent length of fun tion argument list. 6. while the varying fa tor in lambda_fun tor spe ializations was the arity of the lambda fun tor. just assume for now that the return type is orre tly dedu ed to bool.6 After lling the missing arguments with nil-obje ts ( onst_nil()) does this).Now the template parameter A be omes int and the return_type<lambda_fun tor.

This grouping allows us to implement the return type dedu tion rules for sets of operators. The apply fun tion eventually alls the target fun tion. et . lass A. B &b) { return a < b. // the arguments } Figure 4: The lambda_fun tor_base spe ialization for unary fun tors. // sele t substitutes sele t(get<2>(args). The a tion lasses are separated into groups. b. // the argument tuple publi : expli it lambda_fun tor_base( onst Args& a) : args(a) {} } template< lass RET. for instan e.5 Argument substitution The apply fun tion is alled with a merged argument list. void. arithmeti . all arithmeti operators follow the integral promotion rules. ). while the return type of all relational a tions is onst bool. member fun tions et . boolean. // The spe ialization of relational_a tion for less than stru t relational_a tion<less_a tion> { template< lass RET. lass A. template < lass A tionClass> relational_a tion. a. logi al. a. 6. whi h in our example is the built-in operator<. lass B> RET stati apply(A &a. The a tion lass is part of the type of the lambda fun tor instantiation and its sole purpose is to provide a stati member fun tion for alling the target fun tion.template< lass A t. where the arguments have already been substituted for the pla eholders. Here is the ode for the less_a tion lass: lass less_a tion {}. )). b. C& ) onst { return A t::template apply<RET>( sele t(get<1>(args). For instan e. A t>. B& b. } }. The sele t 29 . The groups are dened by similar operation. lass B. lass C> RET all(A& a. Args> { Args args. lass Args> lass lambda_fun tor_base<a tion<2.

lass C> inline RET sele t( onst binary_lambda_fun tor & op.template ret_ all<RET>(a. The all sele t(get<1>(args). and return the result of that all. lass B. lass A. the following spe ialization for the free1 pla eholder overrides the default behavior by sele ting the rst parameter of the lambda_fun tor_base:: all fun tion: template< lass A. lass C> inline Any& sele t(Any& any. C&) { return op. lass C> inline A& sele t( onst lambda_fun tor<pla eholder<FIRST> >&. Fun tion de omposition is also a hieved with overloading sele t fun tions. Instead of just returning one of the arguments. A& a. but the following pseudo ode shows the general idea. b. A& a. ) sele ts either the bound argument from the args tuple (get<1>(args)) or one of the arguments a.2): template< lass RET. the sele t fun tions all the lambda fun tor with some of the other arguments. b or . C&) { return any. b). This spe ialization would mat h for binary lambda fun tors. and we return that argument as su h: template< lass Any. } However. C&) { return a. lass B. the stored argument an be a pla eholder. in whi h ase one of the other arguments should be hosen. B&. We do not go into details. depends on the type of the bound argument. } Analogous overloaded fun tions exist for all pla eholder types. a. lass A. In our example. A&. B&.fun tions alled from lambda_fun tor_base templates perform this substitution. lass B. Noti e the all to ret_ all rather than the fun tion all operator (see se tion 6. B&. This means that we have a bound argument. The default ase is that we do not know anything about the type. } 30 . Whi h it does.

31 . onst_nil()) whi h returns a referen e to i.6 Intermediate summary of the example We have now overed the following fun tion all hain: 1. 3. int&. b. ) be omes sele t(0. i. ) turns into sele t(free1. 4. tuple<lambda_fun tor<pla eholder<FIRST>. i. The overloaded operator< reates a lambda_fun tor of type: lambda_fun tor< lambda_fun tor_args< a tion<2. The return type is dedu ed and the fun tion relational_a tion<less_a tion>::apply< onst bool>(i. onst_nil()) and returns 0. The all fun tion alls the sele t fun tion to hoose between the bound arguments stored in the tuple member and the argument i supplied as a parameter:  The rst argument: sele t(get<1>(args). a. The unary fun tion all operator of this lass is alled with the variable i of type int. The return type and the arguments are forwarded to the all fun tion of the base lass: lambda_fun tor_base< a tion<2. onst int>. onst_nil(). nil>. onst_nil(). nil.relational_a tion<less_a tion> >.  The se ond argument: sele t(get<2>(args.6. onst int> >:: all< onst bool>(i. 0) is alled. onst_nil()). The return type is resolved to onst bool with the instantiation return_type<lambda_fun tor. relational_a tion<less_a tion> >. onst_nil(). tuple<pla eholder<FIRST>. a. FIRST > > 2. whi h returns the result of the expression i < 0 all the way to the aller of the lambda_fun tor::operator(). b.

The template parameter A t is the a tion lass type and the Args tuple type ontains the bound argument and pla eholder types. C>::type X. Args>::type. Hen e. lass B. It takes four template parameters: a lambda fun tor and the types of the three a tual arguments of the lambda fun tor. lass C> stru t return_type<lambda_fun tor<pla eholder<FIRST> >. In our example. is instantiated. Args. The parameters X and Y are obtained re ursively with the return_type template. A. X. A. A. B. C>::type Y. X. lass A. typedef typename return_type_2<A t. int Code. 3 and N argument ases). the a tion lass is relational_a tion<less_a tion>.template< lass A t. lass C> stru t return_type<lambda_fun tor<a tion<2. Code>. lass Args. Args>::type. the rst tuple element type is lambda_fun tor<pla eholder<FIRST> > and the se ond element type is onst int. A. B. 32 . A. B. The denition an be interpreted as follows: Use the traits lass return_type_2<A t. Figure 5: The binary fun tion ase of the topmost layer of the return type dedu tion system. typedef typename return_type<typename tuple_element_type<2. Y> to dedu e the return type of performing the binary a tion A t to some parameters of types X and Y. Y>::type type.7 Return type dedu tion The return_type template onstitutes the topmost layer of the return type dedu tion system. lass B. the a tion is binary and thus the spe ialization in gure 5. }. The return_type template is spe ialized with respe t to the arity of the underlying a tion (1. C> { typedef A type. 6. B and C are the types of the parameters to substitute for the pla eholders. }. 2. In our example. A t>. B. C> { typedef typename return_type<typename tuple_element_type<1. the spe ialization: template < lass A.

}. if short = 300 and long = 500. Above we stated that the return type dedu tion system is easily extendible. The return_type_2 template has spe ializations for dierent (binary) a tions or a tion groups. the following spe ialization denes that the return type of relational operators for a user-dened type FuzzyNumber would be FuzzyTruthValue: template< lass A t> stru t return_type_2<relational_a tion<A t>. lass A t> stru t return_type_2<relational_a tion<A t>. template<> stru t promote_ ode<FuzzyNumber> { stati onst int value = 2000. In this s heme ea h type is given a value. FuzzyNumber. }. value is large enough to get the orre t promotion weight. lass B. lass B. This is a hieved by adding spe ializations for the return_type_2 template. Y is dedu ed to onst int with the primary template: template < lass Arg. All relational operators are dened to return onst bool: template< lass A. 33 . A. i. FuzzyNumber> { typedef onst FuzzyTruthValue type. The value of the result type of an arithmeti operation is the largest of the parameter values.is used to dedu e that X equals to int. the LL has a spe ial type promotion s heme for numeri types.e. Furthermore. Here FuzzyNumber op any native type is promoted to FuzzyNumber. For example. User dened types are added to the promotion s heme by assigning them a value. lass C> stru t return_type { typedef Arg type. then short op long is max(300. B> { typedef onst bool type. or long. For example. }. }. the result type of the addition of a FuzzyNumber and any standard numeri type an be dened with the following template. modeled after the one used in the Blitz library [Bli99℄. lass A.500) or 500.

This is done by wrapping the lambda expression inside a ret<T> fun tion.. impli itly onvertible to T. after substitutions. The following bind expression is an example of a all that mat hes this bind fun tion. fuzzy_numbers. free1. A bind expression is a all to one of the overloaded bind fun tions. transform(fuzzy_numbers. fuzzy_bools. As we stated.8 Bind expressions In the pre eding se tions.3 showed several examples of bind expressions. where the rst argument is the target fun tion and the remaining ones are used as the argument list. had we not dened the above traits for FuzzyNumber lasses.begin().6.. Our example didn't over the implementation of the bind fun tions. pointers to onst and non- onst member fun tions and arbitrary fun tion obje ts as target fun tions. ret<FuzzyTruthValue>(x < free1)). FuzzyNumber x. the gure 6. . where the target fun tion is a pointer to a binary non-member fun tion. int b). the bind fun tion is overloaded to take any number of arguments between zero and 10.1 Overriding return type dedu tion For one-time-only uses it may be an overkill to dene the return type dedu tion traits for some user-dened types. Note that ret<T> does not all any of the C++ ast statements. . 1). to the target fun tion when it is alled. As an example. The se tion 2. shows the 3-argument bind fun tion that mat hes bind invo ations. 34 . that is.7.. for ea h argument list length we need four overloaded denitions to over the ases of non-member fun tion pointers. The true return type of the target expression must be ompatible with T. int foo(float a. bind(&foo.end(). whi h is the topi of this se tion. 6. we ould write as follows: ve tor<FuzzyNumber> fuzzy_numbers. For example. To begin with.. Further. It merely denes the return type for a given lambda expression. where T is the return type. we used the expression free1 < 0 to walk through the entral parts of the implementation. ve tor<FuzzyTruthValue> fuzzy_bools. the return type dedu tion system an be overridden and the return type spe ied on the y within a lambda expression.begin().

Consequently. but rather the fun tion all operator of the member_ptr_lambda_fun tor reates and returns that lambda fun tor. we return a temporary obje t. Hen e. the C++ language doesn't give mu h freedom what to do next. the arity of the a tion lass is 3. these are the only omponents that are spe i to bind expressions. non-member) before the a tual apply fun tion is alled. the member pointer. Other template layers are not dependent on the type of the target fun tion in the lambda fun tor. as ribe to the orthogonal layers in the LL. However. the remaining two arguments to the bind fun tion an be anything. Arg2>::value. the less_a tion. This on ludes our dis ussion about the implementation of the basi features of the Lambda Library.2 we showed the all free1->*(&data::foo) to the unary 35 . If the member pointer is a pointer to a fun tion. fun tion a tion lasses have one extra layer to unify the syntax of alling dierent types of fun tions (member vs. The result is something like a pending member fun tion all. we need some templates for the return type dedu tion system. slightly simplied it sele ts the highest arity ode of its argument types. an refer to either a member obje t or a member fun tion. In the above example ase the target fun tion is a pointer to a fun tion. say. parti ularly they an be lambda fun tors. and the fun tion_a tion templates to implement the alling rules for these fun tions. We need the bind fun tions to implement the interfa e for reating lambda fun tors from various types of fun tions.1 Pointer to member operator In se tion 3. The ombine_arities traits lass omputes the arity ode of the lambda fun tor type to be reated from the arity odes of the argument types. we return a lambda fun tor with a referen e to that obje t. Compared to. sin e the a tion lass treats the target fun tion as a normal substitutable argument.2 we stated that the argument of the operator->*. the only thing that an be done with the result is to pass it a list of arguments (see [Mey99℄ for an in-depth analysis of overloading operator->*). Note that although the target fun tion is binary. Further. If the member pointer is an obje t. whi h has the fun tion all operator dened for the same number of arguments as does the member pointer fun tion. Note the type expression ombine_arities<Arg1. and thus not a lambda fun tor. It is not a lambda fun tor that gets passed to an STL algorithm. The bind fun tion groups the target fun tion with the other arguments into a tuple whi h is stored in the lambda fun tor. 7 Spe ial ases in operator overloading 7. However. This member_ptr_lambda_fun tor serves as a proxy. the global operator->* is overloaded dierently for these ases.The a tion lass of the resulting lambda fun tor is fun tion_a tion<3>. In se tion 3.

a1. a2)). Par2). Arg1>(f. Result(Obje t::* onst & f)(Arg1)) { return member_ptr_lambda_fun tor <1. lass Arg1. lass Par2. ombine_arities<Arg1. Arg2>::value > > bind(Result (* onst & f)(Par1. lass Arg1> inline onst member_ptr_lambda_fun tor <1. Par2). lass Obje t. lass Arg2> inline onst lambda_fun tor< lambda_fun tor_args< a tion<3. fun tion_a tion<3> >. } Figure 7: Member pointer operator for unary non- onst member fun tions. tuple<Result (*)(Par1. ombine_arities<Arg1. Figure 6: The bind fun tion for the ase where the target fun tion is a pointer to a binary non-member fun tion.template < lass Par1. tuple<Result (*)(Par1. }. Arg2>::value > > (make_tuple(f. onst Arg2>. Result (Obje t::*)(Arg1) onst. 36 . onst Arg1. Result(Obje t::*)(Arg1). Arg. onst Arg2& a2) { return lambda_fun tor< lambda_fun tor_args< a tion<3. Par2). Arg1> operator->*( onst lambda_fun tor<Arg>& a. lass Result. lass Result. onst Arg1& a1. Arg. a). fun tion_a tion<3> >. template< lass Arg. onst Arg2>. onst Arg1.

follows these rules to the extent that it is possible. We do not show the ode of the inherited template lass member_ptr_base. This is a all to the operator() of the member_ptr_lambda_fun tor. the spe ialization of the lambda_fun tor_base for the logi al and operator is shown in gure 9. we need to spe ialize the lambda_fun tor_base for ea h of these operators. It does not make a dieren e whi h syntax is used.2 omma. Nevertheless. Rather than spend time dis ussing how this bit of the library works. the lambda fun tor and the member pointer.3 Conditional expression The onditional expression ?: is not allowed to be overloaded. it is just a holder of the arguments. Instead of implementing these operators with an a tion lass like less_a tion and alling the a tion lass from the lambda_fun tor_base template. The return type 37 . we are going to skip it. 7. As an example.member fun tion int data::foo(int).4. and it results in the lambda fun tor that an be passed to an STL algorithm. Indeed. Besides this. Another reason for implementing the omma operator in this way is that the built-in omma operator a epts a void argument. we stated that the same fun tionality an be a hieved using the bind fun tion. with an argument of type void. the result type rules for this operator are very omplex (see [C++98. In the example of se tion 3.2 we passed the onstant 5 to the result of the operator->* all with the expression (free1->*(&data::foo))(5). both interfa es reate the exa t same lambda fun tor instantiation. The reation of the member_ptr_lambda_fun tor proxy in this fun tion triggers the instantiation of the spe ialization shown in gure 8. && and || operators The omma operator and the logi al and and or operators follow the C++ rules for argument evaluation order and short- ir uiting. The && alling rules apply. the arguments are bound into a tuple the same way that bind does it and the a tion lass is fun tion_a tion. the if_then_else_return fun tion template. 7. Therefore we must all the omma operator dire tly. This is the same a tion that is used by bind. Note that the operator&& is alled dire tly in the all fun tion of lambda_fun tor_base. passed to the overloaded operator->*. the right-hand sele t fun tion is never alled if the left-hand sele t returns false. the LL ounterpart for this operator that we showed in se tion 3. For this.16℄). We annot all a fun tion. we all the operator dire tly from lambda_fun tor_base. parti ularly the apply fun tion of an a tion lass. This guarantees that only the ne essary expressions will be evaluated and in the orre t order. se tion 5. The gure 7 shows the overloaded denition of operator->* whi h gets instantiated in this ase. Furthermore.

fun tion_a tion<3> >.template< lass Arg. onst Arg1>. ombine_arites<Arg. tuple<Obje tMemPtr. lambda_fun tor<Arg>. lass Arg1> lass member_ptr_lambda_fun tor<1. fun tion_a tion<3> >. Arg1> : private member_ptr_base<Arg. Obje tMemPtr. a1) ). onst lambda_fun tor<Arg> &a) : inherited(f.a) {} }. 38 . Arg1>::value > > operator()( onst Arg1 &a1) onst { return lambda_fun tor< lambda_fun tor_args< a tion<3. } Figure 8: The member_ptr_lambda_fun tor spe ialization for unary non onst member fun tions. tuple<Obje tMemPtr. Arg1>::value > > ( make_tuple(inherited::m_memPtr. onst Arg1>. ombine_arites<Arg. member_ptr_lambda_fun tor(Obje tMemPtr onst &f. Arg. lambda_fun tor< lambda_fun tor_args< a tion<3. inherited::m_lambdafun tor. lambda_fun tor<Arg>. Obje tMemPtr> { publi : typedef member_ptr_base<Arg Obje tMemPtr> inherited. lass Obje tMemPtr.

b. the LL ontrol onstru ts are again spe ializations of the lambda_fun tor_base lass. the ode will obey the regular C++ rules of a for loop. that is the exa t same argument evaluation order. lass C> RET all(A& a. Sin e the sele t fun tions are written inside the for loop. 8. Args > { Args args. B& b. ) && sele t(get<2>(args). Note that the return type is a spe ialization to return void. therefore no return statement is ne essary. logi al_a tion<and_a tion> >. publi : expli it lambda_fun tor_base( onst Args& a) : args(a) {} template< lass RET. we show the lambda_fun tor_base spe ialization for the for_loop in gure 10. As an example. To implement the swit h lambda expression we have spe ialized the swit h_statement fun tion for up to 9 ase statements. lass A. C& ) onst { return sele t(get<1>(args). a. The swit h lambda expression is a omposition of several lambda expressions. Figure 9: lambda_fun tor_base template spe ialization for the logi al operator. } }. and we show the te hniques to ensure that the omponent lambda expressions are of the right kind. 8 Control onstru ts To be able to provide the exa t same fun tionality. lass B. ). It is a bit hairy but we try our best to explain the whys and the whats.1 Swit h as a lambda expression For the swit h statement we go into a bit more detail. These fun tions 39 . to the underlying C++ ontrol onstru ts. All loop ontrols in the LL are implemented analogously. We invite the interested reader to read the library ode dire tly.template< lass Args> lass lambda_fun tor_base< a tion<2. a. b. and dedu tion is omplex and only narrowly interesting as a te hnique.

Also the lambda_fun tor_base templates are spe ialized dierently for ea h number of ase statements. the one that alls the built-in swit h statement. and one without the default statement. b. Again. C& ) onst { for(sele t(get<1>(args). the swit h lambda expression would not make any sense. the arguments would be arbitrary lambda expressions. sele t(get<3>(args). and a tuple holding the dierent ases. ). return_void_a tion<forloop_a tion> >. b. Consider the following swit h lambda expression onsisting of a single ase and a default: swit h_statement(free1. )) }. ). B& b. if instead of ase lambda expressions. Consequently. publi : expli it lambda_fun tor_base( onst Args& a) : args(a) {} template< lass RET. a. a. we use a simple example to walk through the dierent parts of the implementation. The swit her lass inherits from a lambda_fun tor instantiation. // ase 1: default_statement(free1--)). sele t(get<2>(args). // the test ase_statement<0>(free1 = 100). a. ). lass B. b. // default: The dierent ase statements and the default statement are lambda expressions. whi h is a lambda fun tor that an only be used as a ase of a swit h_statement. A tually. b. lass A. but they would not make any sense outside of a swit h statement. a. there are two spe ializations for ea h su h ase: one with the last ase being the default statement. thus swit her 40 . Further. Args > { Args args. The underlying swit h statement ode. Figure 10: lambda_fun tor_base spe ialization for the for loop. we have dened a swit her lass. is built using one of these spe ializations. return a lambda_fun tor with a swit h_a tion as the a tion lass. lass C> RET all(A& a. } sele t(get<4>(args).template< lass Args> lass lambda_fun tor_base< a tion<4.

the advantage being that we already have a return_void_type lass that we an reuse: template< lass ase1. lambda_fun tor<Arg> > default_statement( onst lambda_fun tor<Arg> &arg) { return swit her<DEFAULT. template<CaseEnumType aseType. lass ase2 = nil. } In order to ombine multiple swit her obje ts into one obje t we have reated a template to hold a group of them. the same template overs both regular ases and the default ase. DEFAULT }. This lass allows us to treat all variations of the swit h as one type for return type dedu tion purposes. } The fun tion for setting up the swit her lass for a default ase statement is similar. . This next template is a fun tion for setting up the swit her lass for a regular ase statement. and for the fa t that the ase value is already set to a known value (0 is an arbitrary hoi e): template< lass Arg> onst swit her<DEFAULT. int CaseValue.. lass ase3 = nil. lass ase 9 = nil> lass swit h_list_a tion.. 0.has all the properties of a lambda fun tor. CaseValue. it is the ase value that the user spe ies expli itly within the all to this fun tion: template<int CaseValue. lass Arg> onst swit her<CASE. Noti e that the CaseValue template parameter is not dedu ible. lass Args> lass swit her : publi lambda_fun tor<Args> { publi : template< lass T> swit her( onst lambda_fun tor<T>& a) : lambda_fun tor<Args>(a) {} }. In order to redu e the number of spe ial ases. lambda_fun tor<Arg> > ase_statement( onst lambda_fun tor<Arg> &arg) { return swit her<CASE. ex ept for the dierent ase type parameter (DEFAULT). CaseValue. 41 . A template parameter is used to indi ate whi h of these types the ase statement is: enum CaseEnumType { CASE. 0. lambda_fun tor<Arg> >(a). . lambda_fun tor<Arg> >(a).

onst swit her<CaseType2. Swit hA t2> > > >. Swit hA t1. Swit hA t1. Swit hA t2>::value > > (make_tuple(a1. ombine_arities<TestArg. int Case1. Swit hA t2>. a2. return_void_a tion< swit h_list_a tion< swit her<CASE. lass Swit hA t1. tuple<lambda_fun tor<TestArg>. Case1. Case2. int Case2. swit her<CaseType2. onst swit her<CASE.template < lass TestArg. Swit hA t1>& a2. Swit hA t1. Swit hA t2> > > >. tuple<lambda_fun tor<TestArg>. Swit hA t2>. Swit hA t1. Swit hA t1>. CaseEnumType CaseType2. ombine_arities<TestArg. a3)). 42 . Swit hA t2>& a3) { return lambda_fun tor< lambda_fun tor_args< a tion<3. Swit hA t1>. swit her<CaseType2. Case1. } Figure 11: The swit h_statement. lass Swit hA t2> inline onst lambda_fun tor< lambda_fun tor_args< a tion<3. Case2. Case1. return_void_a tion< swit h_list_a tion< swit her<CASE. Swit hA t2>::value > > swit h_statement( onst lambda_fun tor<TestArg>& a1. Case2.

By using all these templates we have rea hed a solution. Note also that the swit hers are stored into the argument tuple of the resulting lambda expression as normal lambda fun tors. as part of the swit h lambda expression. even another swit h lambda expression. swit hers is deliberately lost at this point. the fa t that they really are. When the lambda fun tor for the swit h statement has been onstru ted. one for ea h number of ases. Furthermore. whi h means we an use the same lambda_fun tor lass as we used for the example expression free1 > 0 (see se tion 6). shows the spe ialization of lambda_fun tor_base for a swit h statement with one ase statement and the default ase. In this ase the arity is FIRST. The gure 12. 8 Whew! What a lot of templates just to get ba k to plain old C++ ode. The LL has a set of spe ializations of the lambda_fun tor_base for swit h statements. The fun tion onstru ts the omplex lambda_fun tor whi h ontains a tuple of the arguments. We an handle any ombination of pla eholders. this has been guaranteed and the swit hers are not needed anymore (they do prevail in the a tion lass type.Next. where the size of the ode is linear. we do not allow a default statement in any pla e but the last argument. with the sele t alls looking for pla eholders and the return type RET being set to void. The instantiation of the lambda_fun tor lass triggers the sear h for a mat hing lambda_fun tor_base.8). and any lambda expression. The do_on e implementation is analogous to the swit h_statement implementation: we have 9 overloaded do_on e fun tion templates. The end result is a regular C++ swit h statement. and another 9 spe ializations of the lambda_fun tor_base. rather than exponential. however). the overloaded swit h_statement fun tion for two ases is shown in gure 11.8 We used a dierent template to solve ea h layer of the problem. This ode expands similarly to the for loop. Noti e that although we use the generi swit her lass as two of the arguments. and en odes the underlying C++ swit h statement within its template arguments. the rst ase statement and the default ase) for the highest arity ode and sets the arity ode to that number (see se tion 6. The use of return_type_void indi ates that swit h ontrol lambda fun tors have no return type. or were. The purpose of the swit her template is to guarantee that the swit h lambda expressions are omposed of the right kinds of omponents. we do not allow any other lambda expression types as swit hers. This arity ode determines whi h spe ialization of the lambda_fun tor template we will use. The template ombine_arities looks at the three lambda fun tors (the swit h expression part. The ompiler pi ks one of these as the losest mat h and ontinues the instantiation. 43 . with respe t to the number of ase statements we support.

Swit hCase1. lass Swit hArg1. a. ). )) { ase Swit hCase1: sele t(get<2>(args). a. lass C> RET all(A& a. C& ) onst { swit h(sele t(get<1>(args). default: sele t(get<3>(args). break. 44 . Swit hArg1>. 0. a. ). } } }. b. Swit hArg2> > > >. lass B. lass A. int Swit hCase1. swit her<DEFAULT. Args > { Args args. Figure 12: The lambda_fun tor_base spe ialization for a swit h statement with one ase statement and the default statement. b.template< lass Args. return_void_a tion< swit h_list_a tion< swit her<CASE. B& b. publi : expli it lambda_fun tor_base( onst Args& a) : args(a) {} template< lass RET. lass Swit hArg2> lass lambda_fun tor_base< a tion<3. b. break.

for the two at h blo ks ase we had 12 spe ializations. the next 24 and we stopped. the return types an be dierent as the at h blo k return type is never used. The rst argument is an arbitrary lambda fun tor. or rethrow. However. The try_ at h fun tions are similar to the swit h_statement fun tions. or for any ex eption type ( f. the at h(. and the other in whi h it is a at h(. We take a glimpse of the ode with a simple example: The following ode 9 Hen e.9 Delayed ex eption handling The ex eption handling lambda expression onsists of one try blo k lambda expression and potentially many at h blo k lambda expressions. On the other hand. In the ase of a normal exit from a at h blo k. the return types must mat h. Hen e. throw or return void. There is again an upper limit of 9 at h lambda expressions be ause of the limit of the number of arguments to the tuples: 9 ex eption handlers seems more than adequate. sin e the lambda fun tor an return either from the try part or from one of the handlers. Again the implementation relies on spe ializations: two lambda_fun tor_base spe ializations for ea h number of at h blo ks.. with one extra template layer in the all fun tions. 45 . we didn't but we should have. while the return type of a swit h lambda fun tor is void. the ex eption handling lambda fun tor an return a value. A try blo k lambda fun tor an either return an argument.9 This makes things di ult. Our rst attempt to over all these dierent ases had six spe ializations for one at h blo k. the basi stru ture is the same as in the swit h lambda expression.).) statement). an ex eption handling lambda expression an be used just as any lambda expression. or void. and a at h blo k lambda fun tor an throw. Rather than go through the whole gory ode we are going to wave our hands and just tou h on the highlights. or rethrows. i. The at h blo k an either be for a spe i ex eption type.. if the at h blo k always throws. There is a at h_ex eption fun tion that returns a template at her. 10 Ok. Ea h at her holds a at h blo k lambda fun tor. one in whi h the last at h is a at h(Type&). whi h is similar to the template swit her. and the following arguments are at her templates. the at h blo k lambda fun tor must return a type whi h an be onverted to the same type as the return type of the try blo k lambda fun tor. if it is the last at h blo k. as a subexpression of another lambda expression. The spe ializations of the lambda_fun tor_base are similar to the spe ializations for the swit h_statement.. Only the last argument an be a at h_all.e. or exit normally and return a value. If you are really interested we suggest that you get the sour e and take a look. In all these ombinations. Currently there are two ases per number of at h blo ks..10 We added some extra template layers to he k stati ally whether the lambda fun tor in a at h blo k will throw or not and got rid of the ombinatorial explosion of the template spe ializations. 48 spe ializations was really ridi ulous.

When this example is timed using the g 2. at h_ex eption<ExpType>( out << "Caught it. Instead of alling the sele t fun tions dire tly. free). or it must return an argument that an be onverted to type RET. for_ea h(v.8. it is slightly more than 2. Let us start with an example. but we an't say the same about fun tion names. 46 .000 elements to redu e the ee t of the penalty of the extra fun tion all. not the third argument passed into the try blo k (the parameter ).end(). The ode expands to use the spe ialization of the lambda_fun tor_base shown in gure 13. The gure 14 shows a pie e of ode ontaining three variations of a loop performing identi al tasks: the rst uses the LL. and how it is implemented. the se ond uses the lassi STL and the third is a traditional for loop. 10 Performan e testing The pre eding se tions show how the LL works. than the lassi STL version. it gets aught and a message is sent to out: void foo(int). The lient refers to the third argument with free3 and to the ex eption obje t freeE as is explained in se tion 3.begin(). When the lambda ode is timed against a hand oded for loop. Noti e that inside of the at h blo k the third argument to the all fun tion is the ex eption (the parameter e) that was thrown. try_ at h( bind(foo. In this se tion we fo us on the performan e issues. // our fun tion foo.v. we all another all fun tion11 . lambda is only slightly more than 1. This arrangement is the reason why the free3 pla eholder annot be used within a at h blo k.2 on a Pentium 450 with basi -O3 optimizing on.5 times as slow.4 times as slow. If the array size is in reased to 16. Note the extra layer in the all member fun tion of the at h blo k. whi h is a stati member of the return_or_throw template. The ex eption pla eholder takes the pla e of the free3 pla eholder and thus reuses the ma hinery for pla eholder substitutions. 11 The LL may use inventive te hniques. approximately 5%. and handles the two possible out omes: either the ode will throw again. This template alls the sele t fun tions.") ) ). alls a fun tion foo on every element in some ontainer v.95. the opy_if using the LL is slightly faster. The news is both good and bad. If foo throws an ex eption of type exp_type.

e). publi : expli it lambda_fun tor_base( onst Args &a) : args(a) {} template<RET. } } }. } at h(Cat hType1& e) { return return_or_throw< RET. a. lass Cat hType1> lass lambda_fun tor_base< a tion<2.template< lass Args. b. 47 . b. B&b. Figure 13: One of the lambda_fun tor_base spe ializations for ex eption handling. C& ) onst { try { return sele t(get<1>(args). lass A. try_ at h_a tion< at h_list_a tion<Cat hType1> > >. a. lass B. lass C> RET all(A&a. typename tuple_element_type<2. Args > { Args args. Args>::type >:: all(get<2>(args). ).

&v[100℄. vv[100℄. register int onst rr1 = r1. register int onst rr2 = r2. r1 <= free1 && free1 < r2). // using lassi STL: stru t in_range { int imin. imax. // hand oded for loop: register int onst * onst end = &v[100℄.int v[100℄ = 100 values . p<end. 48 . r2)). } }. r2 = 50. opy_if(v. &v[100℄. int y) : imin(x). for(int *p = v. } Figure 14: Three variations of the same loop. in_range(int x. // using lambda: opy_if(v.++p) if (rr1 <= *p && *p < rr2) { *pp++ = *p. onst int r1 = 30. in_range(r1. imax(y) {} bool operator()(int i) { return imin <= i && i < imax. register int *pp = vv.

The ET library also required that the user spe ify the type of the elements of the ontainer for its pla eholders and de lare them before using them.3 times as slow. Our implementation provides lambda fun tions whi h are both easier to use and more general than the expression templates in the ET library. These binary fun tors require that the arguments and return obje t be of the same type.utu. hanging the omparison to: opy_if(v. but ompared to the hand optimized version. They are all standard features of the C++ language. Complex lambda templates an easily ex eed this limit.fi. the lambda ode be omes 15% slower than the lassi STL version. The ET library used internally the STL template fun tors plus. The urrent implementation of the Lambda Library uses member templates and partial spe ialization of templates. extended and generalized these ideas to reate the Lambda Library. Spe i ally.12 The good news from all of this is that with a reasonable optimizing ompiler. &v[100℄. using the g again. r1 < (free1 + 3) && (free1 .3) from the Binder Library (BL) by Järvi [Jär00℄. These restri tions are lifted in the LL. but may require you to in rease this limit expli itly via a ommand line argument. the lambda ode is 5% faster than the hand written for loop! When we add some more omplexity. but not all vendors support them at the time of writing this. using simple lambda expressions you are no worse o than using lassi STL. 10. We have ombined. When this same ode is ompiled with KCC. known to be one of the best optimizing C++ ompilers. to handle the standard operators. 11 Relation to previous work We've taken the operator overloading on ept from the Expression Template library by Powell and Higley [PH00℄. the binding me hanism is more general than the one des ribed in [Jär00℄. 12 We know it is a small performan e gain but it is a gain.1 Current ompiler limitations The C++ Standard [C++98℄ suggests a template nesting level of 17 to help dete t innite re ursion.3).10) < r2). Most ompilers allow a greater number of nested templates. even the target fun tion an be left unbound (see se tion 2. the binding me hanism (see se tion 2.This is the bad news. s. The other good news is that with a great optimizing ompiler there is no penalty at all. 49 . Now for the good news. We maintain a list of ompilers that an ompile the LL in our web page http://lambda. is now slightly faster at 2. minus et . Further.

50 . and it extends better to the more omplex problems ( f. As it is not a built-in feature of C++. Inspired by this feature in FACT. it more or less embeds a fun tional sublanguage to C++. et . Our foremost goal is to provide lambda fun tors whi h mat h perfe tly with the STL style of programming. all the above features are just subfeatures of a single language onstru t. The LL implements lambda fun tions. FACT and FC++ take the fun tional features in a 'pure' form. bind1st. The LL removes several restri tions and simplies the use of STL in many ways. FACT supports a smaller set of operators. both developed oevally with the LL. LL does provide a me hanism for user extensions to enable the use of other expression template libraries. FACT and FC++ introdu e some very innovative template te hniques and we urge the reader to take a look at both of these libraries. Further. 12 Con lusion With the Lambda Library we hope to provide a valuable set of tools for working with STL algorithms. the lambda abstra tion. the LL introdu es a set of entirely new possibilities for STL usage: The LL makes it possible to loop through multiple nested ontainers. To our understanding. and Blitz++ [Bli99℄. even though the feature omes with some ost: the lambda fun tor be omes dynami ally bound. FACT deliberately allows no side ee ts in lambda fun tions. nor will be in the near future. and the looping in the STL algorithm an be ontinued. The FC++ is another library adding fun tional features to C++. like PETE [HCKS99℄. FACT lambda fun tions are 'expression template aware' (see [SS00℄). The LL is quite a step forward from the ET [PH00℄ and BL [Jär00℄ libraries. not to rigorously follow the fun tional programming paradigm. we hope you will nd omfort from the Lambda Library. Basi ally. and the return type and the argument types of the lambda fun tor must be dened expli itly by the lient. although the syntax of the lambda expressions is dierent.Other related work in ludes the FACT [Fa 00℄ and FC++ [MS00℄ libraries. while LL lambda fun tions are not. whi h means that assignments and ontrol onstru ts are not supported. The users of the LL have a natural way to write simple fun tors leanly. but does some adjustments for a better t to C++. The basi idea behind FACT is quite similar to the LL. Compared to LL. This is very onvenient. A unique feature of FC++ is the possibility to dene variables for storing lambda fun tors. aught and handled within the fun tor. not to mention the standard binders and fun tors. mem_fun. Ex eptions an be thrown. Tea hers of STL algorithms an have students writing lear ode qui kly: it is easier to explain how to use the LL than the urrent alternative of ptr_fun. bind3rdAnd4th).

This is an open issue. but we know we should. fun tion overloading and some help from the ompiler (e. Gary's inexhaustible ow of new ideas has brought LL mu h further I ould have ever imagined. but g 2. All that was needed was template spe ialization. We looked at using a template library whi h emulates a true typeof operator [Gib00℄. I've learned a lot about using templates from Jaakko.1 Future Work We are planning to add some new features to the LL. 10 arguments has been su ient for our needs. Gary's: Starting from modest improvements to the standard binders. the LL is still experimental. The Lambda Library would really benet from a full ompiler supported "typeof()" expression as it would eliminate a great deal of the omplex return type dedu tion ode. We are also on the lookout for ommon idioms. but in general. there is work to do in porting the library to new ompilers. The ex hange of emails was the mat h to the tinder. At the same time I dis overed Jaakko's bind library. and thus subje t to hanges. We may also onsider in reasing the number of allowed arguments to tuples.95. some of whi h Jaakko xed. namely ex eption spe i ations in fun tion prototypes and the volatile qualier. There are two gray areas whi h we haven't really looked at yet. Jaakko did most of it. It was truly interesting to dis over that standard C++ had all the ingredients for it. and others he repla ed with a more robust implementation. we gladly wel ome volunteers. whi h ould benet from be oming a lambda expression template and be in orporated into this library. I suggested a number of additions to the library whi h either I oded an initial attempt. Finally. but there is some support for this initiative in the C++ ommunity. In any ase.g. I probably would have added ontrol stru tures Jaakko's: 51 . When g releases the next major update we will revisit this. We shared papers. heavy inlining) and a lot of help from Gary. Jaakko dis overed me.12. I had been working on expression templates and had not been satised with the implementation. A knowledgements Jaakko and I met via the boost list server.2 failed to ompile it. and e ient. lambda abstra tion. Some ode is from both of us. partly inspired by some of the te hniques used in the FACT [Fa 00℄ and FC++ [MS00℄ libraries. There has been some dis ussion about adding a typeof operator (for querying a type of an expression stati ally) into the standard C++. we have steadily approa hed a proper. Jaakko wrote the ore of the Lambda Library and re ognized the need for a multilayered approa h whi h we have extended to isolate ea h problem. and there has been no stopping us sin e. For the most part.

de/zam/FACT. Outside help: No proje t exists in a va uum. Referen es [Bli99℄ The Blitz++ library home page. 2000. so to prove or impossible may prove to be subtle on epts.4.. November 2000. espe ially Dave Abrahams. unfortunately it proved to be impossible. 1998. Bonnard. Gary's onstant help has been invaluable. 1999. Peter Higley has reviewed early versions of this paper. http://www. Programming Languages  C++. We would also like to thank the boost readership for their insightful omments.0 ompiler and it fails. but ex eption handling is not something you would rst think of. Generative Programming : Methods. [Bon00℄ V. William Kempf is giving it another try. Czarne ki and U. Addison-Wesley. nal. Bjarne Stroustrup's [Str00℄ omments have inspired us to divide an intermixed paper into two distin t parts. Tools. [Gib00℄ B. it may even be 4. we've worked together very hard for quite some time and have a tually never met.oonumeri s.fz-jueli h. org/blitz. Corresponden e via the Boost listserver. Eisene ker. 2000. 2000. and made some good omments to add to its larity. 52 C/C++ Users Jour- . Also. [CE00℄ K. ISO/IEC:14882. Thus the sum of two individuals working together is greater than 2. A portable "typeof" operator. He also attempted a port to the Intel 4. Gibbons. However.3. reviewed the ode and ported it to KAI 3. whi h has allowed us to ode around the lo k. Jeremy Siek has helped us with the performan e testing. and attempted a port to MSVC 6. [C++98℄ International Standard. Valentin Bonnard suggested a way to globally overload the operator->*() [Bon00℄. so forgive us the abundan e of mutual thanking. [Fa 00℄ The FACT! library home page. Andrei Alexandres u's lever ode for testing type ompatibility at ompile time is used at some parts of the return type dedu tion system. and Appli ations.at some point. We are working with a 9-hour time dieren e. http://www. We are indebted to those who have gone before us and helped us along the way. The return type promotion ode of arithmeti types is modeled after that used in the Blitz++ library [Bli99℄.

[PH00℄ G. Powell and P. O tober 2000. 2001. gate h. C/C++ Users [Mey96℄ S. pages 8895. To appear. Expression templates as a repla ement for simple fun tors. An expression template aware lambda fun tion. http://www. [SS00℄ J. Addison-Wesley. Lee. Dobb's Journal. May 2000. Te hni al Report HPL-94-34(R. A.gov/pete. More Ee tive C++: 35 New Ways to Improve Your Programs and Designs. 2000. Te hni al Report 267. Smith. ACM. Hewlett-Pa kard Laboratories. Smith. . Germany. Veldhuizen. [Jär99a℄ J. Turku Centre for Computer S ien e. ML-style tuple assignment in standard C++  extending the multiple return value formalism. Järvi. April 1994. S. W. [SL94℄ A.hpl. Expression templates. 2000. [Vel95℄ T. and S. Meyers. Tuples and multiple return values in C++. Järvi. om/te hreports. [Mey99℄ S. Dobb's Journal. .sgi. Mar h 1999. Karmesin. The standard template library. Turku Centre for Computer S ien e. Dr. [Jär99b℄ J. Te hnology/STL. Smaragdakis. http://www. 53 Dr. the portable expression template engine. Stroustrup.[HCKS99℄ S. Mar h 1999.lanl. [Jär00℄ J. Te hni al Report 249. In Pro eedings of The 2000 International Conferen e on Fun tional Programming (ICFP). Striegnitz and S. O tober 1999.1).edu/~yannis/f ++. 1996. Pete. http://www. Higley. A. volume 1799 of Le ture Notes in Computer S ien e. J. [STL00℄ The SGI Standard Template Library. In Pro eedings of the Generative and Component-Based Software Engineering'99. [Str00℄ B. C++ fun tion obje t binders made easy. [Jär01℄ J. Meyers. M Namara and Y. Erfurt. Journal. Fun tional Programming in C++. Stepanov and M.a l. 7(5):2631. In First Workshop on C++ Template Programming. 12(5).hp. [MS00℄ B. http://www. Implementing operator->* for smart pointers. June 1995. Tuple types and multiple return values. Järvi. Crotinger. L. 2000. C++ Report. Personal orresponden e via email. Haney. O tober 1999. Järvi. om/ C++ Report. Au- gust 2000.

 University of Turku  Department of Mathemati al S ien es Åbo Akademi University  Department of Computer S ien e  Institute for Advan ed Management Systems Resear h Turku S hool of E onomi s and Business Administration  Institute of Information Systems S ien e .Turku Centre for Computer S ien e Lemminkäisenkatu 14 FIN-20520 Turku Finland http://www.abo.tu s.