Metaprogramming by Design Not by Accident
Metaprogramming by Design Not by Accident
Document #: P0425R0
Date: 2017-06-18
Project: Programming Language C++
Audience: SG 7
Reply-to: Louis Dionne <ldionne.2@gmail.com>
1 Introduction
2 Motivation
Following [P0633] and discussion with various people in SG 7, we feel like there is a clear preference
for homogeneous constexpr-based metaprogramming over the other alternatives. Classic template-
based metaprogramming is very difficult to use, does not scale well and is basically equivalent to
inventing a new language within C++. [Boost.Hana]-style metaprogramming is more usable, but it
also does not scale in terms of compilation times, and it still has some usability concerns anyway.
On the other hand, constexpr-based type-level metaprogramming is closest to runtime C++ and
could yield the best compile-time performance. If this can be made to work, this would be the best
and most consistent approach to do metaprogramming in C++. The goal of this paper is to lay
down what’s required to make this possible.
1
3 Proposed path
The cut in the design space that we’re exploring in this paper is constexpr programming and
homogeneous value-based reflection. This is in essence very similar to [P0598] and [P0597] taken
together. Note that the following path is meant to be concrete enough to be actionable and discussed
with compiler implementers, but vague enough to allow for different implementation strategies if
one fails:
1. Allow support for variable-size containers in constant expressions
2. Provide a constexpr representation for C++ types (e.g. std::meta::type)
3. Provide a way to convert from this representation back to a C++ type
4. Allow interoperation between constexpr containers and parameter packs
This is of utmost importance if we want to implement the equivalent of a type list (e.g. mpl::vector)
using constexpr. We have a few options to get there:
1. Provide a compiler-backed magic type that represents a contiguous sequence of objects usable
inside constant expressions. This is constexpr_vector proposed in [P0597].
2. Provide a constexpr-friendly allocator, as proposed in P0639R0 (should be published in the
pre-Toronto 2017 mailing).
3. Add support for new[]-expressions in constant expressions.
We believe that solving this problem is worth doing on its own, even without the other steps
proposed in this paper, since that will make constexpr much more powerful and will allow complex
applications to be built on top of it without jumping through many hoops (see for example the JSON
parser in [Constexpr all the things!]). Note that options (2) and (3) are particularly nice, since
they basically mean we could use std::vector (or any other standard container!) within constant
expressions, which is clearly the most intuitive and consistent way of expressing compile-time
sequences, since it does not differ from runtime programming.
In addition, it would be immensely useful to have the ability to use placement-new inside constant
expressions, as this solves a bunch of problems that come with the approaches documented above.
For example, with placement new, we could implement constexpr variants, which not only makes
sense but also solves several concerns for the reflection proposal (the reflected members of a type
could be a sequence of variants of different reflected entities). However, it seems like that would add
a lot of complexity to implementations.
Just like we have runtime type information, we could have compile-time type information through a
type like std::meta::type. This is similar to std::metainfo from [P0598], but we probably need
2
something to represent types and not just arbitrary reflected entities, since that is too general for
most use cases. The specific API should be left to the reflection proposal, but we suggest having
a way to replicate at least the functionality present in <type_traits> one way or another. This
paper will use REFLEXPR(T) to denote the std::meta::type representing T, while happily punting
on the actual syntax.
Since most compile-time computations will yield sequences of types, we believe it would be useful to
have a way of extracting the contents of such a sequence in a way that makes it easy to influence the
program. Our current de-facto compile-time sequence is parameter packs, so we suggest translating
constexpr sequences to that representation. One possible solution would be to add support for
expanding constexpr sequences using ...:
constexpr std::vector<std::meta::type> types = {
REFLEXPR(Foo), REFLEXPR(Bar), REFLEXPR(Baz)
};
f(types...); // calls f(types[0], types[1], types[2])
f(g(types)...); // as usual; calls f(g(types[0]), g(types[1]), g(types[2]))
The exact customization point that we would use to provide this functionality is to be determined.
We could use std::get, but we would have to be careful not to clash with structured bindings. We
may be able to use some iterator-based protocol instead, a bit like what the range-based for loop
does.
4 Examples
This section contains a few examples of how type-level programming would look like with all the steps
in this proposal completed. It is highly desirable to come up with more examples to validate that
this design will help us solve the problems that we currently solve with template metaprogramming.
3
4.1 Sorting types by alignment
Here, we show how to sort types by their alignment. In conjunction with additional functionality to
track which types are at which index, this could be used to do things like structure packing:
constexpr std::vector<std::meta::type>
sort_by_alignment(std::vector<std::meta::type> types) {
std::sort(v.begin(), v.end(), [](std::meta::type t, std::meta::type u) {
return t.alignment() < u.alignment();
});
return v;
}
Given a set of tuples, we can find the type of the tuple to which all tuples can be converted. This is
the tuple of the common types for all 0th elements, all 1st elements, 2nd elements, etc... Something
like this is implemented in the [range-v3] library:
// Assuming something like this:
namespace std::meta {
constexpr std::meta::type common_type(std::initializer_list<std::meta::type> types) {
return REFLEXPR(std::common_type_t<TYPENAME(types)...>);
}
}
4
return common_args;
}
Since this proposal does not include code synthesis/generation, causing runtime side effects is not
convenient with this proposal alone. For example, it is not clear how one would write a generic
JSON serializer using just the functionality included in this proposal, since that would require
bridging between iteration on a constexpr sequence of reflected members and the runtime world
(e.g. to write the data to a stream). That being said, pure type-level computations without runtime
side effects are a thing, and this is what we’re trying to solve here (while leaving the door fully open
to adding code synthesis on top), as presented in [P0633].
6 Going forward
If the general direction presented in this paper seems pleasing to the Committee, we can start
solving the subproblems presented here (and in fact some people have already started). We should
also generate more examples of how this will be used in the real world, perhaps by taking existing
template metaprogramming applications and translating them to this proposal.
7 References
5
[P0598] Daveed Vandevoorde, Reflect through values instead of types
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0598r0.html
[P0633] Daveed Vandevoorde & Louis Dionne, Exploring the design space of metaprogramming and
reflection
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0633r0.pdf
[Boost.MPL] Aleksey Gurtovoy and David Abrahams, The Boost MPL library
http://www.boost.org/doc/libs/release/libs/mpl/doc/index.html
[Boost.Hana] Louis Dionne, Hana
https://github.com/boostorg/hana
[Constexpr all the things!] Bean Deane and Jason Turner
https://youtu.be/HMB9oXFobJc
[range-v3] Eric Niebler, Range v3
https://github.com/ericniebler/range-v3