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) |
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"
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.
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.
- ISO/IEC 14882:2014, Programming Languages - C++, referenced as (C++ [section])
- ISO/IEC TS 19217:2015, Programming Languages - C++ Extensions for Concepts, referenced as (Concepts-TS [section]).
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 ofstd::experimental::reflect::v1
intostd::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 withstd::
.
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 satisfyingreflect::Object
and othermeta
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 thereflexpr
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 thereflexpr
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):
- If the operand of
reflexpr
is::
, then the result will be satisfyingreflect::GlobalScope
and will reflect the global scope.- Otherwise, the kind of id-expression is matched against the first column of Table 2, row by row, until the first match is found; the second column determines the Concept which the result will be satisfying.
- The invocation of
reflexpr
on any other kind of id-expression is ill-formed.
Table 2 — meta
Concept satisfied by result ofreflexpr
operation when invoked on id-expression kindid-expression kind meta
Conceptunion-name reflect::Record
class-name reflect::Class
enum-name reflect::Enum
decltype-specifier both reflect::Type
andreflect::Alias
typedef-name both reflect::Type
andreflect::Alias
template type-parameter both reflect::Type
andreflect::Alias
a name introduced by a using-declaration or using-directive both reflect::Type
andreflect::Alias
type-name reflect::Type
namespace-name reflect::Namespace
namespace-alias both reflect::Namespace
andreflect::Alias
class-data-member-name reflect::Variable
variable-name reflect::Variable
enumerator-name reflect::Constant
If the scope of the declaration of the id-expression is a class type, then the result of
reflexpr
is also satisfyingreflect::RecordMember
.If the scope of the declaration of the id-expression is an enum, then the result of
reflexpr
is also satisfyingreflect::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 entityint
. — 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 ifT
is a meta-object, as generated by thereflexpr
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 ifT
is a sequence ofObject
s, 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 ifT
is anObject
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 ifT
is aNamed
that reflects atypedef
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 ifT
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 ifT
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 ifT
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 ifT
satisfiesScope
,RecordMember
,Enumerator
orVariable
.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 ifT
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 ifT
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 ifT
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 ifT
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 ifT
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 ifT
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 ifT
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 ifT
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 ifT
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 ifT
reflects a base-specifier, as returned by the templateget_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 conceptsA
andB
will be defined as follows:— end example]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> {...};
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 theBinaryTypeTrait
requirements (C++ [reflect.rqmts]), with aBaseCharacteristic
oftrue_type
if two meta-objects reflect the same base-level entity, otherwise with aBaseCharacteristic
offalse_type
.- [Note: With
class A; using a0 = reflexpr(A); class A {}; using a1 = reflexpr(A);
reflects_same_v<a0, a1>
will betrue
asa0
anda1
reflect the same underlying entity. — end note]
template <Object T> struct get_source_location;
- All specializations of
get_source_location<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]) with aBaseCharacteristic
ofsource_location
. Thesource_location
returned byget_source_location<T>
is thesource_location
of the most recent declaration of the base-level entity described byT
.
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 aBaseCharacteristic
ofuint_least32_t
and a value ofget_source_location<T>::line()
(forget_source_line<T>
) andget_source_location<T>::column()
(forget_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 theUnaryTypeTrait
requirements (C++ [reflect.rqmts]) with aBaseCharacteristic
ofintegral_constant<const char (&)[N], STR>
, whereSTR
is the name (or a reference to a name) of a static, constant expression character array (NTBS) of lengthN
, as if declared asstatic constexpr char STR[N] = ...;
. The value of the NTBS consists of the same character values as the character sequence referred to byget_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 theUnaryTypeTrait
requirements (C++ [reflect.rqmts]) with aBaseCharacteristic
ofintegral_constant<size_t, N>
, whereN
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 theTransformationTrait
requirements (C++ [reflect.rqmts]). The nested type namedtype
corresponds to theI
th elementObject
inS
, 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 theTransformationTrait
requirements (C++ [reflect.rqmts]). The nested type namedtype
is an alias to the templateTpl
specialized with the types inS
.5.4.5 Named operations[reflect.ops.named]
template <Named T> struct is_anonymous;
- All specializations of
is_anonymous<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]). IfT
reflects an anonymous base-level entity, theBaseCharacteristic
ofis_anonymous<T>
istrue_type
, otherwise it isfalse_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 aBaseCharacteristic
ofintegral_constant<const char (&)[N], STR>
, whereSTR
is the name (or a reference to a name) of a static, constant expression character array (NTBS) of lengthN
, as if declared asstatic constexpr char STR[N] = ...;
.
- For
T
reflecting an anonymous base-level entity, the string's value is the empty string.- For
T
reflecting adecltype-specifier
, the string's value is the empty string forget_name<T>
and implementation-defined forget_display_name<T>
.- If
T
is an array, pointer or reference type, or cv-qualified, the string value ofget_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 forget_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
the value ofnamespace n { template <class T> class A; } using a_m = reflexpr(n::A<int>);
get_name_v<a_m>
is"A"
while the value ofget_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 theTransformationTrait
requirements (C++ [reflect.rqmts]). The nested type namedtype
is theNamed
meta-object reflectingThe nested type named
- 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.
type
must not itself be anAlias
; instead, it is reflecting the underlying non-Alias
entity.- [Example:
- For
using i0 = int; using i1 = i0;
get_aliased_t<i1>
reflectsint
. — end example]5.4.7 Type operations[reflect.ops.type]
template <Typed T> struct get_type;
- All specializations of
get_type<T>
shall meet theTransformationTrait
requirements (C++ [reflect.rqmts]). The nested type namedtype
is theType
reflecting the type of the base-level entity reflected byT
.- [Example:
- For
int v; using v_m = reflexpr(v);
get_type_t<v_m>
isreflexpr(int)
. — end example]
template <Type T> struct get_reflected_type;
- All specializations of
get_reflected_type<T>
shall meet theTransformationTrait
requirements (C++ [reflect.rqmts]). The nested type namedtype
is the base-level type reflected byT
.
template <Type T> struct is_enum;
- All specializations of
is_enum<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]). IfT
reflects an enumeration type, the BaseCharacteristic ofis_enum<T>
istrue_type
, otherwise it isfalse_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]). IfT
reflects a class with class-keyclass
(foris_class<T>
),struct
(foris_struct<T>
), orunion
(foris_union<T>
), the BaseCharacteristic of the respective template specialization istrue_type
, otherwise it isfalse_type
.5.4.8 Member operations[reflect.ops.member]
template <ScopeMember T> struct get_scope;
- All specializations of
get_scope<T>
shall meet theTransformationTrait
requirements (C++ [reflect.rqmts]). The nested type namedtype
is theScope
reflecting a scope S. With ST being the scope of the declaration of the base-level entity reflected byT
, 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]). IfT
reflects a public member (foris_public
), protected member (foris_protected
) or private member (foris_private
), the BaseCharacteristic of the respective template specialization istrue_type
, otherwise it isfalse_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 namedtype
is an alias to anObjectSequence
specialized withRecordMember
types that reflect the following subset of data members of the class reflected byT
:The order of the elements in the
- 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 ofreflexpr
which generated (directly or indirectly)T
;- for
get_data_members
, all data members, irrespective of their accessibility.ObjectSequence
is the order of the declaration of the data members in the class reflected byT
. The class reflected byT
must be complete at the point of the invocation ofreflexpr
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 namedtype
is an alias to anObjectSequence
specialized withType
types that reflect the following subset of types declared in the class reflected byT
:The order of the elements in the
- 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 ofreflexpr
which generated (directly or indirectly)T
;- for
get_member_types
, all member types, irrespective of their accessibility.ObjectSequence
is the order of the first declaration of the types in the class reflected byT
. The class reflected byT
must be complete at the point of the invocation ofreflexpr
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 namedtype
is an alias to anObjectSequence
specialized withBase
types that reflect the following subset of base classes of the class reflected byT
:The order of the elements in the
- 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 ofreflexpr
which generated (directly or indirectly)T
;- for
get_bases
, all bases are taken into account, irrespective of their accessibility.ObjectSequence
is the order of the declaration of the base classes in the class reflected byT
. The class reflected byT
must be complete at the point of the invocation ofreflexpr
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 theUnaryTypeTrait
requirements (C++ [reflect.rqmts]). IfT
reflects a class that is marked with the class-virt-specifierfinal
, the BaseCharacteristic of the respective template specialization istrue_type
, otherwise it isfalse_type
.
template <Enum T> struct is_scoped_enum;
- All specializations of
is_scoped_enum<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]). IfT
reflects a scoped enumeration, the BaseCharacteristic of the respective template specialization istrue_type
, otherwise it isfalse_type
.
template <Enum T> struct get_enumerators;
- All specializations of
get_enumerators<T>
shall meet theTransformationTrait
requirements (C++ [reflect.rqmts]). The nested type namedtype
is an alias to anObjectSequence
specialized withConstant
types that reflect the enumerators of the enumeration type reflected byT
.5.4.10 Value operations[reflect.ops.value]
template <Constant T> struct get_constant;
- All specializations of
get_constant<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]), with a BaseCharacteristic ofintegral_constant<X, x>
, whereX
is the type of the value reflected byT
andx
its value.
template <Variable T> struct is_constexpr;
- All specializations of
is_constexpr<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]). IfT
reflects a variable declared with the decl-specifierconstexpr
, the BaseCharacteristic of the respective template specialization istrue_type
, otherwise it isfalse_type
.
template <Variable T> struct is_static;
- All specializations of
is_static<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]). IfT
reflects a variable with static storage duration, the BaseCharacteristic of the respective template specialization istrue_type
, otherwise it isfalse_type
.
template <Variable T> struct get_pointer;
- All specializations of
get_pointer<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]), with a BaseCharacteristic ofintegral_constant<X, x>
, where
- for variables with static storage duration:
X
isadd_pointer<Y>
, whereY
is the type of the variable reflected byT
andx
is the address of that variable; otherwise,X
is the pointer-to-member type of the member variable reflected byT
andx
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 theTransformationTrait
requirements (C++ [reflect.rqmts]). The nested type namedtype
is an alias toreflexpr(X)
, whereX
is the class of the base-specifier reflected byT
.
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]). IfT
reflects a base-specifier with thevirtual
specifier (foris_virtual
), with thepublic
specifier or with an assumed (see C++ [class.access.base])public
specifier (foris_public
), with theprotected
specifier (foris_protected
), or with theprivate
specifier or with an assumedprivate
specifier (foris_private
), then the BaseCharacteristic of the respective template specialization istrue_type
, otherwise it isfalse_type
.5.4.11 Namespace operations[reflect.ops.namespace]
template <Namespace T> struct is_inline;
- All specializations of
is_inline<T>
shall meet theUnaryTypeTrait
requirements (C++ [reflect.rqmts]). IfT
reflects an inline namespace, the BaseCharacteristic of the template specialization istrue_type
, otherwise it isfalse_type
.
Thanks to Ricardo Fabiano de Andrade, Roland Bock and Klaim-Joël Lamotte who provided valuable feedback, criticism and suggestions.
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.
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.
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.
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).
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.
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 enum
s - 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.
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.
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.
1. Static reflection. Rationale, design and evolution. p0385
2. Static reflection in a nutshell. p0578