Created
April 1, 2012 13:27
-
-
Save pyrtsa/2275320 to your computer and use it in GitHub Desktop.
C++11 metaprogramming
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Here are a few tricks I've used with the trunk versions of clang and libc++ | |
// with C++11 compilation turned on. Some might be obvious, some not, but at | |
// least they are some kind of improvement over their C++03 counterparts. | |
// | |
// Public domain. | |
// ============================================================================= | |
// 1) Using variadic class templates recursively, like in the definitions for | |
// "add<T...>" here: | |
#include <type_traits> | |
// A few convenience aliases first | |
template <typename T, T N> using ic = std::integral_constant<T, N>; | |
template <int N> using int_ = std::integral_constant<int, N>; | |
// Sum any number of integral constants: | |
template <typename... Args> struct add; | |
template <> | |
struct add<> | |
: ic<int, 0> {}; | |
template <typename A> | |
struct add<A> | |
: ic<decltype(+A::value), A::value> {}; | |
template <typename A, typename B, typename... More> | |
struct add<A, B, More...> | |
: add<ic<decltype(A::value + B::value), | |
A::value + B::value>, More...> {}; | |
// --- example ------------------------------------------------------------- | |
add<>::value; // 0 | |
add<int_<1>>::value; // 1 | |
add<int_<1>, int_<2>>::value; // 3 | |
add<int_<1>, int_<2>, int_<3>>::value; // 6 | |
add<int_<1>, int_<2>, int_<3>, int_<4>>::value; // 10 | |
// etc. | |
// ============================================================================= | |
// 2a) With decltype, the "sizeof(yes_type)" trick is no longer needed for | |
// implementing traits. This one tests whether there is a type T::type | |
// defined: | |
using std::true_type; | |
using std::false_type; | |
namespace detail { | |
template <typename T, typename Type=typename T::type> | |
struct has_type_helper; | |
template <typename T> true_type has_type_test(has_type_helper<T> *); | |
template <typename T> false_type has_type_test(...); | |
} | |
template <typename T> | |
struct has_type : decltype(detail::has_type_test<T>(nullptr)) {}; | |
// --- example ------------------------------------------------------------- | |
has_type<int>::value; // false | |
has_type<std::is_integral<int>>::value; // true, said type is "bool" | |
has_type<std::integral_constant<int,1>>::value; // true, said type is "int" | |
// ----------------------------------------------------------------------------- | |
// 2b) This trait tests whether T is an integral constant: | |
namespace detail { | |
template <typename T, decltype(T::value)> | |
struct integral_constant_helper; | |
template <typename T> true_type integral_constant_test( | |
integral_constant_helper<T,T::value> *); | |
template <typename T> false_type integral_constant_test(...); | |
} | |
template <typename T> | |
struct is_integral_constant | |
: decltype(detail::integral_constant_test<T>(nullptr)) {}; | |
// --- example ------------------------------------------------------------- | |
is_integral_constant<int>::value; // false | |
is_integral_constant<std::is_integral<int>>::value; // true | |
is_integral_constant<std::integral_constant<int,1>>::value; // true | |
// ============================================================================= | |
// 3) Selection of the first matching type from a list of cases (or "pattern | |
// matching", if you will): | |
template <typename... When> struct match; | |
template <> struct match<> { static constexpr bool value = false; }; | |
template <typename When, typename... More> struct match<When, More...> | |
: std::conditional<When::value, When, match<More...>>::type {}; | |
// 'match' is meant to be used together with 'when', 'otherwise' and | |
// friends: | |
template <bool Cond, typename Then=void> struct when_c; | |
template <typename Then> struct when_c<true, Then> { | |
typedef Then type; | |
static constexpr bool value = true; | |
}; | |
template <typename Then> struct when_c<false, Then> { | |
static constexpr bool value = false; | |
}; | |
template <bool Cond, typename Then=void> | |
struct when_not_c : when_c<!Cond, Then> {}; | |
template <typename Cond, typename Then=void> | |
struct when : when_c<Cond::value, Then> {}; | |
template <typename Cond, typename Then=void> | |
struct when_not : when_not_c<Cond::value, Then> {}; | |
template <typename Then> struct otherwise { | |
typedef Then type; | |
static constexpr bool value = true; | |
}; | |
// --- example ------------------------------------------------------------- | |
struct fizz {}; | |
struct buzz {}; | |
struct fizzbuzz {}; | |
template <int N> struct game : match< | |
when_c<N % 3 == 0 && N & 5 == 0, fizzbuzz>, | |
when_c<N % 3 == 0, fizz>, | |
when_c<N % 5 == 0, buzz>, | |
otherwise< int_c<N>> | |
> {}; | |
game<1>::type; // int_<1> | |
game<2>::type; // int_<2> | |
game<3>::type; // fizz | |
game<4>::type; // int_<4> | |
game<5>::type; // buzz | |
game<6>::type; // fizz | |
game<7>::type; // int_<7> | |
game<8>::type; // int_<8> | |
game<9>::type; // fizz | |
game<10>::type; // buzz | |
game<11>::type; // int_<11> | |
game<12>::type; // fizz | |
game<13>::type; // int_<13> | |
game<14>::type; // int_<14> | |
game<15>::type; // fizzbuzz | |
game<16>::type; // int_<16> | |
// ============================================================================= | |
// 4a) Variadic template template parameters. For instance, | |
// boost::mpl::quoteN<...> can be reimplemented with just: | |
template <template <typename...> class F> struct quote { | |
template <typename... Args> struct apply : F<Args...> {}; | |
}; | |
// ----------------------------------------------------------------------------- | |
// 4b) Here's another use for variadic template template parameters. Of course, | |
// the standard library offers std::tuple_size<T> for getting the number of | |
// elements in a tuple. But that metafunction cannot be used for any other | |
// tuple-like class. Suppose we defined boost::mpl::vector like: | |
template <typename... T> struct vector {}; | |
// By using a variadic template template, we can define a metafunction which | |
// works equally for both std::tuple<T...> as well as vector<T...>: | |
template <typename T> struct size {}; // (no size defined by default) | |
template <template <typename...> class C, typename... T> | |
struct size<C<T...>> : ic<std::size_t, sizeof...(T)> {}; | |
template <typename T> struct size<T &> : size<T> {}; | |
template <typename T> struct size<T &&> : size<T> {}; | |
template <typename T> struct size<T const> : size<T> {}; | |
template <typename T> struct size<T volatile> : size<T> {}; | |
template <typename T> struct size<T const volatile> : size<T> {}; | |
// --- example ------------------------------------------------------------- | |
size<tuple<int, int> &>::value; // 2 | |
size<vector<int, int, int>>::value; // 3 | |
size<vector<> const &>::value; // 0 | |
// ============================================================================= | |
// 5) Using nested variadic templates to get many template parameter packs to | |
// play with: | |
namespace detail { | |
template <typename A> struct con; | |
template <typename... T> struct con<vector<T...>> { | |
template <typename B> struct cat; | |
template <typename... U> struct cat<vector<U...>> { | |
typedef vector<T..., U...> type; | |
}; | |
}; | |
} | |
template <typename A, typename B> | |
struct concat : detail::con<A>::template cat<B> {}; | |
// --- example ------------------------------------------------------------- | |
struct a; struct b; struct c; struct d; struct e; | |
concat<vector<a, b>, vector<c, d, e>>::type; // vector<a, b, c, d, e> | |
// ============================================================================= | |
// 6) Defining function result and result type at once. | |
#define RETURNS(...) decltype((__VA_ARGS__)) { return (__VA_ARGS__); } | |
// --- example ------------------------------------------------------------- | |
template <typename A, typename B> | |
auto plus(A const & a, B const & b) -> RETURNS(a + b) | |
// It can't be used with recursive definitions like here, though: | |
struct mul_ { | |
int operator()() const { return 1; } | |
template <typename A> | |
A operator()(A const & a) const { return a; } | |
// template <typename A, typename B, typename... C> | |
// auto operator()(A const & a, B const & b, C const &... c) const -> | |
// RETURNS(mul_()(a * b, c...)) | |
// --> Error: invalid use of incomplete type mul_ | |
// std::declval helps, but duplicates the multiplication part: | |
template <typename A, typename B, typename... C> | |
auto operator()(A const & a, B const & b, C const &... c) const -> | |
decltype(std::declval<mul_>()(a * b, c...)) { | |
return mul_()(a * b, c...); | |
} | |
}; | |
constexpr mul_ mul = {}; // global function object | |
// --- example ------------------------------------------------------------- | |
mul(); // 1 | |
mul(10); // 10 | |
mul(10, -20, 30.0); // -6000.0 | |
// ============================================================================= | |
// 7) Counted template recursion. The function "apply_tuple(f, t)" calls the | |
// function (function object) "f" with the elements of the tuple "t" as | |
// arguments. (To simplify things a bit, I omitted the perfect forwarding | |
// support in this example.) | |
// | |
// The count is tracked with a total number of iterations N, and the running | |
// index I. R is the precalculated result type. | |
namespace detail { | |
template <typename R, std::size_t N, std::size_t I=0> | |
struct apply_tuple { | |
template <typename F, typename T, typename... Args> | |
R operator()(F f, T const & t, Args const &... args) const { | |
typedef apply_tuple<R, N, I + 1> next; | |
return next()(f, t, args..., std::get<I>(t)); | |
} | |
}; | |
template <typename R, std::size_t N> struct apply_tuple<R, N, N> { | |
template <typename F, typename T, typename... Args> | |
R operator()(F f, T const &, Args const &... args) const { | |
return f(args...); | |
} | |
}; | |
} | |
template <typename F, typename... T> | |
decltype(std::declval<F>()(std::declval<T const &>()...)) | |
apply_tuple(F f, std::tuple<T...> const & t) { | |
typedef decltype(std::declval<F>()(std::declval<T const &>()...)) | |
result; | |
return detail::apply_tuple<result, sizeof...(T)>()(f, t); | |
} | |
// --- example ------------------------------------------------------------- | |
int f(int a, int b) { return a + b; } | |
apply_tuple(f, std::make_tuple(10, 20)); // 30 | |
auto t = std::make_tuple(10, -20, 30.0); | |
apply_tuple(mul, t); // -6000.0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment