Document Number: | P0670R2, ISO/IEC JTC1 SC22 WG21 |
Audience: | EWG, LEWG |
Date: | 2017-11-08 |
Authors: | Matúš Chochlík (chochlik@gmail.com) |
Axel Naumann (axel@cern.ch) | |
David Sankel (camior@gmail.com) |
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.
Most notably, this proposal relies on P0194 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.
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
.
reflexpr
P0194 allows certain types and variables as operands of reflexpr
. This paper extends this set:
reflexpr
on a function call expression generates a FunctionCallExpression
;reflexpr
on a closure type generates a Lambda
;reflexpr
on a parameter name generates a FunctionParameter
;reflexpr
on a lambda capture generates a LambdaCapture
;
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 */;
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
.
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:
tuple
is not a replacement for classes.
template <typename T>
requires FunctionParameter<T>
struct is_ellipsis;
FunctionParameter
reflects an ellipsis.
template <typename T>
requires FunctionParameter<T>
struct has_default_value;
FunctionParameter
has a default value.Callable
template <class Object> concept Callable = /* implementation defined */;
Named
, ScopeMember
and Scope
. Represents a function or lambda, including operators, constructors and destructors - i.e. anything this paper is dealing with.
template <typename T>
requires Callable<T>
struct get_parameters;
ObjectSequence
of FunctionParameter
s of the reflected Callable
.
template <typename T>
requires Callable<T>
struct is_constexpr;
template <typename T>
requires Callable<T>
struct is_noexcept;
constexpr
or noexcept
, respectively.
template <typename T>
requires Callable<T>
struct is_inline;
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;
= delete
before the invocation of reflexpr
.FunctionCallExpression
template <class Object> concept FunctionCallExpression = /* implementation defined */;
Object
. Reflects a call of a concrete function or other callable.
template <typename T>
requires FunctionCallExpression<T>
struct get_callable;
Callable
that was invoked in a FunctionCallExpression
.Function
template <class Object> concept Function = /* implementation defined */;
Callable
and Typed
. Represents a function or lambda, excluding constructors and destructors.
template <typename T>
requires Function<T>
struct get_pointer;
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 */;
RecordMember
and Function
. Represents a member function, excluding constructors and destructors.
template <typename T>
requires RecordMemberFunction<T>
struct is_static;
template <typename T>
requires RecordMemberFunction<T>
struct is_const;
template <typename T>
requires RecordMemberFunction<T>
struct is_volatile;
const
or volatile
, respectively.
template <typename T>
requires RecordMemberFunction<T>
struct has_lvalueref_qualifier;
template <typename T>
requires RecordMemberFunction<T>
struct has_rvalueref_qualifier;
&
or &&
, respectively.
template <typename T>
requires RecordMemberFunction<T>
struct is_virtual;
template <typename T>
requires RecordMemberFunction<T>
struct is_pure_virtual;
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;
override
or final
, respectively.SpecialMemberFunction
template <class Object> concept SpecialMemberFunction = /* implementation defined */;
RecordMember
. Represents a special member function.
template <typename T>
requires SpecialMemberFunction<T>
struct is_implicitly_declared;
reflexpr
.
template <typename T>
requires SpecialMemberFunction<T>
struct is_defaulted;
= 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 */;
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.Constructor
might also satisfy SpecialMemberFunction
.
template <typename T>
requires Constructor<T>
struct is_explicit;
explicit
.Destructor
template <class Object> concept Destructor = /* implementation defined */;
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;
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 */;
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 */;
Operator
. Represents a conversion operator. The base name is the base name of the operator's target type, for instance "int"
.
template <typename T>
requires ConversionOperator<T>
struct is_explicit;
explicit
.Lambda
template <class Object> concept Lambda = /* implementation defined */;
Function
. Represents a closure type, excluding those for generic lambdas. Its base name is the empty string.
template <typename T>
requires Lambda<T>
struct get_captures;
ObjectSequence
of LambdaCapture
s.
template <typename T>
requires Lambda<T>
struct uses_default_copy_capture;
=
.
template <typename T>
requires Lambda<T>
struct uses_default_reference_capture;
&
.
template <typename T>
requires Lambda<T>
struct is_call_operator_const;
false
if the lambda was declared as mutable
, true otherwise
.LambdaCapture
template <class Object> concept LambdaCapture = /* implementation defined */;
Variable
. Represents a lambda capture as introduced by the capture list or by capture defaults. The LambdaCapture
's scope is its Lambda
.
template <typename T>
requires LambdaCapture<T>
struct is_explicitly_captured;
template <typename T>
requires LambdaCapture<T>
struct is_init_capture;
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;
ObjectSequence
of RecordMemberFunctions
s 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;
ObjectSequence
of a class's Constructor
s, Destructor
s and Operator
s, respectively.Thanks to Jackie Kay who provided valuable feedback, criticism and suggestions!
1. Static reflection. Rationale, design and evolution. p0385
2. Static reflection in a nutshell. p0578