mirror of
https://github.com/boostorg/lambda.git
synced 2026-01-21 17:02:36 +00:00
978 lines
45 KiB
TeX
978 lines
45 KiB
TeX
\documentclass[a4paper,titlepage]{article}
|
|
|
|
\usepackage{ifthen}
|
|
|
|
\usepackage{alltt}
|
|
\usepackage{url}
|
|
|
|
\usepackage{ae}
|
|
|
|
%\newcommand{\snip}[1]{\texttt{#1}} % a code snippet
|
|
\newcommand{\snip}[1]{\mbox{\texttt{#1}}} % a code snippet
|
|
\newcommand{\snipit}[1]{\snip{\textit{#1}}} % a code snippet
|
|
|
|
\newcommand{\shiftleft}{$<\hspace{-0.5mm}<$}
|
|
|
|
\newenvironment{Indent}{\begin{list}{}{
|
|
\setlength{\leftmargin}{\parindent}
|
|
% \setlength{\labelsep}{0ex}
|
|
\setlength{\itemsep}{0ex}%
|
|
%\setlength{\topsep}{0ex}%
|
|
%\setlength{\partopsep}{0ex}%
|
|
}%
|
|
\item[]}
|
|
{\end{list}}
|
|
|
|
\newenvironment{grammar}
|
|
{\begin{Indent}
|
|
\begin{itshape}
|
|
\begin{tabbing} \quad\=\quad\=\quad\=\quad\=\quad\=\quad\= \kill}
|
|
{\end{tabbing}
|
|
\end{itshape}
|
|
\end{Indent}}
|
|
|
|
\newcommand{\leftshift}{$<\vspace{-0.5mm}<$}
|
|
\newcommand{\rightshift}{$>\vspace{-0.5mm}>$}
|
|
|
|
\newenvironment{code}
|
|
{\begin{Indent}
|
|
\begin{sf}
|
|
\begin{tabbing} \quad\=\quad\=\quad\=\quad\=\quad\=\quad\= \kill}
|
|
{\end{tabbing}
|
|
\end{sf}
|
|
\end{Indent}}
|
|
|
|
\newcommand{\valipisteet}{\\[-0.5ex]\>\>\>$\cdots$\\[-0.5ex]}
|
|
|
|
|
|
|
|
\begin{document}
|
|
\title{%
|
|
\Large\bf{This is an incomplete {\Huge DRAFT}, some parts of the text are not yet included. Read at own risk.}\\[1cm]
|
|
\rule{11cm}{0.8mm}\\[1cm]
|
|
The Boost Lambda Library \\[1cm]
|
|
\large{User's Guide}}
|
|
\author{Jaakko J\"arvi \\
|
|
%Turku Centre for Computer Science\\
|
|
%Lemmink\"aisenkatu 14 A, FIN-20520 Turku\\
|
|
%Finland\\
|
|
%\textit{jaakko.jarvi@cs.utu.fi}\\[1cm]
|
|
\rule{11cm}{0.8mm}
|
|
}
|
|
\maketitle
|
|
|
|
\subsection*{Copyright}
|
|
|
|
Copyright (C) 1999--2001 Jaakko J\"arvi \\
|
|
(jaakko.jarvi@cs.utu.fi, jajarvi@indiana.edu)
|
|
|
|
\vspace{1ex}
|
|
\noindent The Boost Lambda Library is free software; Permission to copy and use this software is granted, provided this copyright notice appears in all copies.
|
|
Permission to modify the code and to distribute modified code is granted, provided this copyright notice appears in all copies, and a notice that the code was modified is included with the copyright notice.
|
|
|
|
\vspace{1ex}
|
|
|
|
\noindent In some files, there are two copyright holders:
|
|
|
|
\vspace{1ex}
|
|
|
|
\noindent Copyright (C) 2000 Gary Powell (gary.powell@sierra.com)
|
|
|
|
\noindent Copyright (C) 1999, 2000 Jaakko Järvi (jaakko.jarvi@cs.utu.fi)
|
|
\vspace{1ex}
|
|
|
|
\noindent The conditions are as above.
|
|
|
|
\vspace{1ex}
|
|
\noindent This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.
|
|
|
|
\subsection*{Contributors}
|
|
|
|
The core of the libary is written by Jaakko Järvi.
|
|
Gary Powell has been part of the 'development team' right after the core was written. His contributions are numerous oughout the library.
|
|
|
|
Many people have participated and helped including
|
|
Jeremy Siek, Peter Higley, Peter Dimov, ...
|
|
|
|
\tableofcontents
|
|
|
|
\section{Introduction}
|
|
|
|
The Boost Lambda Library (BLL in the sequel) is a C++ template library, which implements a form of {\em lambda abstraction} for C++.
|
|
The primary motivation for the work is to provide flexible and convenient means to define unnamed function objects for STL algorithms.
|
|
|
|
A line of code says more than a thousand words; the following line outputs the elements of some STL container \snip{a} separated by spaces:
|
|
|
|
\begin{alltt}
|
|
for_each(a.begin(), a.end(), std::cout << _1 << ' ');
|
|
\end{alltt}
|
|
|
|
\noindent The expression \verb|std::cout << _1 << ' '| defines a unary function object.
|
|
The variable \snip{\_1} is the parameter of the function, a {\em placeholder} for the actual argument.
|
|
Within each iteration of \snip{for\_each}, the function is called with with an element of \snip{a} as the actual argument.
|
|
This actual argument is substituted for the placeholder, and the function is evaluated.
|
|
|
|
The BLL is about letting you define small unnamed function objects, such as the one above, directly on the call site of an STL algorithm.
|
|
|
|
%The goal of the library is allow any C++ code fragment to be turned into an unnamed function, or lambda function.
|
|
%There are some inherent restrictions in the C++ language, that prevent this.
|
|
%...
|
|
|
|
|
|
%\noindent The library contains no binary library files, all template definitions are in a set of header files.
|
|
%The library code is standard C++.
|
|
|
|
\subsection{Contact Information}
|
|
|
|
The BLL is part of the C++ Boost Library collection, \url{http://www.boost.org}.
|
|
|
|
\subsection{Installing the library}
|
|
|
|
The library consists of include files only.
|
|
The \snip{boost} include directory must be on the include path.
|
|
|
|
There are a number of include files, that give different functionality.
|
|
|
|
%TODO: tsekkaa inclusion dependencies
|
|
|
|
\begin{itemize}
|
|
|
|
\item \snip{lambda/lambda.hpp} defines lambda expressions for different C++ operators
|
|
|
|
\item \snip{lambda/bind.hpp} defines \snip{bind} functions for up to 9 arguments.
|
|
|
|
\item \snip{lambda/control\_constructs.hpp} defines lambda function equivalents for the control constructs in C++ (includes \snip{lambda.hpp}).
|
|
|
|
\item \snip{lambda/construct.hpp} provides tools for writing lambda expressions with constructor, destructor, new and delete invocations (includes \snip{lambda.hpp}).
|
|
|
|
\item \snip{lambda/casts.hpp} provides lambda versions of different casts, as well as sizeof and typeid.
|
|
|
|
\item \snip{lambda/exceptions.hpp} gives tools for throwing and catching exceptions within lambda functions (includes \snip{lambda.hpp}).
|
|
|
|
\end{itemize}
|
|
|
|
Any other header files in the package are for internal use.
|
|
Additionally, the library depends on two other Boost Libraries, the {\em Tuple} and the {\em type\_traits} libraries.
|
|
|
|
All definitions are placed in the namespace \snip{boost::lambda}.
|
|
|
|
\subsection{Portability}
|
|
|
|
The BLL is written in standard C++, so it works with any standard conforming compiler.
|
|
|
|
%The current version has been tested with GCC 3.
|
|
%The authors of the library are gratetful for any information concerning problems or success in using the library with other compilers.
|
|
%The current status is as follows:
|
|
|
|
%\vspace{1ex}
|
|
|
|
%\begin{tabular}[t]{p{5cm}|p{5cm}}
|
|
%GCC 2.95 & full support (ok, some minor problems)\\
|
|
%\hline
|
|
%KAI 3.4 & maybe some problems, works for the most part \\
|
|
%\hline
|
|
%Intel 4.5\\MS Visual C++ 6.0 (sp3) \\
|
|
%Metroworks IDE 4.0 build 0356 & fundamental problems, does not work \\
|
|
%\end{tabular}
|
|
|
|
|
|
\section{Lambda expressions and binders}
|
|
|
|
\subsection{Motivation}
|
|
|
|
The Standard Template Library (STL)~\cite{stl}, now part of the C++ Standard Library~\cite{standard}, is a generic container and algorithm library.
|
|
Typically STL algorithms operate on container elements via {\em function objects}. These function objects are passed as arguments to the algorithms.
|
|
|
|
Any C++ construct, which can be called with the function call syntax, is a function object. The STL contains predefined function objects for some common cases (such as \snip{plus}, \snip{less} and \snip{not1}).
|
|
In addition, it contains {\em adaptors} for creating function objects from pointers to unary and binary functions, as well as from pointers to nullary and unary member functions.
|
|
It also contains {\em binder} templates for creating a unary function object from a binary function object by fixing one of the arguments to a constant value.
|
|
%This is in effect a {\em partial function application}.
|
|
Some STL implementations contain function composition operations as extensions to the standard~\cite{sgi_stl}.
|
|
|
|
All these tools aim at one goal: to make it possible to specify {\em unnamed functions} in a call of an STL algorithm, in other words, to pass code fragments as an argument to a function.
|
|
However, this goal is attained only partially.
|
|
For example, the following code adds \snip{1} to each element of \snip{a} and outputs the result in the standard output stream \snip{cout}.
|
|
|
|
\begin{alltt}
|
|
transform(a.begin(), a.end(), ostream_iterator<int>(cout),
|
|
bind1st(plus<int>(), 1));
|
|
\end{alltt}
|
|
|
|
\noindent The expression \snip{bind1st(plus<int>(), 1))} is an unnamed function: the subexpression \snip{plus<int>()} is a binary function object which computes the sum of two integers, \snip{bind1st} invokes this function object partially binding the first argument to \snip{1}.
|
|
|
|
This simple case demonstrates that with the standard tools the definition of unnamed functions is cumbersome.
|
|
Complex expressions involving functors, adaptors, binders and function composition operations tend to be difficult to comprehend.
|
|
In addition to this, there are significant restrictions in applying the standard tools. E.g. the standard binders allow only one argument of a binary function to be bound; there are no binders for 3-ary, 4-ary etc.\ functions.
|
|
|
|
The Boost Lambda Library provides solutions for the problems described above:
|
|
\begin{itemize}
|
|
|
|
\item{Lambda expressions, that is unnamed functions, can be created easily with an intuitive syntax. The above example can be written as:
|
|
\begin{alltt}
|
|
transform(a.begin(), a.end(), ostream_iterator<int>(cout),
|
|
1 + _1);
|
|
\end{alltt}
|
|
\noindent or more intuitively:
|
|
\begin{alltt}
|
|
for_each(a.begin(), a.end(), cout << (1 + _1));
|
|
\end{alltt}
|
|
}
|
|
|
|
\item{Most of the restrictions in argument binding are removed, arbitrary arguments of practically any C++ function can be bound.}
|
|
|
|
\item{Separate function composition operations are not needed, function composition is supported with the same binding and lambda expression syntax.}
|
|
|
|
\end{itemize}
|
|
|
|
\subsection{Introduction to lambda expressions}
|
|
|
|
Lambda expression are common in functional programming languages. Their syntax varies between languages (and between different forms of lambda calculus), but the basic form of a lambda expressions is:
|
|
\begin{trivlist}% trivlist is used just to get similar spacing to equation environment, but without centering
|
|
\item $\lambda x_1 \cdots x_n . e$
|
|
\end{trivlist}
|
|
\noindent A lambda expression defines an unnamed function. A lambda exrpession consists of
|
|
\begin{itemize}
|
|
\item the parameters of this function: $x_1 \cdots x_n$.
|
|
\item the expression $e$ which computes the value of the function in terms of the parameters $x_1 \cdots x_n$.
|
|
\end{itemize}
|
|
|
|
\noindent A simple example of a lambda expression is
|
|
\begin{trivlist}
|
|
\item \( \lambda x y.x+y \)
|
|
\end{trivlist}
|
|
|
|
In function application, the actual arguments are substituted for the parameters in the lambda expression. For example:
|
|
\begin{trivlist}
|
|
\item \( (\lambda x y.x+y)\ 1\ 2 \Rightarrow 1 + 2 \)
|
|
\end{trivlist}
|
|
|
|
In the C++ version of lambda expressions the $\lambda x_1 \ldots x_n$ part is missing and the formal parameters have predefined names.
|
|
There are three such predefined formal parameters, called {\em placeheholders}: \snip{\_1}, \snip{\_2} and \snip{\_3}.
|
|
They refer to the first, second and third argument of the function defined by the lambda expression.
|
|
For example, the C++ version of the definition
|
|
\( \lambda x y.x+y \)
|
|
is
|
|
\begin{alltt}
|
|
_1 + _2
|
|
\end{alltt}
|
|
Hence, there is no syntactic keyword for C++ lambda expressions. The use of a placeholder as an operand implies that the operator invocation is a lambda expression.
|
|
However, this is true only for operator invocations.
|
|
Lambda expressions containing function calls, control constructs, casts etc. require special syntactic constructs.
|
|
Most importantly, function calls need to be wrapped inside a \snip{bind} function.
|
|
As an example, consider the lambda expression:
|
|
\begin{trivlist}
|
|
\item $\lambda x y.foo(x,y)$
|
|
\end{trivlist}
|
|
Rather than \snip{foo(\_1, \_2)}, the C++ counterpart for this expression is:
|
|
\begin{alltt}
|
|
bind(foo, _1, _2)
|
|
\end{alltt}
|
|
We refer to this type of C++ lambda expressions as {\em bind expressions}.
|
|
|
|
A lambda expression defines a C++ function object, hence function application syntax is like calling any other function object:
|
|
the C++ expression \snip{(\_1 + \_2)(1, 2)} corresponds to
|
|
\snip{$(\lambda x y.x+y)\ 1\ 2$}\footnote{Actually, this is not a valid C++ lambda expression. The reason for this is explained in section\ref{make_const}.}.
|
|
|
|
|
|
\paragraph*{Partial function application}
|
|
|
|
A bind expression is in effect a {\em partial function application}.
|
|
In partial function application, some of the arguments of a function are bound to fixed values.
|
|
The result is another function, with possibly fewer arguments.
|
|
When called with the unbound arguments, this new function invokes the original function with the merged argument list of bound and unbound arguments.
|
|
|
|
The underlying implementation of the BLL unifies the two types of lambda expressions (bind expressions and lambda expressions consisting of operator calls).
|
|
If operators are regarded as functions, it is easy to see that lambda expressions using operators are partial function applications as well.
|
|
E.g. the lambda expression \snip{\_1 + 1} can be seen as syntactic sugar for the pseudo code \snip{bind(operator+, \_1, 1)}.
|
|
|
|
\paragraph*{Terminology}
|
|
|
|
A lambda expression defines a function. A C++ lambda expression concretely constructs a function object, {\em a functor}, when evaluated. We use the name {\em lambda functor} to refer to such a function object. Hence, in the terminology adpoted here, the result of evaluating a lambda expression is a lambda functor.
|
|
|
|
|
|
|
|
\subsection{Parameter and return types of lambda functors\label{parameters_and_return_types}}
|
|
|
|
The placeholders arguments do not have a fixed type.
|
|
During the invocation of a lambda functor, the actual arguments are substituted for the placeholders.
|
|
The basic rule is that a lambda function can be called with arguments of any types, as long as the lambda expression with substitutions performed is a valid C++ expression.
|
|
For example, the expression \snip{\_1 + \_2} creates a binary lambda functor.
|
|
It can be called with two objects of any types \snip{A} and \snip{B} for which \snip{operator+(A,B)} is defined (and for which BLL knows the return type of the operator, see below).
|
|
|
|
C++ lacks a mechanism to query a type of an expression.
|
|
However, this precise mechanism is crucial for the implementation of C++ lambda expressions.
|
|
Consequently, BLL includes a somewhat complex type deduction system which uses a set of traits classes for deducing the resulting type of lambda functions.
|
|
It handles expressions where the operands are of built-in types and many of the expressions with operands of standard library types.
|
|
Many of the user defined types are covered as well, particularly if the user defined operators obey normal conventions in defining the return types.
|
|
|
|
%TODO: move this forward, and just refer to it.
|
|
However, there are cases when the return type cannot be deduced. For example, suppose you have defined:
|
|
\begin{alltt}
|
|
C operator+(A, B);
|
|
\end{alltt}
|
|
The following lambda function invocation fails, since the return type cannot be deduced:
|
|
\begin{alltt}
|
|
A a; B b; (_1 + _2)(a, b);
|
|
\end{alltt}
|
|
|
|
There are two alternative solutions to this.
|
|
The first is to extend the BLL type deduction system to cover your own types (see section~\ref{extending_return_type_system}).
|
|
The second is to use a special {\em ret} lambda expression which defines the return type in place:
|
|
\begin{alltt}
|
|
A a; B b; ret<C>(\_1 + \_2)(a, b);
|
|
\end{alltt}
|
|
|
|
For bind expressions, the return type can be defined as a template argument of the bind function as well:
|
|
\begin{alltt}
|
|
bind<int>(foo, _1, _2);
|
|
\end{alltt}
|
|
A rare case, where the \snip{ret<{\em type}>} syntax does not work, but
|
|
\snip{bind<{\em type}>} does, is explained in section~\ref{bind_vs_ret}.
|
|
|
|
\paragraph*{About actual arguments to lambda functions}
|
|
|
|
A general restriction for the actual arguments is that they cannot be nonconst rvalues.
|
|
For example:
|
|
\begin{alltt}
|
|
int i = 1; int j = 2;
|
|
(_1 + _2)(i, j); // ok
|
|
|
|
(_1 + _2)(1, 2); // error (!)
|
|
\end{alltt}
|
|
|
|
\noindent This restriction is not as bad as it may look.
|
|
Since the lambda functions are most often called inside STL-algorithms,
|
|
the arguments originate from dereferencing iterators.
|
|
The dereferencing operators usually do not return rvalues.
|
|
And for the cases where the restriction is encountered, there are workarounds:
|
|
\begin{enumerate}
|
|
\item If the rvalue is of a class type, the return type of the function that creates the rvalue should be defined as const. Due to an unfortunate language restriction this does not work for built-in types, as built-in rvalues cannot be const qualified.
|
|
|
|
\item
|
|
If the lambda function call is accessible, the \snip{make\_const} function can be used to {\em constify} the rvalue. E.g.:
|
|
\begin{alltt}
|
|
int i = 1; int j = 2;
|
|
(_1 + _2)(make\_const(1), make\_const(2)); // ok
|
|
\end{alltt}
|
|
|
|
\item
|
|
If neither of the above is possible, the lambda expression can be wrapped in a \snip{break\_const} function.
|
|
{\bf Note!} This solution can break const correctness.
|
|
It takes its arguments as const, and casts away constness prior to the call to the original lambda functor.
|
|
For example:
|
|
\begin{alltt}
|
|
break_const(_1 + _2)(1, 2); // ok
|
|
\end{alltt}
|
|
The result of \snip{break\_const} is not a lambda functor, so it cannot be used as a subexpression of a lambda expression.
|
|
\begin{alltt}
|
|
break_const(_1 + _2) + _3; // fails.
|
|
\end{alltt}
|
|
However, this kind of code should never be necessary, since calls to sub lambda functors are made inside the BLL, and are not affected by the non-const rvalue problem.
|
|
|
|
\end{enumerate}
|
|
|
|
\paragraph*{Storing bound arguments in lambda functions}
|
|
|
|
The lambda function stores the bound arguments in a tuple object.
|
|
By default, temporary const copies of the arguments are stored.
|
|
This means that the value of a bound argument is fixed at the time of the creation of the lambda function and remains constant during the lifetime of the lambda function object.
|
|
For example:
|
|
\begin{alltt}
|
|
int i = 1;
|
|
(_1 + i)(i = 2);
|
|
\end{alltt}
|
|
The value of the expression in last line is 2, not 3.
|
|
In other words, the lambda expression \snip{\_1 + i} creates a lambda function $\lambda x.x+1$ rather than $\lambda x.x+i$.
|
|
|
|
As said, this is the default behaviour for which there are exceptions.
|
|
The exact rules are as follows:
|
|
\begin{itemize}
|
|
\item The programmer can control the storing mechanism with \snip{ref} and \snip{cref} wrappers, see~\ref{boost_bind}.
|
|
|
|
\item Array types cannot be copied, they are thus stored as const reference by default.
|
|
|
|
\item For some expressions, it makes more sense to store the arguments as references.
|
|
For example, the obvious intention of the lambda expression \snip{i += \_1} is that calls to the lambda functor affect the value of the variable \snip{i}, rather than some temporary copy of it.
|
|
As another example, the streaming operators take their leftmost argument as non-const references. The exact rules are:
|
|
|
|
\begin{itemize}
|
|
\item The left argument of compound assignment operators (\snip{+=}, \snip{*=}, etc.) are stored as references to non-const.
|
|
\item If the left argument of \leftshift\ or \rightshift\ operator is derived from an instantiation of \snip{basic\_ostream} or respectively from \snip{basic\_istream}, the argument is stored as a reference to non-const. For any other types, the argument is stored as a copy.
|
|
\item In pointer arithmetic expressions, non-const array types are stored as non-const references.
|
|
\end{itemize}
|
|
\end{itemize}
|
|
|
|
%TODO (this should be removed???, as there are no special rules for bind expressions anymore
|
|
The rules for bind expressions are explained in section~\ref{bind_expression}.
|
|
|
|
\section{Lambda expressions in details}
|
|
|
|
There are different kind of lambda experessions.
|
|
The highest level of the grammar for C++ lambda expressions is as follows:
|
|
|
|
\begin{grammar}
|
|
lambda-expression::=\\
|
|
\> placeholder $|$\\
|
|
\> ret-expression $|$ \\
|
|
\> operator-expression $|$\\
|
|
\> bind-expression $|$\\
|
|
\> delay-expression $|$\\
|
|
\> control-expression $|$\\
|
|
\> construction-expression $|$\\
|
|
\> throw-expression $|$\\
|
|
\> try-catch-expression $|$\\
|
|
\end{grammar}
|
|
|
|
\noindent In the sequel, we devote a separate section for each of the possible forms of a lambda expression.
|
|
|
|
\subsection{\label{placeholders}Placeholders}
|
|
|
|
The BLL defines three {\em placeholder types}: \snip{free1\_type}, \snip{free2\_type} and \snip{free3\_type}. An object of any of these types is a placeholder.
|
|
BLL has a predefined placeholder variable for each placeholder type: \snip{\_1}, \snip{\_2} and \snip{\_3}.
|
|
However, the user is not forced to use these placeholders.
|
|
It is easy to define placeholders with alternative names.
|
|
This is done by defining new variables of placeholder types.
|
|
For example:
|
|
|
|
\begin{alltt}
|
|
boost::lambda::free1_type X;
|
|
boost::lambda::free2_type Y;
|
|
boost::lambda::free3_type Z;
|
|
\end{alltt}
|
|
With these variables defined, \snip{X += Y * Z} is equivalent to \snip{\_1 += \_2 * \_3}.
|
|
|
|
The use of placeholders in the lambda expression determines whether the resulting function is nullary, unary, binary or 3-ary.
|
|
The highest placeholder index is decisive. For example:
|
|
\begin{alltt}
|
|
_1 + 5 // unary
|
|
_1 * _1 + _1 // unary
|
|
_1 + _2 // binary
|
|
bind(f, _1, _2, _3) // 3-ary
|
|
_3 + 10 // 3-ary
|
|
\end{alltt}
|
|
Note that the last line creates a 3-ary function, which adds \snip{10} to its {\em third} argument.
|
|
The first two arguments are discarded.
|
|
Note also that a placeholder itself is not a lambda functor (an identity function).
|
|
Hence, \snip{\_1(x)} does not return the value of \snip{x}.
|
|
|
|
In addition two these three placeholder types, there is also a fourth placeholder type \snip{freeE\_type}.
|
|
The use of this placeholder is defined in section \ref{exceptions} describing exception handling in lambda expressions.
|
|
|
|
When an actual argument is supplied for a placeholder, the parameter passing mode is always by reference.
|
|
This means that any side-effects to the placeholder are reflected to the actual argument.
|
|
For example:
|
|
\begin{alltt}
|
|
int i = 1;
|
|
(_1 += 2)(i); // i is now 3
|
|
(++_1, cout << _1)(i) // i is now 4, outputs 4
|
|
\end{alltt}
|
|
|
|
\subsubsection{Currying\label{currying}}
|
|
|
|
Lambda functors support currying, that is, calling functions with one argument at a time.
|
|
If too few arguments are provided for a lambda functor, another lambda functor with a lower arity results.
|
|
When all arguments are provided, the original lambda functor is called.
|
|
For example:
|
|
\begin{alltt}
|
|
int i, j, k;
|
|
(_1 + _2 + _3)(i, j, k) \(\equiv\)
|
|
(_1 + _2 + _3)(i)(j)(k) \(\equiv\)
|
|
(_1 + _2 + _3)(i, j)(k) \(\equiv\)
|
|
(_1 + _2 + _3)(i)(j, k)
|
|
\end{alltt}
|
|
A curried lambda functor just stores the arguments that are provided, it does not evaluate any subexpressions that might in theory be possible to evaluate.
|
|
E.g., in the expression \snip{(\_1 * \_1 + \_2)(i)}, the subexpression \snip{i * i} is not evaluated.
|
|
The lambda functor is evaluated only when all the arguments are available.
|
|
|
|
\subsection{Overriding the deduced return value}
|
|
|
|
The return type deduction system may not be able to deduce the return types of some user defined operators or bind expressions with class objects (see the example in section~\ref{parameters_and_return_types}).
|
|
A \snipit{ret-expression} is provided for stating the return type explicitly and overriding the deduction system. To state that the return type of the lambda functor defined by the lambda expression \snip{e} is \snip{T}, you can write:
|
|
\begin{alltt}
|
|
ret<T>(e);
|
|
\end{alltt}
|
|
The effect is that the return type deduction is not initiated for the lambda expression \snip{e} at all, but instead, \snip{T} is used as the return value.
|
|
Obviously \snip{T} cannot be an arbitrary type, the true result of the lambda functor must be implicitly convertible to \snip{T}.
|
|
For example:
|
|
\begin{alltt}
|
|
A a; B b;
|
|
C operator+(A, B);
|
|
int operator*(A, B);
|
|
...
|
|
ret<D>(_1 + _2)(a, b); // error (C cannot be converted to D)
|
|
ret<C>(_1 + _2)(a, b); // ok
|
|
ret<float>(_1 * _2)(a, b); // ok (int can be converted to float)
|
|
...
|
|
struct X \{
|
|
typedef Y result_type;
|
|
Y operator()(); // #1
|
|
Z operator(int)(); // #2
|
|
\};
|
|
...
|
|
X x; int i;
|
|
bind(x)(); // ok, call #1
|
|
bind(x, _1)(i); // try to call #2: error, deduction gives Y
|
|
ret<Z>(bind(x, _1))(i); // ok, call #2
|
|
\end{alltt}
|
|
For bind expressions, there is a short-hand notation that can be used instead of \snip{ret}. The last line could be alternatively written as:
|
|
\begin{alltt}
|
|
bind<Z>(x, _1)(i);
|
|
\end{alltt}
|
|
|
|
Note that within nested lambda expressions, \snip{ret} must be used at each invocation where the deduction would otherwise fail.
|
|
For example:
|
|
\begin{alltt}
|
|
A a; B b;
|
|
C operator+(A, B); D operator-(C);
|
|
...
|
|
ret<D>( - (_1 + _2))(a, b); // error
|
|
ret<D>( - ret<C>(_1 + _2))(a, b); // ok
|
|
\end{alltt}
|
|
%Note also, that to be able to compose lambda expressions, lambda functors must return all temporaries as const. BLL does this for you transparently. For example, \snip{ret$<$int$>$} defines the return type \snip{const int}, \snip{ret$<$C*$>$} the return type \snip{C* const} etc. (Note! This applies only to returns by value, not references. Neither does it turn a {\em pointer to non-const} into a {\em pointer to const}.
|
|
If you find yourself using \snip{ret} repeatedly with the same types, it is worth while extending the return type deduction (see section~\ref{extending_return_type_system}):
|
|
|
|
\subsubsection{Nullary lambda functors and ret}
|
|
|
|
As stated above, the effect of \snip{ret} is to prevent the return type deduction to be performed.
|
|
However, there is an exception. Due to the way the C++ template instantiation works, the compiler is always forced to instantiate the return type deduction templates for zero-argument lambda functors.
|
|
This introduces a slight problem with \snip{ret}, best described with an example:
|
|
\begin{alltt}
|
|
struct F \{ int operator()(int i) const; \};
|
|
F f;
|
|
...
|
|
bind(f, _1); // fails, cannot deduce the return type
|
|
ret<int>(bind(f, _1)); // ok
|
|
...
|
|
bind(f, 1); // fails, cannot deduce the return type
|
|
ret<int>(bind(f, 1)); // fails as well!
|
|
\end{alltt}
|
|
The BLL cannot deduce the return types of the above bind calls, as \snip{F} does not define the typedef \snip{result\_type}.
|
|
One would expect \snip{ret} to fix this, but for a nullary lambda functor that results from a bind expression (last line above) this does not work.
|
|
The return type deduction templates are instantiated, even though it would not be necessary. The result is a compilation error.
|
|
|
|
The solution to this is to use the alternative syntax for specifying the return type with bind expressions:
|
|
\begin{alltt}
|
|
bind<int>(f, 1); // ok
|
|
\end{alltt}
|
|
%TODO: use something else in place of ...
|
|
The lambda functors created by \verb|ret<T>(bind(...)| and \verb|bind<T>(...)| have the exact same functionality ---
|
|
apart from the fact that for some nullary lambda functors, the other works and the other does not.
|
|
|
|
\subsection{Operator expressions}
|
|
|
|
The basic rule, which again has exceptions, is that any C++ operator invocation, with at least one argument being a lambda expression is itself a lambda expression.
|
|
Almost all overloadable operators are supported.
|
|
For example, the following is a valid operator expression:
|
|
|
|
\begin{alltt}
|
|
cout << _1, _2[_3] = _1 && false
|
|
\end{alltt}
|
|
|
|
There are some restrictions that originate from the C++ operator overloading rules, and some special cases:
|
|
|
|
\paragraph{Operators that cannot be supported}
|
|
|
|
Some operators cannot be overloaded at all, or their overloading rules prevent them to be overloaded to create lambda functors.
|
|
These operators are \verb|->.|, \verb|->|, \snip{new}, \snip{new[]}, \snip{delete}, \snip{delete[]} and \verb|?:| (the conditional operator).
|
|
|
|
\paragraph{\snip{operator=} and \snip{operator[]}\label{member_operators}}
|
|
|
|
These operators must be implemented as class members. Consequently, the left operand must be a lambda expression. For example:
|
|
\begin{alltt}
|
|
int i;
|
|
i = _1; // not ok. i is not a lambda expression
|
|
var(i) = _1; // ok, see section \ref{delay_expression}
|
|
\end{alltt}
|
|
|
|
\paragraph{Locigal operators}
|
|
|
|
Logical operators obey the short-circuiting evaluation rules. For example, \snip{i} is never incremented in the following code.
|
|
\begin{alltt}
|
|
bool flag = true; int i = 0;
|
|
(_1 || ++_2)(flag, i)
|
|
\end{alltt}
|
|
|
|
\paragraph{Comma operator}
|
|
Comma operator is the 'statement separator' in lambda expressions. Since comma is also the separator between arguments in a function call, extra parenthesis are sometimes needed:
|
|
\begin{alltt}
|
|
for_each(a.begin(), a.end(), \textbf{(}++_1, cout << _1\textbf{)});
|
|
\end{alltt}
|
|
Comma operator adheres to the C++ rule of always evaluating the left operand before the right one.
|
|
|
|
\paragraph{Function call operator}
|
|
|
|
A lambda functor of arity $n$ defines the $n$-ary function call operator, which evaluates the functor.
|
|
Function call operators of arities between $1$ and $n-1$ are defined to support curried calls (see currying, section~\ref{currying}).
|
|
|
|
Note, placeholders do not define the function call operator.
|
|
|
|
\paragraph{Member pointer operator}
|
|
|
|
C++ allows the member pointer operator \verb|operator->*| to be overloaded freely.
|
|
Hence, for user defined types, member pointer operator is no special case.
|
|
The built-in meaning, however, is a somewhat more complicated case.
|
|
First, the built-in member pointer operator is applied, if the left argument is a pointer to an object of some class \snip{A}, and the right hand argument is a pointer to a member of \snip{A}, or a pointer to a member of a class from which \snip{A} derives.
|
|
We must separate two cases:
|
|
\begin{enumerate}
|
|
|
|
\item The right hand argument is a pointer to a data member.
|
|
In this case the lambda functor simply performs the argument substitution and calls the built-in member pointer operator, which returns a reference to the member pointed to.
|
|
For example:
|
|
\begin{alltt}
|
|
struct A { int d; };
|
|
A* a = new A();
|
|
...
|
|
(a ->* &A::d); // returns a reference to a->d
|
|
(_1 ->* &A::d)(a); // likewise
|
|
\end{alltt}
|
|
|
|
\item The right hand argument is a pointer to a member function.
|
|
For a built-in call like this, the result is kind of a delayed member function call.
|
|
Such an expression must be followed by a function argument list, with which the delayed member function call is performed.
|
|
For example:
|
|
\begin{alltt}
|
|
struct B { int foo(int); };
|
|
B* b = new B();
|
|
...
|
|
(b ->* &B::foo) // returns a delayed call to b->foo, must be followed
|
|
// by a function argument list
|
|
(b ->* &B::foo)(1) // ok, calls b->foo(1)
|
|
|
|
(_1 ->* &B::foo)(b); // returns a delayed call to b->foo, no effect as such
|
|
|
|
(_1 ->* &B::foo)(b)(1); // calls b->foo(1)
|
|
\end{alltt}
|
|
|
|
\end{enumerate}
|
|
|
|
|
|
\subsection{\label{bind_expression}Bind expressions}
|
|
|
|
The grammar for the bind expressions can be outlined as follows:
|
|
\begin{grammar}
|
|
bind-expression::=\\
|
|
\> \textsf{\em bind(} target-function, bind-argument-list \textsf{\em )} $|$\\
|
|
\> \textsf{\em bind(} target-member-function, object-argument, bind-argument-list \textsf{\em )} $|$\\
|
|
\\
|
|
target-function::=\\
|
|
\> pointer-to-function $|$ \\
|
|
\> reference-to-function $|$ \\
|
|
\> object-with-function-call-operator $|$ \\
|
|
\> lambda-expression\\
|
|
\\
|
|
target-member-function::=\\
|
|
\> pointer-to-member-function $|$ \\
|
|
\> lambda-expression\\
|
|
\\
|
|
bind-argument-list::=\\
|
|
\> any-expression, bind-argument-list $|$ \\
|
|
\> lambda-expression, bind-argument-list \\
|
|
\\
|
|
object-argument::=\\
|
|
\> class-object-expression $|$ \\
|
|
\> pointer-to-class-object-expression $|$ \\
|
|
\> lambda-expression
|
|
\end{grammar}
|
|
|
|
A bind expression delays the call of a function. If this {\em target function} is $n$-ary, then the \snipit{bind-argument-list} must contain $n$ arguments as well.
|
|
In the current version of the BLL, $0 \leq n \leq 9$ must hold.
|
|
For member functions, the number of arguments must be $\leq 8$, as the object argument takes one argument position.
|
|
|
|
Basically, the \snipit{bind-argument-list} must be a valid argument list for the target function, except that any argument can be replaced with a placeholder, or more generally, with a lambda expression.
|
|
Note that also the target function can be a lambda expression.
|
|
|
|
The result of a bind expression is either a nullary\footnote{A zero-argument function.}, unary, binary or 3-ary function object depending on the use of placeholders in the \snipit{bind-argument-list} (see section~\ref{placeholders}).
|
|
|
|
Below, we describe the requirements for the different types of bind expressions.
|
|
|
|
\subsubsection{Function pointers or references as targets}
|
|
|
|
The target function can be a pointer or a reference to a function and it can be either bound or unbound. For example:
|
|
\begin{alltt}
|
|
X foo(A, B, C); A a; B b; C c;
|
|
bind(foo, _1, _2, c)(a, b);
|
|
bind(&foo, _1, _2, c)(a, b);
|
|
bind(_1, a, b, c)(foo);
|
|
bind(_1, a, b, c)(make_const(&foo));
|
|
\end{alltt}
|
|
The last line requires the use of \snip{make_const}, since \snip{&foo} is a non-const rvalue, see~\ref{non-const_rvalues}.
|
|
The return type deduction always succeeds with this type of bind expressions.
|
|
|
|
Note, that in C++ it is possible to take the address of an overloaded function only if the address is assigned to or used to initialise a properly typed variable.
|
|
This means, that overloaded functions cannot be used in bind expressions directly, e.g.:
|
|
\begin{alltt}
|
|
void foo(int);
|
|
void foo(float);
|
|
int i;
|
|
...
|
|
bind(&foo, _1)(i); // error
|
|
...
|
|
void (*pf1)(int) = &foo;
|
|
bind(pf1, _1)(i); // ok
|
|
bind(static_cast<void(*)(int)>(&foo), _1)(i); // ok
|
|
\end{alltt}
|
|
|
|
\subsubsection{Function objects as targets}
|
|
|
|
Function objects, that is, class objects which have the function call operator defined, can be used as target functions.
|
|
The return type deduction system requires that the function object class defines the return type of the function call operator as the typedef \snip{result\_type}.
|
|
Function objects in the standard library adhere to this convention.
|
|
|
|
The possible const qualifier in \snip{operator()} is respected, even so that the same function object can define both a const and non-const version of the function call operator.
|
|
For example:
|
|
\begin{code}
|
|
struct A \{\\
|
|
\> typedef B result\_type; \\
|
|
\> B operator()(X, Y, Z); // \#1\\
|
|
\> B operator()(X, Y, Z) const; // \#2\\
|
|
\};
|
|
\end{code};
|
|
The above function object can be used like this.
|
|
\begin{code}
|
|
A a; const A ca = A(); X x; Y y; Z z;\\
|
|
bind(a, x, y, free1)(z); // calls \#1\\
|
|
bind(ca, x, y, free1)(z); // calls \#2\\
|
|
bind(free1, x, y, z)(a); // calls \#1\\
|
|
bind(free1, x, y, z)(ac); // calls \#2\\
|
|
\end{code}
|
|
Note however, that the target function object may not be a nonconst temporary. For example:
|
|
\begin{code}
|
|
bind(A(), x, y, z); // error\\
|
|
bind(make\_const(A()), x, y, z)(); // ok, calls \#1
|
|
\end{code}
|
|
|
|
The overloading of the function call operator is not limited to overloading with respect to constness; arbitrary overloading is allowed. However, the return type deduction system can handle only one return type. Consequently, all the overloaded function call operators must have the same return type or the \snip{ret} wrapper must be used.
|
|
|
|
\subsubsection{\label{members_as_targets}Member functions as targets}
|
|
|
|
A bound object argument can be a reference or pointer to the object, the BLL supports both cases with a uniform interface:
|
|
\begin{code}
|
|
bool A::foo(int); A a; A* ap = \&a;
|
|
vector$<$int$>$ ints;
|
|
\valipisteet
|
|
find\_if(ints.begin(), ints.end(), bind(\&A::foo, a, free1)); \ \ \=// reference is ok\\
|
|
find\_if(ints.begin(), ints.end(), bind(\&A::foo, ap, free1)); \>// pointer is ok
|
|
\end{code}
|
|
The functionality is identical in both cases.
|
|
Similarly, if the object argument is free, the resulting binder object can be called both via a pointer or a reference.
|
|
\begin{code}
|
|
bool A::foo(int); list$<$A$>$ refs; list$<$A*$>$ pointers;
|
|
\valipisteet
|
|
find\_if(refs.befin(), refs.end(), bind(\&A::foo, free1, 1)); \qquad \=\\
|
|
find\_if(pointers.begin(), pointers.end(), bind(\&A::foo, free1, 1));
|
|
\end{code}
|
|
|
|
The exact rules for the object argument (whether it is bound, or supplied in the lambda function invoction) are as follows:
|
|
If the target function is a pointer to a member function of some class \snip{A}, then the object argument must be an expression of type \snip{B}, where either
|
|
\begin{itemize}
|
|
\item \snip{B} = \snip{A} or there is an implicit conversion from \snip{B} to \snip{A}.
|
|
\item \snip{B} = \snip{A*}.
|
|
\item \snip{B} = \snip{C*}, where \snip{C} is any class derived form \snip{A}.
|
|
\end{itemize}
|
|
For example:
|
|
\begin{code}
|
|
struct A \{\\
|
|
\> virtual void f();\\
|
|
\> void fc() const;\\
|
|
\};\\
|
|
\\
|
|
struct B : public A \{ \\
|
|
\>virtual void f(); \\
|
|
\};\\
|
|
\\
|
|
struct C \{ \\
|
|
\>operator A const() \{ return A(); \}\\
|
|
\};\\
|
|
\\
|
|
A a; B b; C c;
|
|
\valipisteet
|
|
bind(\&A::f, a)(); \\
|
|
bind(\&A::f, b)(); // calls B::f\\
|
|
bind(\&A::fc, c)(); \\
|
|
\\
|
|
bind(\&A::f, \&a)();\\
|
|
bind(\&A::f, \&b)(); // calls B::f\\
|
|
bind(\&A::f, \&c)(); // error: no conversion from C* $\rightarrow$ A, C does not inherit A
|
|
\end{code}
|
|
|
|
The parameter passing and storing mechanism of the object argument is by reference. This means that the member of the actual object argument is called, not the member of a copy:
|
|
\begin{code}
|
|
class A \{ \\
|
|
\> int i; \\
|
|
\> mutable int j; \\
|
|
public:\\
|
|
\> void add(int x) \{ i += x; \}; \\
|
|
\> void add\_mutable(int x) const \{ j += x; \}; \\
|
|
\};\\
|
|
A a; a.i = 0; vector$<$int$>$ vec;
|
|
\valipisteet
|
|
for\_each(vec.begin(), vec.end(), bind(\&A::add, a, free1));
|
|
\end{code}
|
|
The \snip{for\_each} changes the state of \snip{a}.
|
|
Analogously, for const member functions the object argument is passed and stored as const reference.
|
|
|
|
There is a small glitch when the target function is a free variable. It is best explained with an example:
|
|
\begin{code}
|
|
void (A::* f)(int) = \&A::add;\\
|
|
bind(free1, a, 1)(f);
|
|
\end{code}
|
|
One would expect the last line to add 1 to \snip{a.i}, but this is not the case.
|
|
Since the first variable is a lambda expression, BLL does not know that the second argument is an object argument, and that it should be stored as reference.
|
|
The standard mechanism for storing bound function arguments is used and thus a temporary copy of \snip{a} is taken and stored in the lambda function.
|
|
In this case, the problem is not serious, since \snip{a} is actually stored as type \snip{const A}, which results in a compile time error when trying to invoke the nonconst member \snip{add}.
|
|
If the member function is const, the error is harder to spot since the compiler accepts the code:
|
|
\begin{code}
|
|
void (A::* g)(int) const = \&A::add\_mutable;\\
|
|
bind(free1, a, 1)(g);
|
|
\end{code}
|
|
The effect of this invocation is: \snip{A tmp = a; tmp.add\_mutable(1);}, which may not be what the programmer wanted.
|
|
|
|
To fix the above two examples, the storing mechanism of the bound object argument must be stated explicitly:
|
|
\begin{code}
|
|
bind(free1, ref(a), 1)(f);\\
|
|
bind(free1, cref(a), 1)(g);
|
|
\end{code}
|
|
|
|
An easier alternative is to use a pointer as the object argument:
|
|
\begin{code}
|
|
bind(free1, \&a, 1)(f);
|
|
bind(free1, \&a, 1)(g);
|
|
\end{code}
|
|
In this case, the members of \snip{a} are always called (not the members of a temporary copy).
|
|
|
|
\subsection{Delaying constants and variables\label{delay_expression}}
|
|
|
|
Calls to unary functions \snip{constant} and \snip{var} are \snipit{delay-expressions}. These functions are used for turning a constant or a variable into a lambda expression. This is sometimes necessary due to the lack of explicit syntax for lambda expressions. For example:
|
|
\begin{code}
|
|
for\_each(a.begin(), a.end(), cout \leftshift\ free1 \leftshift\ ' ');\\
|
|
for\_each(a.begin(), a.end(), cout \leftshift\ ' ' \leftshift\ free1);
|
|
\end{code}
|
|
The first line outputs the elements of \snip{a} separated by spaces, while the second line outputs a space followed by the elements of \snip{a} without any separators.
|
|
The reason for this is that neither of the operands of \snip{cout \leftshift\ ' '} is a lambda expression, hence \snip{cout \leftshift\ ' '} is evaluated immediately.
|
|
|
|
To delay the evaluation of \snip{cout \leftshift\ ' '}, one of the operands must be explicitly marked as a lambda expression. This is accomplished with the \snip{constant} function:
|
|
\begin{code}
|
|
for\_each(a.begin(), a.end(), cout \leftshift\ constant(' ') \leftshift\ free1);
|
|
\end{code}
|
|
\snip{constant(x)} creates a nullary lambda functor which returns the value of \snip{x} when invoked. It is only needed when the operator call has side effects.
|
|
|
|
Sometimes we need to delay the evaluation of a variable. Suppose we wanted to output index-element pairs of a container:
|
|
\begin{code}
|
|
int index = 0; \\
|
|
for\_each(a.begin(), a.end(), cout \leftshift\ ++index \leftshift\ ':' \leftshift\ free1 \leftshift\ endl);\\
|
|
for\_each(a.begin(), a.end(), cout \leftshift\ ++var(index) \leftshift\ ':' \leftshift\ free1 \leftshift\ endl);
|
|
\end{code}
|
|
The first \snip{for\_each} invocation does not do what we wanted to, \snip{index} is incremented only once. As shown in the second case, the evaluation of \snip{index} must be delayed with the \snip{var} function.
|
|
|
|
In sum, \snip{var(x)} creates a nullary lambda functor, which stores a reference to the variable \snip{x}. When the lambda functor is invoked, a reference to \snip{x} is returned.
|
|
The use of \snip{var} is typical with delayed control constructs.
|
|
|
|
\subsubsection{Naming delayed constants and variables}
|
|
Sometimes a delayed variable or a constant is repeated several times in a lambda expression. It is possible to predefine and name a delayed variable or constant outside a lambda expression. The templates \snip{var\_type} and \snip{constant\_type} serve for this purpose. Their syntax is as follows:
|
|
\begin{code}
|
|
var\_type$<$T$>$::type delayed\_i(var(i));\\
|
|
constant\_type$<$T$>$::type delayed\_c(constant(c));
|
|
\end{code}
|
|
The first line defines the variable \snip{delayed\_i} which is a delayed version of the variable \snip{i} of type \snip{T}.
|
|
The second line defines the constant \snip{delayed\_c} which is a delayed version of the constant \snip{c} of type \snip{T}.
|
|
For example:
|
|
\begin{code}
|
|
int i = 0; int j;\\
|
|
for\_each(a.begin(),a.end(), (var(j) = free1, free1 = var(i), var(i) = var(j)));
|
|
\end{code}
|
|
is equivalent to:
|
|
\begin{code}
|
|
int i = 0; int j;\\
|
|
var\_type$<$int$>$::type vi(var(i)), vj(var(j));\\
|
|
for\_each(a.begin(),a.end(), (vj = free1, free1 = vi, vi = vj));
|
|
\end{code}
|
|
Here is an example of naming a delayed constant:
|
|
\begin{code}
|
|
constant\_type$<$char$>$::type space(constant(' '));\\
|
|
for\_each(a.begin(),a.end(), cout \leftshift\ space \leftshift\ free1);
|
|
\end{code}
|
|
|
|
\subsubsection{About assignment and subscript operators}
|
|
|
|
As described in section~{\ref{member_operators}, assignment and subscripting operators must be defined as member functions. This means, that for expressions of the form
|
|
\snip{x = y} or \snip{x[y]} to be interpreted as lambda expressions, the left operand \snip{x} must be a lambda expression.
|
|
This does not hold for combined assignment operators \snip{+=}, \snip{--=} etc.\ which are interpreted as lambda expressions, even if only the right operand is a lambda expression.
|
|
Nevertheless, it is perfectly ok to delay the left operand explicitly. For example, \snip{i += free1} is equivalent to \snip{var(i) += free1}.
|
|
|
|
\subsection{Control expressions}
|
|
|
|
BLL defines several functions to create lambda functors for control expressions. They all take lambda functors as parameters and return \snip{void}. The following constructs are supported and defined in \verb|control_constructs.hpp|.
|
|
|
|
\begin{code}
|
|
if\_then(condition, then\_part)\\
|
|
if\_then\_else(condition, then\_part, else\_part)\\
|
|
while\_loop(condition, body)\\
|
|
while\_loop(condition) // no body case\\
|
|
do\_while\_loop(condition, body)\\
|
|
do\_while\_loop(condition) // no body case\\
|
|
for\_loop(init, condition, increment, body)\\
|
|
for\_loop(init, condition, increment) // no body case\\
|
|
\end{code}
|
|
|
|
In addition to these basic constructs, we BLL defines {\em switch} and {\em do once} constructs.
|
|
The switch is of the form:
|
|
\begin{code}
|
|
switch\_statement(\=test,\\
|
|
\>case\_statement<LABEL,
|
|
\end{code}
|
|
|
|
|
|
\subsection{Construction and destruction}
|
|
|
|
...
|
|
|
|
\subsection{Exceptions}
|
|
|
|
...
|
|
|
|
\section{\label{extending_return_type_system}Return type deduction system}
|
|
|
|
...
|
|
|
|
\section{About using the library}
|
|
|
|
\subsection{Error messages}
|
|
|
|
One problem with the BLL is related to error messages. The BLL is type safe, meaning that the compiler will catch such errors as wrong parameter types etc.
|
|
While compiling BLL bind expression, the diagnostic messages reporting errors tend to be lengthy and somewhat cryptic.
|
|
The reason for this is that the error may be encountered only after several (recursive) template instantiation steps.
|
|
|
|
\begin{thebibliography}{1}
|
|
|
|
\bibitem{bl}
|
|
J\"arvi J.: \textit{C++ Function Object Binders Made Easy'}, In Generative and Component Based Software Engineering, to appear in Lecture Notes in Computer Science, 1999.
|
|
|
|
\bibitem{stl}
|
|
Stepanov, A. A. and Lee, M.:
|
|
\textit{The Standard Template Library}, Hewlett-Packard Laboratories Technical Report HPL-94-34(R.1), April 1994,\newline http://www.hpl.hp.com/techreports.
|
|
|
|
\bibitem{standard}
|
|
\textit{International Standard, Programming Languages -- C++},\\
|
|
ISO/IEC:14882, 1998.
|
|
|
|
\bibitem{sgi_stl}
|
|
The SGI Standard Template Library, Silicon Graphics Computer Systems Inc., http://www.sgi.com/Technology/STL.
|
|
|
|
\bibitem{stroustrup:1997}
|
|
Stroustrup, B.: \textit{The C++ Programming Language - Third Edition},
|
|
Addison-Wesley, Reading, Massachusetts, 1997.
|
|
|
|
\bibitem{tuples}
|
|
J\"arvi J.: \textit{Tuples and multiple return values in C++}, submitted for publication, see TUCS Technical Report No 249, 1999, \newline
|
|
\noindent http://www.tucs.fi/publications.
|
|
|
|
\bibitem{tiers}
|
|
J\"arvi J.: \textit{ML-Style Tuple Assignment in Standard C++ --- Extending the Multiple Return Value Formalism}, TUCS Technical Report No 267, 1999, http://www.tucs.fi/publications.
|
|
|
|
\bibitem{powell}
|
|
Gary Powell and Peter Higley: \textit{Expression Templates as a Replacement for simple Functors}, to appear in the May issue of C++ Report, 2000.
|
|
\end{thebibliography}
|
|
|
|
|
|
\end{document}
|