Metal is a header-only C++14 library designed to make template metaprogramming intuitive. It provides a powerful high-level abstraction for compile-time algorithms that mimic the Standard Algorithms Library, hence Metal - Metaprogramming Algorithms.

There is a myriad of C++ metaprogramming libraries out there so why Metal?

In a Glimpse

#include <metal.hpp>
// First we need some Values
union x { char payload[10]; };
class y { public: char c; };
struct z { char c; int i; };
// ... from which we construct some Lists
using l0 = metal::list<>;
static_assert(metal::same<l1, metal::list<x>>::value, "");
static_assert(metal::same<l2, metal::list<x, z>>::value, "");
static_assert(metal::same<l3, metal::list<x, y, z>>::value, "");
// Lists are versatile, we can check their sizes...
static_assert(metal::size<l0>::value == 0, "");
static_assert(metal::size<l1>::value == 1, "");
static_assert(metal::size<l2>::value == 2, "");
static_assert(metal::size<l3>::value == 3, "");
// retrieve their elements...
static_assert(metal::same<metal::front<l3>, x>::value, "");
static_assert(metal::same<metal::back<l3>, z>::value, "");
static_assert(metal::same<metal::at<l3, metal::number<1>>, y>::value, "");
// count those that satisfy a predicate...
static_assert(metal::count_if<l3, metal::trait<std::is_class>>::value == 2, "");
static_assert(metal::count_if<l3, metal::trait<std::is_union>>::value == 1, "");
// We can create new Lists by removing elements...
static_assert(metal::same<l0, l0_>::value, "");
static_assert(metal::same<l1, l1_>::value, "");
static_assert(metal::same<l2, l2_>::value, "");
// by reversing the order of elements...
// by transforming the elements...
static_assert(metal::same<l2ptrs, metal::list<x*, z*>>::value, "");
static_assert(metal::same<l3refs, metal::list<x&, y&, z&>>::value, "");
// even by sorting them...
template<typename x, typename y>
static_assert(metal::same<sorted, metal::list<y, z, x>>::value, "");
// that and much more!

Be sure to take a look at the examples below.


Template metaprogramming may be seen as a language of its own right. It shares the usual syntax of C++ templates, but has unique semantics. Because constructs assume different meanings in its context it is useful to define a few key concepts.


Values are the objects of metaprogramming.


Any type is a Value.


using val = int;
using val = decltype(3.14);
struct val { /*...*/ };


int not_a_val;
decltype(auto) not_a_val = 3.14;
struct not_a_val { /*...*/ };

See Also

metal::value, metal::is_value


A Number is a compile-time representation of a numerical value.


num is a model of Number if and only if num is a specialization of metal::number.

metal::number<n> is guaranteed to be an alias template to std::integral_constant<metal::int_, n>.


using num = metal::false_;
using num = metal::number<-1>;
using num = metal::number<'a'>;


struct not_a_num :
using not_a_num = std::integral_constant<int, 0>;


Why isn't std::integral_constant a Number in general?

See Also

metal::number, metal::is_number, metal::as_number


Expressions, also called metafunctions, are mappings over the set of Values.


expr is a model of Expression if and only if expr is a class, union or alias template that only expects Values as arguments.


template<typename... vals>
using expr = metal::number<sizeof...(vals)>;
template<typename x, typename y>
struct expr;


template<template<typename...> class...> // non-type parameter
struct not_an_expr;
template<int v> // non-type parameter
using not_an_expr = metal::number<v>;


Lambdas, short for Lambda Expressions, are first-class Expressions. As Values themselves, Lambdas can serve both as argument as well as return value to other Expressions and Lambdas, thus enabling higher-order composition.


lbd is a model of Lambda if and only if lbd is a specialization of metal::lambda.


See Also

metal::lambda, metal::is_lambda, metal::as_lambda


A List is a sequence of Values.


list is a model of List if and only if list is a specialization of metal::list.


using list = metal::list<>; // an empty list


using not_a_list = std::tuple<int, int*, int&>; // not a specialization of metal::list

See Also

metal::list, metal::is_list, metal::as_list


A Pair is a couple of Values.


A Pair is any List whose size is 2.



using not_a_pair = std::pair<int, unsigned>; // not a List

See Also

metal::pair, metal::is_pair, metal::as_pair


A Map is a collection of unique Values, each of which associated with another Value.


A Map is a List of Pairs, whose first elements are all distinct, that is

[[k0, v0], ..., [kn, vn]]; ki != kj for all i, j in {0, n} and i != j


using m = metal::list<>; // an empty map


using not_a_map = metal::list< // repeated keys
using not_a_map = metal::list< // not a list of pairs
metal::list<int, int&>

See Also

metal::map, metal::is_map, metal::as_map


In the following examples, IS_SAME(X, Y) is just a terser shorthand for static_assert(std::is_same<X, Y>{}, "").

Parsing Raw Literals

If you ever considered augmenting std::tuple, so that instead of the rather clunky std::get<N>()

static_assert(std::get<1>(std::tuple<int, char, double>{42, 'a', 2.5}) == 'a', "");

one could use the more expressive subscript operator [N]

static_assert(AugmentedTuple<int, char, double>{42, 'a', 2.5}[1] == 'a', "");

you might have come up with something like this

constexpr auto operator [](std::size_t i)
-> std::tuple_element_t<i, std::tuple<T...>>& {
return std::get<i>(*this);

only to realize the hard way that this is simply not valid C++14.

error: non-type template argument is not a constant expression

While the keyword constexpr tells the compiler the value returned by operator [] might be a compile time constant, it imposes no such constraint on its arguments, which may as well be unknown at compile-time. It might seem we are out of luck at this point, but let us not forget that long before C++ had constexpr variables, integral constants strictly known at compile time could be expressed with the help of non-type template parameters.

So how about refactoring operator [] to take an instance of metal::number and relying on template pattern matching to extract its non-type template argument?

template<typename... T>
struct AugmentedTuple :
using std::tuple<T...>::tuple;
template<metal::int_ i>
constexpr auto operator [](metal::number<i>)
return std::get<i>(*this);
static_assert(AugmentedTuple<int, char, double>{42, 'a', 2.5}[metal::number<1>{}] == 'a', "");

That looks promising, but then again metal::number<1>{} is even clunkier than std::get<1>(), we want something more expressive.

A custom literal operator that constructs Numbers out of integer literals could help reducing the verbosity

static_assert(AugmentedTuple<int, char, double>{42, 'a', 2.5}[1_c] == 'a', "");

but how is operator ""_c implemented?

It might be tempting to try something like this

constexpr auto operator ""_c(long long i)
return {};

but let us not forget the reason why we got this far down the road to begin with, recall we can't instantiate a template using a non-constexpr variable as argument!

All is not lost however, because we can still parse raw literals, in other words, we are in for some fun!

The Raw Literal Operator Template

Raw literal operator templates in C++ are defined as a nullary constexpr function templated over char...

template<char... cs>
constexpr auto operator ""_raw()
-> metal::numbers<cs...> {
return {};

where cs... are mapped to the exact characters that make up the literal, including the prefixes 0x and 0b

IS_SAME(decltype(371_raw), metal::numbers<'3', '7', '1'>);
IS_SAME(decltype(0x371_raw), metal::numbers<'0', 'x', '3', '7', '1'>);

as well as digit separators

IS_SAME(decltype(3'7'1_raw), metal::numbers<'3', '\'', '7', '\'', '1'>);

The operator ""_c

We start by defining the literal operator _c as a function that forwards the raw literal characters as a List of Numbers to parse_number and returns a default constructed object of whatever type it evaluates to, which is guaranteed to be a Number in this case.

template<char... cs>
constexpr auto operator ""_c()
-> metal::eval<parse_number<metal::numbers<cs...>>> {
return {};

Resolving the Radix

In its turn parse_number strips the prefix, if any, thus resolving the radix, then forwards the remaining characters to parse_digits, which is in charge of translating the raw characters to the numerical values they represent. The radix and digits are then forwarded to assemble_number, which adds up the individual digits according to the radix.

template<typename tokens>
struct parse_number {};
template<typename... tokens>
struct parse_number<metal::list<tokens...>> {
using type = assemble_number<metal::number<10>, parse_digits<metal::list<tokens...>>>;
template<typename... tokens>
struct parse_number<metal::list<metal::number<'0'>, tokens...>> {
using type = assemble_number<metal::number<8>, parse_digits<metal::list<tokens...>>>;
template<typename... tokens>
struct parse_number<metal::list<metal::number<'0'>, metal::number<'x'>, tokens...>> {
using type = assemble_number<metal::number<16>, parse_digits<metal::list<tokens...>>>;
template<typename... tokens>
struct parse_number<metal::list<metal::number<'0'>, metal::number<'X'>, tokens...>> {
using type = assemble_number<metal::number<16>, parse_digits<metal::list<tokens...>>>;
template<typename... tokens>
struct parse_number<metal::list<metal::number<'0'>, metal::number<'b'>, tokens...>> {
using type = assemble_number<metal::number<2>, parse_digits<metal::list<tokens...>>>;
template<typename... tokens>
struct parse_number<metal::list<metal::number<'0'>, metal::number<'B'>, tokens...>> {
using type = assemble_number<metal::number<2>, parse_digits<metal::list<tokens...>>>;

Notice how we are able to use template pattern matching and partial template specializations to extract all relevant information from the tokens.

Parsing Digits

Before translating characters to their corresponding numerical values, we need to get rid of all digit separators that may be in the way. To do that we'll use metal::remove, which takes a List l and a Value val and returns another List that contains every element in l and in the same order, except for those that are the same as val.

The remaining characters can then be individually parsed with the help of metal::transform, which takes a Lambda lbd and a List l and returns another List that contains the Values produced by the invocation of lbd for each element in l.

[lbd(l[0]), lbd(l[1]), ..., lbd(l[n-2]), lbd(l[n-1])]

Notice how characters are translated to their actual numerical representation.

Thus we have

Assembling Numbers

We now turn to assemble_number. It takes a List of digits and adds them up according to the radix, in other words

D0*radix^(n-1) + D1*radix^(n-2) + ... + D{n-2}*radix + D{n-1}

which can also be written recursively

((...((0*radix + D0)*radix + D1)*...)*radix + D{n-2})*radix + D{n-1}

This is the equivalent of left folding a List, or, in Metal parlance, metal::accumulate, after its run-time counterpart in the standard library.

using radix = metal::number<10>;
using digits = metal::numbers<3, 7, 1>;
template<typename x, typename y>
using lbd = metal::lambda<expr>;
IS_SAME(metal::accumulate<lbd, metal::number<0>, digits>, metal::number<371>);

Here we introduced a new Expression expr from which we created a Lambda, but we could also have chosen to use bind expressions instead.

using radix = metal::number<10>;
using digits = metal::numbers<3, 7, 1>;
using lbd = metal::bind<
IS_SAME(metal::accumulate<lbd, metal::number<0>, digits>, metal::number<371>);
If bind expressions look scary to you, don't panic, we will exercise Expression composition in our next example. Here it suffices to keep in mind that bind expressions return anonymous Lambdas, just like std::bind returns anonymous functions, and that metal::_1 and metal::_2 are the equivalents of std::placeholders.


template<typename radix, typename digits>
using assemble_number = metal::accumulate<
metal::bind<metal::lambda<metal::mul>, metal::always<radix>, metal::_1>,

Fun With operator ""_c

IS_SAME(decltype(01234567_c), metal::number<342391>); //octal
IS_SAME(decltype(123456789_c), metal::number<123456789>); //decimal
IS_SAME(decltype(0xABCDEF_c), metal::number<11259375>); //hexadecimal

It also works for very long binary literals.


And ignores digit separators too.

IS_SAME(decltype(1'2'3'4'5'6'7'8'9_c), metal::number<123456789>);

Church Booleans

Church Booleans refer to a mathematical framework used to express logical operation in the context of lambda notation, where they have an important theoretical significance. Of less practical importance in C++, even in the context of template metaprogramming, they will nevertheless help us acquaint with bind expressions in this toy example.

The boolean constants true_ and false_ are, by definition, Lambdas that return respectively the first and second argument with which they are invoked.

using true_ = metal::_1;
using false_ = metal::_2;

Now, using the fact that booleans are themselves Lambdas, it's not too hard to realize that invoking a boolean with arguments <false_, true> always yields its negation.

template<typename b>
IS_SAME(not_<true_>, false_);
IS_SAME(not_<false_>, true_);

However, to enable higher-order composition we really need not_ to be a Lambda , not an Expression . Granted one could easily define former in terms of the latter as metal::lambda<not_>, but that would defeat the whole purpose of this exercise, the idea is to use bind expressions directly.

Admittedly a little more verbose, but that saves us from introducing a new named alias template.

Using a similar technique, we can also define operators and_ and or_.

This exercise might me mind-boggling at first, but you'll get used to it soon enough.

Without further ado we present the logical operator xor.

Notice how we bind not_, which is only possible due to the fact it is a Lambda .

Automatic Test Cases Generation

Suppose you have a component you want to test, say an algorithm on sequences. Because it's a generic algorithm, it's able to work with any kind of containers and, as such, is specialized for different categories of iterators so that it always delivers optimal performance. Moreover, to provide exception safety guarantees without compromising on execution time or memory consumption, it must also specialize on noexcept properties of the elements, particularly of their move constructors. Finally, it's conceivable that such a generic algorithm could also take advantage of other properties, such as whether elements can be ordered or not.

As such a complex implementation, it ought to be thoroughly tested for all relevant combinations of iterator categories and interesting properties implemented by the contained elements. Furthermore, it's crucial that all corner cases are covered, such as empty sequences to name the most obvious, so, before we even start to get fancy with our test cases, we already have three dimensions that vary independently, namely

  1. Iterator category;
  2. Element properties;
  3. Size of the sequence.

How can we possibly implement this testing suite?

First of all, it's a good idea to subdivide each test case into three steps: the set-up phase constructs the particular sequence to be tested, the execution phase runs our algorithm and the tear-down phase releases the resources. This way we can leverage on RAII and reduce the boilerplate to a minimum.

template<typename Container, typename Type, typename Size>
struct test_case {
test_case() : sequence(Size{}) { /* set-up */ }
~test_case() { /* tear-down */ }
void operator ()() { /* algorithm(this->sequence) */ }

That's a good start, but in order to run all test cases we still have to manually instantiate each and every combination of iterator categories, element type and sequence size. That is, if we want to test for M different categories of iterators, N sets of interesting properties that could be provided by elements and O different sizes of containers, we end up with M*N*O distinct instantiations to maintain! That's too troublesome and prone to error, there ought to be a way to generate all test cases automatically.

Fortunately Metal can do just that for us. With the help of metal::cartesian, we can generate every test case automatically, which we can then run by instantiating a single driver class. It's actually very simple.

template<typename... Cases>
struct test_cases : Cases... {
void operator()() {
// call operator () on every base class
void(std::initializer_list<int>{(static_cast<Cases*>(this)->operator()(), 0)...});
template<typename Containers, typename Types, typename Sizes>
auto generate_test_cases()
> {
return {};
using containers = metal::list<
using types = metal::list<A, /* ..., */ Z>;
using sizes = metal::numbers<0, /* ..., */ MAX_SIZE>;
void test() {
auto cases = generate_test_cases<containers, types, sizes>(); // generate all test cases
cases(); // run

And that was it.

To verify that we are in fact generating all possible combinations of test cases under the hood, let's inspect the return type of generate_test_cases

decltype(generate_test_cases<containers, types, sizes>()),
test_case<metal::lambda<std::forward_list>, A, metal::number<MIN_SIZE>>,
test_case<metal::lambda<std::list>, A, metal::number<MIN_SIZE>>,
test_case<metal::lambda<std::vector>, A, metal::number<MIN_SIZE>>,
/* ... */
test_case<metal::lambda<std::forward_list>, Z, metal::number<MIN_SIZE>>,
test_case<metal::lambda<std::list>, Z, metal::number<MIN_SIZE>>,
test_case<metal::lambda<std::vector>, Z, metal::number<MIN_SIZE>>,
/* ... */
test_case<metal::lambda<std::forward_list>, A, metal::number<MAX_SIZE>>,
test_case<metal::lambda<std::list>, A, metal::number<MAX_SIZE>>,
test_case<metal::lambda<std::vector>, A, metal::number<MAX_SIZE>>,
/* ... */
test_case<metal::lambda<std::forward_list>, Z, metal::number<MAX_SIZE>>,
test_case<metal::lambda<std::list>, Z, metal::number<MAX_SIZE>>,
test_case<metal::lambda<std::vector>, Z, metal::number<MAX_SIZE>>

A Word on SFINAE-Friendliness

An Expression is said to be SFINAE-friendly when it is carefully designed so as never to prevent the SFINAE rule to be triggered. In general, such Expressions may only trigger template substitution errors at the point of declaration of a type, which includes the instantiation of alias templates, default template arguments and the signature of function templates. SFINAE-friendly Expressions are exceedingly powerful, because they may be used to drive overload resolution, think std::enable_if on steroids. For this reason, all Expressions in Metal are guaranteed to be strictly SFINAE-friendly.

Conversely, a SFINAE-unfriendly Expression produces so called hard errors, which require the compilation to halt immediately. Examples of hard errors are failed static_assert'ions or template substitution errors at the point of definition of a class or function template. SFINAE-unfriendly Expressions are very inconvenient, because they force compilation to halt when they are not selected by overload resolution, thereby hindering the usage of the entire overloaded set.


To illustrate how useful SFINAE-friendliness can be, suppose we need a factory function make_array that takes an arbitrary number of arguments and returns a std::array. Because arrays are homogeneous collections, we need the common type of all its arguments, that is, the type to which every argument can be converted to.

The base case is straightforward.

template<typename... Xs,
typename R = std::array<std::common_type_t<Xs...>, sizeof...(Xs)>
constexpr R make_array(Xs&&... xs) {
return R{{std::forward<Xs>(xs)...}};
IS_SAME(decltype(make_array(42, 42L, 42LL)), std::array<long long, 3>);

Now suppose we need an array of tuples

using namespace std::chrono;
using namespace std::literals::chrono_literals;
using namespace std::literals::complex_literals;
auto tup1 = std::make_tuple(42ns, 0x42, 42.f);
auto tup2 = std::make_tuple(42us, 042L, 42.L);
auto tup3 = std::make_tuple(42ms, 42LL, 42.i);
auto array_of_tuples = make_array(tup1, tup2, tup3);

error: no matching function for call to 'make_array'

Even though the common tuple is really just a tuple of common types, std::common_type_t is unable to find it in general. That means we need to overload make_array and handle the array of tuples case.

make_array of tuples

The idea is to define a metafunction that computes the common tuple from a set of tuples and then use it to overload our factory function.

This sounds like a use-case for Boost.Hana, let's try it.

template<typename... xs>
using hana_common_tuple_t = typename decltype(
boost::hana::zip_with(boost::hana::decltype_, std::declval<xs>())...
template<typename... Xs,
typename R = std::array<std::common_type_t<Xs...>, sizeof...(Xs)>
constexpr R hana_make_array(Xs&&... xs) {
return R{{std::forward<Xs>(xs)...}};
template<typename Head, typename... Tail,
typename R = std::array<hana_common_tuple_t<std::decay_t<Head>, std::decay_t<Tail>...>, 1 + sizeof...(Tail)>
constexpr R hana_make_array(Head&& head, Tail&&... tail) {
return R{{std::forward<Head>(head), std::forward<Tail>(tail)...}};

It works as expected for std::tuples

auto array_of_tuples = hana_make_array(tup1, tup2, tup3);
IS_SAME(decltype(array_of_tuples), std::array<std::tuple<nanoseconds, long long, std::complex<double>>, 3>);

but we get a compilation error as soon as we try to make an array of anything that is not a Boost.Hana Sequence, even though the first overload remains available and would be a perfect match otherwise.

IS_SAME(decltype(hana_make_array(42, 42L, 42LL)), std::array<long long, 3>);

error: static_assert failed "hana::zip_with(f, xs, ys...) requires 'xs' and 'ys...' to be Sequences"

make_array of tuples done right

The reason why Boost.Hana can't help us overload make_array is the fact that it doesn't provide any SFINAE-friendliness guarantees, which essentially means that it cannot be used effectively to control overload resolution. Metal, on the other hand, was carefully designed to never trigger hard errors but rather substitution failures, which makes it able to select candidates from an overloaded set by means of the SFINAE rule.

Let's try the same approach using Metal.

template<typename... xs>
using common_tuple_t = metal::apply<
std::common_type_t<metal::lambda<std::tuple>, metal::as_lambda<xs>...>,
template<typename... Xs,
typename R = std::array<std::common_type_t<Xs...>, sizeof...(Xs)>
constexpr R make_array(Xs&&... xs) {
return R{{std::forward<Xs>(xs)...}};
template<typename Head, typename... Tail,
typename R = std::array<common_tuple_t<std::decay_t<Head>, std::decay_t<Tail>...>, 1 + sizeof...(Tail)>
constexpr R make_array(Head&& head, Tail&&... tail) {
return R{{std::forward<Head>(head), std::forward<Tail>(tail)...}};

This time it works not only for std::tuple's

auto array_of_tuples = make_array(tup1, tup2, tup3);
IS_SAME(decltype(array_of_tuples), std::array<std::tuple<nanoseconds, long long, std::complex<double>>, 3>);

but also for numerical values

IS_SAME(decltype(make_array(42, 42L, 42LL)), std::array<long long, 3>);

Again, this only works as expected because of the strict SFINAE-friendliness guarantees provided by Metal.

Migrating from Boost.MPL

Metal was heavily influenced by Boost.MPL and at a quick glance they even look very similar, however, because Metal leverages modern language features that were not available at the time Boost.MPL was developed, there are fundamental differences between these libraries that one must keep in mind.


The representation of metafunctions has been completely redesigned in Metal. Instead of expressing them as class templates that define a nested typename type, Metal assumes metafunctions to be templates, usually but not necessarily alias, that evaluate directly to the result type.

That is, instead of something like this

struct na {};
template<typename arg_1 = na, typename arg_2 = na, /*...*/ typename arg_n = na>
struct metafunction
typedef /*...*/ type;
template<typename arg_1, typename arg_2, /*...*/ typename arg_n>
struct compound_metafunction
typedef typename metafunction<
typename metafunction<arg_1>::type,
typename metafunction<arg_2>::type,
typename metafunction<arg_n>::type
>::type type;

you should simply write this

template<typename... args>
using metafunction = /*...*/;
template<typename... args>
using compound_metafunction = metafunction<metafunction<args>...>;

Notice that traditional lazy metafunctions are still valid Expressions in Metal, but keep in mind that their nested typename type is never implicitly evaluated.

IS_SAME(metal::invoke<metal::lambda<std::add_pointer>, void>, std::add_pointer<void>);
IS_SAME(metal::invoke<metal::lambda<std::common_type>, void*, char[]>, std::common_type<void*, char[]>);

Don't worry, you can still use lazy metafunctions with Metal just fine, you just need to use the adaptor metal::lazy instead of metal::lambda.

IS_SAME(metal::invoke<metal::lazy<std::common_type>, void*, char[]>, void*);

Additionally, you can also explicitly evaluate lazy values using metal::eval.

IS_SAME(metal::eval<std::add_pointer<void>>, void*);
IS_SAME(metal::eval<std::common_type<void*, char[]>>, void*);

Type Traits

Traditionally, type traits are represented as class templates that define a nested integral constant value. Metal on the other hand defines a type trait as any Expression that returns a Number, but that's not to say you can't use good old type traits with Metal just fine, on the contrary, in a similar fashion to lazy metafunctions, all you have to do is use the trait adaptor metal::trait instead of metal::lambda.

Alternatively, you can also explicitly convert traits to Numbers using metal::as_number.

IS_SAME(metal::as_number<std::is_pointer<void*>>, metal::true_);
IS_SAME(metal::as_number<std::is_pointer<void()>>, metal::false_);
IS_SAME(metal::as_number<std::is_convertible<char[], void*>>, metal::true_);
IS_SAME(metal::as_number<std::is_convertible<void*, char[]>>, metal::false_);

Metafunction Classes

The concept of Metafunction Class became obsolete with Metal. Instead of defining first-class metafunctions like you would with Boost.MPL

struct first_class_metafunction
template<typename arg_1, typename arg_2, /*...*/ typename arg_n>
struct apply {
typedef /*...*/ type;

you just have to wrap regular metafunctions using metal::lambda instead

template<typename... args>
using metafunction = /*...*/;
using first_class_metafunction = metal::lambda<metafunction>;

It's that simple.


Even though Metal cannot interoperate with natively with Boost.MPL types, it is always possible to translate Boost.MPL concepts to their equivalents in Metal, that is Sequences to Lists, Metafunction Classes to Lambdas and Integral Constants to Numbers, but it's not always trivial to do so in a portable way. For this reason, Metal provides a helper metal::from_mpl, which takes any type that follows Boost.MPL conventions and returns the semantic equivalent in Metal. All you have to do is include metal/external/mpl.hpp to make it available.

#include <boost/mpl/int.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/integral_c.hpp>
IS_SAME(metal::from_mpl<boost::mpl::int_<42>>, metal::number<42>);
IS_SAME(metal::from_mpl<boost::mpl::integral_c<char, 'a'>>, metal::number<'a'>);
#include <boost/mpl/list.hpp>
#include <boost/mpl/list_c.hpp>
using vals_list = boost::mpl::list<int, float, char, bool>;
using nums_list = boost::mpl::list_c<int, 1, 2, 3, 5, 7, 12, 19, 31>;
#include <boost/mpl/vector.hpp>
#include <boost/mpl/vector_c.hpp>
using vals_vec = boost::mpl::vector<int, float, char, bool>;
using nums_vec = boost::mpl::vector_c<int, 1, 2, 3, 5, 7, 12, 19, 31>;
#include <boost/mpl/map.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/integral_c.hpp>
using sizes_map = boost::mpl::map<
boost::mpl::pair<int, boost::mpl::integral_c<std::size_t, sizeof(int)>>,
boost::mpl::pair<float, boost::mpl::integral_c<std::size_t, sizeof(float)>>,
boost::mpl::pair<char, boost::mpl::integral_c<std::size_t, sizeof(char)>>,
boost::mpl::pair<bool, boost::mpl::integral_c<std::size_t, sizeof(bool)>>
metal::pair<int, metal::number<sizeof(int)>>,
metal::pair<float, metal::number<sizeof(float)>>,
metal::pair<char, metal::number<sizeof(char)>>,
metal::pair<bool, metal::number<sizeof(bool)>>
#include <boost/mpl/int.hpp>
#include <boost/mpl/lambda.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/vector.hpp>
using cons = boost::mpl::lambda<
boost::mpl::push_back<boost::mpl::_1, boost::mpl::_2>
using nil = boost::mpl::vector<>;
using one = boost::mpl::int_<1>;

Getting Started


There are a few ways to get Metal, the easiest might be to simply download the latest release as a compressed package.

If you have git installed and would rather have the latest stable Metal, you may consider cloning branch master from GitHub.

git clone

Likewise, the bleeding edge development version can be obtained by cloning branch develop instead.

git clone --branch=develop

Install (optional)

Metal may optionally be installed system-wide to ease integration with external projects. If you'd rather use Metal locally, you can skip to the next section.

Make sure to have CMake v3.4 or newer installed on your system, then, from within an empty directory, issue the following commands.

cmake /path/to/Metal
cmake --build . --target install

At this point Metal's include tree will be installed in /usr/local/include on Posix systems and C:\Program Files\Metal\include on Windows.


If you chose to install Metal system-wide, you just have to make sure the installation prefix is looked up by your compiler.

Using CMake it suffices to add the following to your CMakeLists.txt.

find_package(Metal REQUIRED)

To use your local copy of Metal instead, just add its include/ sub-directory to the include search paths of your project and you are all set.

Supported Compilers

The following compilers are tested in continuous integration using Travis CI and Appveyor CI.

Compiler Version
GCC ≥ 4.7
Clang ≥ 3.4
Xcode ≥ 6.4
Visual Studio ≥ 14 (2015)
MinGW ≥ 5

Project Organization

Header files are divided in modules named after each concept. Modules are organized in directories and contain algorithms that operate on models of that concept. The complete hierarchy of modules and headers is available on Metal's repository on GitHub.

You may simply include metal.hpp and get access to all that Metal has to offer without concerning yourself with which specific headers to include.

Frequently Asked Questions

What are some advantages of Metal with respect to Boost.MPL?

The most apparent advantage of Metal with respect to Boost.MPL is the fact Metal Lists and Maps can easily exceed the hundreds and even thousands of elements with little impact to the compiler performance, whereas Boost.MPL Sequences, such as mpl::vector and mpl::map, are hard-limited to at most a couple dozen elements and even then at much longer compilation times and higher memory consumption than Metal. Another obvious improvement in Metal is the much terser syntax made possible by alias templates, which were not available at the time Boost.MPL was developed.

Visit for up to date benchmarks that compare Metal against Boost.MPL and other notable metaprogramming libraries. For a brief discussion about fundamental design differences between Boost.MPL and Metal, refer to Migrating from Boost.MPL .

What are some advantages of Metal with respect to Boost.Hana?

As a tool specifically designed for type level programming, Metal is able to provide stronger guarantees and much faster compilation times than Boost.Hana when used for similar purposes. In fact, Metal guarantees SFINAE-friendliness, whereas Boost.Hana does not. Check out A Word on SFINAE-Friendliness for a real world example of the limitations of Boost.Hana with this respect.

Moreover, since Metal concepts are defined by their type signatures, it is always safe to use template pattern matching on them to partially specialize class templates or overload function templates. In contrast, the types of most Boost.Hana objects are left unspecified.

Why isn't std::integral_constant a Number in general?

Numbers are defined as a specific specialization of std::integral_constants, whose binary representation is fixed to metal::int_, an implementation-defined integral type. This design choice stems from the fact two Values compare equal if and only if they have the same type signature. As Values themselves, Numbers are also subject to this requirement, thus had Numbers been defined as a numerical value plus its binary representation, would two Numbers only compare equal if they had both the same numerical value and the same binary representation. This is unreasonable in the context of metaprogramming, where the binary representation of numerical values is entirely irrelevant.