July 15, 2025·4 min read·engineeringcppreflectionstandardsapi

C++ reflection cleanup addresses the gaps between adopted papers

P3795 proposes consistent reflection errors and fills gaps left by coordinated Sofia adoptions. Its details show why a feature freeze still leaves substantial integration work.

Adopting several related proposals at the same meeting can leave their combined interfaces less consistent than any paper appears in isolation. Reflection's July cleanup paper documents exactly that problem. Some operations retained an older error model, while newly reflectable entities could not use annotations introduced beside them. These are integration defects in a growing feature, with consequences for the libraries built on it.

Barry Revzin's P3795R0, dated July 15, proposes corrections following the Sofia adoptions. The baseline P2996R13 reflection paper provides reflection values, queries, and splicing. P3795 is follow-up work, not evidence that its corrections have already been accepted into the working draft.

I find this phase of standards work useful to read because it exposes the actual obligations of a reusable abstraction. A compile-time API still needs consistent errors and sensible argument structure for its library consumers. Moving work out of runtime does not remove those design requirements, and an inconsistent compile-time failure can be particularly difficult to diagnose through templates.

One error model needs complete coverage

The central reflection proposal originally used a Constant When condition to specify whether a query could be a constant expression. The separately adopted error-handling work changed much of that model to throwing std::meta::exception. Revzin identifies operations that the coordinated edits did not cover.

His examples include annotation queries and parameter reflection functions such as parameters_of and return_type_of. Other affected operations come from base-subobject reflection and static-array support. A generic reflection library should not have to remember which independent adoption introduced each function merely to understand its failure behavior.

P3795 proposes using the exception model for those cases as well. That makes the error mechanism consistent without specifying a fixed human-readable diagnostic string. The paper treats the text of an error and a programmatic indication of the failed operation as different interface requirements.

The proposed precision concerns std::meta::exception::from(). Revzin wants it to identify the function whose specified exception occurred through that function's reflection. A caller could then inspect the failed operation using the reflection model instead of interpreting diagnostic prose. That is a concrete benefit of defining the programmatic part of an error even while leaving its display wording to implementations.

Generated members need a coherent options structure

The paper also revisits data_member_spec, which supplies member descriptions for define_aggregate. The adopted form takes the type separately from an options object. An earlier design had allowed omitting the name as a request for a generated unique name, but that behavior changed during the main proposal's development.

Under the changed rule, omitting the name means describing an unnamed bit-field, so a width must be supplied. Ordinary generated members therefore need some options information as well as their type. Revzin proposes putting the type inside the options structure to give the interface a uniform description of a member.

This is an API revision, not a new claim that arbitrary source declarations can be injected. It concerns the existing aggregate-definition facility. Grouping the member's type with its other description fields makes that particular facility easier to express consistently.

P3795 also proposes annotations for generated members. That follows from combining aggregate generation with the separately adopted annotations feature. The motivation is the interaction of two capabilities that already belong to the reflection design, rather than an unrelated expansion of reflection's purpose.

Parameters reveal another combination gap

Function parameter reflection and annotations arrived through independent papers. As Revzin explains, neither paper could directly assume the other facility's adoption when specifying its own coverage. Their combination consequently lacks annotations on function parameters.

The cleanup proposal adds that possibility so a parameter reflection can expose its attached annotation. The relevant operation is not merely discovering that a function has a parameter. It is obtaining application-supplied metadata associated with that parameter through the same query framework used elsewhere.

Missing predicates receive similar attention. Revzin proposes is_inline, is_constexpr, and is_consteval. His choice of is_consteval deliberately asks whether a declaration uses that specification, avoiding the more complicated question of whether a constexpr function becomes immediate through escalation. A simple-looking name can otherwise conceal a query whose answer depends on the evaluation that asks it. The paper also proposes direct scope-identification helpers, because obtaining the current class indirectly through access-control context would otherwise encourage libraries to duplicate the same traversal of enclosing scopes.

These changes show why draft cleanup matters after a feature freeze. The core reflection model can be adopted while its interactions still need precise reconciliation. P3795 identifies specific operations whose behavior or coverage should become consistent before users build assumptions around them. Its contribution is a reviewable set of corrections to the draft's interfaces, with the adoption decision for those corrections still ahead.