|
|
|
Sponsored Link •
|
|
Advertisement
|
Now that you have an idea of the semantics of MPL's lambda
facility, let's formalize that understanding and look at things a
little more deeply.
The definition of "placeholder" may surprise you:
A placeholder is a metafunction class of the form mpl::arg<X>.
The convenient names _1, _2,... _5 are actually
typedefs for specializations of mpl::arg that simply
select the Nth argument for any N.
[6] The
implementation of placeholders looks something like this:
namespace boost { namespace mpl { namespace placeholders {
template <int N> struct arg; // forward declarations
struct void_;
template <>
struct arg<1>
{
template <
class A1, class A2 = void_, ... class Am = void_>
struct apply
{
typedef A1 type; // return the first argument
};
};
typedef arg<1> _1;
template <>
struct arg<2>
{
template <
class A1, class A2, class A3 = void_, ...class Am = void_
>
struct apply
{
typedef A2 type; // return the second argument
};
};
typedef arg<2> _2;
more specializations and typedefs...
}}}
Remember that invoking a metafunction class is the same as invoking
its nested apply metafunction. When a placeholder in a lambda
expression is evaluated, it is invoked on the expression's actual
arguments, returning just one of them. The results are then
substituted back into the lambda expression and the evaluation
process continues.
There's one special placeholder, known as the unnamed placeholder, that we haven't yet defined:
namespace boost { namespace mpl { namespace placeholders {
typedef arg<-1> _; // the unnamed placeholder
}}}
The details of its implementation aren't important; all you really
need to know about the unnamed placeholder is that it gets special
treatment. When a lambda expression is being transformed into a
metafunction class by mpl::lambda,
the nth appearance of the unnamed placeholder in a given
template specialization is replaced with _n.
So, for example, every row of Table 3.1 below contains two equivalent lambda expressions.
mpl::plus<_,_> |
mpl::plus<_1,_2> |
boost::is_same<
_
, boost::add_pointer<_>
>
|
boost::is_same<
_1
, boost::add_pointer<_1>
>
|
mpl::multiplies< mpl::plus<_,_> , mpl::minus<_,_> > |
mpl::multiplies< mpl::plus<_1,_2> , mpl::minus<_1,_2> > |
Especially when used in simple lambda expressions, the unnamed placeholder often eliminates just enough syntactic "noise" to significantly improve readability.
Now that you know just what placeholder means, we can define placeholder expression:
A placeholder expression is either:
- a placeholder
or
- a template specialization with at least one argument that is a placeholder expression.
In other words, a placeholder expression always involves a placeholder.
There is just one detail of placeholder expressions that we haven't
discussed yet. MPL uses a special rule to make it easier to
integrate ordinary templates into metaprograms: After all of the
placeholders have been replaced with actual
arguments, if the resulting template specialization X doesn't
have a nested ::type, the result is just
X itself.
For example, mpl::apply<std::vector<_>, T> is always just
std::vector<T>. If it weren't for this behavior, we would
have to build trivial metafunctions to create ordinary template
specializations in lambda expressions:
// trivial std::vector generator
template<class U>
struct make_vector { typedef std::vector<U> type; };
typedef mpl::apply<make_vector<_>, T>::type vector_of_t;
Instead, we can simply write:
typedef mpl::apply<std::vector<_>, T>::type vector_of_t;
Recall the definition of always_int from the previous chapter:
struct always_int
{
typedef int type;
};
Nullary metafunctions might not seem very important at first, since
something like add_pointer<int> could be replaced by int* in
any lambda expression where it appears. Not all nullary
metafunctions are that simple, though:
struct add_pointer_f
{
template <class T>
struct apply : boost::add_pointer<T> {};
};
typedef mpl::vector<int, char*, double&> seq;
typedef mpl::transform<seq, add_pointer_f> calc_ptr_seq;
Note that calc_ptr_seq is a nullary metafunction, since it has
transform's nested ::type. A C++ template is not
instantiated until we actually "look inside it," though. Just
naming calc_ptr_seq does not cause it to be evaluated, since we
haven't accessed its ::type yet.
Metafunctions can be invoked lazily, rather than immediately upon
supplying all of their arguments. We can use lazy evaluation to
improve compilation time when a metafunction result is only going
to be used conditionally. We can sometimes also avoid contorting
program structure by naming an invalid computation without
actually performing it. That's what we've done with
calc_ptr_seq above, since you can't legally form double&*.
Laziness and all of its virtues will be a recurring theme
throughout this book.
By now you should have a fairly complete view of the fundamental concepts and language of both template metaprogramming in general and of the Boost Metaprogramming Library. This section reviews the highlights.
type of a metafunction by accessing the one
provided by its base class.apply.#include <boost/mpl/component-name.hpp>
If the component's name ends in an underscore, however, the
corresponding MPL header name does not include the trailing
underscore. For example, mpl::bool_ can be found in
<boost/mpl/bool.hpp>. Where the library deviates from this
convention, we'll be sure to point it out to you.
A kind of lambda expression that, through the use of placeholders, enables in-place partial metafunction application and metafunction composition. As you will see throughout this book, these features give us the truly amazing ability to build up almost any kind of complex type computation from more primitive metafunctions, right at its point of use:
// find the position of a type x in some_sequence such that:
// x is convertible to 'int'
// && x is not 'char'
// && x is not a floating type
typedef mpl::find_if<
some_sequence
, mpl::and_<
boost::is_convertible<_1,int>
, mpl::not_<boost::is_same<_1,char> >
, mpl::not_<boost::is_float<_1> >
>
>::type iter;
Placeholder expressions make good on the promise of algorithm reuse without forcing us to write new metafunction classes. The corresponding capability is often sorely missed in the runtime world of the STL, since it is often much easier to write a loop by hand than it is to use standard algorithms, despite their correctness and efficiency advantages.
lambda metafunction.lambda and the lambda evaluation process,
please see the MPL reference manual.apply metafunction.mpl::apply along with the arguments you want to apply it
to in lieu of using lambda and invoking the result "manually."::types, so we can supply all
of their arguments without performing any computation and
delay evaluation to the last possible moment.plus_f, but since it's a little subtle,
we introduced the straightforward but verbose formulation
first.
__EDG_VERSION__, which is defined by all EDG-based compilers.
mpl::apply.
Discuss this article in the Articles Forum topic,
A Deeper Look at Metafunctions.
Resources
David Abrahams and Aleksey Gurtovoy are the authors of Template Metaprogramming, which can
be pre-ordered from on Amazon.com at:
http://www.amazon.com/exec/obidos/ASIN/0321227255/
David Abrahams is a leading authority on C++ software development. His company, Boost Consulting (http://www.boost-consulting.com), provides support and development services for the open-source Boost C++ libraries, and delivers professional training in the practice of software construction. As a founding member of Boost (http://www.boost.org), David has worked with some of the best developers from all over the world in designing and building widely-used, reliable, maintainable software components. David has been an ANSI/ISO C++ committee member since 1996, where he is best known for developing the theory, and implementation of exception safety in the C++ standard.
Aleksey Gurtovoy is a technical lead at MetaCommunications, Inc, and a contributing member of the C++ Boost community. He holds a MS degree in Computer Science from Krasnoyarsk Technical State University, Russia. He can be reached at agurtovoy@meta-comm.com.
|
Sponsored Links
|