Document Number: | P0670R0, ISO/IEC JTC1 SC22 WG21 |
Audience: | SG7 (Reflection), EWG, LEWG |
Date: | 2017-06-18 |
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);
using func_overload_m = reflexpr(func);
using func_m = get_element_t<0, get_overloads_t<func_overload_m>>;
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 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 name generates an OverloadSet
;reflexpr
on a function declaration generates a Callable
that additionally satisfies other concepts defined in this paper, for instance Constructor
;reflexpr
on a closure type generates a Lambda
;reflexpr
on a parameter name generates a FunctionParameter
;reflexpr
on a lambda capture generates a LambdaCapture
;
When invoking reflexpr
on a name, the resulting OverloadSet
comprises all functions (member or not) that are part of the overload set at the point of invocation of the reflexpr
operator. Example:
struct A;
int operator+(A, A);
struct B {
B& operator+(B);
auto op_plus_overloads = reflexpr(operator+);
};
Here, op_plus_overloads
contains both int operator+(A, A)
and B& B::operator+(B)
.
The way to generate a Callable
might seem baroque. But instead of inventing a new syntax, for instance reflexpr(foo(int,int))
, or re-using the mechanism of matching the address of an overloaded function name reflexpr((void(&)(int, int))foo)
, we decided to use the far more common and legible approach reflexpr(void foo(int,int))
, similar to the way a function definition would be introduced. This approach also works for constructors and destructors, for instance reflexpr(std::string::string())
reflects the default constructor of string
.
FunctionParameter
template <class Object> concept bool FunctionParameter();
Named
and ScopeMember
. Represents a function parameter. Unlike Variable
, it does not offer a get_pointer
interface. The name of a parameter is the name used in the most recent redeclaration at the point of invocation of the reflexpr
operator. 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.Callable
template <class Object> concept bool Callable();
Named
, ScopeMember
and Scope
. Represents a function or lambda, including operators, constructors and destructors - i.e. anything this paper is dealing with.
template <Callable> struct get_parameters;
ObjectSequence
of FunctionParameter
s of the reflected Callable
.
template <Callable> struct is_constexpr;
template <Callable> struct is_noexcept;
constexpr
or noexcept
, respectively.
template <Callable> struct is_inline;
struct X{ inline void f(); void g() {} }
, is_inline
is true
for both f
and g
.
template <Callable> struct is_deleted;
= delete
before the invocation of reflexpr
.OverloadSet
template <class Object> concept bool OverloadSet();
Named
. Represents a sequence of overloads with a given name. Example: reflexpr(sin)
returns a type that satisfies OverloadSet
.
template <OverloadSet> struct get_overloads;
ObjectSequence
of Callable
s of a given name, e.g. get_overloads_t<reflexpr(sin)>
returns a ObjectSequence
of all sin
overloads.
This includes template specializations at the point of invocation of the reflexpr
operator. The order of the elements is the order of declaration, ignoring possible redeclarations.Function
template <class Object> concept bool Function();
Callable
and Typed
. Represents a function or lambda, excluding constructors and destructors.
template <Function> struct get_pointer;
auto p_sin = get_pointer_v<reflexpr(double sin(double))>
holds the address of sin(double)
.RecordMemberFunction
template <class Object> concept bool RecordMemberFunction();
RecordMember
and Function
. Represents a member function, excluding constructors and destructors.
template <RecordMemberFunction> struct is_static;
template <RecordMemberFunction> struct is_const;
template <RecordMemberFunction> struct is_volatile;
const
or volatile
, respectively.
template <RecordMemberFunction> struct has_lvalueref_qualifier;
template <RecordMemberFunction> struct has_rvalueref_qualifier;
&
or &&
, respectively.
template <RecordMemberFunction> struct is_virtual;
template <RecordMemberFunction> struct is_pure_virtual;
struct A { virtual void X(); };
struct B: A { void X(); };
the value of is_virtual_v<reflexpr(void B::X())>
is true
, irrespectively of whether virtual
is implicit of explicit.
template <RecordMemberFunction> struct is_override;
template <RecordMemberFunction> struct is_final;
override
or final
, respectively.SpecialMemberFunction
template <class Object> concept bool SpecialMemberFunction();
RecordMember
. Represents a special member function.
template <SpecialMemberFunction> struct is_implicitly_declared;
reflexpr
.
template <SpecialMemberFunction> 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 bool Constructor();
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 <Constructor> struct is_explicit;
explicit
.Destructor
template <class Object> concept bool Destructor();
Callable
, SpecialMemberFunction
and RecordMember
. Represents a destructor. The base name is the base name if the destructor's class, prefixed with '~'
.
template <Destructor> struct is_virtual;
template <Destructor> struct is_pure_virtual;
struct A { virtual ~A(); };
struct B: A { B(); };
the value of is_virtual_v<reflexpr(void B::~B())>
is true
, irrespectively of whether virtual
is implicit of explicit.Operator
template <class Object> concept bool Operator();
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 bool ConversionOperator();
Operator
. Represents a conversion operator. The base name is the base name of the operator's target type, for instance "int"
.
template <ConversionOperator> struct is_explicit;
explicit
.LambdaCapture
template <class Object> concept bool LambdaCapture();
Variable
. Represents a lambda capture as introduced by the capture list or by capture defaults. The LambdaCapture
's scope is its Lambda
.
template <LambdaCapture> struct is_explicitly_captured;
template <LambdaCapture> struct is_init_capture;
Lambda
template <class Object> concept bool Lambda();
Function
. Represents a closure type, excluding those for generic lambdas. Its base name is the empty string.
template <Lambda> struct get_captures;
ObjectSequence
of LambdaCapture
s.
template <Lambda> struct uses_default_copy_capture;
=
.
template <Lambda> struct uses_default_reference_capture;
&
.
template <Lambda> struct is_call_operator_const;
false
if the lambda was declared as mutable
, true otherwise
.Record
This proposal adds the following interfaces to the Record
concept of P0194:
template <Record T> struct get_public_member_functions;
template <Record T> struct get_accessible_member_functions;
template <Record T> struct get_member_functions;
ObjectSequence
of OverloadSet
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
).
template <Record T> struct get_constructors;
template <Record T> struct get_destructors;
template <Record T> struct get_operators;
ObjectSequence
of a class's Constructor
s, Destructor
s and Operator
s, respectively.
template<Record T, class U> get_conversion_op;
OverloadSet
of the ConversionOperator
s converting the class to T
. It is ill-formed to invoke this if no such operator exists.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