Meta  0.1
A tiny metaprogramming library
User Manual

Meta is a C++11 tiny metaprogramming library developed by Eric Niebler to facilitate the computation and manipulation of types and lists of types (aka, variadic parameter packs).

It is released under the Boost Software License and it is header only; that is, to compile with meta you just have to:

#include <meta/meta.hpp>

The documentation of Meta is currently scarce. The best resources are the Reference and the Examples.

As a motivation and introduction to the library you can read Eric's original blog post, but please keep in mind that the library has evolved quite a bit since then.


Quick Start

TODO show some simple uses. Make sure we show what Meta is good for before diving into terminology and esoteric concepts.

Tutorial

The tutorial begins with a brief introduction to traits, aliases, and callables. Then it moves to trait composition and currying. Finally, it covers type list algorithms and algorithms for working on integer sequences.

TODO This feels backwards. Algorithms come first. Everything else is in support of them.

Traits

Traits are class templates that have a nested type alias called (by convention) type. For example,

template <typename... Args>
struct t
{
using type = void;
};
using result = typename t<int, double>::type;
static_assert(std::is_same<result, void>{}, "");

is a trait taking an arbitrary number of types that always "returns" void. There are many familiar examples of traits in the Standard Library; std::remove_reference and std::is_void to name two.

Aliases

An alias is a synonym for a type. C++11 introduced alias templates, which are names that refer to a family of types. Alias templates simplify template syntax and smooth out interface differences. Below is an example of an alias template:

template <typename... Args>
using t_t = typename t<Args...>::type;
using result = t_t<int, double>;
static_assert(std::is_same<result, void>{}, "");

Notice how t_t<int, double> becomes a synonym for void. The C++14 standard library provides _t alias templates for all the traits in the standard library.

Meta provides meta::_t<T>, which evaluates the trait T by aliasing the nested T::type alias. This allows us to alias the nested type of a trait as follows:

template <typename... Args>
using t2_t = meta::_t<t<Args...>>;
using result = t2_t<int, double>;
static_assert(std::is_same<result, void>{}, "");
Note
Alias templates have primacy in Meta. This is different from other metaprogramming libraries you may be familiar with, which make traits (aka metafunctions) the prime abstraction. The rest of this guide uses the term "alias" to mean "alias template".

Callables

A Callable is a kind of alias suitable for higher-order metaprogramming. It is a class (not a template!) with a nested alias called (by convention) invoke:

struct ac
{
template <typename... Args>
using invoke = void;
};

All of the algorithms that take "functions" as arguments expect Callables instead of raw aliases. Meta provides the meta::invoke<F, Args...> alias that evaluates the Callable F with the arguments Args:

static_assert(std::is_same<result, void>{}, "");

To turn an ordinary alias into a Callable Meta provides the meta::quote<F> trait:

using t_callable0 = meta::quote<t>;
static_assert(std::is_same<result0, t<int, double>>{}, "");
static_assert(std::is_same<meta::_t<result0>, void>{}, "");
using t_callable1 = meta::quote<t_t>;
static_assert(std::is_same<result1, void>{}, "");

Note that in the first case we create a Callable that evaluates to the trait itself, while in the second case we create a Callable that evaluates to the nested type of the trait.

When "quoting" a trait, it is often desirable for the resulting Callable to refer to the nested type instead of the trait itself. For that we can use meta::quote_trait. Consider:

static_assert(std::is_same<result0, int *>{}, "");
#if __cplusplus > 201103L
using t_callable1 = meta::quote<std::add_pointer_t>;
static_assert(std::is_same<result1, int *>{}, "");
#endif

Notice that meta::quote<std::add_pointer_t> and meta::quote_trait<std::add_pointer> mean the same thing.

Note
You may wonder what advantage Callables have over alias templates. A Callable is a type that represents a computation. Much of Meta revolves around types and the computation of types. Sometimes it's desirable to compute a computation, or to use a computation as an argument to another computation. In those cases, it's very handy for computations to themselves be types and not templates.

Composition

Multiple Callables can be composed into a single Callable using meta::compose<F0, F1, ..., FN>, which names a new Callable that performs F0(F1(...(FN(Args...)))):

template <class T>
template <class T>
template <class T>
static_assert(std::is_same<meta::invoke<t, unsigned>, int const &>{}, "");

Partial function application

You can turn a Callable expecting N arguments into a Callable expecting N-M arguments by binding M arguments to the front or the back of its argument list. You can use meta::bind_front and meta::bind_back for that. Below we create a Callable that tests whether a type is float by reusing the std::is_same trait:

static_assert(meta::invoke<is_float, float>{}, "");
static_assert(!meta::invoke<is_float, double>{}, "");
using is_float2 = meta::bind_back<meta::quote<std::is_same>, float>;
static_assert(meta::invoke<is_float2, float>{}, "");
static_assert(!meta::invoke<is_float2, double>{}, "");
Note
If std::is_same is a trait, why did we use meta::quote instead of meta::quote_trait? In this case, it makes no difference. In addition to being a trait, std::is_same<X, Y> inherits from std::integral_constant<bool, true-or-false> so we can construct an instance of std::is_same<X, Y> and test it in a constexr Boolean context.

Logical operations

The traits meta::if_, meta::and_, meta::or_, and meta::not_ cover the basic logical operations with types:

static_assert(!t0{}, "");
using t1 = meta::and_<meta::bool_<true>, meta::bool_<false>, meta::bool_<true>>;
static_assert(!t1{}, "");
using t2 = meta::or_<meta::bool_<true>, meta::bool_<false>, meta::bool_<true>>;
static_assert(t2{}, "");
using t3 = meta::not_<t1>;
static_assert(t3{}, "");

Eager and lazy evaluation

TODO aliases are eager, meta::defer, meta::lazy namespace.

Lambdas

Lambda functions allow you to define Callables in place:

Type lists

A list of types Ts... can be stored in the type meta::list<Ts...>. It provides a O(1) static member function meta::list::size() that returns the size of the list.

static_assert(list::size() == 3, "");
static_assert(std::is_same<front, int>{}, "");
static_assert(std::is_same<back, float>{}, "");
using at_1 = meta::at_c<list, 1>;
static_assert(std::is_same<at_1, double>{}, "");
using index_double = meta::find_index<list, double>;
static_assert(index_double{} == 1, "");
using index_char = meta::find_index<list, char>;
static_assert(index_char{} == meta::npos(), "");
static_assert(!meta::empty<list>{}, "");

As you can see, the meta::front<List>, meta::back<List>, and meta::at_c<List, std::size_t> aliases provide access to the elements of the list. The meta::empty<List> alias is std::true_type if the list is empty. The meta::at<List, meta::size_t<N>> alias differs from meta::at_c in that it takes a meta::size_t<N> (std::integral_constant<std::size_t, N>) instead of an integer:

using i = meta::size_t<1>;
using at_1 = meta::at<list, i>;
static_assert(std::is_same<at_1, double>{}, "");

You can add and remove elements from a list by using the transformation algorithms:

static_assert(std::is_same<l2, meta::list<char, int, double, float>>{}, "");
using l3 = meta::pop_front<l2>; // equivalent to meta::drop<l2, 1>;
static_assert(std::is_same<l3, list>{}, "");
static_assert(std::is_same<l4, meta::list<int, double, float, char>>{}, "");
using l5 = meta::drop_c<l4, 3>;
static_assert(std::is_same<l5, meta::list<char>>{}, "");

You can concatenate, flatten, and zip multiple lists using meta::concat<Lists...>, meta::join<ListOfLists>, and meta::zip<ListOfLists>:

using list0 = meta::list<int, double>;
using list1 = meta::list<>;
using list2 = meta::list<float, char>;
using concatenated = meta::concat<list0, list1, list2>;
static_assert(std::is_same<concatenated, meta::list<int, double, float, char>>{}, "");
using list_of_lists = meta::list<list0, list1, list2>;
using flattened = meta::join<list_of_lists>;
static_assert(std::is_same<flattened, meta::list<int, double, float, char>>{}, "");
using list_of_lists_of_same_length = meta::list<list0, list2>;
static_assert(

TODO: meta::zip_with examples

Other typical operations on type lists include iteration, reductions, finding elements, removing duplicates:

using namespace meta::lazy;
using size_of_largest_type =
static_assert(size_of_largest_type{} == meta::sizeof_<long long>{}, "");
using largest_type =
static_assert(std::is_same<largest_type, long long>{}, "");
using first_type_larger_than_char =
static_assert(std::is_same<first_type_larger_than_char, int>{}, "");
using unique_types = meta::unique<l>;
static_assert(std::is_same<unique_types, meta::list<char, int, long, long long, float>>{}, "");

To convert other type sequences into a meta::list, the utility trait meta::as_list<Sequence> is provided. For example:

using t = std::tuple<int, double, float>;
using l = meta::as_list<t>;
static_assert(std::is_same<l, meta::list<int, double, float>>{}, "");
using il = meta::as_list<i>;
static_assert(std::is_same<il, meta::list<std::integral_constant<std::size_t, 0>,
std::integral_constant<std::size_t, 1>,
std::integral_constant<std::size_t, 2>>>{},
"");

To use meta with your own data types you can specialize the meta::extension::apply trait for your own data type. For example, to use meta with C++14 std::integer_sequence, you can:

Overview

This is a brief overview of the functionality in meta:

See the reference section for more details.