Layout-compatibility and Pointer-interconvertibility Traits

Similar documents
Layout-compatibility and Pointer-interconvertibility Traits

P0591r4 Utility functions to implement uses-allocator construction

Expansion statements. Version history. Introduction. Basic usage

Class Types in Non-Type Template Parameters

Proposed Resolution to TR1 Issues 3.12, 3.14, and 3.15

Use Cases for Compile-Time Reflection

Prohibit aggregate types with user-declared constructors

Class Types in Non-Type Template Parameters

simd<t> Finding the right set of traits for Document Number: P0964R1 Date: Matthias Kretz Target: Parallelism TS 2

Modules: ADL & Internal Linkage

Variadic functions: Variadic templates or initializer lists? -- Revision 1

Wording for lambdas in unevaluated contexts

American National Standards Institute Reply to: Josee Lajoie

Working Draft, Extensions to C++ for Modules

P1267R0: Custom Constraint Diagnostics

Classes. Logical method to organise data and functions in a same structure. Also known as abstract data type (ADT).

C++11/14 Rocks. Clang Edition. Alex Korban

Coroutine concepts and metafunctions

A polymorphic value-type for C++

Working Draft, Extensions to C++ for Modules

common_type and duration

Proposal to Simplify pair (rev 4)

Contra Support Merged Proposal

Implicit Evaluation of auto Variables and Arguments

Fixing Atomic Initialization, Rev1

register lock_guard(mtx_); string_view s = register to_string(42); We propose register-expression to grant the temporary objects scope lifetimes.

Bind Returned/Initialized Objects to the Lifetime of Parameters, Rev0

C++11 Introduction to the New Standard. Alejandro Cabrera February 1, 2012 Florida State University Department of Computer Science

Preliminaries. Part I

QUIZ. What is wrong with this code that uses default arguments?

Modules: Contexts of Template Instantiations and Name Lookup Gabriel Dos Reis Microsoft

The C++ Programming Language, Core Working Group. Title: Unary Folds and Empty Parameter Packs (revision 1)

Override Control Using Contextual Keywords

Implicit Evaluation of auto Variables and Arguments

Declaration Syntax. Declarations. Declarators. Declaration Specifiers. Declaration Examples. Declaration Examples. Declarators include:

Sizes Should Only span Unsigned

Basic concepts. Chapter Toplevel loop

Revised Latches and Barriers for C++20

Template Issue Resolutions from the Stockholm Meeting

C++11 and Compiler Update

std::assume_aligned Abstract

A polymorphic value-type for C++

Client Code - the code that uses the classes under discussion. Coupling - code in one module depends on code in another module

McCa!"s Triangle of Quality

Transformation Trait remove_cvref 1

p0407r2 - Allocator-aware basic_stringbuf

Proposed Wording for Concurrent Data Structures: Hazard Pointer and Read Copy Update (RCU)

More Improvements to std::future<t> - Revision 1

A Unit Type for C++ Introduction. Related proposals. Proposed changes

p0408r4 - Efficient Access to basic_stringbuf s Buffer Including wording from p0407 Allocator-aware basic_stringbuf

Programming in C++ Prof. Partha Pratim Das Department of Computer Science and Engineering Indian Institute of Technology, Kharagpur

Allowing Class Template Specializations in Associated Namespaces (revision 1)

Implementing Interfaces. Marwan Burelle. July 20, 2012

Use Cases for Compile-Time Reflection (Rev. 2)

Explicit Conversion Operator Draft Working Paper

Apply the following edits to N4741, the working draft of the Standard. The feature test macros cpp_lib_latch and cpp_lib_barrier should be created.

The Syntax of auto Declarations

C++ Important Questions with Answers

Generalized Constant Expressions

Generalized pointer casts

B.6 Types and Overloading

Lambdas in unevaluated contexts

Class Types in Non-Type Template Parameters

Module 10 Inheritance, Virtual Functions, and Polymorphism

Modules:Context-Sensitive Keyword

Bring Back the Obvious Definition of count()

Hardening the C++ Standard Template Library

Note in particular that users of the standard are not special in this: abuse of these rules is generally bad usage for any library.

Qualified std::function signatures

Semantic constraint matching for concepts

Constraining unique_ptr

Math Constants. Introduction. Motivation

A polymorphic value type for C++

C The new standard

COSC 2P95. Procedural Abstraction. Week 3. Brock University. Brock University (Week 3) Procedural Abstraction 1 / 26

New wording for C++0x Lambdas

Lambda Correctness and Usability Issues

Doc. No.: WG21/N0937=X3J16/ Date: 28 May 1996 Project: C++ Standard Library Reply to: Nathan Myers

Let return Be Direct and explicit

Sizes Should Only span Unsigned

Bounding Object Instantiations, Part 2

Frama-Clang, a C++ front-end for Frama-C

std::optional from Scratch

Template deduction for nested classes P0293R0

Type Inference auto for Note: Note:

Responses to National Body Comments for ISO/IEC PDTS 19750, C++ Extensions for Parallelism Version 2

The Mercury project. Zoltan Somogyi

Let s get schizophrenic

Verification and Validation

Progress toward Opaque Typedefs for C++0X

SG5 Transactional Memory Support for C++

polymorphic_allocator<> as a vocabulary type

OOPS Viva Questions. Object is termed as an instance of a class, and it has its own state, behavior and identity.

Ch. 12: Operator Overloading

ValuedOrError and ValueOrNone types. Table of Contents. Introduction. Date: ISO/IEC JTC1 SC22 WG21 Programming Language C++

CS558 Programming Languages

C++ for Java Programmers

An Alternative to Name Injection from Templates

On the correctness of template metaprograms

Type checking. Jianguo Lu. November 27, slides adapted from Sean Treichler and Alex Aiken s. Jianguo Lu November 27, / 39

Transcription:

Document: P0466R2 Date: 2018-03-29 Reply-to: Lisa Lippincott <lisa.e.lippincott@gmail.com> Audience: Evolution; LEWG; LWG Layout-compatibility and Pointer-interconvertibility Traits Lisa Lippincott Abstract Over dinner at CppCon, Marshall Clow and I discussed a bit of code that relied on two types being layout-compatible. As it happened, the types weren t layout-compatible after all. I opined that there should be a way to statically assert layout-compatibility, so that the error would be caught at compile time, rather than dinner time. Marshall replied, Write a proposal. This is that proposal. In addition to a test for layout-compatibility, I propose tests for correspondence in the initial common sequence of two types, and for situations in which objects are pointer-interconvertible. Changes from r1 to r2: These changes are based on feedback in the second Core discussion at Jacksonville, 2018-03-16. Each of these changves is more directly relative to the draft presented there. Adding wording to insist on complete types as arguments to the traits. Correcting the order of template parameters in the synopsis of is corresponding member. When describing is pointer interconvertible with class, writing of each object in the singular. On my own initiative, likewise changing is pointer interconvertible base of. Changing happily fails to fails, as desired. These changes are based on feedback in the first Core discussion at Jacksonville, 2018-03-13. Rewriting the abstract and much of the front matter to remove incorrect blather about reinterpret cast. Instead, I ve tried to restrict the text to mostly true statements. Restoring the constexpr functions from revision 0, as core-preferred alternatives to the traits in revision 1. The traits wording is kept and updated as an alternative. Renaming pointer-interconvertibility tests to express their function, rather than their mechanism, and changing their definitions to refer to core definitions, rather than mimic core definitions. is initial base of is pointer interconvertible base of is initial member is pointer interconvertible with class This renaming more directly expresses the intent of these facilities, simplifies their wording, and allows them to track future changes in core wording. More generally, it is better to say what one means, rather than say what means what one means. Using phrases, rather than declarator syntax, when naming pointer-to-member types. Rebasing on draft n4713 of the standard. Correction of various typographic errors. These changes are on my own initiative: Moving the enclosing-class template parameters of is corresponding member to the front of the parameter list, for use with explicit template arguments. 1

Removing the increasingly-pointless requirement that the functions be ill-formed when applied to pointers to member functions. They can return false instead. Consolidating the notes about pointer to member literals. Adding v definitions to the synopses where needed. Changes from r0 to r1: These changes are based on the Library Evolution discussion at Kona in 2017. First, renaming the plural traits: are layout compatible is layout compatible are common members is corresponding member Second, changing is initial member and is corresponding member from constexpr functions to ordinary traits using template <auto>. My thanks go to Louis Dionne for the sample implementation code. On my own initiative, I have added a discussion and notes on the dangers of deducing the containing type from a member pointer constant. 1 Introduction Currently, a program may rely on layout-compatibility, but cannot assert that the layout-compatibility it relies upon pertains. Even when a programmer carefully verifies layout-compatibility, a future change to the types involved may break the compatibility, silently introducing a bug. A compiler, having full information about the types, can easily check layout-compatibility. But the compiler currently has no way to determine which types need to be layout-compatible. This gap can be bridged straightforwardly with a type trait expressing the layout-compatibility relationship: template <class T, class U> struct is_layout_compatible; Using this trait, a function may statically assert the layout-compatibility it relies upon. Delving deeper into the problem, I found another situation where a programmer might rely on a fact about the type system that can t be asserted: the pointer-interconvertiblity of an object and an initial base or member subobject. A simple type trait handles the base subobject case: template <class Base, class Derived> struct is_pointer_interconvertible_base_of; The initial member subobject case turns out to be trickier. The test should take a member pointer as a parameter: template <class S, class M, M S::*m> struct is_pointer_interconvertible_with_class; That works, but with three template parameters, it s really cumbersome. In use, the first two parameters are redundant the type of m determines S and M. But, because this is a class template, the earlier parameters can t be inferred. A function template is easier to use: template <class S, class M> is_pointer_interconvertible_with_class( M S::*m ) noexcept; The use of this function is a little more broad: it can be called in a non-constexpr context. An alternative formulation retains the traits syntax, at the expense of this breadth: template <auto m> struct is_pointer_interconvertible_with_class; Such a trait can be implemented by forwarding decltype(m). A similar situation can occur with layout-compatibility: a programmer may rely on particular members of layout-compatible types overlaying each other. More generally, the overlap of the common initial sequence of two types (12.2 [class.mem]) can only be relied upon if the programmer is sure that particular members correspond. So I m proposing a second function for testing correspondence in the common initial sequence: 2

template <class S1, class S2, class M1, class M2> is_corresponding_member( M1 S1::*m1, M2 S2::*m2 ) noexcept; As above, an alternative would be to stick to traits: template <auto m1, auto m2> struct is_corresponding_member; Note: There is a danger in deducing the type of the containing class from the type of a pointer-to-member literal. Consider the following example: struct A { int a; }; struct B { int b; }; struct C: public A, public B {}; static_assert( is_pointer_interconvertible_with_class( &C::b ) ); // Succeeds because, despite its appearance, &C::b has type // "pointer to member of B of type int." static_assert( is_pointer_interconvertible_with_class<c>( &C::b ) ); // Forces the use of class C, and happily fails. static_assert( is_corresponding_member( &C::a, &C::b ) ); // Succeeds because, despite appearances, &C::a and &C::b have types // "pointer to member of A of type int" and // "pointer to member of B of type int," respectively. static_assert( is_corresponding_member<c,c>( &C::a, &C::b ) ); // Forces the use of class C, and happily fails. The awkwardness of the deduced type of pointer-to-member constants was discussed in core language issue 203; no action was taken for fear of breaking existing code. 2 is layout compatible Add to table 40 in 23.15.6 [meta.rel]: Template Condition Comments template <class T, class U> struct is layout compatible; T and U are layout-compatible (6.7 [basic.types]) T and U shall be complete types. Add to 23.15.2 [meta.type.synop], in the section corresponding to 23.15.6 [meta.rel]: template <class T, class U> struct is_layout_compatible; template<class T, class U> inline is_layout_compatible_v = is_layout_compatible<t,u>::value; 3 is pointer interconvertible base of Add to table 44 in 23.15.6 [meta.rel]: 3

Template Condition Comments template <class Base, class Derived> struct is pointer interconvertible base of; Derived is unambiguously derived from Base, and each object of type Derived is pointer-interconvertible (6.7.2 [basic.compound]) with its Base subobject. Base and Derived shall be complete types. I note here that it may be possible to relax the requirement that Base be complete. Add to 23.15.2 [meta.type.synop], in the section corresponding to 23.15.6 [meta.rel]: template <class Base, class Derived> struct is_pointer_interconvertible_base_of; template<class Base, class Derived> inline is_pointer_interconvertible_base_of_v = is_pointer_interconvertible_base_of<base,derived>::value; 4 is pointer interconvertible with class This pretty clearly belongs in <type traits> (23.15 [meta]), but I don t see a clear choice of subsection to put it in. Perhaps it goes in 23.15.6 [meta.rel], or perhaps a new subsection, Member relationships is appropriate. Wherever it fits, here is some text to add: template <class S, class M> is_pointer_interconvertible_with_class( M S::*m ) noexcept; Requires: S shall be a complete type. Returns: true if and only if each object s of type S is pointer-interconvertible (6.7.2 [basic.compound]) with its subobject s.*m. Add to 23.15.2 [meta.type.synop], in the corresponding section: template <class S, class M> is_pointer_interconvertible_with_class( M S::*m ) noexcept; 5 is corresponding member Add this text to the same subsection as is pointer interconvertible with class: template <class S1, class S2, class M1, class M2> is_corresponding_member( M1 S1::*m1, M2 S2::*m2 ) noexcept; Requires: S1 and S2 shall be complete types. Returns: true if and only if m1 and m2 point to corresponding members of the common initial sequence (12.2 [class.mem]) of S1 and S2. Add to 23.15.2 [meta.type.synop], in the corresponding section: 4

template <class S1, class S2, class M1, class M2> is_corresponding_member( M1 S1::*m1, M2 S2::*m2 ) noexcept; 6 Note about pointer to member literals To the same section as the functions above, add a note: [Note: The type of a pointer-to-member literal is not always as it appears, and this may lead to surprising results when using these functions in conjunction with inheritance in classes that are not standard-layout. Consider the following example: struct A { int a; }; struct B { int b; }; struct C: public A, public B {}; static_assert( is_pointer_interconvertible_with_class( &C::b ) ); // Succeeds because, despite its appearance, &C::b has type // "pointer to member of B of type int." static_assert( is_pointer_interconvertible_with_class<c>( &C::b ) ); // Forces the use of class C, and fails, as desired. static_assert( is_corresponding_member( &C::a, &C::b ) ); // Succeeds because, despite appearances, &C::a and &C::b have types // "pointer to member of A of type int" and // "pointer to member of B of type int," respectively. static_assert( is_corresponding_member<c,c>( &C::a, &C::b ) ); // Forces the use of class C, and fails, as desired. end note] 7 Alternative wording as traits Instead of the above wording, is pointer interconvertible with class and is corresponding member can be provided as traits. I favor the function approach above, largely because it allows the enclosing classes to be easily specified as explicit template arguments. This wording replaces the wording in sections 4, 5, and 6 above. 7.1 General wording for traits of non-type parameters First, it is necessary to introduce general wording for traits of non-type parameters. Rather than duplicate the already-duplicate requirements of UnaryTypeTrait and BinaryTypeTrait, I introduce the common notion of IntegralTrait. Modify 23.15.1 [meta.rqmts]: An IntegralTrait describes a property of or relationship between template parameters. It shall be a class template whose specializations are DefaultConstructible, CopyConstructible, and publicly and unambiguously derived, directly or indirectly, from its base characteristic, which is a specialization of the template integral constant (23.15.3), with the arguments to the template integral constant determined by the requirements for the particular property or relationship being described. The member names of the base characteristic shall not be hidden and shall be unambiguously available in the IntegralTrait. 5

A UnaryTypeTrait is an IntegralTrait with one primary type parameter. A BinaryTypeTrait is an IntegralTrait with two primary type parameters. In each case, other parameters of lesser importance may be present. A UnaryTypeTrait describes a property of a type. It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the property being described. It shall be DefaultConstructible, CopyConstructible, and publicly and unambiguously derived, directly or indirectly, from its base characteristic, which is a specialization of the template integral constant (23.15.3), with the arguments to the template integral constant determined by the requirements for the particular property being described. The member names of the base characteristic shall not be hidden and shall be unambiguously available in the UnaryTypeTrait. A BinaryTypeTrait describes a relationship between two types. It shall be a class template that takes two template type arguments and, optionally, additional arguments that help define the relationship being described. It shall be DefaultConstructible, CopyConstructible, and publicly and unambiguously derived, directly or indirectly, from its base characteristic, which is a specialization of the template integral constant (23.15.3), with the arguments to the template integral constant determined by the requirements for the particular relationship being described. The member names of the base characteristic shall not be hidden and shall be unambiguously available in the BinaryTypeTrait. 7.2 is pointer interconvertible with class trait Add in place of the description of the is pointer interconvertible with class function above: template <auto m> struct is_pointer_interconvertible_with_class; An IntegralTrait with a BaseCharacteristic of true type if m has type pointer to member of S of type D, D is an object type, and each object s of type S is pointer-interconvertible (6.7.2 [basic.compound]) with its subobject s.*m. Otherwise, the BaseCharacteristic is false type. A program is ill-formed if it instantiates the definition of this template where S is incomplete. Add to 23.15.2 [meta.type.synop], in place of the corresponding synopsis: template <auto m> struct is_pointer_interconvertible_with_class; template<auto m> inline is_pointer_interconvertible_with_class_v = is_pointer_interconvertible_with_class<m>::value; 7.3 is corresponding member trait Add in place of the description of the is corresponding member function above: template <auto m1, auto m2> struct is_corresponding_member; An IntegralTrait with a BaseCharacteristic of true type if m1 has type pointer to member of S1 of type D1, m2 has type pointer to member of S2 of type D2, and m1 and m2 point to corresponding members of the common initial sequence (12.2 [class.mem]) of S1 and S2. Otherwise, the BaseCharacteristic is false type. A program is ill-formed if it instantiates the definition of this template where S1 or S2 is incomplete. Add to 23.15.2 [meta.type.synop], in place of the corresponding synopsis: 6

template <auto m1, auto m2> struct is_corresponding_member; template<auto m1, auto m2> inline is_corresponding_member_v = is_corresponding_member<m1,m2>::value; 7.4 Note about pointer to member literals, traits version Add in place of the similar note above: [Note: The type of a pointer-to-member literal is not always as it appears, and this may lead to surprising results when using these traits in conjunction with inheritance in classes that are not standard-layout. Consider the following example: struct A { int a; }; struct B { int b; }; struct C: public A, public B {}; static_assert( is_pointer_interconvertible_with_class_v< &C::b > ); // Succeeds because, despite its appearance, &C::b has type // "pointer to member of B of type int." static_assert( is_corresponding_member_v< &C::a, &C::b > ); // Succeeds because, despite appearances, &C::a and &C::b have types // "pointer to member of A of type int" and // "pointer to member of B of type int," respectively. end note] 7