October 6, 2019·4 min read·engineeringcppreflectionstandards

Attribute reflection proposes typed metadata for C++

P1887R0 explores user-defined attributes whose parameters become compile-time objects, with validation delayed until reflection queries them.

A serializer can discover a field's C++ name and still need another name for its external representation. That information belongs to the application, so reflecting the declaration alone cannot supply it. Corentin Jabot's P1887R0, dated October 6, explores typed user-defined attributes to attach that missing metadata.

The paper explicitly targets work beyond C++20 and calls itself an overview rather than a complete proposal. Its central mechanism turns an annotation into a compile-time value of a user-defined type. A reflection query can then ask for that value using the type that describes it.

I find this direction interesting where it replaces conventions encoded in macros or misleading declarations. C++ already asks readers to understand many forms of indirection. Metadata should make an interface's additional meaning visible without disguising it as an unrelated runtime type.

Attribute tokens are not ordinary arguments

P1887R0 begins with a limitation of attributes. Their names identify them, but their argument text is a balanced token sequence. That representation does not give an ordinary C++ program typed access to each parameter.

The proposed alternative associates an attribute with a class that can be constructed at compile time. The annotation uses constructor-like syntax, and a reflection operation obtains the resulting object. A metadata class can therefore expose a field such as a serialized name through ordinary typed access.

That makes the metadata schema a C++ type. The type's constructor and members describe the value the annotation carries. A consumer does not have to recover its structure by interpreting a string representation of arbitrary tokens.

The paper requires suitable regular-type behavior, including constant-expression copying and equality comparison. Those requirements matter because the compiler needs to reason about attribute values as values. Merely declaring a class with the same spelling as an annotation would not satisfy the whole proposed contract.

Reflection queries supply the typed access

The proposed interface includes has_attribute parameterized by the attribute type, and attribute returning an object of that type. A presence query asks whether the reflected declaration has the annotation. Obtaining its value is the second operation.

That division is useful for a generator. It can first filter entities according to the presence of a marker, then inspect the marker's parameters where required. A test framework, for example, can identify test functions and retrieve a descriptive name for each.

P1887R0 also gives serialization and command-line parsing as use cases. A member can have a name appropriate for C++ while carrying a distinct name for a serialized field. A command-line option can carry a help description and default information without changing the member's underlying value type.

These annotations express facts that structural reflection cannot infer. A variable name does not necessarily describe a stable wire format, and a member's type does not explain which short command-line option the author intends. The metadata supplies application decisions alongside the declaration.

Validation can wait until a query

The design attempts to preserve the ignorable nature of attributes. It proposes parsing a user-defined annotation as such when a reflection query asks about it. The defining type need only be available before that query.

This is a consequential tradeoff. A declaration can carry annotation text without every consumer importing the metadata type immediately. A consumer that uses reflection takes on the requirement to provide that type and make the constructor expression valid.

The paper also sketches a spelling that unambiguously identifies a user-defined attribute and could require earlier checking. That alternative exposes the tension between allowing annotations to be ignored and making every annotation immediately type-checked.

I prefer to see that timing choice stated explicitly. Typed metadata is only as useful as the rule explaining when its validity is established. An annotation that has never been queried and one whose constructor has been checked do not provide the same evidence to a tool author.

Metadata complements code generation

Herb Sutter's earlier P0707R4 metaclass paper explores compile-time transformations that produce ordinary class definitions. It assumes reflection and compile-time programming as supporting mechanisms. P1887R0 identifies metadata as additional input such transformations can need.

For example, a generator may need to distinguish a signal from an ordinary member function. Encoding that distinction through a special return type can interfere with the function's actual signature. Separate annotations can state several independent properties while leaving the return type to describe the operation's result.

The attribute paper includes consistency requirements across declarations and rejects duplicate instances of the same user-defined attribute on one declaration. Such rules show that metadata participates in a program-wide contract. A generated result cannot be well defined if separate declarations disagree about the metadata that controls it.

The prototype described in P1887R0 is incomplete. The paper nevertheless supplies a concrete question for reflection design, whether application annotations can become ordinary compile-time objects with explicit types. Its delayed-validation rule determines how that capability could coexist with declarations consumed by code that ignores the annotations.