Overview
Mp11 is a C++11 metaprogramming library for compile-time manipulation of data structures that contain types. It’s based on template aliases and variadic templates and implements the approach outlined in the article "Simple C++ metaprogramming" and its sequel. Reading these articles before proceeding with this documentation is highly recommended.
The general principles upon which Mp11 is built are that algorithms and metafunctions are
template aliases of the form F<T…>
and data structures are lists of the form L<T…>
,
with the library placing no requirements on L
. mp_list<T…>
is the built-in list type,
but std::tuple<T…>
, std::pair<T1, T2>
and std::variant<T…>
are also perfectly
legitimate list types, although of course std::pair<T1, T2>
, due to having exactly two elements,
is not resizeable and will consequently not work with algorithms that need to add or remove
elements.
Another distinguishing feature of this approach is that lists (L<T…>
) have the same form as
metafunctions (F<T…>
) and can therefore be used as such. For example, applying std::add_pointer_t
to the list std::tuple<int, float>
by way of mp_transform<std::add_pointer_t, std::tuple<int, float>>
gives us std::tuple<int*, float*>
, but we can also apply mp_list
to the same tuple:
using R = mp_transform<mp_list, std::tuple<int, float>>;
and get std::tuple<mp_list<int>, mp_list<float>>
.
Definitions
A list is a — usually but not necessarily variadic — template class whose parameters are all types,
for example mp_list<char[], void>
, mp_list<>
, std::tuple<int, float, char>
, std::pair<int, float>
, std::shared_ptr<X>
.
A metafunction is a class template or a template alias whose parameters are all types, for example std::add_pointer_t
,
std::is_const
, mp_second
, mp_push_front
, mp_list
, std::tuple
, std::pair
, std::shared_ptr
, or
template<class...> using F1 = void;
template<class T> using F2 = T*;
template<class... T> using F3 = std::integral_constant<std::size_t, sizeof...(T)>;
A quoted metafunction is a class with a public metafunction member called fn
, for example
struct Q1 { template<class...> using fn = void; };
struct Q2 { template<class T> using fn = T*; };
struct Q3 { template<class... T> using fn =
std::integral_constant<std::size_t, sizeof...(T)>; };
An integral constant type is a class with a public member value
that is an integral constant in the C++ sense. For example,
std::integral_constant<int, 7>
, or
struct N { static int constexpr value = 2; };
A set is a list whose elements are unique.
A map is a list of lists, the inner lists having at least one element (the key.) The keys of the map must be unique. For example,
using M1 = std::tuple<std::pair<int, int*>, std::pair<float, float*>,
std::pair<void, void*>>;
using M2 = mp_list<mp_list<int, int*>, mp_list<float>,
mp_list<char, char[1], char[2]>>;
Examples
Generating Test Cases
Let’s suppose that we have written a metafunction result<T, U>
:
template<class T> using promote = typename std::common_type<T, int>::type;
template<class T, class U> using result =
typename std::common_type<promote<T>, promote<U>>::type;
that ought to represent the result of an arithmetic operation on the integer types T
and U
,
for example t + u
. We want to test whether result<T, U>
gives correct results for various combinations
of T
and U
, so we write the function
template<class T1, class T2> void test_result()
{
using T3 = decltype( T1() + T2() );
using T4 = result<T1, T2>;
std::cout << ( std::is_same<T3, T4>::value? "[PASS]": "[FAIL]" ) << std::endl;
}
and then need to call it a substantial number of times:
int main() { test_result<char, char>(); test_result<char, short>(); test_result<char, int>(); test_result<char, unsigned>(); // ... }
Writing all those type combinations by hand is unwieldy, error prone, and worst of all, boring. This is how we can leverage Mp11 to automate the task:
#include <boost/mp11.hpp>
#include <boost/core/demangle.hpp>
#include <type_traits>
#include <iostream>
#include <typeinfo>
using namespace boost::mp11;
template<class T> std::string name()
{
return boost::core::demangle( typeid(T).name() );
}
template<class T> using promote = typename std::common_type<T, int>::type;
template<class T, class U> using result =
typename std::common_type<promote<T>, promote<U>>::type;
template<class T1, class T2> void test_result( mp_list<T1, T2> const& )
{
using T3 = decltype( T1() + T2() );
using T4 = result<T1, T2>;
std::cout << ( std::is_same<T3, T4>::value? "[PASS] ": "[FAIL] " )
<< name<T1>() << " + " << name<T2>() << " -> " << name<T3>()
<< ", result: " << name<T4>() << std::endl;
}
int main()
{
using L = std::tuple<char, short, int, unsigned, long, unsigned long>;
tuple_for_each( mp_product<mp_list, L, L>(), [](auto&& x){ test_result(x); } );
}
How does it work?
mp_product<F, L1, L2>
calls F<T1, T2>
where T1
varies over the elements of L1
and T2
varies over
the elements of L2
, as if by executing two nested loops. It then returns a list of these results, of the same
type as L1
.
In our case, both lists are the same std::tuple
, and F
is mp_list
, so mp_product<mp_list, L, L>
will get us
std::tuple<mp_list<char, char>, mp_list<char, short>, mp_list<char, int>, …, mp_list<unsigned long, long>, mp_list<unsigned long, unsigned long>>
.
We then default-construct this tuple and pass it to tuple_for_each
. tuple_for_each(tp, f)
calls f
for every
tuple element; we use a (C++14) lambda that calls test_result
.
In pure C++11, we can’t use a lambda with an auto&&
parameter, so we’ll have to make test_result
a function object with
a templated operator()
and pass that to tuple_for_each
directly:
struct test_result
{
template<class T1, class T2> void operator()( mp_list<T1, T2> const& ) const
{
using T3 = decltype( T1() + T2() );
using T4 = result<T1, T2>;
std::cout << ( std::is_same<T3, T4>::value? "[PASS] ": "[FAIL] " )
<< name<T1>() << " + " << name<T2>() << " -> " << name<T3>()
<< ", result: " << name<T4>() << std::endl;
}
};
int main()
{
using L = std::tuple<char, short, int, unsigned, long, unsigned long>;
tuple_for_each( mp_product<mp_list, L, L>(), test_result() );
}
Writing common_type Specializations
The standard trait std::common_type
, used to obtain a type to which all of its arguments can convert without
unnecessary loss of precision, can be user-specialized when its default implementation (based on the ternary ?:
operator) is unsuitable.
Let’s write a common_type
specialization for two std::tuple
arguments. For that, we need a metafunction that
applies std::common_type
to each pair of elements and gathers the results into a tuple:
template<class... T> using common_type_t =
typename std::common_type<T...>::type; // standard in C++14
template<class Tp1, class Tp2> using common_tuple =
mp_transform<common_type_t, Tp1, Tp2>;
then specialize common_type
to use it:
namespace std
{
template<class... T1, class... T2>
struct common_type<std::tuple<T1...>, std::tuple<T2...>>:
mp_defer<common_tuple, std::tuple<T1...>, std::tuple<T2...>>
{
};
} // std
(There is no need to specialize std::common_type
for more than two arguments - it takes care of synthesizing the appropriate semantics from
the binary case.)
The subtlety here is the use of mp_defer
. We could have defined a nested type
to common_tuple<std::tuple<T1…>, std::tuple<T2…>>
,
and it would still have worked in all valid cases. By letting mp_defer
define type
, though, we make our specialization SFINAE-friendly.
That is, when our common_tuple
causes a substitution failure instead of a hard error, mp_defer
will not define a nested type
,
and common_type_t
, which is defined as typename common_type<…>::type
, will also cause a substitution failure.
As another example, consider the hypothetical type expected<T, E…>
that represents either a successful return with a value of T
,
or an unsuccessful return with an error code of some type in the list E…
. The common type of expected<T1, E1, E2, E3>
and
expected<T2, E1, E4, E5>
is expected<common_type_t<T1, T2>, E1, E2, E3, E4, E5>
. That is, the possible return values are
combined into their common type, and we take the union of the set of error types.
Therefore,
template<class T1, class E1, class T2, class E2> using common_expected =
mp_rename<mp_push_front<mp_unique<mp_append<E1, E2>>, common_type_t<T1, T2>>,
expected>;
namespace std
{
template<class T1, class... E1, class T2, class... E2>
struct common_type<expected<T1, E1...>, expected<T2, E2...>>:
mp_defer<common_expected, T1, mp_list<E1...>, T2, mp_list<E2...>>
{
};
} // std
Here we’ve taken a different tack; instead of passing the expected
types to common_expected
, we’re passing the T
types and lists of
the E
types. This makes our job easier. mp_unique<mp_append<E1, E2>>
gives us the concatenation of E1
and E2
with the duplicates
removed; we then add common_type_t<T1, T2>
to the front via mp_push_front
; and finally, we mp_rename
the resultant mp_list
to expected
.
Fixing tuple_cat
The article Simple C++11 metaprogramming builds an
implementation of the standard function tuple_cat
, with the end result given below:
template<class L> using F = mp_iota<mp_size<L>>;
template<class R, class...Is, class... Ks, class Tp>
R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
{
return R{ std::get<Ks::value>(std::get<Is::value>(tp))... };
}
template<class... Tp,
class R = mp_append<std::tuple<>, typename std::remove_reference<Tp>::type...>>
R tuple_cat( Tp &&... tp )
{
std::size_t const N = sizeof...(Tp);
// inner
using list1 = mp_list<
mp_rename<typename std::remove_reference<Tp>::type, mp_list>...>;
using list2 = mp_iota_c<N>;
using list3 = mp_transform<mp_fill, list1, list2>;
using inner = mp_apply<mp_append, list3>;
// outer
using list4 = mp_transform<F, list1>;
using outer = mp_apply<mp_append, list4>;
//
return tuple_cat_<R>( inner(), outer(),
std::forward_as_tuple( std::forward<Tp>(tp)... ) );
}
This function, however, is not entirely correct, in that it doesn’t handle some cases properly. For example,
trying to concatenate tuples containing move-only elements such as unique_ptr
fails:
std::tuple<std::unique_ptr<int>> t1;
std::tuple<std::unique_ptr<float>> t2;
auto result = ::tuple_cat( std::move( t1 ), std::move( t2 ) );
Trying to concatenate const
tuples fails:
std::tuple<int> const t1;
std::tuple<float> const t2;
auto result = ::tuple_cat( t1, t2 );
And finally, the standard tuple_cat
is specified to work on arbitrary tuple-like types (that is, all types
that support tuple_size
, tuple_element
, and get
), while our implementation only works with tuple
and
pair
. std::array
, for example, fails:
std::array<int, 2> t1{ 1, 2 };
std::array<float, 3> t2{ 3.0f, 4.0f, 5.0f };
auto result = ::tuple_cat( t1, t2 );
Let’s fix these one by one. Support for move-only types is easy, if one knows where to look. The problem is
that Tp
that we’re passing to the helper tuple_cat_
is (correctly) tuple<unique_ptr<int>&&, unique_ptr<float>&&>
,
but std::get<0>(tp)
still returns unique_ptr<int>&
, because tp
is an lvalue. This behavior is a bit
surprising, but is intended to prevent inadvertent double moves.
Long story short, we need std::move(tp)
in tuple_cat_
to make tp
an rvalue:
template<class R, class...Is, class... Ks, class Tp> R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp ) { return R{ std::get<Ks::value>(std::get<Is::value>(std::move(tp)))... }; }
Next, const
-qualified tuples. The issue here is that we’re stripping references from the input tuples, but not
const
. As a result, we’re trying to manipulate types such as tuple<int> const
with Mp11 algorithms, and these
types do not fit the list concept. We just need to strip qualifiers as well, by defining the useful remove_cv_ref
primitive that is inexplicably missing from the standard library:
template<class T> using remove_cv_ref = typename std::remove_cv< typename std::remove_reference<T>::type>::type;
and then by using remove_cv_ref<Tp>
in place of typename std::remove_reference<Tp>::type
:
template<class... Tp,
class R = mp_append<std::tuple<>, remove_cv_ref<Tp>...>>
R tuple_cat( Tp &&... tp )
{
std::size_t const N = sizeof...(Tp);
// inner
using list1 = mp_list<mp_rename<remove_cv_ref<Tp>, mp_list>...>;
// ...
Finally, tuple-like types. We’ve so far exploited the fact that std::pair
and std::tuple
are valid Mp11 lists,
but in general, arbitrary tuple-like types aren’t, so we need to convert them into such. For that, we’ll need to
define a metafunction from_tuple_like
that will take an arbitrary tuple-like type and will return, in our case,
the corresponding mp_list
.
Technically, a more principled approach would’ve been to return std::tuple
, but here mp_list
will prove more
convenient.
What we need is, given a tuple-like type Tp
, to obtain mp_list<std::tuple_element<0, Tp>::type, std::tuple_element<1, Tp>::type,
…, std::tuple_element<N-1, Tp>::type>
, where N
is tuple_size<Tp>::value
. Here’s one way to do it:
template<class T, class I> using tuple_element =
typename std::tuple_element<I::value, T>::type;
template<class T> using from_tuple_like =
mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>;
(mp_iota<N>
is an algorithm that returns an mp_list
with elements mp_size_t<0>
, mp_size_t<1>
, …, mp_size_t<N-1>
.)
Remember that mp_product<F, L1, L2>
performs the equivalent of two nested loops over the elements of L1
and L2
,
applying F
to the two variables and gathering the result. In our case L1
consists of the single element T
, so
only the second loop (over mp_iota<N>
, where N
is tuple_size<T>
), remains, and we get a list of the same type
as L1
(an mp_list
) with contents tuple_element<T, mp_size_t<0>>
, tuple_element<T, mp_size_t<1>>
, …,
tuple_element<T, mp_size_t<N-1>>
.
For completeness’s sake, here’s another, more traditional way to achieve the same result:
template<class T> using from_tuple_like = mp_transform_q<mp_bind_front<tuple_element, T>, mp_iota<std::tuple_size<T>>>;
With all these fixes applied, our fully operational tuple_cat
now looks like this:
template<class L> using F = mp_iota<mp_size<L>>;
template<class R, class...Is, class... Ks, class Tp>
R tuple_cat_( mp_list<Is...>, mp_list<Ks...>, Tp tp )
{
return R{ std::get<Ks::value>(std::get<Is::value>(std::move(tp)))... };
}
template<class T> using remove_cv_ref = typename std::remove_cv<
typename std::remove_reference<T>::type>::type;
template<class T, class I> using tuple_element =
typename std::tuple_element<I::value, T>::type;
template<class T> using from_tuple_like =
mp_product<tuple_element, mp_list<T>, mp_iota<std::tuple_size<T>>>;
template<class... Tp,
class R = mp_append<std::tuple<>, from_tuple_like<remove_cv_ref<Tp>>...>>
R tuple_cat( Tp &&... tp )
{
std::size_t const N = sizeof...(Tp);
// inner
using list1 = mp_list<from_tuple_like<remove_cv_ref<Tp>>...>;
using list2 = mp_iota_c<N>;
using list3 = mp_transform<mp_fill, list1, list2>;
using inner = mp_apply<mp_append, list3>;
// outer
using list4 = mp_transform<F, list1>;
using outer = mp_apply<mp_append, list4>;
//
return tuple_cat_<R>( inner(), outer(),
std::forward_as_tuple( std::forward<Tp>(tp)... ) );
}
Computing Return Types
C++17 has a standard variant type, called std::variant
. It also defines a function template
std::visit
that can be used to apply a function to the contained value of one or more variants.
So for instance, if the variant v1
contains 1
, and the variant v2
contains 2.0f
,
std::visit(f, v1, v2)
will call f(1, 2.0f)
.
However, std::visit
has one limitation: it cannot return a result unless all
possible applications of the function have the same return type. If, for instance, v1
and v2
are both of type std::variant<short, int, float>
,
std::visit( []( auto const& x, auto const& y ){ return x + y; }, v1, v2 );
will fail to compile because the result of x + y
can be either int
or float
depending on
what v1
and v2
hold.
A type that can hold either int
or float
already exists, called, surprisingly enough, std::variant<int, float>
.
Let’s write our own function template rvisit
that is the same as visit
but returns a variant
:
template<class F, class... V> auto rvisit( F&& f, V&&... v )
{
using R = /*...*/;
return std::visit( [&]( auto&&... x )
{ return R( std::forward<F>(f)( std::forward<decltype(x)>(x)... ) ); },
std::forward<V>( v )... );
}
What this does is basically calls std::visit
to do the work, but instead of passing it f
, we pass a lambda that does the same as f
except
it converts the result to a common type R
. R
is supposed to be std::variant<…>
where the ellipsis denotes the return types of
calling f
with all possible combinations of variant values.
We’ll first define a helper quoted metafunction Qret<F>
that returns the result of the application of F
to arguments of type T…
:
template<class F> struct Qret { template<class... T> using fn = decltype( std::declval<F>()( std::declval<T>()... ) ); };
(Unfortunately, we can’t just define this metafunction inside rvisit
; the language prohibits defining template aliases inside functions.)
With Qret
in hand, a variant
of the possible return types is just a matter of applying it over the possible combinations of the variant values:
using R = mp_product_q<Qret<F>, std::remove_reference_t<V>...>;
Why does this work? mp_product<F, L1<T1…>, L2<T2…>, …, Ln<Tn…>>
returns L1<F<U1, U2, …, Un>, …>
, where Ui
traverse all
possible combinations of list values. Since in our case all Li
are std::variant
, the result will also be std::variant
. (mp_product_q
is
the same as mp_product
, but for quoted metafunctions such as our Qret<F>
.)
One more step remains. Suppose that, as above, we’re passing two variants of type std::variant<short, int, float>
and F
is
[]( auto const& x, auto const& y ){ return x + y; }
. This will generate R
of length 9, one per each combination, but many of those
elements will be the same, either int
or float
, and we need to filter out the duplicates. So, we pass the result to mp_unique
:
using R = mp_unique<mp_product_q<Qret<F>, std::remove_reference_t<V>...>>;
and we’re done:
#include <boost/mp11.hpp>
#include <boost/core/demangle.hpp>
#include <variant>
#include <type_traits>
#include <typeinfo>
#include <iostream>
using namespace boost::mp11;
template<class F> struct Qret
{
template<class... T> using fn =
decltype( std::declval<F>()( std::declval<T>()... ) );
};
template<class F, class... V> auto rvisit( F&& f, V&&... v )
{
using R = mp_unique<mp_product_q<Qret<F>, std::remove_reference_t<V>...>>;
return std::visit( [&]( auto&&... x )
{ return R( std::forward<F>(f)( std::forward<decltype(x)>(x)... ) ); },
std::forward<V>( v )... );
}
template<class T> std::string name()
{
return boost::core::demangle( typeid(T).name() );
}
template<class V> void print_variant( char const * n, V const& v )
{
std::cout << "(" << name<decltype(v)>() << ")" << n << ": ";
std::visit( []( auto const& x )
{ std::cout << "(" << name<decltype(x)>() << ")" << x << std::endl; }, v );
}
int main()
{
std::variant<char, int, float> v1( 1 );
print_variant( "v1", v1 );
std::variant<short, int, double> v2( 3.14 );
print_variant( "v2", v2 );
auto v3 = rvisit( []( auto const& x, auto const& y ){ return x + y; }, v1, v2 );
print_variant( "v3", v3 );
}
Revision History
Changes in 1.70.0
-
Renamed
mp_invoke
tomp_invoke_q
-
Added
mp_similar
-
Added
mp_set_union
,mp_set_intersection
,mp_set_difference
-
Added
mp_not_fn
-
Added
mp_transform_first
,mp_transform_second
,mp_transform_third
-
Added
mp_filter
-
Added
mp_eval_if_not
,mp_eval_or
,mp_valid_q
-
Added
mp_back
,mp_pop_back
-
Added
BOOST_MP11_VERSION
Changes in 1.69.0
-
Removed dependency on Boost.Config; Mp11 is now standalone
-
Improved code generation for
mp_with_index
-
Added
mp_starts_with
(contributed by Glen Fernandes) -
Added CMake support
Reference
The contents of the library are in namespace boost::mp11
.
Integral Constants, <boost/mp11/integral.hpp>
For an Mp11 integral constant type T
, T::value
is an integral constant in the C++ sense.
mp_bool<B>
template<bool B> using mp_bool = std::integral_constant<bool, B>;
Same as std::bool_constant
in C++17.
mp_true
using mp_true = mp_bool<true>;
Same as std::true_type
.
mp_false
using mp_false = mp_bool<false>;
Same as std::false_type
.
mp_to_bool<T>
template<class T> using mp_to_bool = mp_bool<static_cast<bool>(T::value)>;
mp_not<T>
template<class T> using mp_not = mp_bool< !T::value >;
mp_int<I>
template<int I> using mp_int = std::integral_constant<int, I>;
mp_size_t<N>
template<std::size_t N> using mp_size_t = std::integral_constant<std::size_t, N>;
List Operations, <boost/mp11/list.hpp>
mp_list<T…>
template<class... T> struct mp_list {};
mp_list
is the standard list type of Mp11, although the library is not restricted to it and can operate on arbitrary class templates
such as std::tuple
or std::variant
. Even std::pair
can be used if the transformation does not alter the number of the elements in
the list.
mp_list_c<T, I…>
template<class T, T... I> using mp_list_c = mp_list<std::integral_constant<T, I>...>;
mp_list_c
produces an mp_list
of the std::integral_constant
types corresponding to its integer template arguments.
using L1 = mp_list_c<int, 2, 3>; // mp_list<mp_int<2>, mp_int<3>>
mp_is_list<L>
template<class L> using mp_is_list = /*...*/;
mp_is_list<L>
is mp_true
if L
is a list (an instantiation of a class template whose template parameters are types), mp_false
otherwise.
mp_size<L>
template<class L> using mp_size = /*...*/;
mp_size<L>
returns the number of elements in the list L
, as a mp_size_t
. In other words, mp_size<L<T…>>
is an alias for
mp_size_t<sizeof…(T)>
.
using L1 = mp_list<>;
using R1 = mp_size<L1>; // mp_size_t<0>
using L2 = std::pair<int, int>;
using R2 = mp_size<L2>; // mp_size_t<2>
using L3 = std::tuple<float>;
using R3 = mp_size<L3>; // mp_size_t<1>
mp_empty<L>
template<class L> using mp_empty = mp_bool<mp_size<L>::value == 0>;
mp_empty<L>
is an alias for mp_true
if the list L
is empty, for mp_false
otherwise.
using L1 = std::tuple<float>;
using R1 = mp_empty<L1>; // mp_false
using L2 = std::tuple<>;
using R2 = mp_empty<L2>; // mp_true
mp_assign<L1, L2>
template<class L1, class L2> using mp_assign = /*...*/;
mp_assign<L1<T1…>, L2<T2…>>
is an alias for L1<T2…>
. That is, it replaces the elements of L1
with those of L2
.
using L1 = std::tuple<long>;
using L2 = mp_list<int, float>;
using R1 = mp_assign<L1, L2>; // std::tuple<int, float>
using L1 = std::pair<long, char>;
using L2 = mp_list<int, float>;
using R1 = mp_assign<L1, L2>; // std::pair<int, float>
mp_clear<L>
template<class L> using mp_clear = mp_assign<L, mp_list<>>;
mp_clear<L<T…>>
is an alias for L<>
, that is, it removes the elements of L
.
using L1 = std::tuple<int, float>;
using R1 = mp_clear<L1>; // std::tuple<>
mp_front<L>
template<class L> using mp_front = /*...*/;
mp_front<L>
is the first element of the list L
. That is, mp_front<L<T1, T…>>
is an alias for T1
.
using L1 = std::pair<int, float>;
using R1 = mp_front<L1>; // int
using L2 = std::tuple<float, double, long double>;
using R2 = mp_front<L2>; // float
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_front<L3>; // char[1]
mp_pop_front<L>
template<class L> using mp_pop_front = /*...*/;
mp_pop_front<L>
removes the first element of the list L
. That is, mp_pop_front<L<T1, T…>>
is an alias for L<T…>
.
using L1 = std::tuple<float, double, long double>;
using R1 = mp_pop_front<L1>; // std::tuple<double, long double>
using L2 = mp_list<void>;
using R2 = mp_pop_front<L2>; // mp_list<>
mp_first<L>
template<class L> using mp_first = mp_front<L>;
mp_first
is another name for mp_front
.
mp_rest<L>
template<class L> using mp_rest = mp_pop_front<L>;
mp_rest
is another name for mp_pop_front
.
mp_second<L>
template<class L> using mp_second = /*...*/;
mp_second<L>
is the second element of the list L
. That is, mp_second<L<T1, T2, T…>>
is an alias for T2
.
using L1 = std::pair<int, float>;
using R1 = mp_second<L1>; // float
using L2 = std::tuple<float, double, long double>;
using R2 = mp_second<L2>; // double
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_second<L3>; // char[2]
mp_third<L>
template<class L> using mp_third = /*...*/;
mp_third<L>
is the third element of the list L
. That is, mp_third<L<T1, T2, T3, T…>>
is an alias for T3
.
using L1 = std::tuple<float, double, long double>;
using R1 = mp_third<L1>; // long double
using L2 = mp_list<char[1], char[2], char[3], char[4]>;
using R2 = mp_third<L2>; // char[3]
mp_push_front<L, T…>
template<class L, class... T> using mp_push_front = /*...*/;
mp_push_front<L, T…>
inserts the elements T…
at the front of the list L
. That is, mp_push_front<L<U…>, T…>
is an alias for L<T…, U…>
.
using L1 = std::tuple<double, long double>;
using R1 = mp_push_front<L1, float>; // std::tuple<float, double, long double>
using L2 = mp_list<void>;
using R2 = mp_push_front<L2, char[1], char[2]>; // mp_list<char[1], char[2], void>
mp_push_back<L, T…>
template<class L, class... T> using mp_push_back = /*...*/;
mp_push_back<L, T…>
inserts the elements T…
at the back of the list L
. That is, mp_push_back<L<U…>, T…>
is an alias for L<U…, T…>
.
using L1 = std::tuple<double, long double>;
using R1 = mp_push_back<L1, float>; // std::tuple<double, long double, float>
using L2 = mp_list<void>;
using R2 = mp_push_back<L2, char[1], char[2]>; // mp_list<void, char[1], char[2]>
mp_rename<L, Y>
template<class L, template<class...> class Y> using mp_rename = /*...*/;
mp_rename<L, Y>
changes the type of the list L
to Y
. That is, mp_rename<L<T…>, Y>
is an alias for Y<T…>
.
using L1 = std::pair<double, long double>;
using R1 = mp_rename<L1, std::tuple>; // std::tuple<double, long double>
using L2 = std::tuple<void>;
using R2 = mp_rename<L2, mp_list>; // mp_list<void>
mp_apply<F, L>
template<template<class...> class F, class L> using mp_apply = mp_rename<L, F>;
mp_apply<F, L>
applies the metafunction F
to the contents of the list L
, that is, mp_apply<F, L<T…>>
is an alias for F<T…>
.
(mp_apply
is the same as mp_rename
with the arguments reversed.)
using L1 = std::pair<double, long double>;
using R1 = mp_apply<std::is_same, L1>; // std::is_same<double, long double>
mp_apply_q<Q, L>
template<class Q, class L> using mp_apply_q = mp_apply<Q::template fn, L>;
Same as mp_apply
, but takes a quoted metafunction.
using L1 = std::tuple<double, long double>;
using L2 = mp_list<int, long>;
using R1 = mp_apply_q<mp_bind_front<mp_push_back, L1>, L2>;
// R1 is std::tuple<double, long double, int, long>
mp_append<L…>
template<class... L> using mp_append = /*...*/;
mp_append<L…>
concatenates the lists in L…
into a single list that has the same type as the first list. mp_append<>
is an alias for mp_list<>
. mp_append<L1<T1…>, L2<T2…>, …, Ln<Tn…>>
is an alias for L1<T1…, T2…, …, Tn…>
.
using L1 = std::tuple<double, long double>;
using L2 = mp_list<int>;
using L3 = std::pair<short, long>;
using L4 = mp_list<>;
using R1 = mp_append<L1, L2, L3, L4>;
// std::tuple<double, long double, int, short, long>
mp_replace_front<L, T>
template<class L, class T> using mp_replace_front = /*...*/;
mp_replace_front<L, T>
replaces the first element of the list L
with T
. That is, mp_replace_front<L<U1, U…>, T>
is
an alias for L<T, U…>
.
using L1 = std::pair<int, float>;
using R1 = mp_replace_front<L1, void>; // std::pair<void, float>
using L2 = std::tuple<float, double, long double>;
using R2 = mp_replace_front<L2, void>; // std::tuple<void, double, long double>
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_replace_front<L3, void>; // mp_list<void, char[2], char[3], char[4]>;
mp_replace_first<L, T>
template<class L, class T> using mp_replace_first = mp_replace_front<L, T>;
mp_replace_first
is another name for mp_replace_front
.
mp_replace_second<L, T>
template<class L, class T> using mp_replace_second = /*...*/;
mp_replace_second<L, T>
replaces the second element of the list L
with T
. That is, mp_replace_second<L<U1, U2, U…>, T>
is an alias for L<U1, T, U…>
.
using L1 = std::pair<int, float>;
using R1 = mp_replace_second<L1, void>; // std::pair<int, void>
using L2 = std::tuple<float, double, long double>;
using R2 = mp_replace_second<L2, void>; // std::tuple<float, void, long double>
using L3 = mp_list<char[1], char[2], char[3], char[4]>;
using R3 = mp_replace_second<L3, void>; // mp_list<char[1], void, char[3], char[4]>;
mp_replace_third<L, T>
template<class L, class T> using mp_replace_third = /*...*/;
mp_replace_third<L, T>
replaces the third element of the list L
with T
. That is, mp_replace_third<L<U1, U2, U3, U…>, T>
is an alias for L<U1, U2, T, U…>
.
using L1 = std::tuple<float, double, long double>;
using R1 = mp_replace_third<L1, void>; // std::tuple<float, double, void>
using L2 = mp_list<char[1], char[2], char[3], char[4]>;
using R2 = mp_replace_third<L2, void>; // mp_list<char[1], char[2], void, char[4]>;
mp_transform_front<L, F>
template<class L, template<class...> class F> using mp_transform_front = /*...*/;
mp_transform_front<L, F>
replaces the first element T1
of the list L
with F<T1>
.
mp_transform_front_q<L, Q>
template<class L, class Q> using mp_transform_front_q = mp_transform_front<L, Q::template fn>;
As mp_transform_front
, but takes a quoted metafunction.
mp_transform_first<L, F>
template<class L, template<class...> class F> using mp_transform_first = mp_transform_front<L, F>;
mp_transform_first
is another name for mp_transform_front
.
mp_transform_first_q<L, Q>
template<class L, class Q> using mp_transform_first_q = mp_transform_first<L, Q::template fn>;
As mp_transform_first
, but takes a quoted metafunction.
mp_transform_second<L, F>
template<class L, template<class...> class F> using mp_transform_second = /*...*/;
mp_transform_second<L, F>
replaces the second element T2
of the list L
with F<T2>
.
mp_transform_second_q<L, Q>
template<class L, class Q> using mp_transform_second_q = mp_transform_second<L, Q::template fn>;
As mp_transform_second
, but takes a quoted metafunction.
mp_transform_third<L, F>
template<class L, template<class...> class F> using mp_transform_third = /*...*/;
mp_transform_third<L, F>
replaces the third element T3
of the list L
with F<T3>
.
mp_transform_third_q<L, Q>
template<class L, class Q> using mp_transform_third_q = mp_transform_third<L, Q::template fn>;
As mp_transform_third
, but takes a quoted metafunction.
Utility Components, <boost/mp11/utility.hpp>
mp_identity<T>
template<class T> struct mp_identity { using type = T; };
mp_identity
is a simple transformation type trait (as per the C++ standard)
that just returns the same type. It’s useful both as such, and as a type wrapper
for passing types as values to functions.
template<class T> using addp_if_not_ref =
typename mp_if<std::is_reference<T>, mp_identity<T>, std::add_pointer<T>>::type;
template<class T> void print1()
{
std::cout << typeid(T).name() << std::endl;
}
template<class T> void print2()
{
std::cout << typeid(mp_identity<T>).name() << std::endl;
}
int main()
{
print1<int const&>(); // 'int'
print2<int const&>(); // 'mp_identity<int const &>'
}
mp_identity_t<T>
template<class T> using mp_identity_t = typename mp_identity<T>::type;
mp_inherit<T…>
template<class... T> struct mp_inherit: T... {};
mp_if_c<C, T, E…>
template<bool C, class T, class... E> using mp_if_c = /*...*/;
mp_if_c<true, T, E…>
is an alias for T
. mp_if_c<false, T, E>
is an alias for E
. Otherwise, the result is a substitution failure.
using R1 = mp_if_c<true, int, void>; // int
using R2 = mp_if_c<false, int, void>; // void
template<class I> using void_if_5 = mp_if_c<I::value == 5, void>;
This example returns void
when I::value
is 5, and generates a substitution failure
otherwise. It’s the same as std::enable_if_t<I::value == 5>
in C++14, or
typename std::enable_if<I::value == 5>::type
in C++11.
mp_if<C, T, E…>
template<class C, class T, class... E> using mp_if = mp_if_c<static_cast<bool>(C::value), T, E...>;
Like mp_if_c
, but the first argument is a type.
using R1 = mp_if<mp_true, int, void>; // int
using R2 = mp_if<mp_false, int, void>; // void
template<class T> using void_if_const = mp_if<std::is_const<T>, void>;
template<class... T> using void_if_all_const =
mp_if<mp_all<std::is_const<T>...>, void>;
template<class T> using if_non_const = mp_if<mp_not<std::is_const<T>>, T>;
mp_eval_if_c<C, T, F, U…>
template<bool C, class T, template<class...> class F, class... U> using mp_eval_if_c = /*...*/;
mp_eval_if_c<C, T, F, U…>
is an alias for T
when C
is true
, for F<U…>
otherwise. Its purpose
is to avoid evaluating F<U…>
when the condition is true
as it may not be valid in this case.
template<class... T> using first_or_void =
mp_eval_if_c<sizeof...(T) == 0, void, mp_apply, mp_first, mp_list<T...>>;
mp_eval_if<C, T, F, U…>
template<class C, class T, template<class...> class F, class... U> using mp_eval_if = mp_eval_if_c<static_cast<bool>(C::value), T, F, U...>;
Like mp_eval_if_c
, but the first argument is a type.
template<class L> using first_or_void = mp_eval_if<mp_empty<L>, void, mp_first, L>;
mp_eval_if_q<C, T, Q, U…>
template<class C, class T, class Q, class... U> using mp_eval_if_q = mp_eval_if<C, T, Q::template fn, U...>;
Like mp_eval_if
, but takes a quoted metafunction.
mp_eval_if_not<C, T, F, U…>
template<class C, class T, template<class...> class F, class... U> using mp_eval_if_not = mp_eval_if<mp_not<C>, T, F, U...>;
Same as mp_eval_if
, but the condition is reversed.
mp_eval_if_not_q<C, T, Q, U…>
template<class C, class T, class Q, class... U> using mp_eval_if_not_q = mp_eval_if_not<C, T, Q::template fn, U...>;
Same as mp_eval_if_not
, but takes a quoted metafunction.
mp_valid<F, T…>
template<template<class...> class F, class... T> using mp_valid = /*...*/;
mp_valid<F, T…>
is an alias for mp_true
when F<T…>
is a valid expression, for mp_false
otherwise.
template<class T> using get_nested_type = typename T::type;
template<class T> struct has_nested_type: mp_valid<get_nested_type, T> {};
mp_valid_q<Q, T…>
template<class Q, class... T> using mp_valid_q = mp_valid<Q::template fn, T...>;
Like mp_valid
, but takes a quoted metafunction.
mp_eval_or<T, F, U…>
template<class T, template<class...> class F, class... U> using mp_eval_or = mp_eval_if_not<mp_valid<F, U...>, T, F, U...>;
mp_eval_or<T, F, U…>
is an alias for F<U…>
when this expression is valid, for T
otherwise.
mp_eval_or_q<T, Q, U…>
template<class T, class Q, class... U> using mp_eval_or_q = mp_eval_or<T, Q::template fn, U...>;
Like mp_eval_or
, but takes a quoted metafunction.
mp_cond<C, T, R…>
template<class C, class T, class... R> using mp_cond = /*...*/;
mp_cond<C, T, R…>
is an alias for T
when static_cast<bool>(C::value)
is true
.
When static_cast<bool>(C::value)
is false
, it’s an alias for mp_cond<R…>
.
(If static_cast<bool>(C::value)
is a substitution failure, the result is too a substitution
failure.)
template<int N> using unsigned_ = mp_cond<
mp_bool<N == 8>, uint8_t,
mp_bool<N == 16>, uint16_t,
mp_bool<N == 32>, uint32_t,
mp_bool<N == 64>, uint64_t,
mp_true, unsigned // default case
>;
mp_defer<F, T…>
template<template<class...> class F, class... T> using mp_defer = /*...*/;
When mp_valid<F, T…>
is mp_true
, mp_defer<F, T…>
is a struct with a nested type type
which is an alias for F<T…>
. Otherwise,
mp_defer<F, T…>
is an empty struct.
mp_quote<F>
template<template<class...> class F> struct mp_quote { template<class... T> using fn = F<T...>; };
mp_quote<F>
transforms the template F
into a quoted metafunction, a type with a nested template fn
such that fn<T…>
returns F<T…>
.
using LQ = mp_list<mp_quote<std::is_const>, mp_quote<std::is_volatile>>;
mp_quote_trait<F>
template<template<class...> class F> struct mp_quote_trait { template<class... T> using fn = typename F<T...>::type; };
mp_quote_trait<F>
transforms the C++03-style trait F
into a quoted metafunction.
using L1 = mp_list<int, void, float>;
using R1 = mp_transform_q<mp_quote_trait<std::add_pointer>, L1>;
// mp_list<int*, void*, float*>
mp_invoke_q<Q, T…>
template<class Q, class... T> using mp_invoke_q = typename Q::template fn<T...>;
mp_invoke_q<Q, T…>
evaluates the nested template fn
of a quoted metafunction. mp_invoke_q<mp_quote<F>, T…>
returns F<T…>
.
using LQ = mp_list<mp_quote<std::is_const>, mp_quote<std::is_volatile>>;
template<class T> using is_const_and_volatile =
mp_all<mp_product<mp_invoke_q, LQ, mp_list<T>>>;
template<class T> using is_const_and_volatile =
mp_all<mp_transform_q<mp_bind_back<mp_invoke_q, T>, LQ>>;
template<class T> using is_const_and_volatile =
mp_all<mp_transform<mp_invoke_q, LQ, mp_fill<LQ, T>>>;
mp_not_fn<P>
template<template<class...> class P> struct mp_not_fn { template<class... T> using fn = mp_not<P<T...>>; };
mp_not_fn<P>
returns a quoted metafunction Q
such that Q::fn<T…>
returns mp_not<P<T…>>
.
That is, it negates the result of P
.
mp_not_fn_q<Q>
template<class Q> using mp_not_fn_q = mp_not_fn<Q::template fn>;
As mp_not_fn
, but takes a quoted metafunction.
Algorithms, <boost/mp11/algorithm.hpp>
mp_transform<F, L…>
template<template<class...> class F, class... L> using mp_transform = /*...*/;
mp_transform<F, L1<T1…>, L2<T2…>, …, Ln<Tn…>>
applies F
to each successive tuple of elements and returns L1<F<T1, T2, …, Tn>…>
.
template<class T> using add_pointer_t =
typename std::add_pointer<T>::type; // std::add_pointer_t in C++14
using L1 = std::tuple<void, int, float>;
using R1 = mp_transform<add_pointer_t, L1>; // std::tuple<void*, int*, float*>
using L1 = std::tuple<void, int, float>;
using L2 = mp_list<void, int, float>;
using R1 = mp_all<mp_transform<std::is_same, L1, L2>>; // mp_true
template<class T1, class T2> using eq = mp_bool<T1::value == T2::value>;
using L1 = std::tuple<mp_int<1>, mp_int<2>, mp_int<3>>;
using L2 = mp_list<mp_size_t<1>, mp_size_t<2>, mp_size_t<3>>;
using R1 = mp_all<mp_transform<eq, L1, L2>>; // mp_true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mp_transform_q<Q, L…>
template<class Q, class... L> using mp_transform_q = mp_transform<Q::template fn, L...>;
As mp_transform
, but takes a quoted metafunction.
void
in a listusing L1 = std::tuple<void, int, float, void, int>;
using R1 = mp_apply<mp_plus,
mp_transform_q<mp_bind_front<std::is_same, void>, L1>>; // mp_int<2>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mp_transform_if<P, F, L…>
template<template<class...> class P, template<class...> class F, class... L> using mp_transform_if = /*...*/;
mp_transform_if<P, F, L1, L2, …, Ln>
replaces the elements of the list L1
for which mp_to_bool<P<T1, T2, …, Tn>>
is mp_true
with
F<T1, T2, …, Tn>
, and returns the result, where Ti
are the corresponding elements of Li
.
using L1 = std::tuple<void, int, float, void, int>;
using L2 = std::tuple<char[1], char[2], char[3], char[4], char[5]>;
template<class T1, class T2> using first_is_void = std::is_same<T1, void>;
template<class T1, class T2> using second = T2;
using R1 = mp_transform_if<first_is_void, second, L1, L2>;
// std::tuple<char[1], int, float, char[4], int>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mp_transform_if_q<Qp, Qf, L…>
template<class Qp, class Qf, class... L> using mp_transform_if_q = mp_transform_if<Qp::template fn, Qf::template fn, L...>;
As mp_transform_if
, but takes quoted metafunctions.
using L1 = std::tuple<void, int, float, void, int>;
using L2 = std::tuple<char[1], char[2], char[3], char[4], char[5]>;
using R1 = mp_transform_if_q<mp_bind<std::is_same, _1, void>, _2, L1, L2>;
// std::tuple<char[1], int, float, char[4], int>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mp_filter<P, L…>
template<template<class...> class P, class... L> using mp_filter = /*...*/;
mp_filter<P, L1, L2, …, Ln>
removes the elements of the list L1
for which mp_to_bool<P<T1, T2, …, Tn>>
is mp_false
and returns the result, where Ti
are the corresponding elements of Li
.
See also mp_copy_if
and mp_remove_if
, less general variants of mp_filter
that only take a single list.
mp_filter_q<Qp, L…>
template<class Qp, class... L> using mp_filter_q = mp_filter<Qp::template fn, L...>;
As mp_filter
, but takes a quoted metafunction.
using L1 = std::tuple<void, int, float>;
using L2 = mp_list<mp_true, mp_false, mp_true>;
using R1 = mp_filter_q<_2, L1, L2>; // std::tuple<void, float>
mp_fill<L, V>
template<class L, class V> using mp_fill = /*...*/;
mp_fill<L<T…>, V>
returns L<V, V, …, V>
, with the result having the same size as the input.
using L1 = std::tuple<void, int, float>;
using R1 = mp_fill<L1, double>; // std::tuple<double, double, double>
using L1 = std::pair<int, float>;
using R1 = mp_fill<L1, void>; // std::pair<void, void>
|
|
|
|
|
|
|
|
|
|
mp_count<L, V>
template<class L, class V> using mp_count = /*...*/;
mp_count<L, V>
returns mp_size_t<N>
, where N
is the number of elements of L
same as V
.
mp_count_if<L, P>
template<class L, template<class...> class P> using mp_count_if = /*...*/;
mp_count_if<L, P>
returns mp_size_t<N>
, where N
is the number of elements T
of L
for which mp_to_bool<P<T>>
is mp_true
.
mp_count_if_q<L, Q>
template<class L, class Q> using mp_count_if_q = mp_count_if<L, Q::template fn>;
As mp_count_if
, but takes a quoted metafunction.
mp_contains<L, V>
template<class L, class V> using mp_contains = mp_to_bool<mp_count<L, V>>;
mp_contains<L, V>
is mp_true
when L
contains an element V
, mp_false
otherwise.
mp_starts_with<L1, L2>
template<class L1, class L2> using mp_starts_with = /*...*/;
mp_starts_with<L1, L2>
is mp_true
when L1
starts with L2
, mp_false
otherwise.
mp_repeat_c<L, N>
template<class L, std::size_t N> using mp_repeat_c = /*...*/;
mp_repeat_c<L, N>
returns a list of the same form as L
that consists of N
concatenated copies of L
.
using L1 = tuple<int>;
using R1 = mp_repeat_c<L1, 3>; // tuple<int, int, int>
using L2 = pair<int, float>;
using R2 = mp_repeat_c<L2, 1>; // pair<int, float>
using L3 = mp_list<int, float>;
using R3 = mp_repeat_c<L3, 2>; // mp_list<int, float, int, float>
using L4 = mp_list<int, float, double>;
using R4 = mp_repeat_c<L4, 0>; // mp_list<>
mp_repeat<L, N>
template<class L, class N> using mp_repeat = /*...*/;
Same as mp_repeat_c
but with a type argument N
. The number of copies is N::value
and must be nonnegative.
mp_product<F, L…>
template<template<class...> class F, class... L> using mp_product = /*...*/;
mp_product<F, L1<T1…>, L2<T2…>, …, Ln<Tn…>>
evaluates F<U1, U2, …, Un>
for values Ui
taken from
the Cartesian product of the lists, as if the elements Ui
are formed by n
nested loops, each traversing Li
.
It returns a list of the form L1<V…>
containing the results of the application of F
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
mp_product_q<Q, L…>
template<class Q, class... L> using mp_product_q = mp_product<Q::template fn, L...>;
As mp_product
, but takes a quoted metafunction.
mp_drop_c<L, N>
template<class L, std::size_t N> using mp_drop_c = /*...*/;
mp_drop_c<L, N>
removes the first N
elements of L
and returns the result.
|
|
|
|
|
|
|
|
|
|
|
mp_drop<L, N>
template<class L, class N> using mp_drop = /*...*/;
Same as mp_drop_c
, but with a type argument N
. N::value
must be a nonnegative number.
mp_from_sequence<S>
template<class S> using mp_from_sequence = /*...*/
mp_from_sequence
transforms an integer sequence produced by make_integer_sequence
into an mp_list
of the corresponding std::integral_constant
types. Given
template<class T, T... I> struct S;
mp_from_sequence<S<T, I…>>
is an alias for mp_list<std::integral_constant<T, I>…>
.
mp_iota_c<N>
template<std::size_t N> using mp_iota_c = /*...*/;
mp_iota_c<N>
is an alias for mp_list<mp_size_t<0>, mp_size_t<1>, …, mp_size_t<N-1>>
.
mp_iota<N>
template<class N> using mp_iota = /*...*/;
Same as mp_iota_c
, but with a type argument N
. N::value
must be a nonnegative number. Returns
mp_list<std::integral_constant<T, 0>, std::integral_constant<T, 1>, …, std::integral_constant<T, N::value-1>>
where T
is the type of N::value
.
|
|
|
|
|
mp_at_c<L, I>
template<class L, std::size_t I> using mp_at_c = /*...*/;
mp_at_c<L, I>
returns the I
-th element of L
, zero-based.
mp_at<L, I>
template<class L, class I> using mp_at = /*...*/;
Same as mp_at_c
, but with a type argument I
. I::value
must be a nonnegative number.
mp_take_c<L, N>
template<class L, std::size_t N> using mp_take_c = /*...*/;
mp_take_c<L, N>
returns a list of the same form as L
containing the first N
elements of L
.
|
|
|
|
|
|
|
|
|
|
|
mp_take<L, N>
template<class L, class N> using mp_take = /*...*/;
Same as mp_take_c
, but with a type argument N
. N::value
must be a nonnegative number.
mp_back<L>
template<class L> using mp_back = mp_at_c<L, mp_size<L>::value - 1>;
mp_back<L>
returns the last element of the list L
.
mp_pop_back<L>
template<class L> using mp_pop_back = mp_take_c<L, mp_size<L>::value - 1>;
mp_pop_back<L>
removes the last element of the list L
and returns the result.
mp_insert_c<L, I, T…>
template<class L, std::size_t I, class... T> using mp_insert_c = mp_append<mp_take_c<L, I>, mp_push_front<mp_drop_c<L, I>, T...>>;
Inserts the elements T…
into the list L
at position I
(a zero-based index).
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
mp_insert<L, I, T…>
template<class L, class I, class... T> using mp_insert = mp_append<mp_take<L, I>, mp_push_front<mp_drop<L, I>, T...>>;
Same as mp_insert_c
, but with a type argument I
.
mp_erase_c<L, I, J>
template<class L, std::size_t I, std::size_t J> using mp_erase_c = mp_append<mp_take_c<L, I>, mp_drop_c<L, J>>;
Removes from the list L
the elements with indices from I
(inclusive) to J
(exclusive).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mp_erase<L, I, J>
template<class L, class I, class J> using mp_erase = mp_append<mp_take<L, I>, mp_drop<L, J>>;
Same as mp_erase_c
, but with a type arguments I
and J
.
mp_replace<L, V, W>
template<class L, class V, class W> using mp_replace = /*...*/;
Replaces all V
elements of L
with W
and returns the result.
|
|
|
|
|
|
|
|
|
|
mp_replace_if<L, P, W>
template<class L, template<class...> class P, class W> using mp_replace_if = /*...*/;
Replaces all T
elements of L
for which mp_to_bool<P<T>>
is mp_true
with W
and returns the result.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mp_replace_if_q<L, Q, W>
template<class L, class Q, class W> using mp_replace_if_q = mp_replace_if<L, Q::template fn, W>;
As mp_replace_if
, but takes a quoted metafunction.
mp_replace_at_c<L, I, W>
template<class L, std::size_t I, class W> using mp_replace_at_c = /*...*/;
Replaces the element of L
at zero-based index I
with W
and returns the result.
mp_replace_at<L, I, W>
template<class L, class I, class W> using mp_replace_at = /*...*/;
Same as mp_replace_at_c
, but with a type argument I
. I::value
must be a nonnegative number.
mp_copy_if<L, P>
template<class L, template<class...> class P> using mp_copy_if = /*...*/;
Copies the elements T
of L
for which mp_to_bool<P<T>>
is mp_true
to a new list of the same form and returns it.
mp_copy_if_q<L, Q>
template<class L, class Q> using mp_copy_if_q = mp_copy_if<L, Q::template fn>;
As mp_copy_if
, but takes a quoted metafunction.
mp_remove<L, V>
template<class L, class V> using mp_remove = /*...*/;
Removes all V
elements of L
and returns the result.
mp_remove_if<L, P>
template<class L, template<class...> class P> using mp_remove_if = /*...*/;
Removes all elements T
of L
for which mp_to_bool<P<T>>
is mp_true
and returns the result.
mp_remove_if_q<L, Q>
template<class L, class Q> using mp_remove_if_q = mp_remove_if<L, Q::template fn>;
As mp_remove_if
, but takes a quoted metafunction.
mp_partition<L, P>
template<class L, template<class...> class P> using mp_partition = /*...*/;
mp_partition<L<T…>, P>
partitions L
into two lists L<U1…>
and L<U2…>
such that mp_to_bool<P<T>>
is mp_true
for the elements of L<U1…>
and mp_false
for the elements of L<U2…>
. Returns L<L<U1…>, L<U2…>>
.
mp_partition_q<L, Q>
template<class L, class Q> using mp_partition_q = mp_partition<L, Q::template fn>;
As mp_partition
, but takes a quoted metafunction.
mp_sort<L, P>
template<class L, template<class...> class P> using mp_sort = /*...*/;
mp_sort<L, P>
sorts the list L
according to the strict weak ordering mp_to_bool<P<T, U>>
.
#include <ratio> using L1 = mp_list<std::ratio<1,2>, std::ratio<1,4>>; using R1 = mp_sort<L1, std::ratio_less>; // mp_list<ratio<1,4>, ratio<1,2>>
mp_sort_q<L, Q>
template<class L, class Q> using mp_sort_q = mp_sort<L, Q::template fn>;
As mp_sort
, but takes a quoted metafunction.
mp_nth_element_c<L, I, P>
template<class L, std::size_t I, template<class...> class P> using mp_nth_element_c = /*...*/;
Returns the element at position I
in mp_sort<L, P>
.
mp_nth_element<L, I, P>
template<class L, class I, template<class...> class P> using mp_nth_element = /*...*/;
Like mp_nth_element_c
, but with a type argument I
. I::value
must be a nonnegative number.
mp_nth_element_q<L, I, Q>
template<class L, class I, class Q> using mp_nth_element_q = mp_nth_element<L, I, Q::template fn>;
Like mp_nth_element
, but takes a quoted metafunction.
mp_min_element<L, P>
template<class L, template<class...> class P> using mp_min_element = /*...*/;
mp_min_element<L, P>
returns the minimal element of the list L
according to the ordering mp_to_bool<P<T, U>>
.
It’s equivalent to mp_fold<mp_rest<L>, mp_first<L>, F>
, where F<T, U>
returns mp_if<P<T, U>, T, U>
.
mp_min_element_q<L, Q>
template<class L, class Q> using mp_min_element_q = mp_min_element<L, Q::template fn>;
As mp_min_element
, but takes a quoted metafunction.
mp_max_element<L, P>
template<class L, template<class...> class P> using mp_max_element = /*...*/;
mp_max_element<L, P>
returns the maximal element of the list L
according to the ordering mp_to_bool<P<T, U>>
.
It’s equivalent to mp_fold<mp_rest<L>, mp_first<L>, F>
, where F<T, U>
returns mp_if<P<U, T>, T, U>
.
mp_max_element_q<L, Q>
template<class L, class Q> using mp_max_element_q = mp_max_element<L, Q::template fn>;
As mp_max_element
, but takes a quoted metafunction.
mp_find<L, V>
template<class L, class V> using mp_find = /*...*/;
mp_find<L, V>
returns the index at which the type V
is located in the list L
. It’s an alias for mp_size_t<I>
,
where I
is the zero-based index of the first occurrence of V
in L
. If L
does not contain V
, mp_find<L, V>
is mp_size<L>
.
mp_find_if<L, P>
template<class L, template<class...> class P> using mp_find_if = /*...*/;
mp_find_f<L, P>
is an alias for mp_size_t<I>
, where I
is the zero-based index of the first element T
in L
for which
mp_to_bool<P<T>>
is mp_true
. If there is no such element, mp_find_if<L, P>
is mp_size<L>
.
mp_find_if_q<L, Q>
template<class L, class Q> using mp_find_if_q = mp_find_if<L, Q::template fn>;
As mp_find_if
, but takes a quoted metafunction.
mp_reverse<L>
template<class L> using mp_reverse = /*...*/;
mp_reverse<L<T1, T2, …, Tn>>
is L<Tn, …, T2, T1>
.
|
|
|
|
|
|
|
|
|
|
mp_fold<L, V, F>
template<class L, class V, template<class...> class F> using mp_fold = /*...*/;
mp_fold<L<T1, T2, …, Tn>, V, F>
is F< F< F< F<V, T1>, T2>, …>, Tn>
, or V
, if L
is empty.
#include <ratio> using L1 = mp_list<std::ratio<1,8>, std::ratio<1,4>, std::ratio<1,2>>; using R1 = mp_fold<L1, std::ratio<0,1>, std::ratio_add>; // std::ratio<7,8>
mp_fold_q<L, V, Q>
template<class L, class V, class Q> using mp_fold_q = mp_fold<L, V, Q::template fn>;
As mp_fold
, but takes a quoted metafunction.
mp_reverse_fold<L, V, F>
template<class L, class V, template<class...> class F> using mp_reverse_fold = /*...*/;
mp_reverse_fold<L<T1, T2, …, Tn>, V, F>
is F<T1, F<T2, F<…, F<Tn, V>>>>
, or V
, if L
is empty.
mp_reverse_fold_q<L, V, Q>
template<class L, class V, class Q> using mp_reverse_fold_q = mp_reverse_fold<L, V, Q::template fn>;
As mp_reverse_fold
, but takes a quoted metafunction.
mp_unique<L>
template<class L> using mp_unique = /*...*/;
mp_unique<L>
returns a list of the same form as L
with the duplicate elements removed.
mp_all_of<L, P>
template<class L, template<class...> class P> using mp_all_of = mp_bool< mp_count_if<L, P>::value == mp_size<L>::value >;
mp_all_of<L, P>
is mp_true
when P
holds for all elements of L
, mp_false
otherwise. When L
is empty, the result is mp_true
.
mp_all_of_q<L, Q>
template<class L, class Q> using mp_all_of_q = mp_all_of<L, Q::template fn>;
As mp_all_of
, but takes a quoted metafunction.
mp_none_of<L, P>
template<class L, template<class...> class P> using mp_none_of = mp_bool< mp_count_if<L, P>::value == 0 >;
mp_none_of<L, P>
is mp_true
when P
holds for no element of L
, mp_false
otherwise. When L
is empty, the result is mp_true
.
mp_none_of_q<L, Q>
template<class L, class Q> using mp_none_of_q = mp_none_of<L, Q::template fn>;
As mp_none_of
, but takes a quoted metafunction.
mp_any_of<L, P>
template<class L, template<class...> class P> using mp_any_of = mp_bool< mp_count_if<L, P>::value != 0 >;
mp_any_of<L, P>
is mp_true
when P
holds for at least one element of L
, mp_false
otherwise. When L
is empty, the result is mp_false
.
mp_any_of_q<L, Q>
template<class L, class Q> using mp_any_of_q = mp_any_of<L, Q::template fn>;
As mp_any_of
, but takes a quoted metafunction.
mp_for_each<L>(f)
template<class L, class F> constexpr F mp_for_each(F&& f);
mp_for_each<L>(f)
calls f
with T()
for each element T
of the list L
, in order.
Returns std::forward<F>(f)
.
template<class... T> void print( std::tuple<T...> const & tp )
{
std::size_t const N = sizeof...(T);
mp_for_each<mp_iota_c<N>>( [&]( auto I ){
// I is mp_size_t<0>, mp_size_t<1>, ..., mp_size_t<N-1>
std::cout << std::get<I>(tp) << std::endl;
});
}
mp_with_index<N>(i, f)
template<std::size_t N, class F> constexpr auto mp_with_index( std::size_t i, F && f ) -> decltype(std::declval<F>()(std::declval<mp_size_t<0>>()));
mp_with_index<N>(i, f)
calls f
with mp_size_t<i>()
and returns the result. i
must be less than N
.
Only constexpr
on C++14 and higher.
template<class N, class F> constexpr auto mp_with_index( std::size_t i, F && f ) -> decltype(std::declval<F>()(std::declval<mp_size_t<0>>()));
Returns mp_with_index<N::value>(i, f)
.
template<class... T> void print( std::variant<T...> const& v )
{
mp_with_index<sizeof...(T)>( v.index(), [&]( auto I ) {
// I is mp_size_t<v.index()>{} here
std::cout << std::get<I>( v ) << std::endl;
});
}
Set Operations, <boost/mp11/set.hpp>
A set is a list whose elements are unique.
mp_is_set<S>
template<class S> using mp_is_set = /*...*/;
mp_is_set<S>
is mp_true
if S
is a set, mp_false
otherwise.
mp_set_contains<S, V>
template<class S, class V> using mp_set_contains = /*...*/;
mp_set_contains<S, V>
is mp_true
if the type V
is an element of the set S
, mp_false
otherwise.
mp_set_push_back<S, T…>
template<class S, class... T> using mp_set_push_back = /*...*/;
For each T1
in T…
, mp_set_push_back<S, T…>
appends T1
to the end of the set S
if it’s not already an element of S
.
mp_set_push_front<S, T…>
template<class S, class... T> using mp_set_push_front = /*...*/;
mp_set_push_front<S, T…>
inserts at the front of the set S
those elements of T…
for which S
does not already contain the same type.
mp_set_union<L…>
template<class... L> using mp_set_union = /*...*/;
mp_set_union<S, L…>
is mp_set_push_back<S, T…>
, where T…
are the combined elements of the lists L…
.
mp_set_union<>
is mp_list<>
.
mp_set_intersection<S…>
template<class... S> using mp_set_intersection = /*...*/;
mp_set_intersection<S…>
returns a set that contains the elements that occur in all of the sets S…
.
mp_set_intersection<>
is mp_list<>
.
mp_set_difference<L, S…>
template<class L, class... S> using mp_set_difference = /*...*/;
mp_set_difference<L, S…>
removes the elements of the list L
that appear in any of the sets S…
and
returns the result.
Map Operations, <boost/mp11/map.hpp>
A map is a list of lists, the inner lists having at least one element (the key.) The keys of the map must be unique.
mp_is_map<M>
template<class M> using mp_is_map = /*...*/;
mp_is_map<M>
is mp_true
if M
is a map, mp_false
otherwise.
mp_map_find<M, K>
template<class M, class K> using mp_map_find = /*...*/;
mp_map_find<M, K>
is an alias for the element of the map M
with a key K
, or for void
, if there is no such element.
mp_map_contains<M, K>
template<class M, class K> using mp_map_contains = mp_not<std::is_same<mp_map_find<M, K>, void>>;
mp_map_contains<M, K>
is mp_true
if the map M
contains an element with a key K
, mp_false
otherwise.
mp_map_insert<M, T>
template<class M, class T> using mp_map_insert = mp_if< mp_map_contains<M, mp_first<T>>, M, mp_push_back<M, T> >;
Inserts the element T
into the map M
, if an element with a key mp_first<T>
is not already in M
.
mp_map_replace<M, T>
template<class M, class T> using mp_map_replace = /*...*/;
If the map M
does not contain an element with a key mp_first<T>
, inserts it (using mp_push_back<M, T>
); otherwise,
replaces the existing element with T
.
mp_map_update<M, T, F>
template<class M, class T, template<class...> class F> using mp_map_update = /*...*/;
If the map M
does not contain an element with a key mp_first<T>
, inserts it (using mp_push_back<M, T>
); otherwise,
replaces the existing element L<X, Y…>
with L<X, F<X, Y…>>
.
template<class T, class U> using inc2nd = mp_int<U::value + 1>;
template<class M, class T> using count_types =
mp_map_update<M, std::pair<T, mp_int<1>>, inc2nd>;
using L1 = mp_list<float, char, float, float, float, float, char, float>;
using R1 = mp_fold<L1, std::tuple<>, count_types>;
// std::tuple<std::pair<float, mp_int<6>>, std::pair<char, mp_int<2>>>
mp_map_update_q<M, T, Q>
template<class M, class T, class Q> using mp_map_update_q = mp_map_update<M, T, Q::template fn>;
As mp_map_update
, but takes a quoted metafunction.
mp_map_erase<M, K>
template<class M, class K> using mp_map_erase = /*...*/;
If the map M
contains an element with a key K
, removes it.
mp_map_keys<M>
template<class M> using mp_map_keys = mp_transform<mp_first, M>;
mp_map_keys<M>
returns a list of the keys of M
. When M
is a valid map, the keys are unique, so the result is a set.
Helper Metafunctions, <boost/mp11/function.hpp>
mp_void<T…>
template<class... T> using mp_void = void;
Same as std::void_t
from C++17.
mp_and<T…>
template<class... T> using mp_and = /*...*/;
mp_and<T…>
applies mp_to_bool
to the types in T…
, in order. If the result of an application is mp_false
, mp_and
returns mp_false
. If the application causes a substitution failure, returns mp_false
. If all results are mp_true
,
returns mp_true
. mp_and<>
is mp_true
.
using R1 = mp_and<mp_true, mp_true>; // mp_true
using R2 = mp_and<mp_false, void>; // mp_false, void is not reached
using R3 = mp_and<mp_false, mp_false>; // mp_false
using R4 = mp_and<void, mp_true>; // mp_false (!)
mp_all<T…>
template<class... T> using mp_all = /*...*/;
mp_all<T…>
is mp_true
if mp_to_bool<U>
is mp_true
for all types U
in T…
, mp_false
otherwise. Same as
mp_and
, but does not perform short-circuit evaluation. mp_and<mp_false, void>
is mp_false
, but mp_all<mp_false, void>
is an error because void
does not have a nested value
. The upside is that mp_all
is potentially faster and does not
mask substitution failures as mp_and
does.
using R1 = mp_all<mp_true, mp_true>; // mp_true
using R2 = mp_all<mp_false, void>; // compile-time error
using R3 = mp_all<mp_false, mp_false>; // mp_false
using R4 = mp_all<void, mp_true>; // compile-time error
mp_or<T…>
template<class... T> using mp_or = /*...*/;
mp_or<T…>
applies mp_to_bool
to the types in T…
, in order. If the result of an application is mp_true
, mp_or
returns mp_true
. If all results are mp_false
, returns mp_false
. mp_or<>
is mp_false
.
using R1 = mp_or<mp_true, mp_false>; // mp_true
using R2 = mp_or<mp_true, void>; // mp_true, void is not reached
using R3 = mp_or<mp_false, mp_false>; // mp_false
using R4 = mp_or<void, mp_true>; // compile-time error
mp_any<T…>
template<class... T> using mp_any = /*...*/;
mp_any<T…>
is mp_true
if mp_to_bool<U>
is mp_true
for any type U
in T…
, mp_false
otherwise. Same as
mp_or
, but does not perform short-circuit evaluation.
using R1 = mp_any<mp_true, mp_false>; // mp_true
using R2 = mp_any<mp_true, void>; // compile-time error
using R3 = mp_any<mp_false, mp_false>; // mp_false
using R4 = mp_any<void, mp_true>; // compile-time error
mp_same<T…>
template<class... T> using mp_same = /*...*/;
mp_same<T…>
is mp_true
if all the types in T…
are the same type, mp_false
otherwise. mp_same<>
is mp_true
.
mp_similar<T…>
template<class... T> using mp_similar = /*...*/;
mp_similar<T…>
is mp_true
if all the types in T…
are the same type, or instantiations of the same class template
whose parameters are all types, mp_false
otherwise. mp_similar<>
is mp_true
.
using R1 = mp_similar<void>; // mp_true
using R2 = mp_similar<void, void>; // mp_true
using R3 = mp_similar<void, void, void>; // mp_true
using R4 = mp_similar<void, void, float>; // mp_false
template<class T> struct X;
template<class... T> struct Y;
using R5 = mp_similar<X<int>, X<void>, X<float>>; // mp_true
using R6 = mp_similar<Y<>, Y<void>, Y<void, void>>; // mp_true
using R7 = mp_similar<X<void>, Y<void>>; // mp_false
mp_plus<T…>
template<class... T> using mp_plus = /*...*/;
mp_plus<T…>
is an integral constant type with a value that is the sum of U::value
for all types U
in T…
.
mp_plus<>
is mp_int<0>
.
mp_less<T1, T2>
template<class T1, class T2> using mp_less = /*...*/;
mp_less<T1, T2>
is mp_true
when the numeric value of T1::value
is less than the numeric value of T2::value
,
mp_false
otherwise.
(Note that this is not necessarily the same as T1::value < T2::value
when comparing between signed and unsigned types;
-1 < 1u
is false
, but mp_less<mp_int<-1>, mp_size_t<1>>
is mp_true
.)
mp_min<T1, T…>
template<class T1, class... T> using mp_min = mp_min_element<mp_list<T1, T...>, mp_less>;
mp_min<T…>
returns the type U
in T…
with the lowest U::value
.
mp_max<T1, T…>
template<class T1, class... T> using mp_max = mp_max_element<mp_list<T1, T...>, mp_less>;
mp_max<T…>
returns the type U
in T…
with the highest U::value
.
Bind, <boost/mp11/bind.hpp>
mp_arg<I>
template<std::size_t I> struct mp_arg;
mp_arg<I>
is a quoted metafunction whose nested template fn<T…>
returns the I
-th zero-based element of T…
.
_1, …, _9
using _1 = mp_arg<0>; using _2 = mp_arg<1>; using _3 = mp_arg<2>; using _4 = mp_arg<3>; using _5 = mp_arg<4>; using _6 = mp_arg<5>; using _7 = mp_arg<6>; using _8 = mp_arg<7>; using _9 = mp_arg<8>;
_1
to _9
are placeholder types, the equivalent to the placeholders of boost::bind
.
mp_bind<F, T…>
template<template<class...> class F, class... T> struct mp_bind;
mp_bind<F, T…>
is a quoted metafunction that implements the type-based
equivalent of boost::bind
. Its nested template fn<U…>
returns F<V…>
,
where V…
is T…
with the placeholders replaced by the corresponding
element of U…
and the mp_bind
, mp_bind_front
, and mp_bind_back
expressions replaced with their corresponding evaluations against U…
.
For example, mp_bind<F, int, _2, mp_bind<G, _1>>::fn<float, void>
is F<int, void, G<float>>
.
mp_bind_q<Q, T…>
template<class Q, class... T> using mp_bind_q = mp_bind<Q::template fn, T...>;
As mp_bind
, but takes a quoted metafunction.
mp_bind_front<F, T…>
template<template<class...> class F, class... T> struct mp_bind_front;
mp_bind_front<F, T…>
binds the leftmost arguments of F
to T…
. Its nested template fn<U…>
returns F<T…, U…>
.
mp_bind_front_q<Q, T…>
template<class Q, class... T> using mp_bind_front_q = mp_bind_front<Q::template fn, T...>;
As mp_bind_front
, but takes a quoted metafunction.
mp_bind_back<F, T…>
template<template<class...> class F, class... T> struct mp_bind_back;
mp_bind_back<F, T…>
binds the rightmost arguments of F
to T…
. Its nested template fn<U…>
returns F<U…, T…>
.
mp_bind_back_q<Q, T…>
template<class Q, class... T> using mp_bind_back_q = mp_bind_back<Q::template fn, T...>;
As mp_bind_back
, but takes a quoted metafunction.
Integer Sequences, <boost/mp11/integer_sequence.hpp>
integer_sequence<T, I…>
template<class T, T... I> struct integer_sequence { };
integer_sequence<T, I…>
holds a sequence of integers of type T
. Same as C++14’s std::integer_sequence
.
make_integer_sequence<T, N>
template<class T, T N> using make_integer_sequence = /*...*/;
make_integer_sequence<T, N>
is integer_sequence<T, 0, 1, …, N-1>
. Same as C++14’s std::make_integer_sequence
.
index_sequence<I…>
template<std::size_t... I> using index_sequence = integer_sequence<std::size_t, I...>;
index_sequence<I…>
is an alias for integer_sequence<size_t, I…>
. Same as C++14’s std::index_sequence
.
make_index_sequence<N>
template<std::size_t N> using make_index_sequence = make_integer_sequence<std::size_t, N>;
make_index_sequence<N>
is index_sequence<0, 1, …, N-1>
. Same as C++14’s std::make_index_sequence
.
index_sequence_for<T…>
template<class... T> using index_sequence_for = make_integer_sequence<std::size_t, sizeof...(T)>;
index_sequence_for<N>
is make_index_sequence<sizeof…(T)>
. Same as C++14’s std::index_sequence_for
.
Tuple Operations, <boost/mp11/tuple.hpp>
tuple_apply(f, tp)
template<class F, class Tp> constexpr /*...*/ tuple_apply(F&& f, Tp&& tp);
tuple_apply(f, tp)
returns std::forward<F>(f)(std::get<J>(std::forward<Tp>(tp))…)
for J
in 0..N-1
,
where N
is std::tuple_size<typename std::remove_reference<Tp>::type>::value
. Same as std::apply
in C++17.
construct_from_tuple<T>(tp)
template<class T, class Tp> T construct_from_tuple(Tp&& tp);
construct_from_tuple<T>(tp)
returns T(std::get<J>(std::forward<Tp>(tp))…)
for J
in 0..N-1
,
where N
is std::tuple_size<typename std::remove_reference<Tp>::type>::value
. Same as std::make_from_tuple
in C++17.
The name of the function doesn’t match the C++17 one to avoid ambiguities when both are visible or in unqualified calls.
tuple_for_each(tp, f)
template<class Tp, class F> constexpr F tuple_for_each(Tp&& tp, F&& f);
tuple_for_each(tp, f)
applies the function object f
to each element of tp
by evaluating the
expression f(std::get<J>(std::forward<Tp>(tp)))
for J
in 0..N-1
, where N
is std::tuple_size<typename std::remove_reference<Tp>::type>::value
.
Returns std::forward<F>(f)
.
Convenience Header, <boost/mp11.hpp>
The convenience header <boost/mp11.hpp>
includes all of the
headers listed previously in this reference.
MPL Support, <boost/mp11/mpl.hpp>
The header <boost/mp11/mpl.hpp>
, when included, defines the
necessary support infrastructure for mp_list
and std::tuple
to be valid MPL sequences.
Note
|
mpl.hpp is not included by <boost/mp11.hpp> .
|
It’s also possible to only enable support for mp_list
by
including <boost/mp11/mpl_list.hpp>
, and for std::tuple
by including <boost/mp11/mpl_tuple.hpp>
. This may be required
because some libraries, such as Boost.Fusion, contain their own MPL
support for std::tuple
, which conflicts with Mp11’s one.
Appendix A: Copyright, License, and Acknowledgments
This documentation is
-
Copyright 2017-2019 Peter Dimov
-
Copyright 2017 Bjørn Reese
and is distributed under the Boost Software License, Version 1.0.
The "Simple C++11 metaprogramming" articles have been graciously converted to Asciidoc format for incorporation into this documentation by Glen Fernandes.