Reflections on the reflection proposals

published at 02.03.2017 11:29 by Jens Weller

A few weeks ago I wrote a short overview over the most interesting papers for the current C++ Committee meeting in Kona, Hawaii. The big surprise was that there were many papers on reflection, while there already is a very detailed proposal for reflection.

With the C++ committee currently in Kona discussing lots of proposals, there will be some changes to the on going effort for reflection, but the current proposals are detailed enough to give an overview.

Current Situation

There is no support for Reflection currently in the standard, for years some solutions were more popular then others. Qt uses the moc extensively for reflection, boost always had with fusion (and now hana) its ADAPT_STRUCT macros. Also a different approach, writing a reflection engine from scratch was presented at Meeting C++ 2016. We're not going to see an adaption of the moc nor a std::ADAPT_STRUCT being standardized. Same is for any library solutions, the current trend goes to have static reflection become part of the language, with a certain library API through concepts to have access to it at compile/run time.

Current Proposals

For Kona, these are the 5 proposals which are (maybe) debated [if the committee finds time]:

The first 3 proposals are from the same group of authors. The first is the main proposal, the other two focus on providing an overview document and a rationale why they choose this way for the standard. The 4th proposal is from Andrew Sutton and Herb Sutter, while the 5th proposal comes from Daveed Vandevoorde. Details below.

The main reflection proposal

TL;DR: use $reflect but not reflexpr for reflection.

$reflect is a trait, which returns an object according to the std::reflection::Object and other meta concepts. Currently this includes support for unions, enums, structs/classes, and alias. Supported and not supported according to the nutshell document are:

This proposal is also based on facilities currently not in the standard, it provides placeholder implementations for compile time strings and type lists. Also reflection does build upon concepts, it defines a couple of concepts itself, mostly for supporting the above listed types.

The authors aim at providing a minimal set for reflection, the major interfaces are defined as concepts rather then concrete types. The mirror library for accessing the proposed features already existing can be seen as a proof of concept implementation outside of the standard. Also Louis Dionne has demonstrated, that boost::hana can easily adopt and satisfy the requirements of the current reflection proposal.

The base reflection concept is Object, which enables the querying of the source location, and reflects_same concept. The Record concept covers unions, classes and structs. It provides access functions to the reflected type members. The Named concept provides base_name and display_name facilities. The Alias concept allows you to access the aliased type via get_aliased_t.

So far for the current main reflection proposal. Lets look at the 2 other proposals, arguing for a change in the above approach.

A design for static reflection

Andrew Sutton and Herb Sutter present a closely to the above proposal related design, except that...

... reflections are regular objects and not types. This enables a style of metaprogramming without the need for template metaprogrammig.

A noble argument, also, as the main proposal builds so heavily on concepts, its interesting to hear a variation of this idea from Andrew Sutton, who was leading the work on concepts together with Stroustrup, Dos Reis and others.

The proposal aims at making $ a reflection operator, with which you can access reflection meta objects. The difference is visible when we take the examples from both proposals next to each other:

Main proposal This proposal
template <typename T>
T min(const T& a, const T& b) {
  log() << "min<"
        << get_display_name_v<$reflect(T)>
        << ">(" << a << ", " << b << ") = ";
  T result = a < b ? a : b;
  log() << result << std::endl;
  return result;
}
template<typename T> min(T a, T b) {
  log() << "min" << '<'
    << $T.qualified_name() << ">("
    << $a.name() << ':' << $a.type().name() << ','
    << $b.name() << ':' << $b.type().name() << ") = ";
  T r = a < b ? a : b;
  log() << r << '\n';
  return r;
}

Further the authors show, how their approach could be used to provide a hashing function, which is able to hash all simple structs. Another usage could be a stringification function for enums. There is a fork of clang which implements this proposal.

This proposal is very similar to the current favored approach, as it also uses concepts to describe its API, but the difference really is being based on an reflection object with member functions instead of a reflection type. The proposal has a different set of APIs, I am not sure if part of this can be merged into the current main reflection proposal, or if this should be seen as a totally different approach.

Reflect Through Values Instead of Types

Similar to the other non main reflection paper, this paper aims at a different approach in handling reflection. This time through values instead of types (or objects). It is a rather short proposal, and the goal is clearly stated in the first paragraph:

P0194R2 proposes adding compile-time reflection facilities to the language. I’m looking forward to the capabilities themselves, but I’m concerned about using types to perform computations when we’ve come such a long way in providing efficient compile-time computation facilities through constexpr. Let’s design with modern C++ in mind and leave the template metaprogramming shackles of the past behind.

Also, the author doubts, that adding reflection into the type system is such a smart idea, instead he proposes to base reflection on a std::metainfo type, reflexpr(X) (or $reflect(X)) would then return a value of such type. The idea to base reflection on constexpr and values instead of a mechanism of it self is noble. Yet, the shortness of this paper shows that this idea is just in its beginning, while the main proposal has already progressed very far.

Future reflection in C++

So the current situation is, that the main reflection proposal has progressed very well, but other ideas could still gain enough support to be merged into or change the main approach. This shows that reflection has become a serious feature to be added to C++, but we might not want to fast track things. Providing a first, minimal interface for reflection based on keywords or an operator starting with $ is a good idea. But also one should have in mind, that the main proposal emerged from a couple of other proposals for reflection in the past already, so the Committee seems to have a certain consensus where it wants to go with reflection. A complete new approach or do over is unlikely.