On 30 April 2014 15:03, Zach Laine wrote:
Here are the metaprogramming capabilities I needed for my Fusion-like data structures:
1) compile-time type traits, as above 2) simple compile-time computation, as above 3) purely compile-time iteration over every element of a single list of types 4) purely compile-time iteration over every pair of elements in two lists of types (for zip-like operations, e.g. elementwise matrix products) 5) runtime iteration over every element of a single tuple 6) runtime iteration over every pair of elements in two tuples (again, for zip-like operations)
For my purposes, operations performed at each iteration in 3 through 6 above may sometimes require the index of the iteration. Again, this is probably atypical.
1 is covered nicely by existing traits, and 2 is covered by ad hoc application-specific code (I don't see how a library helps here).
There are several solutions that work for at least one of 3-6:
- Compile-time foldl(); I did mine as constexpr, simply for readability. - Runtime foldl(). - Direct expansion of a template parameter pack; example:
template <typename MatrixLHS, typename MatrixRHS, std::size_t ...I> auto element_prod_impl ( MatrixLHS lhs, MatrixRHS rhs, std::index_sequence<I...> ) { return std::make_tuple( (tuple_access::get<I>(lhs) * tuple_access::get<I>(rhs))... ); }
(This produces the actual result of multiplying two matrices element-by-element (or at least the resulting matrix's internal tuple storage). I'm not really doing any metaprogramming here at all, and that's sort of the point.
I found your whole email very interesting, thanks for sharing your experience, but I wanted to comment on the point above. In some ways MPL is beautiful, for what it manages to do and the design and ideas present in it, but syntactically it is hideous and hairy. I'm extremely pleased that the addition of two "small" features (return type deduction and variadic templates, the latter enabling tuples and index sequences) to the core C++ language enables you to write code like that above, rather than jumping through complex hoops with MPL. That's a real success story in my opinion.
Any MPL successor should be as easy to use as the above was to write, or I'll always write the above instead.
I wholeheartedly agree. The MPL was necessary in the past because recreating even small parts of that framework was a massive undertaking. Now it's comparatively simple to do some things directly (or with a small ad-hoc utility) rather than needing to leverage chunks of the MPL. If MPL v2 isn't much easier to read and write than MPL then it will be a wasted opportunity.
I've been using these to write less code, if only a bit less.
Instead of:
template <typename Tuple> struct meta;
template <typename ...T> struct meta<std::tuple<T...>> { using type = /*...*/; };
I've been writing:
template <typename ...T> constexpr auto meta (std::tuple<T...>) { return /*...*/; }
...and calling it as decltype(meta(std::tuple</*...*/>{})). This both eliminates the noise coming from having a base/specialization template pair instead of one template, and also removes the need for a *_t template alias and/or typename /*...*/::type.
Yes, I've found constexpr functions can greatly simplify some aspects of metaprogramming, although so far a lot of it has been seeing what other people are doing with them and I haven't quite got into the habit of using them fully myself.