forked from boostorg/utility
added section describing named parameters, default parameters, and
a conclusion [SVN r10328]
This commit is contained in:
@ -66,7 +66,6 @@ Florham Park, NJ 07932, USA \\
|
|||||||
|
|
||||||
\maketitle
|
\maketitle
|
||||||
|
|
||||||
|
|
||||||
\begin{abstract}
|
\begin{abstract}
|
||||||
The iterator abstraction is one of the most commonly used in
|
The iterator abstraction is one of the most commonly used in
|
||||||
programming and a considerable amount of time is spent building new
|
programming and a considerable amount of time is spent building new
|
||||||
@ -214,7 +213,7 @@ types: \code{value\_\-type}, \code{reference}, \code{pointer},
|
|||||||
example above, the last two would be delegated to the \code{Base}
|
example above, the last two would be delegated to the \code{Base}
|
||||||
iterator type. For many iterator adaptors, all five must be delegated.
|
iterator type. For many iterator adaptors, all five must be delegated.
|
||||||
|
|
||||||
\subsection{Iterator Complexities}
|
\subsection{Iterator Implementation Complexities}
|
||||||
|
|
||||||
In addition to the tedious aspects of iterator implementation, there
|
In addition to the tedious aspects of iterator implementation, there
|
||||||
are some complexities that trip up even the most experienced of
|
are some complexities that trip up even the most experienced of
|
||||||
@ -235,7 +234,6 @@ corresponding versions of \code{operator!=}, and (for
|
|||||||
\stlconcept{RandomAccessIterator}), operators \code{<}, \code{>},
|
\stlconcept{RandomAccessIterator}), operators \code{<}, \code{>},
|
||||||
\code{<=}, \code{>=}, and \code{-}.
|
\code{<=}, \code{>=}, and \code{-}.
|
||||||
|
|
||||||
|
|
||||||
{\footnotesize
|
{\footnotesize
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
bool operator==(const C::iterator& x, const C::iterator& y);
|
bool operator==(const C::iterator& x, const C::iterator& y);
|
||||||
@ -463,39 +461,53 @@ actual iterator type. The best way to package the construction of the
|
|||||||
transform iterator is to create a \emph{type generator}, which is a
|
transform iterator is to create a \emph{type generator}, which is a
|
||||||
class template whose sole purpose is to simplify the instantiation of
|
class template whose sole purpose is to simplify the instantiation of
|
||||||
some other complicated class template. It fulfills the same need as a
|
some other complicated class template. It fulfills the same need as a
|
||||||
templated typedef would if that were part of the {C++} language.
|
templated typedef would if that were part of the {C++} language. The
|
||||||
The following code shows the type generator for the transform
|
first template parameter to the generator is the type of the function
|
||||||
iterator.
|
object and the second is the base iterator type. The following code
|
||||||
|
shows the type generator for the transform iterator.
|
||||||
|
|
||||||
{\footnotesize
|
{\footnotesize
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
template <class AdaptableUnaryFunction, class Iterator>
|
template <class AdaptableUnaryFunction, class BaseIterator>
|
||||||
struct transform_iterator_generator
|
struct transform_iterator_generator
|
||||||
{
|
{
|
||||||
typedef typename AdaptableUnaryFunction::result_type value_type;
|
typedef typename AdaptableUnaryFunction::result_type val_t;
|
||||||
public:
|
public:
|
||||||
typedef iterator_adaptor<Iterator,
|
typedef iterator_adaptor<BaseIterator,
|
||||||
transform_iterator_policies<AdaptableUnaryFunction>,
|
transform_iterator_policies<AdaptableUnaryFunction>,
|
||||||
value_type, value_type, value_type*, std::input_iterator_tag> type;
|
iterator_category_is<std::input_iterator_tag>,
|
||||||
|
value_type_is<val_t>, reference_is<val_t> > type;
|
||||||
};
|
};
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
}
|
}
|
||||||
|
|
||||||
We use \code{iterator\_adaptor} to define the transform iterator type
|
We use \code{iterator\_adaptor} to define the transform iterator type
|
||||||
as a nested \code{typedef} inside the
|
as a nested \code{typedef} inside the
|
||||||
\code{transform\_iterator\_generator} class. The first template
|
\code{transform\_\-iterator\_\-generator} class. The first parameter
|
||||||
parameter to the generator is the type of the function object and the
|
to \code{iterator\_\-adaptor} is the base iterator type and the second
|
||||||
second is the base iterator type. The remaining types specify the
|
is the policies class. The remaining parameters specify the iterators
|
||||||
traits of the iterator: \code{value\_type}, \code{reference},
|
associated types and are given as \emph{named parameters}. We will
|
||||||
\code{pointer}, and \code{iterator\_\-category}. Because the function
|
discuss this technique in \S\ref{sec:named-template-parameters}.
|
||||||
may return by-value, we must limit the \code{iterator\_category} to
|
|
||||||
\code{std::input\_\-iterator\_\-tag}, and the iterator's
|
|
||||||
\code{reference} type cannot be a true reference (the standard allows
|
|
||||||
this for input iterators). The last template parameter of the
|
|
||||||
\code{iterator\_adaptor} is for the \code{difference\_\-type}, which
|
|
||||||
in this case will default to the \code{difference\_\-type} of the base
|
|
||||||
iterator.
|
|
||||||
|
|
||||||
|
The \code{iterator\_category} is set to
|
||||||
|
\code{std::input\_\-iterator\_\-tag} because the function object may
|
||||||
|
return by-value. For the same reason the \code{reference} type (which
|
||||||
|
will be the return type of \code{operator*}) is set to \code{val\_t}
|
||||||
|
(and not \code{val\_t\&}). There are two parameters that are left out:
|
||||||
|
the \code{pointer} type defaults to \code{value\_type*} and the
|
||||||
|
\code{difference\_\-type} defaults to the \code{difference\_\-type} of
|
||||||
|
the base iterator.
|
||||||
|
|
||||||
|
It is tempting to create a \code{transform\_\-iterator} by deriving
|
||||||
|
from \code{iterator\_\-adaptor} (as an alternative to using the
|
||||||
|
generator). This approach does not work, for example, because the
|
||||||
|
return type of \code{operator++} of an iterator is required to be the
|
||||||
|
same iterator type, but in this case the return type would be
|
||||||
|
\code{iterator\_\-adaptor} and not \code{transform\_\-iterator}.
|
||||||
|
|
||||||
|
%It
|
||||||
|
%would be possible to arrange for using inheritance by applying the
|
||||||
|
%Barton and Nackman trick, but we felt that design
|
||||||
|
|
||||||
\subsection{Iterator Object Generator}
|
\subsection{Iterator Object Generator}
|
||||||
\label{sec:iter-object-generator}
|
\label{sec:iter-object-generator}
|
||||||
@ -506,7 +518,6 @@ following constructor.
|
|||||||
|
|
||||||
{\footnotesize
|
{\footnotesize
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
explicit
|
|
||||||
iterator_adaptor(const Base& it, const Policies& p = Policies())
|
iterator_adaptor(const Base& it, const Policies& p = Policies())
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
}
|
}
|
||||||
@ -556,7 +567,6 @@ printing the result to standard output.
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <boost/iterator_adaptors.hpp>
|
#include <boost/iterator_adaptors.hpp>
|
||||||
|
|
||||||
int main(int, char*[])
|
int main(int, char*[])
|
||||||
{
|
{
|
||||||
int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
int x[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||||
@ -592,14 +602,14 @@ discussed in the introduction were solved.
|
|||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
namespace boost {
|
namespace boost {
|
||||||
template <class Base, class Policies,
|
template <class Base, class Policies,
|
||||||
class Value = default_argument,
|
class Param1 = default_argument,
|
||||||
class Reference = default_argument,
|
class Param2 = default_argument,
|
||||||
class Pointer = default_argument,
|
class Param3 = default_argument,
|
||||||
class Category = default_argument,
|
class Param4 = default_argument,
|
||||||
class Distance = default_argument>
|
class Param5 = default_argument>
|
||||||
struct iterator_adaptor {
|
struct iterator_adaptor {
|
||||||
// Deduce iterator associated types (value_type, etc.) from the
|
// Deduce iterator associated types (value_type, etc.) from the
|
||||||
// template parameters, and handle any named template parameters.
|
// named template parameters, and resolve any defaults.
|
||||||
public:
|
public:
|
||||||
// Core operators, delegate to policies class.
|
// Core operators, delegate to policies class.
|
||||||
// Redundant operators, implemented in terms of the core operators.
|
// Redundant operators, implemented in terms of the core operators.
|
||||||
@ -620,9 +630,106 @@ namespace boost {
|
|||||||
|
|
||||||
\subsection{Deducing the Associates Types}
|
\subsection{Deducing the Associates Types}
|
||||||
|
|
||||||
\subsubsection{Default Types}
|
Iterators have five associated types: \code{value\_\-type},
|
||||||
|
\code{reference}, \code{pointer}, \code{iterator\_\-category}, and
|
||||||
|
\code{difference\_\-type}. Each of these types must either be supplied
|
||||||
|
by the user, using the named parameter technique described below in
|
||||||
|
\S\ref{sec:named-template-parameters}, or a default must be computed
|
||||||
|
for the type.
|
||||||
|
|
||||||
|
\subsubsection{Defaults for the Associated Types}
|
||||||
|
|
||||||
|
The defaults for the \code{value\_type}, \code{iterator\_\-category},
|
||||||
|
and \code{difference\_\-type} are straightforward: they are the
|
||||||
|
respective types from the base iterator. So, for example, the default
|
||||||
|
for the \code{value\_type} is
|
||||||
|
\code{std::iterator\_\-traits\-<Base>::\-value\_type}.
|
||||||
|
|
||||||
|
Computing the defaults for \code{reference} and \code{pointer} is a
|
||||||
|
bit more complicated. If a \code{value\_type} is specified by the
|
||||||
|
user, then \code{value\_type\&} and \code{value\_type*} are used for
|
||||||
|
the \code{reference} and \code{pointer} type. Otherwise, if the
|
||||||
|
default was used for \code{value\_type} then the \code{reference} and
|
||||||
|
\code{pointer} types are taken from the base iterator using
|
||||||
|
\code{std::iterator\_traits}.
|
||||||
|
% Dave, is the above right? I'm not sure...
|
||||||
|
|
||||||
|
|
||||||
\subsubsection{Named Template Parameters}
|
\subsubsection{Named Template Parameters}
|
||||||
|
\label{sec:named-template-parameters}
|
||||||
|
|
||||||
|
The \code{iterator\_adaptor} class has five template parameters that
|
||||||
|
specify the associated types of the iterator. Each of the parameters
|
||||||
|
has defaults as described above, so the user may specify zero or more
|
||||||
|
parameters. One difficulty with {C++} templates is that if a default
|
||||||
|
is used for a parameter then all the following parameters must also be
|
||||||
|
default. When there are a large number of parameters this becomes
|
||||||
|
inconvenient.
|
||||||
|
|
||||||
|
A solution to this problem is the idea of named parameters. Instead
|
||||||
|
of matching arguments to parameters based on order, the assignment of
|
||||||
|
arguments to parameters is made explicitly by name (so the ordering no
|
||||||
|
longer matters). The way this works for the \code{iterator\_\-adaptor}
|
||||||
|
class is that there is a wrapper class for each parameter, for
|
||||||
|
example:
|
||||||
|
|
||||||
|
{\footnotesize
|
||||||
|
\begin{verbatim}
|
||||||
|
template <class Value> struct value_type_is {
|
||||||
|
typedef value_type_tag tag;
|
||||||
|
typedef Value type;
|
||||||
|
};
|
||||||
|
\end{verbatim}
|
||||||
|
}
|
||||||
|
|
||||||
|
Instead of passing the argument for the \code{Value} type directly to
|
||||||
|
\code{iterator\_\-adaptor} the user passes
|
||||||
|
\code{value\_type\_is<Value>}. The \code{iterator\_\-adaptor} now has
|
||||||
|
five arguments for the associated types, each of which could be used
|
||||||
|
to specify any of the actual parameters. The
|
||||||
|
\code{iterator\_\-adaptor} must deduce which argument is for which
|
||||||
|
parameter based on the \code{tag} type inside the wrapper.
|
||||||
|
|
||||||
|
First we take all of the parameters and place them in a
|
||||||
|
lisp-style list, using \code{std::pair} for \code{cons}. Each
|
||||||
|
parameter wrapper has a key/value pair (the \code{tag} and \code{type}
|
||||||
|
respectively), so we can treat this as an associative list.
|
||||||
|
|
||||||
|
{\footnotesize
|
||||||
|
\begin{verbatim}
|
||||||
|
typedef pair<Param1, pair<Param2, pair<Param3, pair<Param4,
|
||||||
|
pair<Param5, list_end_type> > > > > NamedParamList;
|
||||||
|
\end{verbatim}
|
||||||
|
}
|
||||||
|
|
||||||
|
\noindent For each parameter we perform a look-up in the associative
|
||||||
|
list using a template meta-program utility class named
|
||||||
|
\code{find\_param}.
|
||||||
|
|
||||||
|
{\footnotesize
|
||||||
|
\begin{verbatim}
|
||||||
|
template <class AssociativeList, class Key>
|
||||||
|
struct find_param {
|
||||||
|
typedef ... type;
|
||||||
|
};
|
||||||
|
\end{verbatim}
|
||||||
|
}
|
||||||
|
|
||||||
|
\noindent So, for example, to retrieve the argument for the
|
||||||
|
\code{value\_type} we write the following:
|
||||||
|
|
||||||
|
{\footnotesize
|
||||||
|
\begin{verbatim}
|
||||||
|
typedef typename find_param<NamedParamList, value_type_tag>::type Value;
|
||||||
|
\end{verbatim}
|
||||||
|
}
|
||||||
|
|
||||||
|
The result of this look-up will either be the argument specified by the
|
||||||
|
user, or if there is none, the \code{default\_argument} type. If it is
|
||||||
|
the default, then a further step is taken to resolve what the default
|
||||||
|
should be for the parameter. The defaults for some of the parameters
|
||||||
|
depend on other parameters, so the order in which defaults are
|
||||||
|
resolved is tailored to respect these dependencies.
|
||||||
|
|
||||||
|
|
||||||
\subsection{Core Operators}
|
\subsection{Core Operators}
|
||||||
@ -732,7 +839,7 @@ appropriate type for the result of an iterator's \code{operator->}:
|
|||||||
{
|
{
|
||||||
// Is it an input iterator, or something more?
|
// Is it an input iterator, or something more?
|
||||||
static bool const is_input_iter
|
static bool const is_input_iter
|
||||||
= is_convertible<Category*,std::input_iterator_tag*>::value
|
= is_convertible<Category*, std::input_iterator_tag*>::value
|
||||||
&& !is_convertible<Category*,std::forward_iterator_tag*>::value;
|
&& !is_convertible<Category*,std::forward_iterator_tag*>::value;
|
||||||
|
|
||||||
typedef typename type_if<is_input_iter,
|
typedef typename type_if<is_input_iter,
|
||||||
@ -815,7 +922,6 @@ in a generic algorithm, you can not use \code{operator[]} for
|
|||||||
assignment (on the left hand side) but instead must write \code{*(i +
|
assignment (on the left hand side) but instead must write \code{*(i +
|
||||||
n) = x}.
|
n) = x}.
|
||||||
|
|
||||||
|
|
||||||
Alternatively, it would be nice to return by-reference for some
|
Alternatively, it would be nice to return by-reference for some
|
||||||
iterators and by-value for others. However, the current
|
iterators and by-value for others. However, the current
|
||||||
\code{iterator\_\-traits} does not provide enough information make the
|
\code{iterator\_\-traits} does not provide enough information make the
|
||||||
@ -826,11 +932,26 @@ iterator would either return by-value for both \code{operator*} and
|
|||||||
in~\cite{siek01:_improved_iter_cat} would solves these problems, but
|
in~\cite{siek01:_improved_iter_cat} would solves these problems, but
|
||||||
of course that will take some time to gain acceptance.
|
of course that will take some time to gain acceptance.
|
||||||
|
|
||||||
|
|
||||||
\section{Conclusion}
|
\section{Conclusion}
|
||||||
|
|
||||||
talk about how this approach generalizes to other things, containers,
|
Constructing iterators and iterator adaptors is a common task for
|
||||||
etc.
|
modern C++ programming. Despite the conceptual simplicity of most
|
||||||
|
iterators, implementing {C++} Standard conforming iterators requires
|
||||||
|
is non-trivial amount of code: some of which is challenging to get
|
||||||
|
right, and a lot of which is tedious to write. The
|
||||||
|
\code{iterator\_adaptor} class that we present in this paper solves
|
||||||
|
this problem by providing a mechanism by which the user provides a
|
||||||
|
minimal specification (by way of the policies class) for the iterator,
|
||||||
|
and then the \code{iterator\_\-adaptor} takes care of most of the
|
||||||
|
implementation details.
|
||||||
|
|
||||||
|
Taking a step back, the design approach was to create a canonical
|
||||||
|
implementation of a concept (iterator) and then delegate the core
|
||||||
|
implementation issues to a policies class. This approach can be
|
||||||
|
applied in situations where there is large family of components that
|
||||||
|
share the same interface. For example, we plan on applying this design
|
||||||
|
approach to containers and algebraic types.
|
||||||
|
|
||||||
|
|
||||||
\bibliographystyle{abbrv}
|
\bibliographystyle{abbrv}
|
||||||
@ -841,6 +962,6 @@ etc.
|
|||||||
% LocalWords: adaptors istream ostream iter MTL InputIterator adaptor const
|
% LocalWords: adaptors istream ostream iter MTL InputIterator adaptor const
|
||||||
% LocalWords: RandomAccessIterator dereference interoperate Implementers tmpw
|
% LocalWords: RandomAccessIterator dereference interoperate Implementers tmpw
|
||||||
% LocalWords: dereferencing adaptor's lvalues iterator's instantiation typedef
|
% LocalWords: dereferencing adaptor's lvalues iterator's instantiation typedef
|
||||||
% LocalWords: AdaptableUnaryFunction templated dereferenced lvalue
|
% LocalWords: AdaptableUnaryFunction templated dereferenced lvalue val param
|
||||||
% LocalWords: parameterization implementers combinatorial InputIterators
|
% LocalWords: parameterization implementers combinatorial InputIterators
|
||||||
% LocalWords: Convertibility
|
% LocalWords: Convertibility
|
||||||
|
Reference in New Issue
Block a user