Boost C++ Libraries

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

Front Page / Technical Details / Portability / Incomplete Support for Lambda Expressions

Incomplete Support for Lambda Expressions

Seasoned MPL users will agree with us that if there is anything in the MPL that is seemingly magical both in power and its nature, then it's MPL lambda expressions. In reality, the mechanism that bring this to life is very straightforward and probably can be explained to anyone generally familiar with C++ templates in less than 10 minutes.

Unfortunately, this mechanism also happens to rely on support for partial template specialization and template template parameters. Among the so-called deficient compilers — basically, most of the compilers released before the year 2000 — the chances are poor that you'll find complete support for both of these features. Please see our compatibility table for the list of the products which fall into this category.

Although it's not possible to implement fully transparent lambda expressions without these two features, a slightly more limited implementation that requires some manual assistance from the metafunction author is possible. This section describes the manual work required and the limitations of the result.

The Problem

If your compiler falls into the "deficient" category, the following valid MPL metaprogram will fail to compile for you:

#include <boost/mpl/apply.hpp>

using namespace boost::mpl;

template< typename T > struct add_const
{
    typedef T const type;
};

typedef apply1< add_const<_1>,int >::type t; // t == int const

Worse yet, chances are it wil fail with a diagnostic backtrace leading you into the inside of the library and possibly creating an impression that there's something's wrong there. The fact is, both the program and the library are defect free (for the purpose of this particular demonstraction), and it's your compiler that is to blame.

The Solution

As previously mentioned, the solution requires some work from metafunction authors, but for the users of those metafunctions, the result is relatively transparent. Here's what we have to do to our earlier example:

#include <boost/mpl/apply.hpp>
#include <boost/mpl/aux_/lambda_support.hpp>

using namespace boost::mpl;

template< typename T > struct add_const
{
    typedef T const type;
    BOOST_MPL_AUX_LAMBDA_SUPPORT(1, add_const, (T))
};

typedef apply1< add_const<_1>,int >::type t; // t == int const

With these two modifications, now the compiler that has been barking at us now happily accepts it. "Hey, that's not that bad at all!" you might say. Just put a little macro inside and be happy again.

Limitations

Unfortunately, that's not quite the end of the story. There are still cases where the above approach will fail and we will have to resort to writing out-of-line metafunction class. Here are the details:

To make the lambda expression work without partial template specialization and template template parameters, the MPL has to implement some other way of pulling apart the template instantiations' expression tree, and the only way to do it is through an intrusive metafunction introspection mechanism. That's what hidden behind the BOOST_MPL_AUX_LAMBDA_SUPPORT macro we've seen above.

But then, after we've got the information we need (the metafunction's arity and its exact template arguments) stored inside the metafunction itself, the only way for the library to access it is to look inside the metafunction. The latter, in its turn, means instantiating the metafunction, prematurely, before the actuall call, with one or more placeholder arguments. This last part is a potential problem.

In other words, the mechanism works as long as your metafunction is "placeholder-safe" (can be safely instantiated on placeholder arguments), which comes down to the follwing two criteria:

  1. The metafunction doesn't access its arguments' nested members, or
  2. The only accessed members are types named ::tag or ::type (the placeholders do contain these).

If these two hold, you can safely put BOOST_MPL_AUX_LAMBDA_SUPPORT inside your metafunction and forget about the issue. If not, your are out of luck and probably have to write a metafunction class instead.

The good news are that most of the MPL's own metafunctions and Boost.Type Traits templates are "placeholder-safe" and have the workaround applied to them, so even on broken compilers things "just work" in about 90% of use cases.

Please refer to the MPL reference manual for the details on the BOOST_MPL_AUX_LAMBDA_SUPPORT macro.