Document Number: P0670R3, ISO/IEC JTC1 SC22 WG21
Audience:CWG, LWG
Date:2018-05-07
Authors:Matúš Chochlík (chochlik@gmail.com)
Axel Naumann (axel@cern.ch)
David Sankel (camior@gmail.com)

Function reflection

0. Introduction

P0194 introduced static reflection for types and variables. This paper adds static reflection of functions.

Reflection proposed here behaves as follows:


void func(int);
void func(std::string);
using func_call_m = reflexpr(func(123));
using func_m = get_callable_t<func_call_m>; // reflects void func(int)
using param0_m = get_element_t<0, get_parameters_t<func_m>>;
cout << get_name_v<get_type_t<param0_m>> << '\n'; // prints "int"

The functionality introduced here allows for reflection of calls of concrete functions. This enables, for instance, GUI generation, building a catalogue for remote procedure calls, documentation generation, and signal/slot frameworks. Like P0194, this proposal omits attributes, templates, and reification (i.e. conversion of a meta object to the base level): all warrant a separate paper. We would welcome a paper especially on static reflection of attributes, matching the interface-style of P0194 and this paper! Linkage and friends will be part of a follow-up paper to P0194; they will have a combined "effect" on P0194 and this paper.

0.1 Interplay with other proposals

Most notably, this proposal relies on the Reflection TS and the Concepts TS.

P0385 discusses use cases, rationale, design decisions, and the future evolution of the proposed reflection facility. It also has usage examples and replies to frequently asked questions.

1 Scope [intro.scope]

This document is written as a set of changes against the Reflection TS (N4746). Instructions to modify or add paragraphs are written as explicit instructions. Modifications made directly to existing text from the Reflection TS use underlining to represent added text and strikethrough to represent deleted text.

2 Normative references [intro.refs]

No changes are made to Clause 2 of the Reflection TS.

3 Terms and definitions [intro.defs]

No changes are made to Clause 3 of the Reflection TS.

4 General [intro]

4.3 Acknowledgements [intro.ack]

Modify the section as follows:

This work is the result of a collaboration of researchers in industry and academia. We wish to thank people who made valuable contributions within and outside these groups, including Ricardo Fabiano de Andrade, Roland Bock, Chandler Carruth, Jackie Kay, Klaim-Joël Lamotte, Jens Maurer, and many others not named here who contributed to the discussion.

5 Lexical conventions [lex]

No changes are made to Clause 5 of the Reflection TS.

6 Basic concepts [basic]

No changes are made to Clause 6 of the Reflection TS.

7 Standard conversions[conv]

No changes are made to Clause 7 of the Reflection TS.

8 Expressions[expr]

No changes are made to Clause 8 of the Reflection TS.

9 Statements[stmt.stmt]

No changes are made to Clause 9 of the Reflection TS.

10 Declarations [dcl.dcl]

10.1 Specifiers [dcl.spec]

10.1.7 Type specifiers [dcl.type]

10.1.7.2 Simple type specifiers [dcl.type.simple]

In C++ [dcl.type.simple], apply the following change

10.1.7.6 Reflection type specifier [dcl.type.reflexpr]

Apply the following modification to the enumeration:

For the operand ::, the type specified by the reflexpr-specifier satisfies reflect::GlobalScope. For an operand that can be parsed as a function call expression [expr.call], the type satisfies reflect::FunctionCallExpression. For an operand of the form identifier where identifier is a template type-parameter, the type satisfies both reflect::Type and reflect::Alias.

Modify Table 12 as follows:

Categoryidentifier or simple-template-id kindreflect Concept
typeclass-name designating a unionreflect::Record
class-name designating a closure typereflect::Lambda
class-name designating a non-union classreflect::Class
enum-namereflect::Enum
type-name introduced by a using-declarationboth reflect::Type and reflect::Alias
any other typedef-nameboth reflect::Type and reflect::Alias
namespacenamespace-aliasboth reflect::Namespace and reflect::Alias
any other namespace-nameboth reflect::Namespace and reflect::ScopeMember
data memberthe name of a data memberreflect::Variable
valuethe name of a variable or structured binding that is not a local entityreflect::Variable
the name of an enumeratorboth reflect::Enumerator and reflect::Constant
the name of a function parameterreflect::FunctionParameter
the name of a captured entity [expr.prim.lambda.capture]reflect::LambdaCapture

Modify the following paragraph as follows:

If the reflexpr-operand designates an entity or alias at block scope (6.3.3) or function prototype scope (6.3.4) and the entity is not captured or a function parameter, the program is ill-formed. If the reflexpr-operand designates a class member, the type represented by the reflexpr-specifier also satisfies reflect::RecordMember. If the reflexpr-operand designates an variable or a data member, a function parameter, or a captured entity, it is an unevaluated operand (expr.context). If the reflexpr-operand designates both an alias and a class name, the type represented by the reflexpr-specifier reflects the alias and satisfies Alias. If the overload resolution of the function call expression is ambiguous, the programm is ill-formed.

11 Declarators [dcl.decl]

No changes are made to Clause 11 of the Reflection TS.

13 Derived classes[class.derived]

No changes are made to Clause 13 of the Reflection TS.

14 Member access control[class.access]

No changes are made to Clause 14 of the Reflection TS.

15 Special member functions[special]

No changes are made to Clause 15 of the Reflection TS.

16 Overloading[over]

No changes are made to Clause 16 of the Reflection TS.

17 Templates [temp]

No changes are made to Clause 17 of the Reflection TS.

18 Exception handling[except]

No changes are made to Clause 18 of the Reflection TS.

19 Preprocessing directives[cpp]

No changes are made to Clause 19 of the Reflection TS.

20 Library introduction[library]

No changes are made to Clause 20 of the Reflection TS.

21 Language support library [language.support]

21.11 Static reflection [reflect]

21.11.1 In general[reflect.general]

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

Modify this section as follows:

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

// 21.11.3 Concepts for meta-object types
template <class T> concept Object;
template <class T> concept ObjectSequence;
template <class T> concept Named;
template <class T> concept Alias;
template <class T> concept RecordMember;
template <class T> concept Enumerator;
template <class T> concept Variable;
template <class T> concept ScopeMember;
template <class T> concept Typed;
template <class T> concept Namespace;
template <class T> concept GlobalScope;
template <class T> concept Class;
template <class T> concept Enum;
template <class T> concept Record;
template <class T> concept Scope;
template <class T> concept Type;
template <class T> concept Constant;
template <class T> concept Base;
template <class T> concept FunctionParameter;
template <class T> concept Callable;
template <class T> concept FunctionCallExpression;
template <class T> concept Function;
template <class T> concept RecordMemberFunction;
template <class T> concept SpecialMemberFunction;
template <class T> concept Constructor;
template <class T> concept Destructor;
template <class T> concept ConversionOperator;
template <class T> concept Lambda;
template <class T> concept LambdaCapture;
...

// 21.11.4 Meta-object operations
// 21.11.4.1 Multi-concept operations
template <class T> struct is_public;
template <class T> struct is_protected;
template <class T> struct is_private;
template <class T> struct is_constexpr;
template <class T> struct is_static;
template <class T> struct is_final;
template <class T> struct get_pointer;
template <class T> struct is_explicit;
template <class T> struct is_inline;
template <class T> struct is_virtual;
template <class T> struct is_pure_virtual;

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;
template <class T>
   constexpr auto is_constexpr_v = is_constexpr<T>::value;
template <class T>
   constexpr auto is_static_v = is_static<T>::value;
template <class T>
   constexpr auto is_final_v = is_final<T>::value;
template <class T>
   constexpr auto get_pointer_v = get_pointer<T>::value;
template <class T>
   constexpr auto is_explicit_v = is_explicit<T>::value;
   template <class T>
   constexpr auto is_inline_v = is_inline<T>::value;
template <class T>
   constexpr auto is_virtual_v = is_virtual<T>::value;
template <class T>
   constexpr auto is_pure_virtual_v = is_pure_virtual<T>::value;
...

// 21.11.4.8 Record 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_functions;
template <Record T> struct get_accessible_member_functions;
template <Record T> struct get_member_functions;
template <Record T> struct get_public_member_types;
template <Record T> struct get_accessible_member_types;
template <Record T> struct get_member_types;
template <Record T> struct get_constructors;
template <Record T> struct get_destructors;
template <Record T> struct get_operators;
template <Class T> struct get_public_base_classes;
template <Class T> struct get_accessible_base_classes;
template <Class T> struct get_base_classes;
template <Class T> struct is_final<T>;

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_functions_t = typename get_public_member_functions<T>::type;
template <Record T>
  using get_accessible_member_functions_t = typename get_accessible_member_functions<T>::type;
template <Record T>
  using get_member_functions_t = typename get_member_functions<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 <Record T>
  using get_constructors_t = typename get_constructors<T>::type;
template <Record T>
  using get_destructors_t = typename get_destructors<T>::type;
template <Record T>
  using get_operators_t = typename get_operators<T>::type;
template <Class T>
  using get_public_base_classes_t = typename get_public_base_classes<T>::type;
template <Class T>
  using get_accessible_base_classes_t = typename get_accessible_base_classes<T>::type;
template <Class T>
  using get_base_classes_t = typename get_base_classes<T>::type;
template <Class T>
  constexpr auto is_final_v = is_final<T>::value;

...

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

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;

// 21.11.4.11 Base operations
template <Base T> struct get_class;
template <Base T> struct is_virtual<T>;
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;

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

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

...

// 21.11.4.13 FunctionParameter operations
template <FunctionParameter T> struct is_ellipsis;
template <FunctionParameter T> struct has_default_value;

template <FunctionParameter T>
  constexpr auto is_ellipsis_v = is_ellipsis<T>::value;
template <FunctionParameter T>
  constexpr auto has_default_value_v = has_default_value<T>::value;

// 21.11.4.14 Callable operations
template <Callable T> struct get_parameters;
template <Callable T> struct is_constexpr<T>;
template <Callable T> struct is_noexcept<T>;
template <Callable T> struct is_inline<T>;
template <Callable T> struct is_deleted;

template <Callable T>
  using get_parameters_t = typename get_parameters<T>::type;
template <Callable T>
  constexpr auto is_deleted_v = is_deleted<T>::value;

// 21.11.4.15 FunctionCallExpression operations
template <FunctionCallExpression T> struct get_callable;

template <FunctionCallExpression T>
  using get_callable_t = typename get_callable<T>::type;

// 21.11.4.16 Function operations
template <Function T> struct get_pointer<T>;

// 21.11.4.17 RecordMemberFunction operations
template <RecordMemberFunction T> struct is_static<T>;
template <RecordMemberFunction T> struct is_const;
template <RecordMemberFunction T> struct is_volatile;
template <RecordMemberFunction T> struct has_lvalueref_qualifier;
template <RecordMemberFunction T> struct has_rvalueref_qualifier;
template <RecordMemberFunction T> struct is_virtual<T>;
template <RecordMemberFunction T> struct is_pure_virtual<T>;
template <RecordMemberFunction T> struct is_override;
template <RecordMemberFunction T> struct is_final<T>;

template <RecordMemberFunction T>
  constexpr auto is_const_v = is_const<T>::value;
template <RecordMemberFunction T>
  constexpr auto is_volatile_v = is_volatile<T>::value;
template <RecordMemberFunction T>
  constexpr auto has_lvalueref_qualifier_v = has_lvalueref_qualifier<T>::value;
template <RecordMemberFunction T>
  constexpr auto has_rvalueref_qualifier_v = has_rvalueref_qualifier<T>::value;
template <RecordMemberFunction T>
  constexpr auto is_override_v = is_override<T>::value;

// 21.11.4.18 SpecialMemberFunction operations
template <SpecialMemberFunction T> struct is_implicitly_declared;
template <SpecialMemberFunction T> struct is_defaulted;

template <SpecialMemberFunction T>
  constexpr auto is_implicitly_declared_v = is_implicitly_declared<T>::value;
template <SpecialMemberFunction T>
  constexpr auto is_defaulted_v = is_defaulted<T>::value;

// 21.11.4.19 Constructor operations
template <Constructor T> struct is_explicit<T>;

// 21.11.4.20 Destructor operations
template <Destructor T> struct is_virtual<T>;
template <Destructor T> struct is_pure_virtual<T>;

// 21.11.4.21 ConversionOperator operations
template <ConversionOperator T> struct is_explicit<T>;

// 21.11.4.22 Lambda operations
template <Lambda T> struct get_captures;
template <Lambda T> struct uses_default_copy_capture;
template <Lambda T> struct uses_default_reference_capture;
template <Lambda T> struct is_call_operator_const;

template <Lambda T>
  using get_captures_t = typename get_captures<T>::type;
template <Lambda T>
  constexpr auto uses_default_copy_capture_v = uses_default_copy_capture<T>::value;
template <Lambda T>
  constexpr auto uses_default_reference_capture_v = uses_default_reference_capture<T>::value;
template <Lambda T>
  constexpr auto is_call_operator_const_v = is_call_operator_const<T>::value;

// 21.11.4.23 LambdaCapture operations
template <LambdaCapture T> struct is_explicitly_captured;
template <LambdaCapture T> struct is_init_capture;

template <LambdaCapture T>
  constexpr auto is_explicitly_captured_v = is_explicitly_captured<T>::value;
template <LambdaCapture T>
  constexpr auto is_init_capture_v = is_init_capture<T>::value;

21.11.3 Concepts for meta-object types [reflect.concepts]

Add the following paragraphs at the end of [reflect.concepts]:

21.11.3.19 Concept FunctionParameter[reflect.concepts.fctparam]

template <class T> concept FunctionParameter = see below;

FunctionParameter<T> is satisfied if and only if T reflects a function patameter. Any such T also satisfies Named and ScopeMember. The Scope of a FunctionParameter is the Callable this parameter appertains to.

21.11.3.20 Concept Callable[reflect.concepts.callable]

template <class T> concept Callable = see below;

Callable<T> is satisfied if and only if T reflects a function including operators, constructors and destructors, or an object with a call operator, for instance a closure type. Any such T also satisfies Named, ScopeMember and Scope.

21.11.3.21 Concept FunctionCallExpression[reflect.concepts.expr.fctcall]

template <class T> concept FunctionCallExpression = see below;

FunctionCallExpression<T> is satisfied if and only if T reflects a function call expression [expr.call]. Any such T also satisfies Object.

21.11.3.22 Concept Function[reflect.concepts.fct]

template <class T> concept Function = see below;

Function<T> is satisfied if and only if T reflects an object with a call operator (for instance a closure object) or a function, excluding constructors and destructors. Any such T also satisfies Callable and Typed.

21.11.3.23 Concept RecordMemberFunction[reflect.concepts.memfct]

template <class T> concept RecordMemberFunction = see below;

RecordMemberFunction<T> is satisfied if and only if T reflects a member function, excluding constructors and destructors. Any such T also satisfies RecordMember and Function.

21.11.3.24 Concept SpecialMemberFunction[reflect.concepts.specialfct]

template <class T> concept SpecialMemberFunction = see below;

SpecialMemberFunction<T> is satisfied if and only if T reflects a special member function [special]. Any such T also satisfies RecordMember.

21.11.3.25 Concept Constructor[reflect.concepts.ctor]

template <class T> concept Constructor = see below;

Constructor<T> is satisfied if and only if T reflects a constructor. Any such T also satisfies Callable and RecordMember. Some specializations of Constructor satisfy SpecialMemberFunction.

21.11.3.26 Concept Destructor[reflect.concepts.dtor]

template <class T> concept Destructor = see below;

Destructor<T> is satisfied if and only if T reflects a destructor. Any such T also satisfies Callable, SpecialMemberFunction and RecordMember.

21.11.3.27 Concept Operator[reflect.concepts.oper]

template <class T> concept Operator = see below;

Operator<T> is satisfied if and only if T reflects an operator function [over.oper] or a conversion function [class.conv.fct]. Any such T also satisfies Function. Some specializations of Operator satisfy RecordMemberFunction or SpecialMemberFunction.

21.11.3.28 Concept ConversionOperator[reflect.concepts.convfct]

template <class T> concept ConversionOperator = see below;

ConversionOperator<T> is satisfied if and only if T reflects a conversion function [class.conv.fct]. Any such T also satisfies Function. Some specializations of Operator satisfy RecordMemberFunction.

21.11.3.29 Concept Lambda[reflect.concepts.lambda]

template <class T> concept Lambda = see below;

Lambda<T> is satisfied if and only if T reflects an closure object (excluding generic lambdas). Any such T also satisfies Function.

21.11.3.30 Concept LambdaCapture[reflect.concepts.lambdacapture]

template <class T> concept LambdaCapture = see below;

LambdaCapture<T> is satisfied if and only if T reflects a lambda capture as introduced by the capture list or by capture defaults. Any such T also satisfies Variable. The Scope of a LambdaCapture is its Lambda.

21.11.4 Meta-object Operations [reflect.ops]

21.11.4.1 Multi-concept operations [reflect.ops.over]

Modify [reflect.ops.over] as follows:

template <class T> struct is_public;
template <class T> struct is_protected;
template <class T> struct is_private;
template <class T> struct is_constexpr;
template <class T> struct is_static;
template <class T> struct is_final;
template <class T> struct get_pointer;
template <class T> struct is_explicit;
template <class T> struct is_inline;
template <class T> struct is_virtual;
template <class T> struct is_pure_virtual;

These meta-object operations are applicable to to both RecordMember and Basemore than one of the concepts defined in the reflect namespace.

21.11.4.4 Named operations[reflect.ops.named]

Modify the relevant part of [reflect.ops.over] as follows:

21.11.4.7 Member operations[reflect.ops.member]

Modify the relevant part of [reflect.ops.over] as follows:

With ST being the scope of the declaration of the entity or, typedef or value 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, function scope, or closure type.

21.11.4.8 Record operations[reflect.ops.record]

Modify the relevant part of [reflect.ops.record] as follows:

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_functions;
template <Record T> struct get_accessible_member_functions;
template <Record T> struct get_member_functions;

All specializations of these templates shall meet the TransformationTrait requirements ([meta.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_data_members (get_member_functions), all data (function) members.
  • for get_public_data_members (get_public_member_functions), all public data (function) members;
  • for get_accessible_data_members (get_accessible_member_functions), all data (function) members that are accessible from the scope of the invocation of reflexpr which (directly or indirectly) generated T.
The order of the elements in the ObjectSequence is the order of the declaration of the data members in the class reflected by T.
Remarks:
The program is ill-formed if T reflects a closure type.

template <Record T> struct get_constructors;
template <Record T> struct get_destructors;
template <Record T> struct get_operators;

All specializations of these templates shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with RecordMember types that reflect the following subset of function members of the class reflected by T:
  • for get_constructors, all constructors.
  • for get_destructors, all destructors;
  • for get_operators, all conversion functions [class.conv.fct] and operator functions [over.oper].
The order of the elements in the ObjectSequence is the order of the declaration of the members in the class reflected by T.
Remarks:
The program is ill-formed if T reflects a closure type.

Modify the relevant part of [reflect.ops.record] as follows:

template <Class T> struct is_final<T>;

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

21.11.4.10 Value operations[reflect.ops.value]

Modify the relevant part of [reflect.ops.value] as follows:

template <Variable T> struct is_constexpr<T>;

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

template <Variable T> struct is_static<T>;

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

template <Variable T> struct get_pointer<T>;

All specializations of get_pointer<T> shall meet the UnaryTypeTrait requirements ([meta.rqmts]), with a static data member named value of type X and value 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.

21.11.4.11 Base operations[reflect.ops.derived]

Modify the relevant part of [reflect.ops.derived] as follows:

template <Base T> struct get_class;

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

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

21.11.4.12 Namespace operations[reflect.ops.namespace]

Modify the relevant part of [reflect.ops.derived] as follows:

template <Namespace T> struct is_inline<T>;

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

Add the following paragraphs at the end of [reflect.ops]:

21.11.4.13 FunctionParameter operations [reflect.ops.fctparam]

template <FunctionParameter T> struct is_ellipsis;

All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If T reflects a terminating ellipsis of a function's parameter-declaration-clause [dcl.fct], the base characteristic of is_ellipsis<T> is true_type, otherwise it is false_type.

template <FunctionParameter T> struct has_default_value;

All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If T reflects a parameter with a default argument, the base characteristic of is_enum<T> ( is_union<T>) is true_type, otherwise it is false_type.

21.11.4.14 Callable operations[reflect.ops.callable]

template <Callable T> struct get_parameters;

All specializations of this template shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with FunctionParameter types that reflect the parameters of the function reflected by T. If that function's parameter-declaration-clause [dcl.fct] terminates with an ellipsis, the ObjectSequence contains an additional, final element of type ELL for which is_ellipsis_v<ELL> is true.

template <Callable T> struct is_constexpr<T>;
template <Callable T> struct is_noexcept<T>;
template <Callable T> struct is_inline<T>;
template <Callable T> struct is_deleted;

All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If their template parameter reflects an entity that is (where applicable implicitly or explicitly) declared as constexpr (for is_constexpr), noexcept (for is_noexcept), as an inline function [dcl.inline] (for is_inline), or as deleted (for is_deleted), the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

21.11.4.15 FunctionCallExpression operations[reflect.ops.fctcall]

template <FunctionCallExpression T> struct get_callable;

All specializations of get_callable<T> shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is the Callable type reflecting the function or callable object invoked by the function call expression which is reflected by T.

21.11.4.16 Function operations[reflect.ops.fct]

template <Function T> struct get_pointer<T>;

All specializations of get_pointer<T> shall meet the UnaryTypeTrait requirements ([meta.rqmts]), with a static data member named value of type X and value x, where
  • for non-static member functions, X is the pointer to member function type of the member function reflected by T and x a pointer to the member function; otheriwse,
  • X is add_pointer<Y>, where Y is the type of the function reflected by T and x is the address of that function.

21.11.4.17 RecordMemberFunction operations[reflect.ops.memfct]

template <RecordMemberFunction T> struct is_static<T>;
template <RecordMemberFunction T> struct is_const;
template <RecordMemberFunction T> struct is_volatile;
template <RecordMemberFunction T> struct has_lvalueref_qualifier;
template <RecordMemberFunction T> struct has_rvalueref_qualifier;
template <RecordMemberFunction T> struct is_virtual<T>;
template <RecordMemberFunction T> struct is_pure_virtual<T>;
template <RecordMemberFunction T> struct is_override;
template <RecordMemberFunction T> struct is_final<T>;

All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If their template parameter reflects a member function that is static (for is_static), const (for is_const), volatile (for is_volatile), declared with a ref-qualifier & (for has_lvalueref_qualifier) or && (for has_rvalueref_qualifier), implicitly or expicitly virtual (for is_virtual), pure virtual (for is_pure_virtual), or marked with override (for is_override) or final (for is_final), the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

21.11.4.18 SpecialMemberFunction operations[reflect.ops.specialfct]

template <SpecialMemberFunction T> struct is_implicitly_declared;
template <SpecialMemberFunction T> struct is_defaulted;

All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If their template parameter reflects a special member function that is implicitly declared (for is_implicitly_declared) or that is defaulted (for is_defaulted, the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

21.11.4.19 Constructor operations[reflect.ops.ctor]

template <Constructor T> struct is_explicit<T>;

All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects an explicit constructor, the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

21.11.4.20 Destructor operations[reflect.ops.dtor]

template <Destructor T> struct is_virtual<T>;
template <Destructor T> struct is_pure_virtual<T>;

All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects a virtual (for is_virtual) or pure virtual (for is_pure_virtual) destructor, the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

21.11.4.21 ConversionOperator operations[reflect.ops.convfct]

template <ConversionOperator T> struct is_explicit<T>;

All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects an explicit conversion function, the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

21.11.4.22 Lambda operations[reflect.ops.lambda]

template <Lambda T> struct get_captures;

All specializations of this template shall meet the TransformationTrait requirements ([meta.rqmts]). The nested type named type is an alias to an ObjectSequence specialized with LambdaCapture types that reflect the captures of the closure object reflected by T. The elements are in order of appearance in the lambda-capture.

template <Lambda T> struct uses_default_copy_capture;
template <Lambda T> struct uses_default_reference_capture;

All specializations of these templates shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects a closure object with a capture-default that is = (for uses_default_copy_capture) or & (for uses_default_reference_capture), the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

template <Lambda T> struct is_call_operator_const;

All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects a closure object with a const call operator, the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

21.11.4.23 LambdaCapture operations[reflect.ops.lambdacapture]

template <LambdaCapture T> struct is_explicitly_captured;

All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects an explicitly captured entity, the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

template <LambdaCapture T> struct is_init_capture;

All specializations of this template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). If the template parameter reflects an init-capture, the base characteristic of the respective template specialization is true_type, otherwise it is false_type.

Concepts and Operations

Following P0194's lead, function reflection requires a couple new concepts to restrict operations on the meta level. It builds upon the concepts introduced by P0194 and, like P0194, all declarations are inside the reflect namespace. Some of the concepts below extend P0194's concepts. Whenever a concept A requires another concept B, the operations defined for concept B are also available for concept A, building a graph similar to an inheritance graph.

Proper wording will be provided when needed (and when P0194 has progressed through LWG, increasing to the authors' experience on wording of reflection papers); we believe that missing wording should not hinder SG7's design discussion. The wording is planned to be similar to that of P0194; specifically, it uses nested type and value entities, with the usual using declarations ..._t and ..._v.

Extending operands of reflexpr

P0194 allows certain types and variables as operands of reflexpr. This paper extends this set:

The way to obtain a Callable might seem complicated. But instead of inventing a new syntax, for instance reflexpr(foo(int,int)), re-using the mechanism of matching the address of an overloaded function name reflexpr((void(&)(int, int))foo), or syntax similar to function definition reflexpr(void foo(int,int)), we rely on the existing, familiar rules for function overload resolution and generate the Callable only when a concrete overload is selected. This approach also works for constructors and operators, for instance reflexpr(std::string()) reflects the call of the default constructor of string; reflexpr(1+1) reflects the call of the built-in addition operator for type int.

FunctionParameter

template <class Object> concept FunctionParameter = /* implementation defined */;

Requires Named and ScopeMember. Represents a function parameter. Unlike Variable, it does not offer a get_pointer interface. The name of a parameter is one of the names used in an unspecified declaration. If at least one of the declarations does not specify the parameter name, then get_name is allowed to return an empty string. Its scope is the Callable declaring this FunctionParameter.
Given the lack of relevance that C++ attributes to parameter names in function declarations one might ask: "o really?!" We believe that the answer is "yes": parameter names often carry significant meaning. Examples:
  
    double Gauss(double x, double mean, double width, double height);

    void f() {
      // Don't confuse those!
      func(true /*willLeave*/,
           false /*knewWhatTheyVotedOn*/,
           false /*willBeHappyEverAfter*/);
    }
  
  
The bare combination of type and index is almost meaningless in these cases. There are many reflection applications that can benefit from this, for instance: To put it differently: functions without parameter names are like classes without member names. And tuple is not a replacement for classes.
Another common objection is that multiple declarations could potentially have different parameter names ascribed. This concern is mitigated in two ways:
  1. Modern coding conventions have the declarations for a particular function showing up in exactly one header file.
  2. Modern coding conventions discourage the use of different argument names between function declarations (in a header) and function definitions (in a '.cpp' file). Dedicated compiler warnings exist to protecte against this case.

Operations

template <typename T>
requires FunctionParameter<T>
struct is_ellipsis;

Indicates that a FunctionParameter reflects an ellipsis.

template <typename T>
requires FunctionParameter<T>
struct has_default_value;

Indicates that a FunctionParameter has a default value.

Callable

template <class Object> concept Callable = /* implementation defined */;

Requires Named, ScopeMember and Scope. Represents a function or lambda, including operators, constructors and destructors - i.e. anything this paper is dealing with.

Operations

template <typename T>
requires Callable<T>
struct get_parameters;

Returns an ObjectSequence of FunctionParameters of the reflected Callable.

template <typename T>
requires Callable<T>
struct is_constexpr;

template <typename T>
requires Callable<T>
struct is_noexcept;

Returns whether the function was declared as constexpr or noexcept, respectively.

template <typename T>
requires Callable<T>
struct is_inline;

Returns whether the function is an inline function. With struct X{ inline void f(); void g() {} }, is_inline is true for both f and g.

template <typename T>
requires Callable<T>
struct is_deleted;

Returns whether the function was defined as = delete before the invocation of reflexpr.

FunctionCallExpression

template <class Object> concept FunctionCallExpression = /* implementation defined */;

Requires Object. Reflects a call of a concrete function or other callable.

Operations

template <typename T>
requires FunctionCallExpression<T>
struct get_callable;

Returns the Callable that was invoked in a FunctionCallExpression.

Function

template <class Object> concept Function = /* implementation defined */;

Requires Callable and Typed. Represents a function or lambda, excluding constructors and destructors.

Operations

template <typename T>
requires Function<T>
struct get_pointer;

Returns a pointer to the function. This is a pointer-to-member for non-static member functions (including lambda calls), and a function pointer otherwise. It is ill-formed to invoke this for deleted functions. Example: auto p_sin = get_pointer_v<get_callable_t<reflexpr(sin(1.0))>> holds the address of sin(double).

RecordMemberFunction

template <class Object> concept RecordMemberFunction = /* implementation defined */;

Requires RecordMember and Function. Represents a member function, excluding constructors and destructors.

Operations

template <typename T>
requires RecordMemberFunction<T>
struct is_static;

Returns whether this is a static member function.

template <typename T>
requires RecordMemberFunction<T>
struct is_const;

template <typename T>
requires RecordMemberFunction<T>
struct is_volatile;

Returns whether the function is declared as const or volatile, respectively.

template <typename T>
requires RecordMemberFunction<T>
struct has_lvalueref_qualifier;

template <typename T>
requires RecordMemberFunction<T>
struct has_rvalueref_qualifier;

Returns whether the function is declared as with a ref-qualifier being & or &&, respectively.

template <typename T>
requires RecordMemberFunction<T>
struct is_virtual;

template <typename T>
requires RecordMemberFunction<T>
struct is_pure_virtual;

Returns whether the function is a virtual or pure virtual function, respectively. For
  
    struct A { virtual void X(); };
    struct B: A { void X(); };
  
the value of is_virtual_v<get_callable_t<reflexpr(std::declval<B>().X())>> is true, irrespectively of whether virtual is implicit of explicit.

template <typename T>
requires RecordMemberFunction<T>
struct is_override;

template <typename T>
requires RecordMemberFunction<T>
struct is_final;

Returns whether the function is declared as override or final, respectively.

SpecialMemberFunction

template <class Object> concept SpecialMemberFunction = /* implementation defined */;

Requires RecordMember. Represents a special member function.

Operations

template <typename T>
requires SpecialMemberFunction<T>
struct is_implicitly_declared;

Returns whether the special member function is known to be implicitly declared at the point of invocation of reflexpr.

template <typename T>
requires SpecialMemberFunction<T>
struct is_defaulted;

Returns whether the function is defined as = default before the invocation of reflexpr, independently of whether the special member function is implicitly or explicitly declared.

Constructor

template <class Object> concept Constructor = /* implementation defined */;

Requires Callable and RecordMember. Represents a constructor. The base name of the constructor is the base name of the constructor's class. Even though the standard explicitly says that constructors do not have a name, for usability purposes (e.g. generating messages), having them state the class name is a usability improvement.
Some instances of Constructor might also satisfy SpecialMemberFunction.

Operations

template <typename T>
requires Constructor<T>
struct is_explicit;

Returns whether the constructor is known to be declared as explicit.

Destructor

template <class Object> concept Destructor = /* implementation defined */;

Requires Callable, SpecialMemberFunction and RecordMember. Represents a destructor. The base name is the base name if the destructor's class, prefixed with '~'.

template <typename T>
requires Destructor<T>
struct is_virtual;

template <typename T>
requires Destructor<T>
struct is_pure_virtual;

Returns whether the destructor is a virtual or pure virtual function, respectively. For
  
    struct A { virtual ~A(); };
    struct B: A { B(); };
  
the value of is_virtual_v<get_callable_t<reflexpr(std::declval<B>.~B())>> is true, irrespectively of whether virtual is implicit of explicit.

Operator

template <class Object> concept Operator = /* implementation defined */;

Requires Function. Some instances might implement RecordMemberFunction or SpecialMemberFunction. Represents an operator. The base name is the operator "symbol", for instance "+".

ConversionOperator

template <class Object> concept ConversionOperator = /* implementation defined */;

Requires Operator. Represents a conversion operator. The base name is the base name of the operator's target type, for instance "int".

Operations

template <typename T>
requires ConversionOperator<T>
struct is_explicit;

Returns whether the function is declared as explicit.

Lambda

template <class Object> concept Lambda = /* implementation defined */;

Requires Function. Represents a closure type, excluding those for generic lambdas. Its base name is the empty string.

Operations

template <typename T>
requires Lambda<T>
struct get_captures;

Returns an ObjectSequence of LambdaCaptures.

template <typename T>
requires Lambda<T>
struct uses_default_copy_capture;

Returns whether the capture-default is =.

template <typename T>
requires Lambda<T>
struct uses_default_reference_capture;

Returns whether the capture-default is &.

template <typename T>
requires Lambda<T>
struct is_call_operator_const;

Returns false if the lambda was declared as mutable, true otherwise.

LambdaCapture

template <class Object> concept LambdaCapture = /* implementation defined */;

Requires Variable. Represents a lambda capture as introduced by the capture list or by capture defaults. The LambdaCapture's scope is its Lambda.

Operations

template <typename T>
requires LambdaCapture<T>
struct is_explicitly_captured;

Returns whether the entity was captured explicitly.

template <typename T>
requires LambdaCapture<T>
struct is_init_capture;

Returns whether the entity is an init-capture.

Extending Record

This proposal adds the following interfaces to the Record concept of P0194:

template <typename T>
requires Record<T>
struct get_public_member_functions;

template <typename T>
requires Record<T>
struct get_accessible_member_functions;

template <typename T>
requires Record<T>
struct get_member_functions;

Returns an ObjectSequence of RecordMemberFunctionss representing a class's public member functions (for get_public_member_functions), member functions accessible from the point of invocation of reflexpr (for get_accessible_member_functions) and all member functions, irrespective of their accessibility (for get_member_functions). This includes static member functions, but not friend functions.

template <typename T>
requires Record<T>
struct get_constructors;

template <typename T>
requires Record<T>
struct get_destructors;

template <typename T>
requires Record<T>
struct get_operators;

Returns an ObjectSequence of a class's Constructors, Destructors and Operators, respectively.

Acknowledgments

Thanks to Jackie Kay who provided valuable feedback, criticism and suggestions!

References

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

2. Static reflection in a nutshell. p0578