Document Number: P0194R4, ISO/IEC JTC1 SC22 WG21
Audience:EWG, LEWG
Date:2017-06-18
Authors:Matúš Chochlík (chochlik@gmail.com)
Axel Naumann (axel@cern.ch)
David Sankel (dsankel@bloomberg.net)

Static reflection

Table of Contents

Introduction

This paper proposes to add support for compile-time reflection to standard C++. We propose that the compiler shall generate meta-objects — representations of certain program declarations, which can be reasoned-about at compile time. These meta-objects can then be used, through a set of operations to obtain various pieces of metadata, like declaration names, lists of scope members, information about specifiers, and so on. The meta-objects are implemented as anonymous types conforming to defined concepts, and the operations are implemented as class templates.

This paper is accompanied by two more papers — p0385, which discusses the use cases, rationale, design decisions, future evolution of the proposed reflection facility and contains multiple examples of use and replies to frequently asked questions; and p0578 which gives a concise introduction to the feature set presented here.

Reflection proposed here behaves as follows:


enum E0194 { kFirst, kSecond };
using E0194_m = reflexpr(E0194);
using kFirst_m = get_element_t<0, get_enumerators_t<E0194_m>>;
cout << get_name_v<kFirst_m> << '\n'; // prints "kFirst"

Interplay with other proposals

This proposal relies on the Concepts TS. It also assumes that WG21 will incorporate proper compile-time strings; until then, this proposal provides a placeholder implementation (see [reflect.ops.named]) to facilitate reasoning about the interfaces.

Proposed wording

The wording is assumed to provide the working paper for a new Technical Specification. Grayish background indicates proposed wording.

This paper does not propose a feature-test macro as it provides a new header that can be tested against.

1 General [intro]

1.1 Scope [intro.scope]

This specification describes extensions to the C++ Programming Language as specified by the International Standard, ISO/IEC 14882. These extensions permit operations on source code. They include changes and additions to the existing library facilities as well as the extension of one core language facility.

This specification requires the availability of ISO/IEC TS 19217:2015, Programming Languages - C++ Extensions for Concepts Concepts as a prerequisite.

1.2 References [intro.refs]

The following referenced documents are indispensable for the application of this document.

1.3 Namespaces and headers [intro.namespaces]

All components described here are declared in namespace std::experimental::reflect::v1 unless otherwise specified. The header described in this specification shall import the contents of std::experimental::reflect::v1 into std::experimental::reflect as if by:


namespace std::experimental::reflect {
  inline namespace v1 {}
}

Unless otherwise specified, references to other entities described here are assumed to be qualified with std::experimental::reflect::v1::, references to entities described in the C++ standard are assumed to be qualified with std::.

Table 1 — Reflection library headers
<experimental/reflect>

2 Lexical conventions [lex]

2.1 General [lex.general]

This specification introduces the first steps of a new expression syntax to operate on the meta-level of C++.

2.3 Keywords [lex.key]

In addition to the keywords specified by (C++ [lex.key]), as laid out in its Table 5, this specification adds reflexpr to the set of keywords.

3 Basic concepts [basic]

3.1 Fundamental types [basic.fundamental]

In addition to (C++ [basic.fundamental]), an expression of type cv void can be used also as the operand of reflexpr.

4 Reflection operator [reflect-op]

The result of the invocation of the reflexpr trait (or template-level "operator") is a type satisfying reflect::Object and other meta concepts, depending on the operand. The returned type is implementation-defined. Meta-operations on it describe the operand, or the most recent redeclaration for operands that are declarations.

The reflexpr operator takes an id-expression (C++ [expr.prim.id]) as operand, or ::. The id-expression is understood as if used outside of the reflexpr operator at the point of invocation of the reflexpr operator; see also (C++ [stmt.ambig]). The program is ill-formed if the use of the operand as an existing name were ill-formed at the point of invocation of the reflexpr operator.

The id-expression operand of the reflexpr operator is an unevaluated operand (C++ [expr]).

If the id-expression operand of the reflexpr operator is declared inside a function body, the program is ill-formed.

The resulting type of the reflexpr operator reflects (describes) the operand. Depending on the operand, the resulting type satisfies the following concept requirements (see Concepts-TS):

Table 2 — meta Concept satisfied by result of reflexpr operation when invoked on id-expression kind
id-expression kindmeta Concept
union-namereflect::Record
class-namereflect::Class
enum-namereflect::Enum
decltype-specifierboth reflect::Type and reflect::Alias
typedef-nameboth reflect::Type and reflect::Alias
template type-parameterboth reflect::Type and reflect::Alias
a name introduced by a using-declaration or using-directiveboth reflect::Type and reflect::Alias
type-namereflect::Type
namespace-namereflect::Namespace
namespace-aliasboth reflect::Namespace and reflect::Alias
class-data-member-namereflect::Variable
variable-namereflect::Variable
enumerator-namereflect::Constant

If the scope of the declaration of the id-expression is a class type, then the result of reflexpr is also satisfying reflect::RecordMember.

If the scope of the declaration of the id-expression is an enum, then the result of reflexpr is also satisfying reflect::Enumerator.

If the operand is a dependent name or type, reflexpr is not evaluated. On such operands, evaluation is performed once the template is specialized and the dependent name or type is resolved.

5 Library introduction[reflect]

5.1 In general[reflect.general]

Compile-time constant metadata describing various aspects of a compiled program are provided indirectly by the means of types generated by the compiler — meta-objects. A meta-object is an anonymous type reflecting (representing) a particular declaration or another entity in a C++ program, while a base-level entity refers to code that is reflected by a meta-object. A meta-object can be reasoned-about at compile-time and provides access to metadata describing various properties of the reflected entity through a set of templates. The meta-object types themselves are unspecified except that they are trivial classes (C++ [class]).

[Example: With


using int_m = reflexpr(int);
int_m is the meta-object reflecting the base-level entity int. — end example]

The actual metadata is obtained by instantiating templates constituting the interface of the meta-objects. These templates are collectively referred to as meta-object operations.

meta-objects reflecting different kinds of declarations conform to different meta-object concepts and have different interfaces — sets of templates applicable to the meta-objects. These concepts can also be used for meta-object classification.

meta-object concepts form a generalization-specialization hierarchy, with reflect::Object being the common generalization for all meta-objects.

5.2 Header <experimental/reflect> synopsis [reflect.synopsis]



namespace std::experimental::reflect {

// 5.3 Meta-object concepts
template <class T> concept bool Object();
template <class T> concept bool ObjectSequence();
template <Object T> concept bool Named();
template <Object T> concept bool Alias();
template <Object T> concept bool RecordMember();
template <Object T> concept bool Enumerator();
template <Object T> concept bool Variable();
template <Object T> concept bool ScopeMember();
template <Object T> concept bool Typed();
template <Object T> concept bool Namespace();
template <Object T> concept bool GlobalScope();
template <Object T> concept bool Class();
template <Object T> concept bool Enum();
template <Object T> concept bool Record();
template <Object T> concept bool Scope();
template <Object T> concept bool Type();
template <Object T> concept bool Constant();
template <Object T> concept bool Base();


// 5.4 Meta-object operations
// 5.4.2 Overloaded operations
template <class T> is_public;
template <class T> is_protected;
template <class T> is_private;

template <class T>
  constexpr auto is_public_v = is_public<T>::value;
template <class T>
  constexpr auto is_protected_v = is_protected<T>::value;
template <class T>
  constexpr auto is_private_v = is_private<T>::value;

// 5.4.3 Object operations
template <Object T1, Object T2> struct reflects_same;
template <Object T> struct get_source_location;
template <Object T> struct get_source_line;
template <Object T> struct get_source_column;
template <Object T> struct get_source_file_name;

template <Object T1, Object T2>
  constexpr auto reflects_same_v = reflects_same<T1, T2>::value;
template <Object T>
  constexpr auto get_source_location_v = get_source_location<T>::value;
template <Object T>
  constexpr auto get_source_line_v = get_source_line<T>::value;
template <Object T>
  constexpr auto get_source_column_v = get_source_column<T>::value;
template <Object T>
  constexpr auto get_source_file_name_v = get_source_file_name<T>::value;

// 5.4.4 ObjectSequence operations
template <ObjectSequence S> struct get_size;
template <size_t I, ObjectSequence S> struct get_element;
template <template <class...> class Tpl, ObjectSequence S>
  struct unpack_sequence;

template <ObjectSequence T>
  constexpr auto get_size_v = get_size<T>::value;
template <size_t I, ObjectSequence S>
  using get_element_t = typename get<I, S>::type;
template <template <class...> class Tpl, ObjectSequence S>
  constexpr auto unpack_sequence_v = unpack_sequence<Tpl, S>::value;

// 5.4.5 Named operations
template <Named T> struct is_anonymous;
template <Named T> struct get_name;
template <Named T> struct get_display_name;

template <Named T>
  constexpr auto is_anonymous_v = is_anonymous<T>::value;
template <Named T>
  constexpr auto get_name_v = get_name<T>::value;
template <Named T>
  constexpr auto get_display_name_v = get_display_name<T>::value;

// 5.4.6 Alias operations
template <Alias T> struct get_aliased;

template <Alias T>
  using get_aliased_t = typename get_aliased<T>::type;

// 5.4.7 Type operations
template <Typed T> struct get_type;
template <Type T> struct get_reflected_type;
template <Type T> struct is_enum;
template <Type T> struct is_class;
template <Type T> struct is_struct;
template <Type T> struct is_union;

template <Typed T>
  using get_type_t = typename get_type<T>::type;
template <Type T>
  using get_reflected_type_t = typename get_reflected_type<T>::type;
template <Type T>
  constexpr auto is_enum_v = is_enum<T>::value;
template <Type T>
  constexpr auto is_class_v = is_class<T>::value;
template <Type T>
  constexpr auto is_struct_v = is_struct<T>::value;
template <Type T>
  constexpr auto is_union_v = is_union<T>::value;

// 5.4.8 Member operations
template <ScopeMember T> struct get_scope;
template <RecordMember T> struct is_public<T>;
template <RecordMember T> struct is_protected<T>;
template <RecordMember T> struct is_private<T>;

template <ScopeMember T>
  using get_scope_t = typename get_scope<T>::type;

// 5.4.9 Record and Enum operations
template <Record T> struct get_public_data_members;
template <Record T> struct get_accessible_data_members;
template <Record T> struct get_data_members;
template <Record T> struct get_public_member_types;
template <Record T> struct get_accessible_member_types;
template <Record T> struct get_member_types;
template <Class T> struct get_public_bases;
template <Class T> struct get_accessible_bases;
template <Class T> struct get_bases;
template <Class T> struct is_final;
template <Enum T> struct is_scoped_enum;
template <Enum T> struct get_enumerators;

template <Record T>
  using get_public_data_members_t = typename get_public_data_members<T>::type;
template <Record T>
  using get_accessible_data_members_t = typename get_accessible_data_members<T>::type;
template <Record T>
  using get_data_members_t = typename get_data_members<T>::type;
template <Record T>
  using get_public_member_types_t = typename get_public_member_types<T>::type;
template <Record T>
  using get_accessible_member_types_t = typename get_accessible_member_types<T>::type;
template <Record T>
  using get_member_types_t = typename get_member_types<T>::type;
template <Class T>
  using get_public_bases_t = typename get_public_bases<T>::type;
template <Class T>
  using get_accessible_bases_t = typename get_accessible_bases<T>::type;
template <Class T>
  using get_bases_t = typename get_bases<T>::type;
template <Class T>
  constexpr auto is_final_v = is_final<T>::value;
template <Enum T>
  constexpr auto is_scoped_enum_v = is_scoped_enum<T>::value;
template <Enum T>
  using get_enumerators_t = typename get_enumerators<T>::type;


// 5.4.10 Value operations
template <Constant T> struct get_constant;
template <Variable T> struct is_constexpr;
template <Variable T> struct is_static;
template <Variable T> struct get_pointer;

template <Constant T>
  constexpr auto get_constant_v = get_constant<T>::value;
template <Variable T>
  constexpr auto is_constexpr_v = is_constexpr<T>::value;
template <Variable T>
  constexpr auto is_static_v = is_static<T>::value;
template <Variable T>
  const auto get_pointer_v = get_pointer<T>::value;

// 5.4.11 Base operations
template <Base T> struct get_class;
template <Base T> struct is_virtual;
template <Base T> struct is_public<T>;
template <Base T> struct is_protected<T>;
template <Base T> struct is_private<T>;

template <Base T>
  using get_class_t = typename get_class<T>::type;
template <Base T>
  constexpr auto is_virtual_v = is_virtual<T>::value;

// 5.4.12 Namespace operations
template <Namespace T> struct is_inline;

template <Namespace T>
  constexpr auto is_inline_v = is_inline<T>::value;

} // namespace std::experimental::reflect

5.3 Meta-object concepts [reflect.concepts]

5.3.1 In General [reflect.concepts.intro]

Available operations on meta-objects are specified by function concepts (Concepts-TS [dcl.spec.concept]). These concepts are also used to specify the result type for TransformationTrait-style meta-operations that yield meta-objects.

5.3.2 Concept Object[reflect.object]


template <class T> concept bool Object() {
  return see below;
}

Object<T>() is satisfied if and only if T is a meta-object, as generated by the reflexpr operator or any of the meta-object operations.

5.3.3 Concept ObjectSequence[reflect.objseq]


template <class T> concept bool ObjectSequence() {
  return see below;
}

ObjectSequence<T>() is satisfied if and only if T is a sequence of Objects, generated by some of the meta-object operations.

5.3.4 Concept Named[reflect.named]


template <Object T> concept bool Named() {
  return see below;
}

Named<T>() is satisfied if and only if T is an Object with an associated (possibly empty) name.

5.3.5 Concept Alias[reflect.alias]


template <Object T> concept bool Alias() {
  return Named<T>() && see below;
}

Alias<T>() is satisfied if and only if T is a Named that reflects a typedef declaration, an alias-declaration, a namespace-alias, a template type-parameter, a decltype-specifier, or a declaration introduced by using-declaration or a using-directive.

5.3.6 Concept RecordMember[reflect.recordmember]


template <Object T> concept bool RecordMember() {
  return see below;
}

RecordMember<T>() is satisfied if and only if T reflects a base-level member-declaration.

5.3.7 Concept Enumerator[reflect.enumerator]


template <Object T> concept bool Enumerator() {
  return see below;
}

Enumerator<T>() is satisfied if and only if T reflects a base-level enumerator.

5.3.8 Concept Variable[reflect.variable]


template <Object T> concept bool Variable() {
  return see below;
}

Variable<T>() is satisfied if and only if T reflects a base-level variable or non-static data member.

5.3.9 Concept ScopeMember[reflect.scopemember]


template <Object T> concept bool ScopeMember() {
  return Scope<T> || RecordMember<T>() || Enumerator<T>() || Variable<T>();
}

ScopeMember<T>() is satisfied if and only if T satisfies Scope, RecordMember, Enumerator or Variable.

5.3.10 Concept Typed[reflect.typed]


template <Object T> concept bool Typed() {
  return Variable<T>() || Enumerator<T>();
}

Typed<T>() is satisfied if and only if T reflects a base-level variable or enumerator.

5.3.11 Concept Namespace[reflect.namespace]


template <Object T> concept bool Namespace() {
  return see below;
}

Namespace<T>() is satisfied if and only if T reflects a base-level namespace (including the global namespace).

5.3.12 Concept GlobalScope[reflect.globalscope]


template <Object T> concept bool GlobalScope() {
  return see below;
}

GlobalScope<T>() is satisfied if and only if T reflects the global namespace.

5.3.13 Concept Class[reflect.class]


template <Object T> concept bool Class() {
  return see below;
}

Class<T>() is satisfied if and only if T reflects a non-union class type.

5.3.14 Concept Enum[reflect.enum]


template <Object T> concept bool Enum() {
  return see below;
}

Enum<T>() is satisfied if and only if T reflects an enumeration type.

5.3.15 Concept Record[reflect.record]


template <Object T> concept bool Record() {
  return see below;
}

Record<T>() is satisfied if and only if T reflects a class type.

5.3.16 Concept Scope[reflect.scope]


template <Object T> concept bool Scope() {
  return Namespace<T>() || Record<T>() || Enum<T>();
}

Scope<T>() is satisfied if and only if T reflects a base-level namespace (including the global namespace), class or enumeration.

5.3.17 Concept Type[reflect.type]


template <Object T> concept bool Type() {
  return see below;
}

Type<T>() is satisfied if and only if T reflects a base-level type.

5.3.18 Concept Constant[reflect.const]


template <Object T> concept bool Constant() {
  return see below;
}

Constant<T>() is satisfied if and only if T reflects a constant expression (C++ [expr.const]).

5.3.19 Concept Base[reflect.inherit]


template <Object T> concept bool Base() {
  return see below;
}

Base<T>() is satisfied if and only if T reflects a base-specifier, as returned by the template get_bases.

5.4 Meta-object Operations [reflect.ops]

5.4.1 In general [reflect.ops.intro]

A meta-object operation extracts information from meta-objects. It is a class template taking one or more arguments, at least one of which models the meta-object concept. The result of a meta-object operation can be either a constant expression (C++ [expr.const]) or a type.

5.4.2 "Overloaded" operations [reflect.ops.over]

template <class T> is_public;
template <class T> is_protected;
template <class T> is_private;

These meta-object operations are "overloaded" by concept type below. The generic templates do not have a definition. When multiple concepts implement the same meta-object operation, its template will be partially specialized for the concepts implementing the operation. [Note: For "overloaded" operations, any meta-object will always satisfy at most one of the concepts that the operation is "overloaded" for. — end note]
[Example: An an operation OP "overloaded" on concepts A and B will be defined as follows:

  template <class T> concept bool A() { return std::is_signed_v<T>; }
  template <class T> concept bool B() { return std::is_class_v<T>; }
  template <class T> struct OP; // undefined
  template <A T> struct OP<T> {...};
  template <B T> struct OP<T> {...};
  
— end example]

5.4.3 Object operations[reflect.ops.object]

template <Object T1, Object T2> struct reflects_same;

All specializations of reflects_same<T1, T2> shall meet the BinaryTypeTrait requirements (C++ [reflect.rqmts]), with a BaseCharacteristic of true_type if two meta-objects reflect the same base-level entity, otherwise with a BaseCharacteristic of false_type.
[Note: With

  class A; using a0 = reflexpr(A);
  class A {}; using a1 = reflexpr(A);
  
reflects_same_v<a0, a1> will be true as a0 and a1 reflect the same underlying entity. — end note]

template <Object T> struct get_source_location;

All specializations of get_source_location<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]) with a BaseCharacteristic of source_location. The source_location returned by get_source_location<T> is the source_location of the most recent declaration of the base-level entity described by T.

template <Object T> struct get_source_line;
template <Object T> struct get_source_column;

All specializations of above templates shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]) with a BaseCharacteristic of uint_least32_t and a value of get_source_location<T>::line() (for get_source_line<T>) and get_source_location<T>::column() (for get_source_column<T>).
[Note:
These versions of get_source_location members are provided to facilitate template meta programming. — end note]

template <Object T> struct get_source_file_name;

All specializations of get_source_file_name<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]) with a BaseCharacteristic of integral_constant<const char (&)[N], STR>, where STR is the name (or a reference to a name) of a static, constant expression character array (NTBS) of length N, as if declared as static constexpr char STR[N] = ...;. The value of the NTBS consists of the same character values as the character sequence referred to by get_source_location<T>::file_name().
[Note:
This version of get_source_location::file_name() is provided to facilitate template meta programming. — end note]

5.4.4 ObjectSequence operations[reflect.ops.objseq]

template <ObjectSequence S> struct get_size;

All specializations of get_size<S> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]) with a BaseCharacteristic of integral_constant<size_t, N>, where N is the number of elements in the object sequence.

template <size_t I, ObjectSequence S> struct get_element;

Remarks:
All specializations of get_element<I, S> shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type corresponds to the Ith element Object in S, where the indexing is zero-based.

template <template <class...> class Tpl, ObjectSequence S>
  struct unpack_sequence;

Remarks:
All specializations of unpack_sequence<Tpl, S> shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is an alias to the template Tpl specialized with the types in S.

5.4.5 Named operations[reflect.ops.named]

template <Named T> struct is_anonymous;

All specializations of is_anonymous<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects an anonymous base-level entity, the BaseCharacteristic of is_anonymous<T> is true_type, otherwise it is false_type.

template <Named T> struct get_name;
template <Named T> struct get_display_name;

All specializations of these templates shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]) with a BaseCharacteristic of integral_constant<const char (&)[N], STR>, where STR is the name (or a reference to a name) of a static, constant expression character array (NTBS) of length N, as if declared as static constexpr char STR[N] = ...;.
  • For T reflecting an anonymous base-level entity, the string's value is the empty string.
  • For T reflecting a decltype-specifier, the string's value is the empty string for get_name<T> and implementation-defined for get_display_name<T>.
  • If T is an array, pointer or reference type, or cv-qualified, the string value of get_name<T> is the empty string.
  • In all other cases, the string's value is implementation-defined for get_display_name<T> and has the following value for get_name<T>:
    • for an Alias, the unqualified name of the aliasing declaration: the identifier introduced by a type-parameter or the name introduced by a using-declaration, using-directive, typedef or alias declaration;
    • for a specialization of a class template, the template-name;
    • for a class type, the class-name;
    • for a namespace, the unqualified namespace-name;
    • for a enumeration type, the unqualified enum-name;
    • for all other simple-type-specifiers, the name stated in the "Type" column of Table 9 in (C++ [dcl.type.simple]);
    • for a variable, the unqualified variable-name;
    • for a enumerator, the unqualified enumerator-name;
    • for a class data member, the unqualified class-data-member-name.
[Note:
With

  namespace n { template <class T> class A; }
  using a_m = reflexpr(n::A<int>);
the value of get_name_v<a_m> is "A" while the value of get_display_name_v<a_m> might be "n::A<int>". — end note]
[Note:
The length of the NTBS is sizeof(get_display_name_v<T>) - 1. — end note]

5.4.6 Alias operations[reflect.ops.alias]

template <Alias T> struct get_aliased;

All specializations of get_aliased<T> shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is the Named meta-object reflecting
  • the redefined name, if T reflects a typedef or alias-declaration;
  • the template specialization's template argument value, if T reflects a template type-parameter;
  • the original declaration introduced by a using-declaration or a using-directive;
  • the aliased namespace of a namespace-alias.
  • the type denoted by the decltype-specifier.
The nested type named type must not itself be an Alias; instead, it is reflecting the underlying non-Alias entity.
[Example:
For

using i0 = int; using i1 = i0;
get_aliased_t<i1> reflects int. — end example]

5.4.7 Type operations[reflect.ops.type]

template <Typed T> struct get_type;

All specializations of get_type<T> shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is the Type reflecting the type of the base-level entity reflected by T.
[Example:
For

int v; using v_m = reflexpr(v);
get_type_t<v_m> is reflexpr(int). — end example]

template <Type T> struct get_reflected_type;

All specializations of get_reflected_type<T> shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is the base-level type reflected by T.

template <Type T> struct is_enum;

All specializations of is_enum<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects an enumeration type, the BaseCharacteristic of is_enum<T> is true_type, otherwise it is false_type.

template <Type T> struct is_class;
template <Type T> struct is_struct;
template <Type T> struct is_union;

All specializations of these templates shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects a class with class-key class (for is_class<T>), struct (for is_struct<T>), or union (for is_union<T>), the BaseCharacteristic of the respective template specialization is true_type, otherwise it is false_type.

5.4.8 Member operations[reflect.ops.member]

template <ScopeMember T> struct get_scope;

All specializations of get_scope<T> shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is the Scope reflecting a scope S. With ST being the scope of the declaration of the base-level entity reflected by T, S is found as the innermost scope enclosing ST that is either a namespace scope (including global scope), class scope or enumeration scope.

template <RecordMember T> struct is_public<T>;
template <RecordMember T> struct is_protected<T>;
template <RecordMember T> struct is_private<T>;

All specializations of these partial template specializations shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects a public member (for is_public), protected member (for is_protected) or private member (for is_private), the BaseCharacteristic of the respective template specialization is true_type, otherwise it is false_type.

5.4.9 Record and Enum operations[reflect.ops.record_enum]

template <Record T> struct get_public_data_members;
template <Record T> struct get_accessible_data_members;
template <Record T> struct get_data_members;

All specializations of these templates shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with RecordMember types that reflect the following subset of data members of the class reflected by T:
  • for get_public_data_members, all public data members;
  • for get_accessible_data_members, all data members that are accessible from the scope of the invocation of reflexpr which generated (directly or indirectly) T;
  • for get_data_members, all data members, irrespective of their accessibility.
The order of the elements in the ObjectSequence is the order of the declaration of the data members in the class reflected by T. The class reflected by T must be complete at the point of the invocation of reflexpr which generated (directly or indirectly) T, otherwise the program is ill-formed.
Remarks:
The program is ill-formed if T reflects a closure type.

template <Record T> struct get_public_member_types;
template <Record T> struct get_accessible_member_types;
template <Record T> struct get_member_types;

All specializations of these templates shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with Type types that reflect the following subset of types declared in the class reflected by T:
  • for get_public_member_types, all public members types;
  • for get_accessible_member_types, all member types that are accessible from the scope of the invocation of reflexpr which generated (directly or indirectly) T;
  • for get_member_types, all member types, irrespective of their accessibility.
The order of the elements in the ObjectSequence is the order of the first declaration of the types in the class reflected by T. The class reflected by T must be complete at the point of the invocation of reflexpr which generated (directly or indirectly) T, otherwise the program is ill-formed.
Remarks:
The program is ill-formed if T reflects a closure type.

template <Class T> struct get_public_bases;
template <Class T> struct get_accessible_bases;
template <Class T> struct get_bases;

All specializations of these templates shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with Base types that reflect the following subset of base classes of the class reflected by T:
  • for get_public_bases, all public bases;
  • for get_accessible_bases, all bases are taken into account whose public members are accessible from the scope of the invocation of reflexpr which generated (directly or indirectly) T;
  • for get_bases, all bases are taken into account, irrespective of their accessibility.
The order of the elements in the ObjectSequence is the order of the declaration of the base classes in the class reflected by T. The class reflected by T must be complete at the point of the invocation of reflexpr which generated (directly or indirectly) T, otherwise the program is ill-formed.
Remarks:
The program is ill-formed if T reflects a closure type.

template <Class T> struct is_final;

All specializations of is_final<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects a class that is marked with the class-virt-specifier final, the BaseCharacteristic of the respective template specialization is true_type, otherwise it is false_type.

template <Enum T> struct is_scoped_enum;

All specializations of is_scoped_enum<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects a scoped enumeration, the BaseCharacteristic of the respective template specialization is true_type, otherwise it is false_type.

template <Enum T> struct get_enumerators;

All specializations of get_enumerators<T> shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with Constant types that reflect the enumerators of the enumeration type reflected by T.

5.4.10 Value operations[reflect.ops.value]

template <Constant T> struct get_constant;

All specializations of get_constant<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]), with a BaseCharacteristic of integral_constant<X, x>, where X is the type of the value reflected by T and x its value.

template <Variable T> struct is_constexpr;

All specializations of is_constexpr<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects a variable declared with the decl-specifier constexpr, the BaseCharacteristic of the respective template specialization is true_type, otherwise it is false_type.

template <Variable T> struct is_static;

All specializations of is_static<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects a variable with static storage duration, the BaseCharacteristic of the respective template specialization is true_type, otherwise it is false_type.

template <Variable T> struct get_pointer;

All specializations of get_pointer<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]), with a BaseCharacteristic of integral_constant<X, x>, where
  • for variables with static storage duration: X is add_pointer<Y>, where Y is the type of the variable reflected by T and x is the address of that variable; otherwise,
  • X is the pointer-to-member type of the member variable reflected by T and x a pointer to the member.

5.4.11 Base operations[reflect.ops.derived]

template <Base T> struct get_class;

All specializations of get_class<T> shall meet the TransformationTrait requirements (C++ [reflect.rqmts]). The nested type named type is an alias to reflexpr(X), where X is the class of the base-specifier reflected by T.

template <Base T> struct is_virtual;
template <Base T> struct is_public<T>;
template <Base T> struct is_protected<T>;
template <Base T> struct is_private<T>;

All specializations of the template and of these partial template specializations shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects a base-specifier with the virtual specifier (for is_virtual), with the public specifier or with an assumed (see C++ [class.access.base]) public specifier (for is_public), with the protected specifier (for is_protected), or with the private specifier or with an assumed private specifier (for is_private), then the BaseCharacteristic of the respective template specialization is true_type, otherwise it is false_type.

5.4.11 Namespace operations[reflect.ops.namespace]

template <Namespace T> struct is_inline;

All specializations of is_inline<T> shall meet the UnaryTypeTrait requirements (C++ [reflect.rqmts]). If T reflects an inline namespace, the BaseCharacteristic of the template specialization is true_type, otherwise it is false_type.

Acknowledgments

Thanks to Ricardo Fabiano de Andrade, Roland Bock and Klaim-Joël Lamotte who provided valuable feedback, criticism and suggestions.

Revision history

Revision 1 (N3996)

Describes the method of static reflection by means of compiler-generated anonymous types. Introduces the first version of the metaobject concepts and some possibilities of their implementation. Also includes discussion about the motivation and the design rationale for the proposal.

Revision 2 (N4111)

Refines the metaobject concepts and introduces a concrete implementation of their interface by the means of templates similar to the standard type traits. Describes some additions to the standard library (mostly meta-programming utilities), which simplify the use of the metaobjects. Answers some questions from the discussion about N3996 and expands the design rationale.

Revision 3 (N4451)

Incorporates the feedback from the discussion about N4111 at the Urbana meeting, most notably reduces the set of metaobject concepts and refines their definitions, removes some of the additions to the standard library added in the previous revisions. Adds context-dependent reflection.

Revision 4 (P0194R0)

Further refines the concepts from N4111; prefixes the names of the metaobject operations with get_, adds new operations, replaces the metaobject category tags with new metaobject traits. Introduces a nested namespace std::reflect which contains most of the reflection-related additions to the standard library. Rephrases definition of meta objects using Concepts Lite. Specifies the reflection operator name — reflexpr. Introduces an experimental implementation of the reflection operator in clang. Drops the context-dependent reflection from N4111 (will be re-introduced later).

Revision 5 (P0194R1)

Dropped all metaobject traits except is_metaobject. All metaobject classification is now done by using the concepts.

The meta::Scoped concept has been renamed to meta::ScopeMember. The meta::Constant and meta::Specifier concepts, and several new operations have been added.

The aliases for the operation templates returning metaobjects had previously the _t suffix; this has been changed to the _m suffix.

Revision 6 (P0194R2)

The following concepts from P0194R1 were dropped in order to simplify the proposal: meta::Linkable, meta::Enumerator, meta::DataMember, meta::MemberType, meta::EnumClass, meta::TypeAlias and meta::NamespaceAlias.

The following concepts were added to the proposal: meta::TagType, meta::Record, meta::Enumerator.

Unlike in the previous proposal, metaobjects reflecting anonymous entities - the global scope, anonymous namespaces and classes, etc. do conform to the meta::Named concept and implement the name-returning operations.

Unlike in the previous proposal, metaobjects reflecting the global scope do conform to the meta::ScopeMember concept and the meta::get_scope operation. For arguments reflecting the global scope returns a metaobject reflecting the global scope (i.e. the global scope is its own scope).

Metaobjects reflecting built-in types and types like pointers, references, arrays, etc. now don't have a scope (i.e. they do not conform to the meta::ScopeMember concept).

We have added a different mechanism for distinguishing between non-scoped and scoped enums - the meta::is_scoped_enum operation. Unlike in the previous proposal, meta::Enum reflecting a non-scoped enum is a meta::Scope.

We now allow the default construction and copy construction of values of metaobject types.

Direct reflection of class data members, member types and type aliases, enumerators and global scope/namespace-level variables has been added.

The typedef (type-alias) reflection has been simplified based on the feedback from Oulu. Previously we required reflection to be aware about all aliases in a "chain" even in the context of templates.

The mechanism for enumerating public-only vs. all (including non-public ones) class members has been changed. Now the "basic" operations like meta::get_data_members, meta::get_member_types, etc. return all members, and the meta::get_public_data_members, meta::get_public_member_types, return only the public class members.

Revision 7 (P0194R3)

Major wording update, clarifying the behavior for instance on dependent names and function-local declarations. All examples but those required for the wording are now found in the design discussion paper, p0385.

Rename header, namespace from meta to reflect. Target a TS.

Use of function-style concepts, following the example set by the Ranges TS, for consistency reasons. The name of the reflection operator was changed to $reflect; the basic source character set was adjusted accordingly.

In addition to get_public_bases, get_public_data_members and get_public_member_types and their sibling without public, a new sibling get_accessible_... was added. The Specifier, TagType and Reversible concepts were removed. So was the is_metaobject trait. The type of meta-object operations "returning" strings have been updated. The order of template parameters for MetaSequence operations has been matched to similar operations of tuple. The suffix _m was reverted to _t. $reflect() with an empty operand is disallowed.

Revision 8 (P0194R4)

As requested by EWG in Kona, the operator was renamed back to reflexpr; no need to change the source character set anymore. get_base_name is now called get_name and returns an empty string on pointer, references, arrays and cv-qualified types.

References

1. Static reflection. Rationale, design and evolution. p0385

2. Static reflection in a nutshell. p0578