
on Sun Apr 01 2012, Pyry Jahkola <pyry.jahkola-AT-iki.fi> wrote:
Hi,
On 2012-04-01 02:12:24 +0000, Dave Abrahams said:
I am on the C++Now 2012 schedule giving a talk on metaprogramming in C++11 (...) I'm sure y'all have come up with many neat tricks and techniques. If you'd care to share them here, that would be much appreciated.
Below are a few tricks I've used with varying success with the trunk version of clang and libc++ with C++11 compilation turned on. Some might be obvious, some not, but at least they are some improvement over their C++03 counterparts.
The text turned out to be quite lengthy, so here's a link to a syntax-colored and more reader-friendly Gist: https://gist.github.com/2275320
See you all in Aspen! /Pyry Jahkola
Looking forward to it!
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; };
Do the uses of constexpr here add anything w.r.t. plain const?
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
Cute.
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>
Interesting formulation. I used: template <class ...Ss> struct append_; template <class ...T1s> struct append_<vector<T1s...> > : vector<T1s...> {}; template <class ...T1s, class ...T2s> struct append_<vector<T1s...>, vector<T2s...> > : vector<T1s...,T2s...> {}; template <class ...T1s, class ...Ss> struct append_<vector<T1s...>, Ss...> : append_<vector<T1s...>, typename append_<Ss...>::type> {}; Does the nested class template arrangement have any advantages?
* * *
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)
Totally. I have used // RETURNS() is used to avoid writing boilerplate "->decltype(x) { return x; }" phrases. // // USAGE: auto function(<arguments>) RETURNS(<some-expression>); // // Note: we end with a unique typedef so the function can be followed // by a semicolon. If we omit the semicolon, editors get confused and // think we haven't completed the function declaration. #define RETURNS(...) -> decltype(__VA_ARGS__) { return (__VA_ARGS__); } typedef int RETURNS_CAT(RETURNS_, __LINE__) // Standard PP concatenation formula #define RETURNS_CAT_0(x, y) x ## y #define RETURNS_CAT(x, y) RETURNS_CAT_0(x,y) Probably it's a good idea to incorporate noexcept: #define RETURNS(...) noexcept(noexcept(decltype(__VA_ARGS__)(std::move(__VA_ARGS__)))) -> decltype(__VA_ARGS__) { return (__VA_ARGS__); } typedef int RETURNS_CAT(RETURNS_, __LINE__)
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_ } };
Heh, try (*this) instead of mul_(). That works until you try to incorporate noexcept as suggested above (at least on GCC 4.7). After that you have to do something like this: --8<---------------cut here---------------start------------->8--- struct mul_ { int operator()() const { return 1; } template <typename A> A operator()(A const & a) const noexcept(noexcept(A(std::move(a)))) { return a; } // ****** workaround ****** static mul_ get() noexcept { return mul_(); } template <typename A, typename B, typename... C> auto operator()(A const & a, B const & b, C const &... c) const RETURNS(get()(a * b, c...)); }; --8<---------------cut here---------------end--------------->8--- very annoying.
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
Isn't it a bit slicker to do this by creating an argument pack of integers and expanding that with get<Is>(t)... ? --8<---------------cut here---------------start------------->8--- template <class T, T I, class S> struct cons_c; template <template <class T, T...> class S, class T, T I, T ...Is> struct cons_c<T, I, S<T, Is...> > : S<T,I,Is...> {}; template <class T, T ...Is> struct vector_c { typedef vector_c type; }; template <std::size_t N> struct count_ : cons_c<std::size_t, N-1, typename count_<N-1>::type> {}; template <> struct count_<0> : vector_c<std::size_t> {}; template <std::size_t N> using count = typename count_<N>::type; #include <tuple> template <typename F, typename Tuple , std::size_t ...Is> auto apply_tuple(F f, Tuple const & t, vector_c<std::size_t, Is...>) RETURNS(f(std::get<Is>(t)...)); template <typename F, typename ...T> auto apply_tuple(F f, std::tuple<T...> const & t) RETURNS(apply_tuple(f, t, count<sizeof...(T)>())); --8<---------------cut here---------------end--------------->8--- -- Dave Abrahams BoostPro Computing http://www.boostpro.com