properties
- ️Sun Jan 13 2019
Title: | A General Property Customization Mechanism |
Authors: | David Hollman, dshollm@sandia.gov |
Chris Kohlhoff, chris@kohlhoff.com | |
Bryce Lelbach, brycelelbach@gmail.com | |
Jared Hoberock, jhoberock@nvidia.com | |
Gordon Brown, gordon@codeplay.com | |
Michał Dominiak, griwes@griwes.info | |
Other Contributors: | Lee Howes, lwh@fb.com |
Michael Garland, mgarland@nvidia.com | |
Chris Mysen, mysen@google.com | |
Thomas Rodgers, rodgert@twrodgers.com | |
Michael Wong, michael@codeplay.com | |
Document Number: | P1393R0 |
Date: | 2019-01-13 |
Audience: | LEWG |
Reply-to: | sg1-exec@googlegroups.com |
Abstract: | This paper generalizes and extracts the property customization mechanism from P0443r8, as requested by LEWG in the 2018-11 San Diego meeting. This document does not introduce any significant design changes from the design previously discussed in the context of P0443; the separation herein is merely a recognition of the mechanism’s general applicability and is made in anticipation of its use in other contexts. |
Changelog
Revision 0
- Initial design, migrated from P0443R9.
Introduction
At the 2018-11 San Diego meeting, LEWG voted to generalize the mechanism for property-based customization that P0443R9 introduced. They requested that the customization points objects for handling properties be moved to the namespace std
(from namespace std::execution
), and that a paper presenting wording for the mechanism in a manner decoupled from executors be brought to the 2019-02 Kona meeting. LEWG further requested that a separate customization point object be provided for properties that are intended to enforce the presence of a particular interface, and this paper includes that object as require_concept
. The requested changes have been provided here. Discussion pertaining to the design of this mechanism has been omitted here, since significant background and discussion has been included in previous revisions of P0443 and meeting notes on the discussion thereof.
Proposed Wording
Add the following row to the table in [utilities.general]:
Subclause | Header(s) | |
[properties] | Support for the property customization mechanism | <property> |
Add the following subclause to [utilities] in a section which the editor shall determine:
Properties Support
General
This subclause describes components supporting an extensible customization mechanism, currently used most prominently by the execution support library [execution].
namespace std {
// Customization point objects:
inline namespace unspecified {
inline constexpr unspecified require_concept = unspecified;
inline constexpr unspecified require = unspecified;
inline constexpr unspecified prefer = unspecified;
inline constexpr unspecified query = unspecified;
}
// Property applicability trait:
template<class T, class P> struct is_applicable_property;
template<class T, class Property>
inline constexpr bool is_applicable_property_v = is_applicable_property<T, Property>::value;
// Customization point type traits:
template<class T, class P> struct can_require_concept;
template<class T, class... P> struct can_require;
template<class T, class... P> struct can_prefer;
template<class T, class P> struct can_query;
template<class T, class Property>
inline constexpr bool can_require_concept_v = can_require_concept<T, Property>::value;
template<class T, class... Properties>
inline constexpr bool can_require_v = can_require<T, Properties...>::value;
template<class T, class... Properties>
inline constexpr bool can_prefer_v = can_prefer<T, Properties...>::value;
template<class T, class Property>
inline constexpr bool can_query_v = can_query<T, Property>::value;
} // namespace std
Customization point objects
require_concept
inline namespace unspecified { inline constexpr unspecified require_concept = unspecified; }
The name require_concept
denotes a customization point object. The expression std::require_concept(E, P)
for some subexpressions E
and P
(with types T = decay_t<decltype(E)>
and Prop = decay_t<decltype(P)>
) is expression-equivalent to:
If
is_applicable_property_v<T, Prop> && Prop::is_requirable_concept
is not a well-formed constant expression with valuetrue
,std::require_concept(E, P)
is ill-formed.Otherwise,
E
if the expressionProp::template static_query_v<T> == Prop::value()
is a well-formed constant expression with valuetrue
.Otherwise,
(E).require_concept(P)
if the expression(E).require_concept(P)
is well-formed.Otherwise,
require_concept(E, P)
if the expressionrequire_concept(E, P)
is a valid expression with overload resolution performed in a context that does not include the declaration of therequire_concept
customization point object.Otherwise,
std::require_concept(E, P)
is ill-formed.
require
inline namespace unspecified { inline constexpr unspecified require = unspecified; }
The name require
denotes a customization point object. The expression std::require(E, P0, Pn...)
for some subexpressions E
and P0
, and where Pn...
represents N
subexpressions (where N
is 0 or more, and with types T = decay_t<decltype(E)>
and Prop0 = decay_t<decltype(P0)>
) is expression-equivalent to:
If
is_applicable_property_v<T, Prop0> && Prop0::is_requirable
is not a well-formed constant expression with valuetrue
,std::require(E, P0, Pn...)
is ill-formed.Otherwise,
E
ifN == 0
and the expressionProp0::template static_query_v<T> == Prop0::value()
is a well-formed constant expression with valuetrue
.Otherwise,
(E).require(P0)
ifN == 0
and the expression(E).require(P0)
is a valid expression.Otherwise,
require(E, P)
ifN == 0
and the expressionrequire(E, P)
is a valid expression with overload resolution performed in a context that does not include the declaration of therequire
customization point object.Otherwise,
std::require(std::require(E, P0), Pn...)
ifN > 0
and the expressionstd::require(std::require(E, P0), Pn...)
is a valid expression.Otherwise,
std::require(E, P0, Pn...)
is ill-formed.
prefer
inline namespace unspecified { inline constexpr unspecified prefer = unspecified; }
The name prefer
denotes a customization point object. The expression std::prefer(E, P0, Pn...)
for some subexpressions E
and P0
, and where Pn...
represents N
subexpressions (where N
is 0 or more, and with types T = decay_t<decltype(E)>
and Prop0 = decay_t<decltype(P0)>
) is expression-equivalent to:
If
is_applicable_property_v<T, Prop0> && Prop0::is_preferable
is not a well-formed constant expression with valuetrue
,std::prefer(E, P0, Pn...)
is ill-formed.Otherwise,
E
ifN == 0
and the expressionProp0::template static_query_v<T> == Prop0::value()
is a well-formed constant expression with valuetrue
.Otherwise,
(E).require(P0)
ifN == 0
and the expression(E).require(P0)
is a valid expression.Otherwise,
require(E, P0)
ifN == 0
and the expressionrequire(E, P0)
is a valid expression with overload resolution performed in a context that does not include the declaration of therequire
customization point object.Otherwise,
std::prefer(std::prefer(E, P0), Pn...)
ifN > 0
and the expressionstd::prefer(std::prefer(E, P0), Pn...)
is a valid expression.Otherwise,
std::prefer(E, P0, Pn...)
is ill-formed.
query
inline namespace unspecified { inline constexpr unspecified query = unspecified; }
The name query
denotes a customization point object. The expression std::query(E, P)
for some subexpressions E
and P
(with types T = decay_t<decltype(E)>
and Prop = decay_t<decltype(P)>
) is expression-equivalent to:
If
is_applicable_property_v<T, Prop>
is not a well-formed constant expression with valuetrue
,std::query(E, P)
is ill-formed.Otherwise,
Prop::template static_query_v<T>
if the expressionProp::template static_query_v<T>
is a well-formed constant expression.Otherwise,
(E).query(P)
if the expression(E).query(P)
is well-formed.Otherwise,
query(E, P)
if the expressionquery(E, P)
is a valid expression with overload resolution performed in a context that does not include the declaration of thequery
customization point object.Otherwise,
std::query(E, P)
is ill-formed.
Property applicability trait
template<class T, class Property> struct is_applicable_property;
This sub-clause contains a template that may be used to query the applicability of a property to a type at compile time. It may be specialized to indicate applicability of a property to a type. This template is a UnaryTypeTrait (C++Std [meta.rqmts]) with a BaseCharacteristic of true_type
if the corresponding condition is true, otherwise false_type
.
Template | Condition | Preconditions |
---|---|---|
template<class T, class P> struct is_applicable_property |
The expression P::template is_applicable_property_v<T> is a well-formed constant expression with a value of true . |
P and T are complete types. |
Customization point type traits
template<class T, class Property> struct can_require_concept; template<class T, class... Properties> struct can_require; template<class T, class... Properties> struct can_prefer; template<class T, class Property> struct can_query;
This sub-clause contains templates that may be used to query the validity of the application of property customization point objects to a type at compile time. Each of these templates is a UnaryTypeTrait (C++Std [meta.rqmts]) with a BaseCharacteristic of true_type
if the corresponding condition is true, otherwise false_type
.
Template | Condition | Preconditions |
---|---|---|
template<class T, class P> struct can_require_concept |
The expression std::require_concept(declval<const T>(), declval<P>()) is well-formed. |
T and P are complete types. |
template<class T, class... P> struct can_require |
The expression std::require(declval<const T>(), declval<P>()...) is well-formed. |
T and P... are complete types. |
template<class T, class... P> struct can_prefer |
The expression std::prefer(declval<const T>(), declval<P>()...) is well-formed. |
T and P... are complete types. |
template<class T, class P> struct can_query |
The expression std::query(declval<const T>(), declval<P>()) is well-formed. |
T and P are complete types. |
template<class T, class P> struct is_applicable_property |
The expression P::template is_applicable_property_v<T> is a well-formed constant expression with a value of true . |
P and T are complete types. |
The property customization mechanism
In general
When the property customization mechanism is being employed for some library facility, an object’s behavior and effects on that facility in generic contexts may be determined by a set of applicable properties, and each property imposes certain requirements on that object’s behavior or exposes some attribute of that object. As well as modifying the behavior of an object, properties can be applied to an object to enforce the presence of an interface, potentially resulting in an object of a new type that satisfies some concept associated with that property.
Requirements on properties
A property type
P
shall provide a nested constant variable template namedis_applicable_property_v
, usable asP::template is_applicable_property_v<T>
on any complete typeT
, with a value of typebool
.- A property type shall be either a concept-preserving property type or a concept-enforcing property type.
- A concept-preserving property type
PN
shall provide:- A nested constant expression named
is_requirable
of typebool
, usable asPN::is_requirable
. - A nested constant expression named
is_preferable
of typebool
, usable asPN::is_preferable
.
- A nested constant expression named
- A concept-enforcing property type
PC
that shall provide a nested constant expression namedis_requirable_concept
of typebool
, usable asPC::is_requirable_concept
.
- A concept-preserving property type
A property type
P
may provide a nested typepolymorphic_query_result_type
that satisfies theCopyConstructible
andDestructible
requirements. IfP
is a concept-preserving property, andP::is_requirable == true
orP::is_preferable == true
, thenpolymorphic_query_result_type
shall also satisfy theDefaultConstructible
requirements when provided. [Note: When present, this type allows the property to be used with polymorphic wrappers. –end note]- A property type
P
may provide:- A nested variable template
static_query_v
, usable asP::template static_query_v<T>
for any typeT
whereis_applicable_property_v<T, P>
istrue
. [Note: This may be conditionally present. —end note] - A member function
value()
.
- A nested variable template
For a property type
P
and typeT
whereis_applicable_property_v<T, P>
istrue
, ifP::value()
is a valid expression of typeV1
andP::template static_query_v<T>
is a valid constant expression of typeV2
, thenV1
andV2
shall meet the requirements ofEqualityComparableWith<V1, V2>
in [concept.equalitycomparable].[Note: The
static_query_v
andvalue()
members are used to determine whether invokingrequire
orrequire_concept
would result in an identity transformation. —end note]- A concept-enforcing property type
P
may provide a nested template or namedpolymorphic_wrapper_type
, usable with propertiesPs...
astypename P::template polymorphic_wrapper_type<Ps...>
. The typePW
resulting from the instantiation of this template shall:- be implicitly constructible from a (potentially
const
) instance of a typeT
, where:can_query_v<Pn, T>
istrue
for allPn
inPs...
wherePn::polymorhic_query_result_type
is well-formed- for all
Pn
inPs...
withPn::is_requirable == true
, either:can_require_v<Pn, T>
istrue
, orcan_prefer_v<Pn, T>
istrue
can_prefer_v<Pn, T>
istrue
for allPn
inPs...
withPn::is_preferable == true
P::template static_query_v<T> == P::value()
is true ifP::template static_query_v<T> == P::value()
is a valid constant expression
- be valid in the constant expression
is_applicable_property_v<PW, P>
, and that expression shall betrue
. - if
P::template static_query_v<PW> == P::value()
is a valid constant expression, then that expression shall betrue
.
- be implicitly constructible from a (potentially
- Let
Prop
be a concept-preserving property and letCP
be a concept-enforcing property. Lete
be a instance of a typeE
for whichcan_require_v<E, Prop>
istrue
. Letprop
be an instance of typeProp
. LetProps...
be a list of properties for which the expressionCP::template polymorphic_wrapper_type<Props...>(e)
is well-formed. If the expressionCP::template static_query_v<E> == CP::value()
is a well-formed constant expression with valuetrue
, the expressionCP::template polymorphic_wrapper_type<Props...>(std::require(e, prop))
shall be well-formed if and only if, given the typeT = decay_t<decltype(std::require(e, prop))>
,can_query_v<Pn, T>
istrue
for allPn
inProps...
wherePn::polymorphic_query_result_type
is well-formedcan_require_concept_v<Pn, T>
istrue
for allPn
inProps...
withPn::is_requirable_concept == true
- for all
Pn
inProps...
withPn::is_requirable == true
, either:can_require_v<Pn, T>
istrue
, orcan_prefer_v<Pn, T>
istrue
can_prefer_v<Pn, T>
istrue
for allPn
inProps...
withPn::is_preferable == true
[Note: For example, the struct
S
provides the type for a concept-enforcing property:
struct S
{
static constexpr bool is_requirable_concept = true;
template<class... Ps>
class polymorphic_wrapper_type;
using polymorphic_query_result_type = bool;
template<class T>
static constexpr bool static_query_v = /* ... */;
static constexpr bool value() const { return true; }
};
—end note]