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

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].

Header <property> synopsis

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:

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:

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:

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:

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

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]