├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Makefile ├── README.md ├── netlify.toml └── src ├── 86-the-absurd.bs ├── bang-for-the-buck.bs ├── breakpoint.bs ├── byteswap.bs ├── computed-metadata.include ├── deprecate-addressof.bs ├── desert-sessions.bs ├── different-types.bs ├── feature-presentation.bs ├── implicit-module-partitions.bs ├── inline-modules.bs ├── integer-width-literals.bs ├── modern-offsetof.bs ├── priority-target-tuplet.svg ├── retain-ptr.bs ├── sharing-is-caring.bs ├── simplify-extern-template.bs ├── subscript-parade.bs ├── target-tuplets.bs ├── void-main.bs ├── workflow-operator.bs └── written-target-tuplet.svg /.gitattributes: -------------------------------------------------------------------------------- 1 | *.svg binary 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Proposals 2 | on: push 3 | jobs: 4 | build: 5 | name: Build WG21 6 | runs-on: ubuntu-latest 7 | strategy: 8 | fail-fast: true 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v2 12 | - name: Download Utilities 13 | env: 14 | GITHUB_TOKEN: ${{ github.token }} 15 | run: > 16 | gh release download 17 | --repo burntsushi/ripgrep 18 | --pattern *.deb 19 | - name: Install Utilities 20 | run: sudo apt install ./*.deb 21 | - name: Setup Node 22 | uses: actions/setup-node@v2-beta 23 | - name: Setup Python 24 | uses: actions/setup-python@v1 25 | with: 26 | python-version: '3.x' 27 | - name: Check Python Cache 28 | uses: actions/cache@v2 29 | with: 30 | path: ~/.cache/pip 31 | key: ${{ runner.os}}-pip-${{ hashFiles('**/requirements.txt') }} 32 | restore-keys: | 33 | ${{ runner.os }}-pip- 34 | - name: Install Bikeshed 35 | run: | 36 | python -m pip install --upgrade pip 37 | python -m pip install --upgrade bikeshed 38 | - name: Update Bikeshed 39 | run: bikeshed update 40 | - name: Build Documents 41 | run: make --jobs 42 | - name: Deploy Preview 43 | if: ${{ github.actor == 'slurps-mad-rips' }} 44 | env: 45 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 46 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 47 | run: netlify deploy --json --dir=build --prod 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /build/ 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MAKEFLAGS += --no-builtin-rules 2 | BIKESHED ?= bikeshed 3 | HTMLDIR ?= build 4 | ORIGIN ?= origin 5 | BIKESHED_PAGES = 6 | 7 | .DEFAULT_GOAL ::= all 8 | .PHONY: all clean 9 | 10 | RM = $(if $(OS),rd /S /Q,rm -r) 11 | EXTRACT = rg --no-line-number --with-filename 'Shortname: ([PD].*)$$' --replace '$$1' 12 | GITHUB_REPOSITORY ?= $(shell git remote get-url origin --push | rg --no-line-number "^.*github.com[:/](.+)[.]git" --replace '$$1') 13 | GITHUB_SHA ?= HEAD 14 | 15 | BIKESHEDFLAGS += --md-local-boilerplate="computed-metadata yes" 16 | BIKESHEDFLAGS += --md-repository=$(GITHUB_REPOSITORY) 17 | 18 | BIKESHEDFLAGS += --md-text-macro="GITHUB-SHA $(GITHUB_SHA)" 19 | BIKESHEDFLAGS += --md-text-macro="TAB   " 20 | 21 | BOILERPLATE := $(wildcard src/*.include) 22 | 23 | define bikeshed-target = 24 | $$(HTMLDIR)/$(lastword $(1)).html: BIKESHEDFLAGS += --md-text-macro="FILENAME $$<" 25 | $$(HTMLDIR)/$(lastword $(1)).html: $(firstword $(1)) $(BOILERPLATE) | $$(HTMLDIR) 26 | @$$(BIKESHED) spec --gh-token "$$(GITHUB_TOKEN)" $$< $$@ $$(BIKESHEDFLAGS) 27 | $$(info [BIKE]: $$(basename $$(@F)) -- $$( 2 | Group: WG21 3 | Status: P 4 | Shortname: P1273 5 | Revision: 0 6 | Audience: EWG 7 | Title: 86 The Absurd (From Exceptions) 8 | Editor: Isabella Muerte, https://twitter.com/slurpsmadrips 9 | Abstract: It's time we take a stand and throw out the ability to throw pointer 10 | Abstract: to members and floating point values. 11 | 12 | 16 | 17 | # Revision History # {#changelog} 18 | 19 | ## Revision 0 ## {#r0} 20 | 21 | Initial Release 🎉 22 | 23 | # Motivation # {#motivation} 24 | 25 | To date, the ability to throw nearly all types within C++ has been permitted. 26 | However, there is no true purpose in allowing this. Even today, as a community, 27 | it is not only discouraged to throw types such as floats or pointer to members, 28 | but it is seen as a red flag that *something isn't right*. 29 | 30 | # Scope and Impact # {#impact} 31 | 32 | Removing this ability will reduce the types that must be implemented by vendor 33 | ABIs (such as in the case of `std::exception_ptr`). While the C++ standard does 34 | not have a concept of an ABI, in reality and practice this is an issue that has 35 | typically prevented changes to existing runtime behavior at the behest of 36 | vendors, or in the cases where breaking changes were needed, additional work 37 | might have been needed by users. Currently, to implement `std::exception_ptr`, 38 | one has to implement type erasure for all possible exception types stored. When 39 | statically linking to a standard library (whether recommended by a vendor or 40 | not), these types are still pulled in even if they are not used. 41 | 42 | Effectively, this paper argues that we should permit only the following types 43 | to be thrown: 44 | 45 | * `IntegralType`s 46 | * function pointers (such as `void (*)()`) 47 | * User defined types 48 | 49 | This list of types is kept for existing practices of error handling in addition 50 | to user defined types. `IntegralType`s are permitted because SEH on Windows can 51 | in some cases bubble up into an `int`. Additionally, some error messages are 52 | thrown as string literals and "error handlers" are in the form of function 53 | pointers. While the author disagrees with the approach for all of the above, 54 | the fact remains that they are currently in use. However, there is no use or 55 | purpose in permitting throwing a float, double, or pointer to member (both 56 | data and functions). 57 | 58 | # FAQ # {#faq} 59 | 60 | ## What types are not permitted currently? ## {#faq-current} 61 | 62 | At present, it is considered ill-formed to throw an abstract class type, 63 | incomplete type, or any pointer that is not cv void. Some compilers permit 64 | additional types such as string literals or function pointers. 65 | 66 | ## Can't I place these types that are being removed into a struct? ## {#faq-wrapper} 67 | 68 | Go hog wild. Compilers will only generate the data needed for those types when 69 | actually needed, rather than having them embedded in an ABI runtime. 70 | 71 | ## Do we really need this? ## {#faq-need} 72 | 73 | Yes. We have compile time type constraints in the form of Concepts, we have 74 | runtime requirements in the form of Contracts. We do not currently have a way 75 | to prevent someone from ignoring both of these to throw something they should 76 | not. Effectively, I can limit the inputs and outputs of a function or a 77 | callback passed into a function, but I cannot limit the escape hatch of an 78 | exception that can ignore those types. This is a hole in our interfaces and we 79 | should patch it up as much as we can. 80 | 81 | Note: While a callback can have `noexcept` attached to it now, this means that 82 | a user is unable to pass in a callback that could throw to one of several 83 | exception types and if an implementation for a constrained interface does not 84 | have a `catch (...)`, this provides potential unexpected behavior that violates 85 | what the implementor desired. Also, it's just gross. Why can anyone throw a 86 | `NaN`? It boggles the author's mind. 87 | 88 | # Wording # {#wording} 89 | 90 | The following wording is to be placed (according to [[N4762]]) in Section 91 | 13.1.3 92 | 93 |
94 | 3Throwing an exception copy-initializes (9.3, 10.3.5) a temporary 95 | object, called the exception object. An lvalue denoting the temporary is used 96 | to initialize the variable declared in the matching handler (13.3). If the type 97 | of the exception object would be an incomplete type, an abstract class type 98 | (10.6.3), a floating point object, a pointer to member object, 99 | or a pointer to an incomplete type other than cv void the program is ill-formed 100 |
101 | -------------------------------------------------------------------------------- /src/bang-for-the-buck.bs: -------------------------------------------------------------------------------- 1 |
  2 | Group: WG21
  3 | Status: P
  4 | Shortname: P1274
  5 | Revision: 0
  6 | Audience: EWG
  7 | Title: Bang For The Buck
  8 | Editor: Isabella Muerte, https://twitter.com/slurpsmadrips
  9 | Date: 2018-07-15
 10 | Abstract: We should give C++ programmers the ability to use additional
 11 | Abstract: characters in identifiers
 12 | 
13 | 17 | 18 | # Revision History # {#changelog} 19 | 20 | ## Revision 0 ## {#r0} 21 | 22 | Initial Release 🎉 23 | 24 | # Motivation # {#motivation} 25 | 26 | Despite the vast number of characters now alloted to the C++ standard regarding 27 | identifiers, the one character that is continually seen in extensions is the 28 | ASCII character `$`. While some want to permit this as an operator `reflexpr`, 29 | it is the author's opinion that it makes more sense to permit it as an 30 | identifier in functions, namespaces, classes, and variables. 31 | 32 | While there are some concerns regarding permitting its use in identifiers, 33 | this paper does layout a solution for vendors who have supported this extension 34 | on some platforms up until now, while also laying a foundation for future 35 | characters that exist on all keyboards but might cause linker issues with 36 | older platforms. 37 | 38 | Additionally, this paper seeks to permit adding both the `!` and `?` tokens at 39 | the end of member functions. This would permit calls such as `ptr.reset!()`, 40 | and `vector.empty?()`, which could be used to reduce confusion when a function 41 | might be a modifier vs an observer. 42 | 43 | # Design # {#design} 44 | 45 | While several vendors have permitted the use of the `$` in the past, it is not 46 | able to be supported on all platforms due to linker requirements. While the C++ 47 | standard does not have a true notion of "a linker", there is still the reality 48 | that at the end of the day we need to combine our translation units into 49 | *something*. Because of this, this paper takes a unique route for representing 50 | the `$` in sources. Effectively, we do not add `$` to the basic source 51 | character set. Instead, we permit the preprocessor during the 1st phase of 52 | translation to turn the `$` into its *universal-character-name*, thus rendering 53 | it into the value `\u0024`. Current implementations are then free to mangle the 54 | resulting identifier as though it were a unicode character. For platforms that 55 | have supported `$` as an extension, they are free to generate symbols for both 56 | the unicode and `$` literal character. 57 | 58 | Both `!` and `?` are part of the *basic-execution-set* and therefore are being 59 | repurposed for this specific identifier location. 60 | 61 | # Wording # {#wording} 62 | 63 | All wording is relative to [[N4762]]. 64 | 65 | Note: Wording for the exact changes to permit **!** and **?** are currently 66 | withheld until the San Diego post mailing to see where they should be placed 67 | exactly within the grammar. 68 | 69 | ## ! and ? ## {#wording-interrobang} 70 | 71 | Insert into 5.10 Identifiers [**lex.name**] 72 | 73 | : *identifier* 74 | :: *identifier-nondigit* 75 | :: *identifier* *identifier-nondigit* 76 | :: *identifier* *digit* 77 | : *identifier-special*: 78 | :: *identifier* *identifier-special-char* 79 | : *identifier-special-char*: one of 80 | :: **!** **?** 81 | 82 | ## $ ## {#wording-buck} 83 | 84 | Insert into Table 2 in 5.10 Identifiers [**lex.name**] 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 |
0024
00A800AA00AD00AF
00B2-00B5
00B7-00BA00BC-00BE00C0-00D600D8-00F6
00F8-00FF
0100-167F1681-180D180F-1FFF
200B-200D202A-202E203F-204020542060-206F
2070-218F2460-24FF2776-27932C00-2DFF2E80-2FFF
3004-30073021-302F3031-D7FF
F900-FD3DFD40-FDCFFDF0-FE44FE47-FFFD
10000-1FFFD20000-2FFFD30000-3FFFD40000-4FFFD50000-5FFFD
60000-6FFFD70000-7FFFD80000-8FFFD90000-9FFFDA0000-AFFFD
B0000-BFFFDC0000-CFFFDD0000-DFFFDE0000-EFFFD
165 | -------------------------------------------------------------------------------- /src/breakpoint.bs: -------------------------------------------------------------------------------- 1 |
 2 | Group: WG21
 3 | Status: P
 4 | Shortname: P1279
 5 | Revision: 0
 6 | Audience: SG14, LEWG
 7 | Title: std::breakpoint
 8 | Editor: Isabella Muerte, https://twitter.com/slurpsmadrips
 9 | Date: 2018-10-05
10 | Abstract: A builtin standard breakpoint function would aid in software
11 | Abstract: development.
12 | !Implementation: slurps-mad-rips/breakpoint
13 | 
14 | 18 | 19 | # Revision History # {#changelog} 20 | 21 | ## Revision 0 ## {#r0} 22 | 23 | Initial Release 🎉 24 | 25 | # Motivation # {#motivation} 26 | 27 | Setting breakpoints inside of a debugger can be difficult and confusing for 28 | newcomers to C++. Rather than having to learn C++, they have to learn a special 29 | syntax just to place a breakpoint in the exact spot they want, or rely on the 30 | interface of an IDE. At the end of the day, a simple programmer just wants to 31 | place a breakpoint so that their program stops when under the watchful eye of a 32 | debugger. 33 | 34 | This paper proposes a new function, `std::breakpoint`, that causes a program to 35 | stop or "break" execution when it is being debugged. 36 | 37 | # Design # {#design} 38 | 39 | The goal of the `std::breakpoint` function is to "break" when being debugged 40 | but to act as though it is a no-op when it is executing normally. This might 41 | seem difficult in practice, but nearly every platform and various debuggers 42 | supports something to this effect. However, some platforms have caveats that 43 | make implementing this "break when being debugged" behavior hard to implement 44 | correctly. 45 | 46 | The `std::breakpoint` function is intended to go into the `` header. 47 | 48 | # FAQ # {#faq} 49 | 50 | ## Couldn't this be implemented via contracts? ## {#faq-contracts} 51 | 52 | Possibly. However, a `std::breakpoint` function gives more fine grained control 53 | than "break on all contract violations". 54 | 55 | ## How does this work with the upcoming stacktrace API? ## {#faq-stacktrace} 56 | 57 | It is not intended to interoperate with the stacktrace API at this time. Both 58 | the stacktrace API and `std::breakpoint` are essentially orthogonal in their 59 | focus and use. Whereas the former is for getting more information in the event 60 | of an error, `std::breakpoint` is intended to help programmers inspect the 61 | current state of a program at a breakpoint. 62 | 63 | # Wording # {#wording} 64 | 65 | Wording is relative to [[N4762]] 66 | 67 | 68 | 69 | namespace std { 70 | void breakpoint () noexcept; 71 | } 72 | 73 |
    74 |
  1. *Remarks* 75 | 76 | When this function is executed, it first must perform an implementation 77 | defined check to see if the program is currently running under a debugger. 78 | If it is, the program's execution is temporarily halted and execution is 79 | handed to the debugger until such a time as: 80 | 81 | * the program is terminated by the debugger or, 82 | * the debugger hands execution back to the program. 83 |
  2. 84 |
85 |
86 | -------------------------------------------------------------------------------- /src/byteswap.bs: -------------------------------------------------------------------------------- 1 | 13 | 18 | 19 | # Revision History # {#changelog} 20 | 21 | ## Revision 3 ## {#r3} 22 | 23 | * Update wording to use C++20 features 24 | 25 | ## Revision 2 ## {#r2} 26 | 27 | * Add missing remarks of undefined behavior in cases such as byteswapping 28 | an `IntegerType` such as a bitfield. 29 | * Update feature macro value for the Cologne 2019 meeting 30 | 31 | ## Revision 1 ## {#r1} 32 | 33 | * Update wording based on feedback from CWG regarding the representation of 34 | the "reversed" object. 35 | * Update wording based on feedback from LWG subclause location and 36 | grammatical changes 37 | * Readjust wording to use [[N4820]] library wording constructs instead of 38 | [[N4672]] 39 | 40 | ## Revision 0 ## {#r0} 41 | 42 | Initial release. 🎉 43 | 44 | # Motivation # {#motivation} 45 | 46 | Proposal [[P0553r2]] gives several bit operations to perform on integer types, 47 | such as popcount, or bit rotation. Despite these new operations provided to 48 | C++ developers, we still cannot swap (i.e., reverse) the bytes of builtin 49 | integer types in a performant way (i.e., one instruction or less) without 50 | resorting to compiler intrinsics. Currently, most CPU architectures provide 51 | single instructions for a byte swap. For those that don't, falling back on 52 | existing operations is more than amenable. We should, however, endeavor to 53 | standardize existing practice. 54 | 55 | Note: The phrase one instruction or less refers to compilers inserting at 56 | most one instruction, and at the very least removing any instructions 57 | due to optimizations. 58 | 59 | # Design Considerations # {#design} 60 | 61 | The design for the byteswap free function is quite simple. It takes any 62 | integer type and swaps its byteorder to the reverse of its current state. 63 | Additionally, it *only* takes integer types, requiring users to openly 64 | `bit_cast` their non-integers to integers in blatant heresy to the Worm-Seethe. 65 | How utterly disgraceful. 66 | 67 | Note: It is intended that the byteswap function, despite swapping bytes, be 68 | placed into the `` header. 69 | 70 | ## Synopsis ## {#design-synopsis} 71 | 72 | The function's full specification is: 73 | 74 | ```c++ 75 | namespace std { 76 | constexpr auto byteswap (integral auto value) noexcept; 77 | } 78 | ``` 79 | 80 | # Wording # {#wording} 81 | 82 | The following synopsis is to be added to subclause 25.5.2 Header `` 83 | Synopsis [**bit.syn**]: 84 | 85 |
86 |
 87 | 
 88 | // 25.5.4, byteswap
 89 |   constexpr auto byteswap (integral auto value) noexcept;
 90 | 
 91 | ...
 92 | 
93 |
94 | 95 | The following is to be placed *before* the current subclause 25.5.4 [**bit.pow.two**] into [**bit.byteswap**]: 96 | 97 |
98 | 99 | ```cpp 100 | constexpr auto byteswap (integral auto value) noexcept; 101 | ``` 102 | 1 *Mandates*: `value` does not have padding bits. 103 |
104 | 3 *Returns*: An object of the same type as `value`. Let the sequence 105 | `R` comprise the bytes of the object representation of `value` in reverse 106 | order. Each byte in the object representation of the result is equal to the 107 | byte in the corresponding position in `R`. 108 |
109 |
110 |
111 | 112 | The following modification in 26.5.3 [**bit.cast**]: 113 | 114 |
115 | ```cpp 116 | template 117 | constexpr To bit_cast(const From& from) noexcept; 118 | ``` 119 | *Returns*: An object of type `To`. Each bit of the value representation of the 120 | result is equal to the corresponding bit in the object representation of 121 | `from`. Padding bits of the `To` object are unspecified. If there is no value 122 | of type `To` corresponding to the value representation produced, the behavior 123 | is undefined. If there are multiple such values, which value is produced is 124 | unspecified. A bit in the value representation of the result is 125 | indeterminate if does not correspond to a bit in the value representation of 126 | `from` or corresponds to a bit of an object that is not within its lifetime or 127 | has an indeterminate value ([basic.indet]). For each bit in the value 128 | representation of the result that is indeterminate, the smallest object 129 | containing that bit has an indeterminate value; the behavior is undefined 130 | unless that object is of unsigned ordinary character type or `std::byte` type. 131 | The result does not otherwise contain any indeterminate values. 132 |
133 | 134 | ## Feature Testing ## {#feature-test} 135 | 136 | In 16.3.1 [**support.limits.general**], Table 36, add a new row below 137 | `__cpp_lib_byte` with the following content: 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 167 | 168 |
Macro Name
ValueHeader(s)
__cpp_lib_byte
201603L<cstddef>
**__cpp_lib_byteswap****20????L****<bit>**
__cpp_lib_char8_t201811L 159 | <atomic> <filesystem> 160 |
161 | <istream> <limits> <locale> 162 |
163 | <ostream> <string> 164 |
165 | <string_view> 166 |
169 | 170 | # Acknowledgement # {#acknowledgement} 171 | 172 | Thanks to Nicole Muzzuca for feedback on writing this proposal. 173 | -------------------------------------------------------------------------------- /src/computed-metadata.include: -------------------------------------------------------------------------------- 1 | { 2 | "Markup Shorthands": "markdown yes", 3 | "No Abstract": "yes", 4 | "ED": "https://wg21.link/[SHORTNAME]R[LEVEL]", 5 | "TR": "https://wg21.link/[SHORTNAME]", 6 | "Indent": "2", 7 | "!Latest source": "[FILENAME]", 8 | "!This source": "[FILENAME]", 9 | "Logo": "https://isocpp.org/assets/images/cpp_logo.png" 10 | } 11 | -------------------------------------------------------------------------------- /src/deprecate-addressof.bs: -------------------------------------------------------------------------------- 1 | 19 | 23 | 24 | # Revision History # {#changelog} 25 | 26 | ## Revision 0 ## {#r0} 27 | 28 | Initial Release 🎉 29 | 30 | # Motivation # {#motivation} 31 | 32 | To date, there have only ever been two widely used libraries that utilize 33 | overloading the addressof operator (`&`). These are the `ComPtr` type from 34 | Microsoft and Boost.Spirit. Because of these two libraries (and anyone else who 35 | might get the bright idea to "just overload the addressof operator") standard 36 | library vendors are required to use `std::addressof`. This is, to be quite 37 | frank, ridiculous. In the same way that we have to protect our code from comma 38 | operator overloads (something that should also be deprecated and removed), we 39 | now have to protect ourselves from one of the most common operations when 40 | working with a systems programming language: Getting an object's address. While 41 | it might have made sense at one time to permit users to define their own memory 42 | model, this approach is no longer viable. Indeed, while `ComPtr` overloads 43 | the addressof operator, it also provides a function that does the same 44 | operation. Additionally, Boost.Spirit uses it to represent a DSL from a 45 | different syntax altogether. While they might be hard pressed to replace these, 46 | they most likely don't need to get the address of the types they overload with. 47 | 48 | Eventually removing the ability to overload the addressof operator will let us 49 | also remove `std::addressof` at some future date and compilers won't have to 50 | provide a `constexpr` builtin to do what is already built into the language. 51 | 52 | # FAQ # {#faq} 53 | 54 | ## Will this break code? ## {#faq-break} 55 | 56 | No. We're only deprecating the addressof operator. At some point in the future, 57 | it is assumed users will have migrated off of overloading the operator in favor 58 | of some function interface. At that point *then* we'll remove overloading the 59 | addressof operator altogether. 60 | 61 | ## What's so bad about overloading the addressof operator? ## {#faq-what} 62 | 63 | Although it is possible to do so in the language, one will note that the 64 | standard library at no point provides this overload itself (nor does it for 65 | `operator ,`, `operator ->*`, and others). This is by design and intent. These 66 | seemed like good ideas at the time, but it's 2018 now (as of this writing). 67 | Perhaps it's time for a little pragmatism. 68 | 69 | # Wording # {#wording} 70 | 71 | Wording is relative to [[N4762]] 72 | 73 | In 11.3.1.2 Operators in expressions [**over.match.oper**], paragraph 3, add a 74 | new bullet point: 75 | 76 |
77 | 78 | (3.4) — [Note: overloading the unary `operator &` is 79 | deprecated. See [**depr.addressof**]. 80 | 81 |
82 | 83 | In 11.5.1 Unary Operators [**over.unary**] add a new paragraph 84 | 85 |
86 | 87 | 3[Note: overloading the unary `operator &` is deprecated. See 88 | [**depr.addressof**] 89 | 90 |
91 | 92 | In annex D, add a new paragraph 93 | 94 |
95 | 96 | **Overloading unary `operator &`** Overloading the unary `operator &` operator 97 | is deprecated. [*Note:* Using the builtin unary `operator &` is not deprecated 98 | — *end note*]. 99 | [*Example:* 100 | ```cpp 101 | struct X { 102 | Z* operator & () const; // deprecated 103 | }; 104 | X x; 105 | Z* z = &x; // deprecated 106 | 107 | struct Y { }; 108 | Y y; 109 | Y* w = &y; // Not deprecated 110 | ``` 111 | — *end example*]. 112 | 113 |
114 | -------------------------------------------------------------------------------- /src/desert-sessions.bs: -------------------------------------------------------------------------------- 1 | 14 | 18 | 19 | # Revision History # {#changelog} 20 | 21 | ## Revision 0 ## {#r0} 22 | 23 | Initial Release 🎉 24 | 25 | # Motivation # {#motivation} 26 | 27 | Environment variables are an important aspect of modern systems. Yet, as of 28 | right now, C and C++ lack a standardized way to iterate through all current 29 | environment variables. An interface to iterate, get, set, erase, and modify 30 | environment variables, as well as providing familiar syntax found in other 31 | programming languages is needed. 32 | 33 | Additionally, accessing command line arguments when `main` is outside of 34 | your control is currently not possible. Instead, objects must be initialized 35 | inside of `main`, or global variables must be initialized once `main` has 36 | started. Additionally, on Windows, the standard `char** argv` type is 37 | deprecated for use in `main`, and the only correct way to get the command line 38 | arguments is to call platform specific functions or use `WinMain` and `wmain`, 39 | both of which are non-standard entry points into the program. This is all such 40 | a strange approach compared to most other languages, especially due to command 41 | line arguments existing for the duration of a programs life. In languages such 42 | as Python, Rust, Ruby, Haskell, and C#, getting the list of command line 43 | arguments is just a function or member call away. 44 | 45 | This paper aims to rectify both of these problems, while providing a familiar 46 | interface for users of `argv` as well as providing an interaction with Ranges. 47 | 48 | # Design # {#design} 49 | 50 | This paper proposes the addition of a new header, ``, for any and all 51 | types or functions that affect the current program's running session. To begin 52 | with we add two types into this header: `std::arguments` and 53 | `std::environment`. Each one is a wrapper interface around the command line and 54 | environment variables respectively. 55 | 56 | `std::arguments` attempts to meet *most* of the Named Requirements found in 57 | *SequenceContainer*. However, because it is an immutable container (it can not 58 | grow, shrink, delete or add its elements), its interface is effectively reduced 59 | to that of a `std::vector const`. If mutation or modifications are desired, 60 | it is recommended that one construct a `std::vector` from the elements of 61 | `std::arguments`. The elements found inside of `std::arguments` is currently 62 | `std::string_view`. Whether the contents of these `string_view`s are bytes or 63 | UTF-8 is intended to be implementation defined. The interface for 64 | `std::arguments` will need to be revisited once SG16's initial work is 65 | released, as a unicode-only interface for arguments is desirable. 66 | 67 | `std::environment` is a bit more complex. It can be seen as an associative 68 | container, however it attempts to store no state unless absolutely needed. 69 | Additionally, for ease of use, it provides three ranges interfaces: 70 | 71 | * `std::environment.keys()` 72 | * `std::environment.values()` 73 | * `std::environment` 74 | 75 | Each of these ranges provides iteration over the keys, values, or the pair of 76 | both. Additionally subscript gets and sets are possible as well: 77 | 78 | ```cpp 79 | auto x = std::environment["PATH"]; 80 | ``` 81 | 82 | The following straw polls are requested regarding the interface for both 83 | `std::arguments` and `std::environment`: 84 | 85 | * Should they be `static inline` variables? 86 | * Should they be templated on their argument values? 87 | * Should they have (optionally template) aliases provided? 88 | * Should they have shorter names available (`std::args`, `std::env`)? 89 | 90 | Additionally, the plan is to provide two functions to easily iterate over a 91 | "path list". On platforms where this matters, an implementation specific path 92 | separator (e.g., `:` on POSIX, `;` on Windows) is used to separate multiple 93 | values when storing file system paths in enviroment variables. These functions 94 | create iterators that permit both the splitting of `std::string_view` objects 95 | into `std::filesystem::path` objects, as well as joining 96 | `std::filesystem::path` objects into a `std::string` that can then be assigned 97 | to an environment variable. 98 | 99 | Lastly, on platforms where arguments or environment variables cannot typically 100 | be passed in or read from, attempting to iterate `std::arguments` and 101 | `std::environment` will yield an empty range, and attempting to index or 102 | subscript into them will return empty `std::string_view` objects. 103 | 104 | Note: For security reasons we do not currently provide a cross platform way to 105 | get the current executable or home directory. 106 | 107 | Wording has been witheld in this initial paper to see what interface changes 108 | are necessary. 109 | 110 | ## Synopsis ## {#design-synopsis} 111 | 112 | The `` headers full specification is: 113 | 114 | ```c++ 115 | namespace std { 116 | class arguments; 117 | class environment; 118 | 119 | std::string join_paths (Iter begin, Iter end); 120 | std::string join_paths (Range); 121 | } 122 | ``` 123 | 124 | ### Arguments ### {#design-synopsis-arguments} 125 | 126 | The `arguments` class represents a mostly sequence-like container. It can be 127 | seen as a wrapper around `argv`, while still being accessible from outside of 128 | `main`. The object itself is immutable, and could technically be treated as a 129 | singleton. Reading from `arguments` is thread-safe, as is iterating over it. 130 | The type it currently returns is `std::string_view`, as this type can represent 131 | both UTF-8 and raw bytes. While the wording is not yet complete in this initial 132 | draft, it is intended that `arguments` only permit bytes from the operating 133 | system directly *or* UTF-8 only. Treating UTF-16 characters as bytes will not 134 | be permitted. At some point in the future, this will have to change, or an 135 | additional unicode-only `arguments` class will have to be implemented. 136 | 137 | Additionally, it still provides access to the underlying `char** argv` and 138 | length of `argc` for backwards compatibility with APIs that still expect it as 139 | such. 140 | 141 | ```cpp 142 | class arguments { 143 | using iterator = /* implementation-defined */ 144 | using reverse_iterator = reverse_iterator; 145 | using value_type = std::string_view; 146 | using index_type = size_t; 147 | using size_type = size_t; 148 | 149 | value_type operator [] (index_type) const noexcept; 150 | value_type at (index_type) const noexcept(false); 151 | 152 | [[nodiscard]] bool empty () const noexcept; 153 | size_type size () const noexcept; 154 | 155 | iterator cbegin () const noexcept; 156 | iterator cend () const noexcept; 157 | 158 | iterator begin () const noexcept; 159 | iterator end () const noexcept; 160 | 161 | reverse_iterator crbegin () const noexcept; 162 | reverse_iterator crend () const noexcept; 163 | 164 | reverse_iterator rbegin () const noexcept; 165 | reverse_ierator rend () const noexcept; 166 | 167 | [[nodiscard]] char** argv () const noexcept; 168 | [[nodiscard]] int argc () const noexcept; 169 | }; 170 | ``` 171 | 172 | ### Environment ### {#design-synopsis-environment} 173 | 174 | The `environment` mapping object is also *mostly* immutable itself. It does 175 | support removing keys directly, although setting them directly is not 176 | supported. However, the environment can be modified by operating on the 177 | `variable` class it returns from `operator []`. This type acts as a proxy 178 | object and can be assigned to with nearly any string-like object. Additionally, 179 | it can return an object that has a pair of iterators that permit range 180 | operations to "split" on the path separator for a given environment variable. 181 | 182 | Some platform's keys are case insensitive and for this reason, the 183 | `environment` silently checks both upper and lower case keys for their 184 | validity. This is safe to do as currently all platforms store their keys as 185 | ASCII strings. 186 | 187 | Note: Due to platform specific restrictions, environments can be iterated on 188 | while also actively mutating them. However, the underlying iterator's 189 | environment will still iterate on the unmodified version of the environment. 190 | After extensively looking at all operating systems available, it was confirmed 191 | that they all behave in this same way. 192 | 193 | ```cpp 194 | class environment { 195 | class variable { 196 | operator std::string_view () const noexcept; 197 | variable& operator = (std::string_view); 198 | std::string_view key () const noexcept; 199 | /* implementation-defined */ split () const; 200 | }; 201 | 202 | using value_range = /* implementation-defined */ 203 | using key_range = /* implementation-defined */ 204 | using iterator = /* implementation-defined */ 205 | using value_type = variable; 206 | 207 | template 208 | value_type operator [] (T const&) const; // see below 209 | 210 | value_type operator [] (std::string const&) const noexcept; 211 | value_type operator [] (std::string_view) const; 212 | value_type operator [] (char const*) const noexcept; 213 | 214 | template 215 | iterator find (K const&) const noexcept; 216 | 217 | bool contains (std::string_view) const noexcept; 218 | 219 | iterator cbegin () const noexcept; 220 | iterator cend () const noexcept; 221 | 222 | iterator begin () const noexcept; 223 | iterator end () const noexcept; 224 | 225 | size_type size () const noexcept; 226 | bool empty () const noexcept; 227 | 228 | value_range values () const noexcept; 229 | key_range keys () const noexcept; 230 | 231 | template 232 | void erase (K const&) noexcept; 233 | }; 234 | ``` 235 | 236 | All member functions that take a `T const&` follow the same rules regarding 237 | `std::string`'s 10th and 11th constructors regarding `std::string_view` 238 | conversion. 239 | 240 | In the case of `environment::find`, this function follows the same rules of 241 | `std::map::find` as though it's `Compare::is_transparent` were valid. 242 | 243 | Note: `environment` does not have a `Compare` template member, but can act as 244 | though it has one, since it works almost exclusively on strings. 245 | -------------------------------------------------------------------------------- /src/different-types.bs: -------------------------------------------------------------------------------- 1 | 16 | 25 | 26 | # Revision History # {#changelog} 27 | 28 | ## Revision 1 ## {#r1} 29 | 30 | Changed name of `similar_to` to `analagous_to` based on feedback from 31 | LEWG(I) and various other folks reaching out about the use of *similar* 32 | in [conv.qual]/3. This also avoids conflict with P2196. 33 | 34 | ## Revision 0 ## {#r0} 35 | 36 | Initial Release. 🥳 37 | 38 | # Motivation # {#motivation} 39 | 40 | Within the standard library, there are multiple instances of phrases such as 41 | "If *T* is the same type as *U*, the program is ill-formed", or *This function 42 | does not participate in overload resolution unless the following contrainsts 43 | are true*, followed by something like `is_same_v, type>`. It 44 | would, honestly, be easier to express these with concepts. Furthermore, the 45 | inversion of the `std::same_as` concept is much more common than `std::same_as` 46 | itself when trying to constrain signatures of template types. Additionally, in 47 | the realm of ranges, users tend to need to express that *analagous* types are ok 48 | in some instances, while *distinct* (or *different*, see [[#feedback]] regarding 49 | this phrase) are fairly common. In the author's work codebase, the inversion of 50 | `std::same_as` appears about 20 times for each time where `std::same_as` is 51 | used. While it is still early days regarding concepts, it would not be 52 | surprising if other code bases had similar ratios, though most likely they are 53 | not as extreme. 54 | 55 | In the interest of symmetry with `same_as` and `different_from`, this paper 56 | provides both `analagous_to` and `distinct_from`, although the wording found 57 | below only redefines wording in terms of `different_from`, and `distinct_from`. 58 | 59 | # Request for Feedback # {#feedback} 60 | 61 | Currently, there is some ambiguity with the naming of `different_from` and 62 | `distinct_from`. When something is *distinct*, it typically implies that it is 63 | set apart and completely unique from something else. However, the direct 64 | antonym of *same* in english is *different*. The author is fine with swapping 65 | the definition of `different_from` and `distinct_from`, but will defer to 66 | any consensus from LEWG or LWG if it is deemed necessary to swap their 67 | definitions. 68 | 69 | # Wording # {#wording} 70 | 71 | The following wording is based off of the text found at https://eel.is/c++draft as of 2020-07-11. 73 | 74 | The following is to be modified (according to a value as decided by LWG) in 75 | 17.3.2 Header `` synopsis 76 | **[version.syn]**, statement 2 77 | 78 |
79 | `#define __cpp_lib_concepts` 80 | `202002L`20����L 81 |
82 | 83 | The following is to be added to 18.3 Header `` synopsis 84 | **[concepts.syn]** 85 | 86 |
87 | 88 | // [concept.different], concept different_from
89 | `template `
90 |  `concept different_from = ` see below`;`

91 | // [concept.analagous], concept analagous_to
92 | `template `
93 |  `concept analagous_to = ` see below`;`

94 | // [concept.distinct], concept distinct_from
95 | `template `
96 |  `concept distinct_from = ` see below`;`
97 |
98 |
99 | 100 | The following is to be added to 18.4 *Language-related concepts* 101 | [**concepts.lang**] 102 | 103 |
104 | 105 | 18.4.� Concept `different_from` **[concept.different]** 106 | `template `
107 |  `concept different_from = not same_as;` 108 |
109 | 18.4.� Concept `analagous_to` **[concept.analagous]** 110 | `template `
111 |   `concept analagous_to = same_as, remove_cvref_t>;` 112 |
113 | 18.4.� Concept `distinct_from` **[concept.distinct]** 114 | `template `
115 |  `concept distinct_from = not analagous_to;` 116 |
117 |
118 | 119 | The following is to be modified in 20.6.3.1 *Constructors* **[optional.ctor]**, 120 | statement 22 121 | 122 |
123 | *Constraints*: `is_constructible_­v` is `true`, 124 | `is_same_v`, `in_place_­t>``distinct_from`is `false``true`, and 126 | `is_same_v, optional>``distinct_from` is `false`.`true` 128 |
129 | 130 | The following is to be modified in 20.6.3.3 *Assignment* **[optional.assign]**, 131 | statement 14 132 | 133 |
134 | *Constraints*: `is_same_v, optional>` 135 | `distinct_from`is`false``true` 136 |
137 | 138 | The following is to be modified in 20.7.3.3 *Assignment* **[variant.assign]**, 139 | statement 12.1 140 | 141 |
142 | — `is_same_v, 143 | variant>``distinct_from` is 144 | `false``true`. 145 |
146 | 147 | The following is to be modified in 20.8.3.1 *Construction and destruction* 148 | **[any.cons]**, statement 6 149 | 150 |
151 | *Constraints*: `VT` is not the same type as 152 | `any``distinct_from` is `true` 153 |
154 | 155 | The following is to be modified in 20.14.5.1 *Constructors and destructor* 156 | **[refwrap.const]**, statement 2 157 | 158 |
159 | *Constraints*: The expression *FUN*`(declval())` is well-formed and 160 | `is_same_v, 161 | reference_wrapper>``distinct_from` is 162 | `false``true` 163 |
164 | 165 | The following is to be modified from 21.2 *Character traits* **[char.traits]**, 166 | statement 3 167 | 168 |
169 | To specialize those templates to generate a string, string view, or iostream 170 | class to handle a particular character container type 171 | ([defns.character.container]) `C`, that and its related character traits class 172 | `X` are passed as a pair of parameters to the string, string view, or iostream 173 | template as parameters `charT` and `traits`. If `X::char_type` is not the 174 | same type as `C`, the program is ill-formed.If 175 | `different_from` is `true`, the program is ill-formed 176 |
177 | 178 | The following is to be modified from 21.3.2.1 *General requirements* 179 | **[string.require]**, statement 3 180 | 181 |
182 | In every specialization `basic_string`, the type 183 | `allocator_traits::value_type` shall name the same type as `charT`. 184 | Every object of type `basic_string` uses an object of 185 | type `Allocator` to allocate and free storage for the contained `charT` objects 186 | as needed. The `Allocator` object used is obtained as described in 187 | [container.requirements.general]. In every specialization `basic_string`, the type `traits` shall mee the character traits 189 | requirements ([char.traits]). [*Note:* The program is ill-formed if 190 | `traits::char_type` is not the same type as `charT` 191 | `different_from` is `true` -- 192 | *end note*] 193 |
194 | 195 | The following is to be modified from 21.4.2 Class template 196 | `basic_string_view` **[string.view.template]**, statement 1 197 | 198 |
In every specialization `basic_string_view`, the 199 | type `traits` shall mee the character traits requirements ([char.traits]). 200 | [*Note:* The program is ill-formed if `traits::char_type` is not the same 201 | type as `charT` `different_from` is 202 | `true` -- 203 | *end note*] 204 |
205 | 206 | The following is to be removed from 24.5.1 *Helper concepts* 207 | [**range.utility.helpers**] 208 | 209 |
210 | 211 | `template`
212 | [TAB]`concept not-same-as = // exposition only`
213 | [TAB]  `!same_as, remove_cvref_t>;` 214 |
215 |
216 | 217 | The following is to be modified in 24.5.3 *Sub-ranges* [**range.subrange**] 218 | 219 |
220 | `template`
221 | [TAB] `concept convertible-to-non-slicing = // exposition only`
222 | [TAB] [TAB] `convertible_to &&`
223 | [TAB] [TAB] `!(is_pointer_v> &&`
224 | [TAB] [TAB]  `is_pointer_v> &&`
225 | [TAB] [TAB]  `not-same-as>, 226 | remove_pointer_t>>);`
[TAB] 227 | [TAB] `distinct_from>, 228 | remove_pointer_t>>);` 229 |
230 | 231 | The following is to be modified in 24.5.3.1 *Constructors and conversions* 232 | [**range.subrange.ctor**] 233 | 234 |
235 | `template R>`
236 | `template R>`
237 | [TAB] `requires borrowed_range &&`
238 | [TAB] [TAB] `convertible-to-non-slicing, I> &&`
239 | [TAB] [TAB] `convertible_­to, S>`
240 | `constexpr subrange(R&& r) requires (!StoreSize || sized_­range);` 241 |
242 | 243 |
244 | `template PairLike>`
245 | `template PairLike>`
246 | [TAB] `requires pair-like-convertible-from`
247 | `constexpr operator PairLike() const;` 248 |
249 | 250 | The following is to be modified in 24.7.3.1 *Class template ref_view* 251 | [**range.ref.view**] 252 | 253 | Inside of statement 1 254 |
255 | `template T>`
256 | `template T>`
257 | [TAB] `requires /* see below */`
258 | `constexpr ref_view(T&& t);` 259 |
260 | 261 | At the definition preceeding statement 2 262 |
263 | `template T>`
264 | `template T>`
265 | [TAB] `requires /* see below */`
266 | `constexpr ref_view(T&& t);` 267 |
268 | 269 | The following is to be modified in 29.3.2 *Overview* 270 | **[iostream.forward.overview]**, statement 6 271 | 272 |
273 | [*Note:* For each of the class tempaltes above, the program is ill-formed if 274 | `traits::char_type` is not the same type as 275 | `charT``different_from` is `true` 276 | ([char.traits]) -- 277 | *end note*] 278 |
279 | 280 | The following is to be modified in 32.4.2.2 *Constructors* 281 | **[thread.thread.constr]**, statement 3 282 | 283 |
284 | *Constraints*: `remove_cvref_t` is not the same type as 285 | `thread``distinct_from` is `true` 286 |
287 | 288 | The following is to be modified in 32.4.3.1 *Constructors, move, and 289 | assignment*, statement 3 290 | 291 |
292 | *Constraints*: `remove_cvref_t` is not the same type as 293 | `jthread``distinct_from` is `true` 294 |
295 | 296 | The following is to be modified in 32.9.10.1 *Member functions* 297 | **[futures.task.members]**, statement 2 298 | 299 |
300 | *Constraints*: `remove_cvref_t` is not the same type as 301 | `packaged_task``distinct_from` is 302 | `true` 303 |
304 | 305 | The following is to be removed from the *Index of library concepts* 306 | 307 |

308 | not-same-as[range.utility.helpers] 309 |

310 | 311 | The following is to be added to the *Index of library concepts* 312 | 313 |

314 | different_from[concept.different], 315 | analagous_to[concept.analagous], 316 | distinct_from[concept.distinct] 317 |

318 | 319 | -------------------------------------------------------------------------------- /src/feature-presentation.bs: -------------------------------------------------------------------------------- 1 | 13 | 17 | 18 | # Revision History # {#changelog} 19 | 20 | ## Revision 0 ## {#r0} 21 | 22 | Initial Release 🎉 23 | 24 | # Motivation # {#motivation} 25 | 26 | Languages like Rust or D or dynamically typed languages like Python and Ruby 27 | have some form of a global "static" if (static in the sense that it executes at 28 | compile time). While a previous attempt was made to add this to C++, it was 29 | later reduced down to `if constexpr`. However, `if constexpr` is limited in 30 | that it still requires code be semantically correct. This makes is very 31 | difficult to use for wrapping platform specific code for cross platform 32 | interfaces. Instead, we must still rely on preprocessor `ifdef`s in order to 33 | get what we want. In Rust, they have a `#[cfg()]` attribute that allows 34 | specific declarations and definitions to be removed from the AST at compile 35 | time. This paper proposes to add both a `[[feature()]]` attribute as well as a 36 | function `std::feature` that would permit removing the need for `ifdef` at any 37 | non-function level scope. This is desired due to programmers having to target a 38 | platform, and in some cases multiple platforms that behave more or less the 39 | same, but have different identifiers available. 40 | 41 | # Design # {#design} 42 | 43 | The design of the feature presentation is two-fold. When a compiler sees the 44 | `[[feature("key")]]` attribute, it has to see if it is in the allowed or 45 | blocked list of "features". If any of the `key` strings are allowed, the 46 | compiler parses as normal. In the event that the key is absent or in an 47 | explicit block list, the compiler attempts to treat the declaration and all its 48 | contents as a token soup, thus removing it from the AST. In other words, the 49 | code contained within a declaration where the feature attribute key is 50 | disallowed must be syntactically, but not semantically, correct. If the feature 51 | key given is allowed, then the compiler treats the declaration like normal. 52 | 53 | We also provide a `std::feature()` function which allows users to check whether 54 | a given set of strings are valid inside of `if constexpr`. 55 | 56 | Lastly, this attribute does not interfere with ODR, since two translation units 57 | compiled with the same named definition but different features is not 58 | permitted. 59 | 60 | # Example # {#example} 61 | 62 | As a example, let's look at some simple interface code that could be written 63 | with this attribute, where we are targeting both DirectX and Vulkan: 64 | 65 | ```cpp 66 | 67 | struct [[feature("vulkan")]] Device { 68 | [[feature("glsl-to-spirv")]] 69 | static Shader compile(std::filesystem::path filename); 70 | static Shader load (std::filesystem::path spirv_file); 71 | }; 72 | 73 | struct [[feature("direct-x")]] Device { 74 | [[feature("hlsl-to-spirv")]] 75 | static Shader compile (std::filesystem::path filename); 76 | static Shader load (std::filesystem::path spirv_file); 77 | }; 78 | ``` 79 | 80 | In the above sample, we have two interfaces for a class named `Device`. In this 81 | case, we might want to have the ability to compile shaders to an intermediate 82 | SPIR-V representation before we release the product that uses this code. 83 | However, allowing users to use the product's compiler might not be desired and 84 | thus we can choose to not only switch between these operations but to disable 85 | the ability to even call `Device::compile` in all interfaces. 86 | -------------------------------------------------------------------------------- /src/implicit-module-partitions.bs: -------------------------------------------------------------------------------- 1 | 17 | 21 | 22 | # Revision History # {#changelog} 23 | 24 | ## Revision 1 ## {#r1} 25 | 26 | Added actionable language for inclusion into the standard. We now state that 27 | splayed layouts are the minimum requirement and any hierarchy or module name 28 | enforcement is solely the responsibility of the build system. 29 | 30 | ## Revision 0 ## {#r0} 31 | 32 | Initial Release 🎉 33 | 34 | # Motivation # {#motivation} 35 | 36 | As the advent of modules approaches, build systems and developers are still 37 | waiting on the final bits that will make it into C++20. This paper seeks to 38 | provide a path for build system optimizations based on existing tooling (unity 39 | builds, amalgamation headers) that has been reworded for modules. This reduces 40 | work required by build systems, general effort from compilers (e.g., they will 41 | not need to implement a local socket based client for information passing, and 42 | build system developers don't have to worry about CVEs because of said socket 43 | clients), and to make the lives of developers easier as they will get to 44 | experience a fairly consistent development process when moving between or 45 | across projects. 46 | 47 | # Design # {#design} 48 | 49 | The design for this behavior is as follows: 50 | 51 | A so-called *module entrypoint* is placed inside of a directory. A *module 52 | entrypoint*, as defined in [[#wording]] is a file. The name of this 53 | *entrypoint* is user defined, with a fallback to a file with the base name 54 | `module` and possibly some file extension. We do not enforce the requirement of 55 | a file extension so that operating systems without them can do as they please. 56 | This also allows the community to eventually select a "better" file extension. 57 | That said, the author recommends `cxx` as that is the environment variable used 58 | by plenty of build systems to represent the C++ compiler, where as `CPP` 59 | represents the **C** **P**re**P**rocessor. Additionally, moving libraries from 60 | non-module APIs to modularized APIs can have a path of least resistance instead 61 | of requiring something that has nothing to do with the name C++, like `cppm` or 62 | `mxx`. 63 | 64 | Therefore, if a module's interface has `import :list`, the compiler will look for a file with a base name of `list`, and (optionally) the same file extension as `module`. 65 | 66 | This behavior only occurs during the construction of a BMI. If dependent 67 | modules are not yet compiled, or if users are expecting an object file to 68 | magically pop out of the compiler, they will be surprised when the compiler 69 | gives an error. 70 | 71 | The building of object files is still left up to build systems so that existing 72 | distributed build system workflows are not interrupted (such as in the case of 73 | tools like Icecream, distcc, or sccache). 74 | 75 | Where things get interesting is when a user desires to import *another module* 76 | into a *module entrypoint*. Given the following directory layout: 77 | 78 | ``` 79 | . 80 | └── src 81 | └── core 82 | ├── module.cxx 83 | ├── list.cxx 84 | └── io 85 | ├── module.cxx 86 | └── file.cxx 87 | ``` 88 | 89 | We can assume that, perhaps, the source for `core/module.cxx` looks something 90 | like: 91 | 92 | ```cpp 93 | export module name; 94 | 95 | export import core.io; 96 | import :list; 97 | ``` 98 | 99 | In other languages, this would imply that the compiler has to now recurse into 100 | the `core/io` directory. **We do not do this**. Instead, the build system is 101 | required to have seen the `export import` and passed that directory along to 102 | the compiler first. This is a combination of the various modules systems, but 103 | has the following properties: 104 | 105 | 1. It does not make the compiler a build system 106 | 2. Existing work that has been done to handle dependency management does not 107 | need to be thrown away. 108 | 3. The `core.io` module *does not* have to exist in the directory `core/io`. 109 | Rather, it can exist techically anywhere. 110 | 4. Build systems are free to enforce the name of a module to its location on 111 | disk, while also permitting others to ignore it entirely. 112 | 5. Build systems can have a guaranteed fallback location if developers don't 113 | want to have to manually specify the location of each and every module. 114 | 6. This doesn't actually tie the compiler to a filesystem approach, as this 115 | is just a general convention. 116 | 7. Build systems are free to implement, additional conventions, such as the 117 | Pitchfork or Coven filesystem layout and enforce it for modules having 118 | legacy non-module code in the same project layout. 119 | 8. It allows developers to view modules as hierarchical, even if they aren't. 120 | This means that, if treating modules as a hierarchy becomes widespread 121 | enough, the standard could possibly enforce modules as hierarchies in the 122 | future. 123 | 9. Platforms where launching processes are expensive can take advantage of 124 | improved throughput when reading from files. 125 | 10. Build systems and compilers are free to take an optimization where only 126 | the modified times of a directory are checked before the contents of each 127 | directory are checked. On every operating system (yes, every operating 128 | system), directories change their timestamp if any of the *files* 129 | contained within change, but do not update if child directories do as 130 | well. While some operating systems permit mounting drives and locations 131 | without modified times, doing so breaks nearly every other build system 132 | in existence. Thus we can safely assume that a build system does not need 133 | to reparse or rebuild a module if its containing directory has not 134 | changed. 135 | 136 | # Examples # {#examples} 137 | 138 | The following two examples show how implicit module partition lookup can be 139 | used for both hierarchical and "splayed" directory layouts. 140 | 141 | ## Hierarchical ## {#example-hierarchy} 142 | 143 | This sample borrows from the above example. Effectively, to import `core.io`, 144 | one must build it before building `core` simply because the build system 145 | assumes that `core.io` refers to a directory named `core/io` from the project 146 | root. 147 | 148 | ``` 149 | . 150 | └── src 151 | └── core 152 | ├── module.cxx 153 | ├── list.cxx 154 | └── io 155 | ├── module.cxx 156 | └── file.cxx 157 | ``` 158 | 159 | This behavior is *not* enforced by the compiler, but rather by the build 160 | system. If a build system does not support a *hierarchical* implicit lookup, it 161 | can at least support a *splayed* implicit lookup 162 | 163 | ## Splayed ## {#example-splayed} 164 | 165 | This approach is one that might be more commonly seen as C++ developers move 166 | from headers to modules. 167 | 168 | ``` 169 | . 170 | ├── core 171 | │ ├── module.cxx 172 | │ └── list.cxx 173 | └── io 174 | ├── module.cxx 175 | └── file.cxx 176 | ``` 177 | 178 | In the above layout, `core.io` is located in `./io`, rather than under the 179 | `./core` directory. A sufficiently simple build system could be told that 180 | `core.io` resides under `./io` and not to rely on some kind of hierarchical 181 | directory layout. 182 | 183 | # Wording # {#wording} 184 | 185 | The following is to be placed into the current working draft at a location 186 | within close proximity to, or adjacent to, the current merged modules wording: 187 | 188 | 189 | 190 |
**General**
191 | 192 | 1 This subclause describes operations to be supported regarding 193 | modules, their *implicit* partitions, and interactions within the filesystem. 194 | 195 | 2 A *module container* is a collection of files represented by a *directory*. [fs.general] 196 | 197 | 3 A *module entrypoint* is a filesystem object within a *module container* that holds the purview of an *exported module*. 198 | 199 | 4An *implicit module partition* is a filesystem object adjacent to a *module entrypoint*. 200 | 201 |
**Conformance**
202 | 203 | 1Conformance is required for all vendors whose implementations 204 | create a final program to be run on the abstract machine 205 | 206 | 2Implementations must provide a flag for users to declare a *module 207 | container* as a filesystem directory. 208 | 209 | 3Implementations must provide a flag for users to declare the name 210 | of a *module entrypoint* 211 | 212 | 4Implementations must use a predefined *module entrypoint* if none 213 | is provided by a user. This entrypoint must be a file with a base name of 214 | *module*. 215 | 216 | (*Note: The extension of the file is currently left unspecified, but should 217 | be one of the several file extensions that have been historically used. (e.g., 218 | `c++`, `cpp`, `cxx`, and `cc`, et. al. *) 219 | 220 |
**Behavior**
221 | 222 | 1In the *module entrypoint*'s *preamble*, when an implementation 223 | encounters a *module partition* import, it shall look for an *implicit 224 | module partition* with the same base name as the *module partition* 225 | 226 | 227 |
228 | -------------------------------------------------------------------------------- /src/inline-modules.bs: -------------------------------------------------------------------------------- 1 | 11 | 15 | 16 | # Revision History # {#changelog} 17 | 18 | ## Revision 0 ## {#r0} 19 | 20 | Initial Release 🎉 21 | 22 | # Motivation # {#motivation} 23 | 24 | This paper is an addendum to the [[P1242]] proposal. That paper 25 | proposes having a module consist of *at most* two parts: An interface and 26 | implementation part. These are separated by some syntactic marker, (in this 27 | case `module :private` as `private` is a reserved identifier). However, this 28 | proposal goes a step further and advocates having a nearly unlimited number of 29 | so-called *inline module partitions* per file. The author of this paper argues 30 | that [[P1242]] should be the minimum interface implemented for C++20, but that 31 | an alternative superset is available and that's what this proposal discusses. 32 | 33 | # Design # {#design} 34 | 35 | [[P1242]] states that 36 | 37 |
38 | If a module interface unit includes an inline module implementation partition, 39 | it will appear after the interface itself annd be separated from the interface 40 | by some syntactic divider. 41 |
42 | 43 | The current syntax it proposes in it's examples is `module :private`. This 44 | paper instead relies on the `inline` keyword and any desired identifer for a 45 | module partition. So the equivalent use would be `inline module :impl`. 46 | 47 | The reason we don't reserve an identifier for this *partition marker* is to 48 | permit users to define what partition naming conventions make most sense to 49 | them. This approach might not have been possible before C++20, however the 50 | precedece of Contracts requiring vendors to provide a compiler flag gives us 51 | the same capabilities if desired. In this case, we can compile a module 52 | interface unit *up to* a given *marker*. This also provides us with one 53 | additional feature regarding code. Users can now theoretically place their unit 54 | tests under an inline module partition, and compile the file twice, once for 55 | the actual code to be compiled into an executing program, and again to include 56 | unit tests, without having to bloat the final executable or library. 57 | 58 | Thus, the structure of a module interface unit will be as follows: 59 | 60 | 1. Module interface partition 61 | 2. `N` Optional inline module partitions 62 | 63 | When a module interface partition is imported, its corresponding inline module 64 | implementation partitions are imported up to the same *partition marker* as the 65 | current primary module interface partition. 66 | 67 | Much like [[P1242]], inline module partitions are never re-exported. 68 | 69 | # Examples # {#examples} 70 | 71 | These examples are adapted from [[P1242]] 72 | 73 | ```cpp 74 | export module m; 75 | struct s; 76 | export usig s_ptr = s*; 77 | 78 | inline module :impl; 79 | struct s { }; 80 | ``` 81 | 82 | ## Multi source ## {#example-multi} 83 | 84 | Module interface unit for partition `m:impl`: 85 | 86 | ```cpp 87 | export module m :impl; 88 | struct s; 89 | s* f (); 90 | 91 | inline module :priv; 92 | struct s { }; 93 | ``` 94 | 95 | Primary module interface unit for `m`: 96 | 97 | ```cpp 98 | export module m; 99 | export import m:impl; 100 | ``` 101 | 102 | Translation unit using `m`: 103 | 104 | ```cpp 105 | import m; 106 | s var; // ill-formed, S is incomplete 107 | s* s_ptr = f(); // OK 108 | auto sz = sizeof(*f()); // ill-formed, S is incomplete 109 | ``` 110 | 111 |
112 | {
113 |   "P1242": {
114 |     "href": "https://wg21.link/p1242r0",
115 |     "title": "Single-file modules with the Atom semantic properties rule",
116 |     "authors": "Jeff Snyder"
117 |   }
118 | }
119 | 
120 | -------------------------------------------------------------------------------- /src/integer-width-literals.bs: -------------------------------------------------------------------------------- 1 | 12 | 16 | 17 | # Revision History # {#changelog} 18 | 19 | ## Revision 2 ## {#r2} 20 | 21 | * Make all functions `consteval` instead of `constexpr` at the recommendation 22 | of Alisdair Meredith. 23 | 24 | ## Revision 1 ## {#r1} 25 | 26 | * Modify return types to actually be `[u]int_leastXX_t` and friends. This is 27 | to make sure that we are actually replacing the `[U]INTxx_C` macros, as 28 | these return a `[u]int_leastXX_t` 29 | 30 | ## Revision 0 ## {#r0} 31 | 32 | Initial Release 🎉 33 | 34 | # Motivation # {#motivation} 35 | 36 | Proposal [[p0330r2]] proposes literal suffixes for `ptrdiff_t` and `size_t`. In 37 | it, the question [[p0330r2#design-std-ints| What about the fixed/least/max 38 | (unsigned) int types?]] is given regarding the fixed/least/max integer types 39 | provided by ``. As that paper has decided to focus exclusively on 40 | `ptrdiff_t` and `size_t`, this proposal will instead focus on the *fixed* width 41 | integer literals. The problem of least/max literals is left for another time 42 | and paper. The primary goal of this paper is to replace the 43 | `UINTxx_C`/`INTxx_C` macros found in `` with user defined literal 44 | suffixes. 45 | 46 | As an example, one can see the results of the compiler results on 47 | godbolt. 48 | 49 | ```c++ 50 | #include 51 | 52 | void foo(uint32_t); 53 | void foo(uint64_t); 54 | 55 | void test() 56 | { 57 | // change this to 10ul and it fails to compile on MSVC 58 | // change this to 10ull and it fails to compile on GCC and Clang 59 | // The only safe thing to do is to use the UINTXX_C macros. 60 | auto x = 10; 61 | foo(x); 62 | } 63 | ``` 64 | 65 | Note: The author is more than willing to extend this paper's scope for the 66 | least and max integer types, however the fixed width integer literals show 67 | promise of the least complex changes. Also we get to deprecate some C macros in 68 | the process and that's always a good thing. 69 | 70 | # Design # {#design} 71 | 72 | The design for these literals suffixes is to permit explicitly sized 73 | expressions when writing integer literals (e.g., `123i32 + 12i16`). 74 | These operators are declared in the namespace `std::literals::integer_literals` 75 | where both `literals` and `integer_literals` are inline namespaces. Access to 76 | these operators can be gained with: 77 | 78 | * `using namespace std::literals` 79 | * `using namespace integer_literals` 80 | * `using namespace std::literals::integer_literals`. 81 | 82 | It is intended that these operators be placed into the `` header. 83 | 84 | ## Synopsis ## {#design-synopsis} 85 | 86 | The operator's full specification is: 87 | 88 | ```c++ 89 | namespace std::inline literals::inline integer_literals { 90 | consteval uint_least64_t operator ""u64 (unsigned long long arg); 91 | consteval uint_least32_t operator ""u32 (unsigned long long arg); 92 | consteval uint_least16_t operator ""u16 (unsigned long long arg); 93 | consteval uint_least8_t operator ""u8 (unsigned long long arg); 94 | 95 | consteval int_least64_t operator ""i64 (unsigned long long arg); 96 | consteval int_least32_t operator ""i32 (unsigned long long arg); 97 | consteval int_least16_t operator ""i16 (unsigned long long arg); 98 | consteval int_least8_t operator ""i8 (unsigned long long arg); 99 | } 100 | ``` 101 | 102 | # Examples # {#examples} 103 | 104 | A small code example is presented below to show how the suffix can be 105 | used in code. (This code sample assumes that [[P0645]] has been added to the 106 | standard. Additionally as the author of [[P1276]], we use that here as well) 107 | 108 | Note: This code sample does not attempt to solve the previously shown code 109 | in [[#motivation]], as this is easily solved elsewhere. 110 | 111 | ```cpp 112 | template 113 | void println (Args&&... args) { 114 | std::puts(std::format(std::forward(args)...).c_str()); 115 | } 116 | 117 | void main () { 118 | using namespace std::literals; 119 | println("1 + 2 = {}", 1u32 + 2); 120 | println("1 - 2 = {}", 1i32 - 2); 121 | 122 | println("0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101); 123 | println("0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101); 124 | println("0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101); 125 | 126 | println("One million is written as {}", 1'000'000u32) 127 | } 128 | ``` 129 | 130 | # Wording # {#wording} 131 | 132 | The following is wording for the library section. 133 | 134 | 135 | 136 | 137 | namespace std::inline literals::inline integer_literals { 138 | consteval uint_least64_t operator ""u64 (unsigned long long arg); 139 | } 140 | 141 | 142 |
    143 |
  1. *Constraints* 144 | 145 | `arg` must fit in the range `arg <= numeric_limits::max()` 146 |
  2. 147 |
  3. *Returns* 148 | 149 | An integer of type `uint_least64_t` with the value given in `arg` 150 |
  4. 151 |
152 | 153 | 154 | 155 | namespace std::inline literals::inline integer_literals { 156 | consteval uint_least32_t operator ""u32 (unsigned long long arg); 157 | } 158 | 159 | 160 |
    161 |
  1. *Constraints* 162 | 163 | `arg` must fit in the range `arg <= numeric_limits::max()` 164 |
  2. 165 |
  3. *Returns* 166 | 167 | An integer of type `uint_least32_t` with the value given in `arg` 168 |
  4. 169 |
170 | 171 | 172 | 173 | namespace std::inline literals::inline integer_literals { 174 | consteval uint_least16_t operator ""u16 (unsigned long long arg); 175 | } 176 | 177 | 178 |
    179 |
  1. *Constraints* 180 | 181 | `arg` must fit in the range `arg <= numeric_limits::max()` 182 |
  2. 183 |
  3. *Returns* 184 | 185 | An integer of type `uint_least16_t` with the value given in `arg` 186 |
  4. 187 |
188 | 189 | 190 | 191 | namespace std::inline literals::inline integer_literals { 192 | consteval uint_least8_t operator ""u8 (unsigned long long arg); 193 | } 194 | 195 | 196 |
    197 |
  1. *Constraints* 198 | 199 | `arg` must fit in the range `arg <= numeric_limits::max()` 200 |
  2. 201 |
  3. *Returns* 202 | 203 | An integer of type `uint_least8_t` with the value given in `arg` 204 |
  4. 205 |
206 | 207 | 208 | namespace std::inline literals::inline integer_literals { 209 | consteval int_least64_t operator ""i64 (unsigned long long arg); 210 | } 211 | 212 | 213 |
    214 |
  1. *Constraints* 215 | 216 | `arg` must fit in the range `arg <= numeric_limits::max()` 217 |
  2. 218 |
  3. *Returns* 219 | 220 | An integer of type `int_least64_t` with the value given in `arg` 221 |
  4. 222 |
223 | 224 | 225 | namespace std::inline literals::inline integer_literals { 226 | consteval int_least32_t operator ""i32 (unsigned long long arg); 227 | } 228 | 229 | 230 |
    231 |
  1. *Constraints* 232 | 233 | `arg` must fit in the range `arg <= numeric_limits::max()` 234 |
  2. 235 |
  3. *Returns* 236 | 237 | An integer of type `int_least32_t` with the value given in `arg` 238 |
  4. 239 |
240 | 241 | 242 | namespace std::inline literals::inline integer_literals { 243 | consteval int_least16_t operator ""i16 (unsigned long long arg); 244 | } 245 | 246 | 247 |
    248 |
  1. *Constraints* 249 | 250 | `arg` must fit in the range `arg <= numeric_limits::max()` 251 |
  2. 252 |
  3. *Returns* 253 | 254 | An integer of type `int_least16_t` with the value given in `arg` 255 |
  4. 256 |
257 | 258 | 259 | namespace std::inline literals::inline integer_literals { 260 | consteval int_least8_t operator ""i8 (unsigned long long arg); 261 | } 262 | 263 | 264 |
    265 |
  1. *Constraints* 266 | 267 | `arg` must fit in the range `arg <= numeric_limits::max()` 268 |
  2. 269 |
  3. *Returns* 270 | 271 | An integer of type `int_least8_t` with the value given in `arg` 272 |
  4. 273 |
274 | 275 |
276 | 277 | ## Feature Testing ## {#feature-test} 278 | 279 | The `__cpp_lib_integer_literals` feature test macro should be added. 280 | 281 |
282 | {
283 |   "p0330r2": {
284 |     "authors": [
285 |       "JeanHeyd Meneide",
286 |       "Rein Halbersma"
287 |     ],
288 |     "href": "https://wg21.link/p0330r2",
289 |     "title": "Literal Suffixes for ptrdiff_t and size_t"
290 |   },
291 |   "P1276": {
292 |     "authors": "Isabella Muerte",
293 |     "href": "https://wg21.link/p1276r0",
294 |     "title": "Void Main"
295 |   },
296 |   "P0645": {
297 |     "authors": "Victor Zverovich",
298 |     "href": "https://wg21.link/p0645r3",
299 |     "title": "Text Formatting"
300 |   }
301 | }
302 | 
303 | -------------------------------------------------------------------------------- /src/modern-offsetof.bs: -------------------------------------------------------------------------------- 1 | 12 | 16 | 17 | # Revision History # {#changelog} 18 | 19 | ## Revision 0 ## {#r0} 20 | 21 | Initial Release 🎉 22 | 23 | # Motivation # {#motivation} 24 | 25 | `offsetof()` is a relic of its time. It's a macro, works only on standard 26 | layout types, and by its very definition requires a compiler to either 27 | implement it via an intrinsic or to use undefined behavior. 28 | 29 | While it would be nice to assume that `offsetof` and similar tools are no 30 | longer needed, this is unfortunately not the case. When working with some C 31 | APIs, or older C++ APIs, knowing the offset of a type is necessary for a 32 | library to remain "ABI" safe. A good example of this is the Python scripting 33 | language. Python's C API allows users to "bind" C and C++ types to the Python 34 | API. Of important note is that data members can be bound to a Python class 35 | directly via the `PyMemberDef` struct. This struct requires type information 36 | about the "reflected" member in the form of a constant (such as `T_OBJECT` or 37 | `T_LONG`), and the offset to the given member. As using `offsetof()` can in 38 | some cases cause undefined behavior, it means that fully valid code is 39 | *technically speaking* not safe to work in a portable manner, thus upending 40 | Python's C API which is designed for portable use across platforms. 41 | 42 | An example of this problem can be found below (actual final binding code has 43 | been removed for clarity): 44 | 45 | ```cpp 46 | #include 47 | #include "structmember.h" 48 | 49 | struct CustomObject { 50 | PyObject_HEAD 51 | PyObject *first; 52 | PyObject *last; 53 | int number; 54 | }; 55 | 56 | static PyMemberDef Custom_members[] = { 57 | {"first", T_OBJECT_EX, offsetof(CustomObject, first), 0, "first name"}, 58 | {"last", T_OBJECT_EX, offsetof(CustomObject, last), 0, "last name"}, 59 | {"number", T_INT, offsetof(CustomObject, number), 0, "custom number"}, 60 | { } /* Sentinel */ 61 | }; 62 | ``` 63 | 64 | As we can see above, offsets for `first`, `last`, and `number` are all obtained 65 | via the `offsetof()` macro. However, this also places us into the predicament 66 | that we are unable to write a generic function to handle all of this work for 67 | us. Specifically, under standard C++, we cannot implement something equivalent 68 | to the following: 69 | 70 | ```cpp 71 | template 72 | PyMemberDef readonly (PyObject* (T::*member), char const* name, char const* docs=nullptr) { 73 | return { name, T_OBJECT_EX, offsetof(T, member), READONLY, docs }; 74 | } 75 | 76 | template 77 | PyMemberDef member (PyObject* (T::*member), char const* name, char const* docs=nullptr) { 78 | return { name, T_OBJECT_EX, offsetof(T, member), 0, docs }; 79 | } 80 | ``` 81 | 82 | This paper attempts to fix this by creating a new function named `std::offset` 83 | that can take pointer to member data as a parameter directly, without needing 84 | to know the name of the actual member. `std::offset` is intended to be similar 85 | to `offsetof()` in the same way that `std::bit_cast` is similar to 86 | `std::memcpy`. 87 | 88 | # Design Considerations # {#design} 89 | 90 | The design for `std::offset` is simple. Given any pointer to member data for a 91 | Standard Layout class, vendors will return the offset to said member according 92 | to the current target environment. In theory this sounds wild and unwiedly, 93 | however in practice, all current vendors simply place the offset to said member 94 | inside its pointer to member as either a `ptrdiff_t` or an `int`. 95 | 96 | There is, however, one interesting caveat. `std::offset` can be implemented 97 | entirely via `std::bit_cast`. Doing so means that `std::offset` *cannot* be 98 | `constexpr`. This paper is not tackling changing the constraints found on 99 | `std::bit_cast`, but a discussion is needed on whether `std::offset` should be 100 | `constexpr`, and whether `std::bit_cast` should permit `constexpr` casting of 101 | pointer to member data from Standard Layout classes to a `ptrdiff_t`. 102 | 103 | To match `offsetof`, it is the author's recommendation that `std::offset` be 104 | placed into the `` header. 105 | 106 | # Wording # {#wording} 107 | 108 | The following is working for the library section. 109 | 110 | 111 | 112 | namespace std { 113 | template <class T> 114 | ptrdiff_t offset (T const& pmd) noexcept; 115 | } 116 | 117 | 118 |
    119 |
  1. *Constraints* 120 | This function shall not participate in overload resolution unless: 121 | 122 | * `std::is_member_object_pointer_v` is `true` 123 | * `std::is_standard_layout_v>` is `true` 124 | 125 | Note: There is not `std::class_of_t` type trait available in standard C++. 126 | The above is shown for exposition purposes only. 127 |
  2. 128 |
  3. *Returns* 129 | 130 | An object of type `std::ptrdiff_t`, the value of which is the offset in 131 | bytes from the beginning of an object of the *class-of* `T` to the member 132 | specified by `T` including padding if any. 133 |
  4. 134 |
  5. *Remarks* 135 | 136 | While `offsetof` is conditionally supported for non-standard layout types, 137 | `offset` is intended to be more tightly constrained and *only* for standard 138 | layout types. 139 |
  6. 140 |
141 | 142 |
143 | 144 | ## Feature Testing ## {#feature-test} 145 | 146 | The `__cpp_lib_offset` feature test macro should be added. 147 | 148 | 149 | -------------------------------------------------------------------------------- /src/priority-target-tuplet.svg: -------------------------------------------------------------------------------- 1 |
Environment
Operating System
Architecture
Vendor
-------------------------------------------------------------------------------- /src/retain-ptr.bs: -------------------------------------------------------------------------------- 1 | 16 | 20 | 21 | # Acknowledgements # {#acknowledgements} 22 | 23 | I'd like to thank the following: 24 | 25 | * Jackie Kay and Brittany Friedman for encouraging me to submit this proposal. 26 | * Gor Nishanov, whose coroutine slides at CppCon 2016 gave me the final push 27 | to sit down and write this. 28 | * Bryce "Wash" Lelbach for representing this paper when I was unable to. 29 | * Mark Zeren for *constantly* bugging me to see if I might update this paper. 30 | * JF Bastien and Billy O'Neal for assisting me via Twitter with some atomic 31 | operations. 32 | * SG1 at San Diego 2018 for their guidance on the correct atomic operations 33 | needed. 34 | * The vast number of people in the C++ community who have patiently been 35 | waiting for this type to be added to the standard. 36 | 37 | # Revision History # {#changelog} 38 | 39 | ## Revision 2 ## {#r2} 40 | 41 | This is a major update to the proposal to apply and fix a variety of things 42 | including but not limited to: 43 | 44 | * Discussing research regarding various approaches to default actions 45 | * Feedback from real world users on missing or unexpected behavior 46 | * Feedback from SG1 regarding atomic operations 47 | 48 | Additionally, some of the motivation, rationale, and explanations have been 49 | more thoroughly explained. 50 | 51 | Lastly a not-so-complete list of changes made so far include 52 | * Rebase proposal and wording onto most recent C++20 draft 53 | * Added additional examples regarding wrapping OpenSSL 54 | * Update atomic operations found within `atomic_reference_count` 55 | * `retain_traits::default_action` is now called `retain_traits::reset_action` 56 | * `retain_object_t` and `adopt_object_t` have been renamed _back_ to 57 | `retain_t` and `adopt_t` until feedback has been given 58 | * The _Technical Specification_ section now has some type of actual wording. 59 | However, it will not be surprising if Core wants to change a lot of it 🙂 60 | * `retain_ptr` is now `constexpr` because WG21 can have little a `constexpr`, 61 | as a treat 62 | * Removed `static_pointer_cast`, `dynamic_pointer_cast`, 63 | `reinterpret_pointer_cast`, `const_pointer_cast`. 64 | 65 | ## Revision 1 ## {#r1} 66 | 67 | * Rewrote proposal in bikeshed, the hot new format that every cool proposal 68 | writer is using now. 69 | * Clarified section on addressof operator, now that `out_ptr` is being 70 | submitted 71 | * Removed `retain_ptr::unique` as it was removed from `shared_ptr` for C++20. 72 | * Removed code example of implementing a `std::future`-like type. 73 | * Changed `retain_ptr::detach` to `retain_ptr::release` 74 | * Changed name of `retain_t` to `retain_object_t` 75 | * Changed atomicity of `atomic_reference_count` based on suggestions from 76 | Billy O'Neal. 77 | * Changed wording to use C++17/C++20 features. 78 | * Changed order of document. [[#specification]] is now at the very end 79 | of the document, and [[#examples]] are further up. 80 | * Added `adopt_object_t` and `default_action` to let users decide what 81 | action they want on construction and reset. 82 | * Added `static_pointer_cast`, `dynamic_pointer_cast`, `const_pointer_cast`, 83 | and `reinterpret_pointer_cast`. 84 | * Added deduction guides to bring `retain_ptr` in line with other smart 85 | pointers 86 | * Added more code examples 87 | 88 | ## Revision 0 ## {#r0} 89 | 90 | Initial Release 🎉 91 | 92 | # Motivation # {#motivation} 93 | 94 | There are a wide variety of C and C++ APIs that rely on reference counting, 95 | but either because of the language (C) or the age of the library (C++), they 96 | are unable to be safely used with either `std::unique_ptr` or 97 | `std::shared_ptr`. In the former case, there is no guaranteed way to make 98 | sure the `unique_ptr` is the last instance (i.e., that it is _unique_), and in 99 | the latter case, `shared_ptr` manages its own API. In addition, existing 100 | intrusive smart pointers such as 101 | `boost::intrusive_ptr`, Microsoft's 102 | `ComPtr`, or WebKit's aptly named 103 | `WTF::RefPtr` do not meet the needs of modern C++ smart pointers or APIs. 104 | This proposal attempts to solve these shortcomings in an extensible and future 105 | proof manner, while permitting a simple upgrade path for existing project 106 | specific intrusive smart pointers and providing the opportunity for value 107 | semantics when interfacing with opaque interfaces. 108 | 109 | Those that work on systems or tools that rely on reference counting stand to 110 | benefit most from `retain_ptr`. Additionally, `retain_ptr` would be a benefit 111 | to standard library implementers for types that _secretly_ use intrusive 112 | reference counting such as a coroutines capable future and promise. 113 | 114 | If `retain_ptr` is added to the standard library, the C++ community would also 115 | be one step closer to a non-atomic `shared_ptr` and `weak_ptr`, much like 116 | Rust's `Rc`. 117 | 118 | A reference implementation of `retain_ptr`, along with examples of its use, 119 | can be found on [[implementation|github]]. 120 | 121 | # Scope and Impact # {#scope} 122 | 123 | `retain_ptr` would ideally be available in the `` standard 124 | header. It is a pure extension to the C++ standard library and can (more or 125 | less) be implemented using any conforming C++14 or C++11 compiler with very 126 | little effort. See the [[#specification]] for interface and behavior 127 | details. 128 | 129 | # Frequently Asked Questions # {#faq} 130 | 131 | Several common questions regarding the design of `retain_ptr` can be 132 | found below. 133 | 134 | ## How does `intrusive_ptr` not meet the needs of modern C++? ## {#faq-1} 135 | 136 | `boost::intrusive_ptr` has had nearly the same interface since its introduction 137 | in 2001 by Peter Dimov. Furthermore, `boost::intrusive_ptr` has several 138 | failings in its API that cannot be changed from without breaking compatibility. 139 | When constructing a `boost::intrusive_ptr`, by default it increments the 140 | reference count. This is because of its `intrusive_ref_count` mixin, which 141 | starts with a reference count of 0 when it is default constructed. Out of all 142 | the libraries I tried to look at, this was the one instance where an object 143 | required it be incremented after construction. This proposal rectifies this 144 | by permitting each traits instance to choose its default action during 145 | construction. 146 | 147 | Additionally, `boost::intrusive_ptr` does not have the ability to "overload" 148 | its `pointer` type member, requiring some additional work when interfacing with 149 | C APIs (e.g., `boost::intrusive_ptr())>`). This 150 | also precludes it from working with types that meet the _NullablePointer_ 151 | named requirements (a feature that `unique_ptr` supports). 152 | 153 | Furthermore, `boost::intrusive_ptr` relies on ADL calls of two functions: 154 | 155 | * `intrusive_add_ref` 156 | * `intrusive_release` 157 | 158 | While this approach is fine in most cases, it does remove the ability to easily 159 | "swap out" the actions taken when increment or decrementing the reference count 160 | (e.g., setting a debugger breakpoint on a specific traits implementation, but 161 | not on _all_ traits implementations). This naming convention uses terms found 162 | in Microsoft's COM. While this isn't an issue per se, it would be odd to have 163 | functions with those names found in the standard. 164 | 165 | ## Why is it called `retain_ptr` and not `intrusive_ptr`? ## {#faq-2} 166 | 167 | `retain_ptr` diverges from the design of `boost::intrusive_ptr`. The author 168 | decided to change the name so as to not cause assumptions of `retain_ptr`'s 169 | interface and behavior. 170 | 171 | Some additional names that might be considered (for bikeshedding) are: 172 | 173 | * `extend_ptr` 174 | * `counted_ptr` 175 | * `borrow_ptr` 176 | * `mutual_ptr` 177 | * `joint_ptr` 178 | 179 | Comedy Option: 180 | 181 | * `auto_ptr` 182 | 183 | However, in practice, `retain_ptr` has been a fairly expressive name thus far. 184 | 185 | ## Is `retain_ptr` atomic? ## {#faq-3} 186 | 187 | `retain_ptr` is only atomic in its reference count increment and decrements 188 | if the object it manages is _itself_ atomic in its reference count operations. 189 | 190 | ## Does `retain_ptr` support allocators? ## {#faq-4} 191 | 192 | `retain_ptr` itself does not support allocators, however the object whose 193 | lifetime it extends can, much in the same way that `unique_ptr` does not 194 | support allocators, but it's `Deleter` template parameter is more than able to 195 | do so. 196 | 197 | ## Can `retain_ptr` be constexpr? ## {#faq-5} 198 | 199 | Yes. 200 | 201 | ## Does `retain_ptr` adopt or retain an object on construction and reset? ## {#faq-7} 202 | 203 | Thus far there is no consensus between all of the existing APIs that have come 204 | before `retain_ptr` on whether we should adopt or retain by default. 205 | _Currently_, the policy the paper, its implementation, and its author takes is 206 | to _adopt_ the incoming object by default, and require _explicit_ retain 207 | operations when resetting with a new value or constructing an object. 208 | 209 | Unsurprisingly, this has been a point of contention. Thus far the following 210 | feedback has been given 211 | 212 | * We should retain by default 213 | * We should adopt by default 214 | * We should do neither and only allow explicit calls for both. 215 | 216 | A decision _must_ be made regarding this approach, as it will affect behavior 217 | of APIs and systems for years to come and we must, in the author's opinion, get 218 | it right on the first try. 219 | 220 | It should, however, be noted that a user desiring a _specific behavior_ by 221 | default can place `using reset_action = adopt_t` or `using reset_action = 222 | retain_t` in their traits type. 223 | 224 | ## Why provide `retain_t` and `adopt_t`? ## {#faq-8} 225 | 226 | `retain_t` and `adopt_t` act as sentinel types to provide 227 | explicit requests to either _extend_ or _adopt_ an object when constructing 228 | or resetting a `retain_ptr`. This mostly comes into play when interacting 229 | with APIs that return a borrowed (rather than a new) reference to an object 230 | without increment its reference count. 231 | 232 | Technically, an `enum class retain : bool { no, yes }` would be possible. 233 | However, this would be the first time such an API is placed into the standard 234 | library. Using a boolean parameter, as found in `boost::intrusive_ptr` is 235 | unsatisfactory and does not help describe the operation the user is requesting. 236 | 237 | The names of these sentinels are available for bikeshedding. Some other 238 | possible names for `retain_t` include: 239 | 240 | * `retain_element_t` 241 | * `extend_element_t` 242 | * `retain_object_t` 243 | * `retainobj_t` 244 | * `extendobj_t` 245 | 246 | While `adopt_object_t` names include: 247 | 248 | * `borrow_object_t` 249 | * `borrow_element_t` 250 | * `borrowobj_t` 251 | * `adopt_element_t` 252 | * `adopt_object_t` 253 | * `adoptobj_t` 254 | 255 | ## Does `retain_ptr` overload the addressof operator? ## {#faq-9} 256 | 257 | Originally, this proposal suggested `retain_ptr` might in some small edge case 258 | require an overload for the addressof `operator &`. This was, with absolutely 259 | no surprise, contentious and asked to be removed. However, Microsoft's 260 | `ComPtr` overloads the addressof operator for initializing it via C APIs 261 | (i.e., APIs which initialize a pointer to a pointer). Since then, JeanHyde 262 | Meneide's _out_ptr_ proposal [[P1132]] was written and thus solves this slight 263 | issue. 264 | 265 | ## Can `retain_traits` store state? ## {#faq-10} 266 | 267 | No. Any important state regarding the object or how it is retained, can be 268 | stored in the object itself. For example, if the reference count needs to be 269 | external from the object, `std::shared_ptr` would be a better choice. 270 | 271 | This is in line with the rest of the standard library. (For example, 272 | `std::pointer_traits` does not store state) 273 | 274 | ## Why not just wrap a `unique_ptr` with a custom deleter? ## {#faq-11} 275 | 276 | This is an extraordinary amount of code across many existing libraries that 277 | would not be guaranteed to have a homogenous interface. For example, using 278 | `retain_ptr` with an OpenCL context object (without checking for errors in both 279 | implementations) is as simple as: 280 | 281 | ```cpp 282 | struct context_traits { 283 | using pointer = cl_context; 284 | static void increment (pointer p) { clRetainContext(p); } 285 | static void decrement (pointer p) { clReleaseContext(p); } 286 | }; 287 | 288 | struct context { 289 | using handle_type = retain_ptr; 290 | using pointer = handle_type::pointer; 291 | context (pointer p, retain_object_t) : handle(p, retain) { } 292 | context (pointer p) : handle(p) { } 293 | private: 294 | handle_type handle; 295 | }; 296 | 297 | ``` 298 | 299 | Using the `unique_ptr` approach requires more effort. In this case, it is 300 | twice as long to get the same functionality. 301 | 302 | ```cpp 303 | struct context_deleter { 304 | using pointer = cl_context; 305 | void increment (pointer p) const { 306 | if (p) { clRetainContext(p); } // retain_ptr checks for null for us 307 | } 308 | void operator () (pointer p) const { clReleaseContext(p); } 309 | }; 310 | 311 | struct retain_object_t { }; 312 | constexpr retain_object_t retain { }; 313 | 314 | struct context { 315 | using handle_type = unique_ptr; 316 | using pointer = handle_type::pointer; 317 | 318 | context (pointer p, retain_object_t) : 319 | context(p) 320 | { handle.get_deleter().increment(handle.get()); } 321 | 322 | context (pointer p) : handle(p) { } 323 | 324 | context (context const& that) : 325 | handle(that.handle.get()) 326 | { handle.get_deleter().increment(handle.get()) } 327 | 328 | context& operator = (context const& that) { 329 | context(that.handle.get(), retain).swap(*this); 330 | return *this; 331 | } 332 | 333 | void swap (context& that) noexcept { handle.swap(that); } 334 | 335 | private: 336 | handle_type handle; 337 | }; 338 | 339 | ``` 340 | 341 | As we can see, using `retain_ptr` saves effort, allowing us in most cases to 342 | simply rely on the "rule of zero" for constructor management. It will also not 343 | confuse/terrify maintainers of code bases where objects construct a 344 | `unique_ptr` with the raw pointer of another (and _unique ownership_ is not 345 | transferred). 346 | 347 | # Examples # {#examples} 348 | 349 | Some C APIs that would benefit from `retain_ptr` are 350 | 351 | * OpenCL 352 | * Mono (Assembly and Image types) 353 | * CPython 354 | * ObjC Runtime 355 | * Grand Central Dispatch 356 | * OpenSSL 357 | 358 | Inside the [[implementation]] repository is an extremely basic example of using 359 | `retain_ptr` with Python. 360 | 361 | ## OpenCL ## {#examples-opencl} 362 | 363 | As shown above, using `retain_ptr` with OpenCL is extremely simple. 364 | 365 | ```cpp 366 | struct context_traits { 367 | using pointer = cl_context; 368 | static void increment (pointer p) { clRetainContext(p); } 369 | static void decrement (pointer p) { clReleaseContext(p); } 370 | }; 371 | 372 | struct context { 373 | using handle_type = retain_ptr; 374 | using pointer = handle_type::pointer; 375 | context (pointer p, retain_object_t) : handle(p, retain) { } 376 | context (pointer p) : handle(p) { } 377 | private: 378 | handle_type handle; 379 | }; 380 | ``` 381 | 382 | ## ObjC Runtime ## {#examples-objc} 383 | 384 | Additionally, while some additional work is needed to interact with the rest of 385 | the ObjC runtime, `retain_ptr` can be used to simulate ARC and remove its 386 | need entirely when writing ObjC. This means that one could, theoretically, 387 | write ObjC and ObjC++ capable code without having to actually write ObjC or 388 | ObjC++. 389 | 390 | ```cpp 391 | struct objc_traits { 392 | using pointer = CFTypeRef; 393 | static void increment (pointer p) { CFRetain(p); } 394 | static void decrement (pointer p) { CFRelease(p); } 395 | }; 396 | ``` 397 | 398 | ## OpenSSL ## {#examples-openssl} 399 | 400 | What some might find surprising is that most of the well known OpenSSL library 401 | is reference counted. 402 | 403 | ```cpp 404 | template <> std::retain_traits { 405 | static void increment (SSL* ptr) noexcept { SSL_up_ref(ptr); } 406 | static void decrement (SSL* ptr) noexcept { SSL_free(ptr); } 407 | }; 408 | 409 | struct connection { 410 | private: 411 | std::retain_ptr handle; 412 | }; 413 | ``` 414 | 415 | ## DirectX and COM ## {#examples-directx} 416 | 417 | Because DirectX is a COM interface, it can also benefit from the use of 418 | `retain_ptr` by simply using the same common traits interface used for all COM 419 | objects. The following code is slightly adapted from Microsoft's GpuResource 420 | class in the DirectX Graphics Samples. The current form of the code uses the 421 | Microsoft provided `WRL::ComPtr`, however the point here is to show how 422 | `retain_ptr` can work as a drop-in replacement for this type. 423 | 424 | ```cpp 425 | struct com_traits { 426 | static void increment (IUnknown* ptr) { ptr->AddRef(); } 427 | static void decrement (IUnknown* ptr) { ptr->Release(); } 428 | }; 429 | 430 | template using com_ptr = retain_ptr; 431 | 432 | struct GpuResource { 433 | 434 | friend class GraphicsContext; 435 | friend class CommandContext; 436 | friend class ComputeContext; 437 | 438 | GpuResource (ID3D12Resource* resource, D3D12_RESOURCE_STATES current) : 439 | resource(resource), 440 | usage_state(current) 441 | { } 442 | 443 | GpuResource () = default; 444 | 445 | ID3D12Resource* operator -> () noexcept { return this->resource.get(); } 446 | ID3D12Resource const* operator -> () const noexcept { 447 | return this->resource.get(); 448 | } 449 | 450 | protected: 451 | com_ptr resource { }; 452 | D3D12_RESOURCE_STATES usage_state = D3D12_RESOURCE_STATE_COMMON; 453 | D3D12_RESOURCE_STATES transitioning_state = D3D_RESOURCE_STATES(-1); 454 | D3D12_GPU_VIRTUAL_ADDRESS virtual_address = D3D12_GPU_VIRTUAL_ADDRESS_NULL; 455 | void* user_memory = nullptr; 456 | }; 457 | ``` 458 | 459 | ## WebKit's RefPtr ## {#examples-webkit} 460 | 461 | As a small demonstration of replacing existing intrusive smart pointers with 462 | `retain_ptr` the author presents the following code from WebKit that uses the 463 | existing `RefPtr` type, followed by the same code expressed with `retain_ptr`. 464 | This is not meant to be a fully functionioning code sample, but rather what the 465 | effects of a refactor to using `retain_ptr` might result in 466 | 467 | ```cpp 468 | RefPtr node = adoptRef(rawNodePointer); 469 | ``` 470 | 471 | ```cpp 472 | auto node = std::retain_ptr(rawNodePointer, std::adopt); 473 | ``` 474 | 475 | # Technical Specification # {#specification} 476 | 477 | A _retain pointer_ is an object that extends the lifetime of another object 478 | (which in turn manages its own dispostion) and manages that other object 479 | through a pointer. Specifically, a retain pointer is an object _r_ that stores 480 | a pointer to a second object _p_ and will cease to extend the lifetime of _p_ 481 | when _r_ is itself destroyed (e.g., when leaving a block scope). In this 482 | context, _r_ is said to _retain_ ``p``, and _p_ is said to be a _self disposing 483 | object_. 484 | 485 | When _p_'s lifetime has reached its end, _p_ will dispose of itself as it sees 486 | fit. The conditions regarding _p_'s lifetime is handled by some count _c_ that 487 | _p_ comprehends, but is otherwise not directly accessible to _r_. 488 | 489 | The mechanism by which _r_ retains and manages the lifetime of _p_ is known as 490 | _p_'s associated _retainer_, a stateless object that provides mechanisms for 491 | _r_ to increment, decrement, and (optionally) provide access to _c_. In this 492 | context, _r_ is able to _increment_ ``c``, _decrement_ ``c``, or access the _c_ 493 | of _p_. 494 | 495 | Let the notation _r.p_ denote the pointer stored by _r_. Upon request, _r_ can 496 | explicitly choose to increment _c_ when _r.p_ is replaced. 497 | 498 | Additionally, _r_ can, upon request, _transfer ownership_ to another retain 499 | pointer _r2_. Upon completion of such a transfer, the following postconditions 500 | hold: 501 | 502 | - _r2.p_ is equal to the pre-transfer _r.p_, and 503 | - _r.p_ is equal to ``nullptr`` 504 | 505 | Furthermore, _r_ can, upon request, _extend ownership_ to another retain 506 | pointer _r2_. Upon completion of such an extension, the following 507 | postconditions hold: 508 | 509 | - _r2.p_ is equal to _r.p_ 510 | - _c_ has been incremented by 1 511 | 512 | Each object of a type ``U`` instantiated from the ``retain_ptr`` template 513 | specified in this proposal has the lifetime extension semantics specified 514 | above of a retain pointer. In partical satisfaction of these semantics, each 515 | such ``U`` is ``MoveConstructible``, ``MoveAssignable``, ``CopyConstructible`` 516 | and ``CopyAssignable``. The template parameter ``T`` of ``retain_ptr`` may be 517 | an incomplete type. (_Note: The uses of_ ``retain_ptr`` _include providing 518 | exception safety for self disposing objects, extending management of self 519 | disposing objects to a function, and returning self disposing objects from a 520 | function._) 521 | 522 | ```cpp 523 | class atomic_reference_count; 524 | class reference_count; 525 | 526 | class retain_t; 527 | class adopt_t; 528 | 529 | template struct retain_traits; 530 | 531 | template > class retain_ptr; 532 | 533 | template 534 | void swap (retain_ptr& x, retain_ptr& y) noexcept; 535 | 536 | template 537 | requires three_way_comparaible_with< 538 | typename retain_ptr::pointer, 539 | typename retain_ptr::pointer 540 | > compare_three_way_result_t< 541 | typename retain_ptr::pointer, 542 | typename retain_ptr::pointer 543 | > operator <=> (retain_ptr const& x, retain_ptr const& y); 544 | 545 | template 546 | requires three_way_comparaible_with< 547 | typename retain_ptr::pointer, 548 | nullptr 549 | > compare_three_way_result_t< 550 | typename retain_ptr::pointer, 551 | nullptr 552 | > operator <=> (retain_ptr const& x, std::nullptr_t); 553 | ``` 554 | 555 | ## `atomic_reference_count` and `reference_count` ## {#wording-mixins} 556 | 557 | ``atomic_reference_count`` and ``reference_count`` are mixin types, 558 | provided for user defined types that simply rely on ``new`` and ``delete`` to 559 | have their lifetime extended by ``retain_ptr``. The template parameter ``T`` is 560 | intended to be the type deriving from ``atomic_reference_count`` or 561 | ``reference_count`` (a.k.a. the curiously repeating template pattern, CRTP). 562 | 563 | ```cpp 564 | template 565 | struct atomic_reference_count { 566 | friend retain_traits; 567 | protected: 568 | atomic_reference_count () = default; 569 | private: 570 | atomic count { 1 }; // provided for exposition 571 | }; 572 | 573 | template 574 | struct reference_count { 575 | friend retain_traits; 576 | protected: 577 | reference_count () = default; 578 | private: 579 | uint_least64_t count { 1 }; // provided for exposition 580 | }; 581 | ``` 582 | 583 | ## `retain_object_t` and `adopt_object_t` ## {#wording-sentinels} 584 | 585 | ``retain_object_t`` and ``adopt_object_t`` are sentinel types, with constexpr 586 | instances ``retain_object`` and ``adopt_object`` respectively. 587 | 588 | ```cpp 589 | namespace std { 590 | struct retain_object_t { constexpr retain_object_t () = default; }; 591 | struct adopt_object_t { constexpr adopt_object_t () = default; }; 592 | constexpr retain_object_t retain_object { }; 593 | constexpr adopt_object_t adopt_object { }; 594 | } 595 | ``` 596 | 597 | ## `retain_traits` ## {#wording-traits} 598 | 599 | The class template `retain_traits` serves the default traits object for the 600 | class template `retain_ptr`. Unless `retain_traits` is specialized for a 601 | specific type, the template parameter `T` must inherit from either 602 | `atomic_reference_count` or `reference_count`. In the event that 603 | `retain_traits` is specialized for a type, the template parameter `T` may be 604 | an incomplete type. 605 | 606 | ```cpp 607 | namespace std { 608 | template struct retain_traits { 609 | static void increment (atomic_reference_count*) noexcept; 610 | static void decrement (atomic_reference_count*) noexcept; 611 | static long use_count (atomic_reference_count*) noexcept; 612 | 613 | static void increment (reference_count*) noexcept; 614 | static void decrement (reference_count*) noexcept; 615 | static long use_count (reference_count*) noexcept; 616 | }; 617 | } 618 | ``` 619 | 620 | > `static void increment (atomic_reference_count* ptr) noexcept;` 621 | >
1 Effects: Increments the internal reference count for *ptr* 622 | > with `memory_order::relaxed` 623 | >
2 Postcondition: `ptr->count` has been incremented by 1. 624 | 625 | > `static void decrement (atomic_reference_count* ptr) noexcept;` 626 | >
1 Effects: Decrements the internal reference count for *ptr* 627 | > with `memory_order::acq_rel`. If the internal reference count of *ptr* 628 | > reaches 0, it is disposed of via ``delete``. 629 | 630 | > `static long use_count (atomic_reference_count* ptr) noexcept;` 631 | >
1 Returns: The internal reference count for *ptr* with 632 | > `memory_order::acquire`. 633 | 634 | > `static void increment (reference_count* ptr) noexcept;` 635 | >
1 Effects: Increments the internal reference count for *ptr* 636 | > by 1. 637 | 638 | > `static void decrement (reference_count* ptr) noexcept;` 639 | >
1 Effects: Decrements the internal reference count for *ptr* 640 | > by 1. If the count reaches 0, *ptr* is disposed of via `delete`. 641 | 642 | > `static long use_count (reference_count* ptr) noexcept;` 643 | >
1 Returns: The reference count for *ptr*. 644 | 645 | ## `retain_ptr` ## {#wording-pointer} 646 | 647 | The default type for the template parameter `R` is `retain_traits`. A 648 | client supplied template argument `R` shall be an object with non-member 649 | functions for which, given a `ptr` of type `retain_ptr::pointer`, the 650 | expressions `R::increment(ptr)` and `R::decrement(ptr)` are valid and has the 651 | effect of retaining or disposing of the pointer as appropriate for that 652 | retainer. 653 | 654 | If the _qualified-id_ `R::pointer` is valid and denotes a type, then 655 | `retain_ptr::pointer` shall be synonymous with `R::pointer`. Otherwise 656 | `retain_ptr::pointer` shall be a synonym for `element_type*`. The type 657 | `retain_ptr::pointer` shall satisfy the requirements of 658 | *NullablePointer*. 659 | 660 | ```cpp 661 | template > 662 | struct retain_ptr { 663 | using element_type = T; 664 | using traits_type = R; 665 | using pointer = /* see below */ 666 | 667 | retain_ptr (pointer, retain_object_t); 668 | retain_ptr (pointer, adopt_object_t) noexcept; 669 | explicit retain_ptr (pointer); 670 | retain_ptr (nullptr_t) noexcept; 671 | 672 | retain_ptr (retain_ptr const&) noexcept; 673 | retain_ptr (retain_ptr&&) noexcept; 674 | retain_ptr () noexcept; 675 | ~retain_ptr (); 676 | 677 | retain_ptr& operator = (retain_ptr const&); 678 | retain_ptr& operator = (retain_ptr&&) noexcept; 679 | retain_ptr& operator = (nullptr_t) noexcept; 680 | 681 | void swap (retain_ptr&) noexcept; 682 | 683 | explicit operator pointer () const noexcept; 684 | explicit operator bool () const noexcept; 685 | 686 | element_type& operator * () const noexcept; 687 | pointer operator -> () const noexcept; 688 | pointer get () const noexcept; 689 | 690 | long use_count () const; 691 | 692 | [[nodiscard]] pointer release () noexcept; 693 | 694 | void reset (pointer, retain_object_t); 695 | void reset (pointer, adopt_object_t); 696 | void reset (pointer p = pointer { }); 697 | }; 698 | ``` 699 | 700 | ### `retain_ptr` constructors ### {#wording-pointer-ctor} 701 | 702 |
703 | ```cpp 704 | retain_ptr (pointer p, retain_object_t); 705 | ``` 706 |
707 | 708 | *Effects*: Constructs a `retain_ptr` that retains `p`, initializing the stored 709 | pointer with `p`, and increments the reference count of `p` if `p != nullptr` 710 | by way of `traits_type::increment`. 711 | 712 | *Postconditions*: `get() == p` 713 | 714 | *Remarks*: If an exception is thrown during increment, this constructor will 715 | have no effect. 716 | 717 |
718 | ```cpp 719 | retain_ptr (pointer p, adopt_object_t) noexcept; 720 | ``` 721 |
722 | 723 | *Effects*: Constructs a `retain_ptr` that adopts `p`, initializing the stored 724 | pointer with `p`. 725 | 726 | *Postconditions*: `get() == p` 727 | 728 | *Remarks*: `p`'s refrence count remains untouched. 729 | 730 |
731 | ```cpp 732 | explicit retain_ptr (pointer p); 733 | ``` 734 |
735 | 736 | *Effects*: Constructs a `retain_ptr` by delegating to another `retain_ptr` 737 | constructor via `traits_type::default_action`. If `traits_type::default_action` 738 | does not exist, `retain_ptr` is constructed as if by `retain_ptr(p, 739 | adopt_object_t)`. 740 | 741 | *Postconditions*: `get() == p` 742 | 743 | *Remarks*: If an exception is thrown, this constructor will have no effect. 744 | 745 |
746 | ```cpp 747 | retain_ptr () noexcept; 748 | ``` 749 |
750 | 751 | *Effects*: Constructs a `retain_ptr` object that retains nothing, 752 | value-initializing the stored pointer. 753 | 754 | *Postconditions*: `get() == nullptr` 755 | 756 |
757 | ```cpp 758 | retain_ptr(retain_ptr const& r); 759 | ``` 760 |
761 | 762 | *Effects*: Constructs a `retain_ptr` by extending management from `r` to 763 | `*this`. The stored pointer's reference count is incremented. 764 | 765 | *Postconditions*: `get() == r.get()` 766 | 767 | *Remarks*: If an exception is thrown, this constructor will have no effect. 768 | 769 |
770 | ```cpp 771 | retain_ptr(retain_ptr&& r) noexcept; 772 | ``` 773 |
774 | 775 | *Effects*: Constructs a `retain_ptr` by transferring management from `r` to 776 | `*this`. The stored pointer's reference count remains untouched. 777 | 778 | *Postconditions*: `get()` yields the value `r.get()` yielded before the 779 | construction. 780 | 781 | ### `retain_ptr` destructor ### {#wording-pointer-dtor} 782 | 783 |
784 | ```cpp 785 | ~retain_ptr(); 786 | ``` 787 |
788 | 789 | *Effects*: If `get() == nullptr`, there are no effects. Otherwise, 790 | `traits_type::decrement(get())`. 791 | 792 | ### `retain_ptr` assignment ### {#wording-pointer-assignment} 793 | 794 |
795 | ```cpp 796 | retain_ptr& operator = (retain_ptr const& r); 797 | ``` 798 |
799 | 800 | *Effects*: Extends ownership from `r` to `*this` as if by calling 801 | `reset(r.get(), retain)`. 802 | *Returns*: `*this` 803 | 804 |
805 | ```cpp 806 | retain_ptr& operator = (retain_ptr&& r) noexcept; 807 | ``` 808 |
809 | 810 | *Effects*: Transfers ownership from `r` to `*this` as if by calling 811 | `reset(r.release())` 812 | *Returns*: `*this` 813 | 814 |
815 | ```cpp 816 | retain_ptr& operator = (nullptr_t) noexcept; 817 | ``` 818 |
819 | 820 | *Effects*: `reset()` 821 | *Postconditions*: `get() == nullptr` 822 | *Returns*: `*this` 823 | 824 | ### `retain_ptr` observers ### {#wording-pointer-observers} 825 | 826 |
827 | ```cpp 828 | element_type& operator * () const noexcept; 829 | ``` 830 |
831 | 832 | *Requires*: `get() != nullptr` 833 | *Returns*: `*get()` 834 | 835 |
836 | ```cpp 837 | pointer operator -> () const noexcept; 838 | ``` 839 |
840 | 841 | *Requires*: `get() != nullptr` 842 | *Returns*: `get()` 843 | *Note*: Use typically requires that `element_type` be a complete type. 844 | 845 |
846 | ```cpp 847 | pointer get () const noexcept; 848 | ``` 849 |
850 | 851 | *Returns*: The stored pointer 852 | 853 |
854 | ```cpp 855 | explicit operator pointer () const noexcept; 856 | ``` 857 |
858 | 859 | *Returns*: `get()` 860 | 861 |
862 | ```cpp 863 | explicit operator bool () const noexcept; 864 | ``` 865 |
866 | 867 | *Returns*: `get() != nullptr` 868 | 869 |
870 | ```cpp 871 | long use_count () const; 872 | ``` 873 |
874 | 875 | *Returns*: Value representing the current reference count of the current stored 876 | pointer. If `traits_type::use_count(get())` is not a valid expression, `-1` is 877 | returned. If `get() == nullptr`, `0` is returned. 878 | 879 | *Remarks*: Unless otherwise specified, the value returned should be considered 880 | stale. 881 | 882 | ### `retain_ptr` modifiers ### {#wording-pointer-modifiers} 883 | 884 |
885 | ```cpp 886 | [[nodiscard]] pointer release () noexcept; 887 | ``` 888 |
889 | 890 | *Postconditions*: `get() == nullptr` 891 | *Returns*: The value `get()` had at the start of the call to `release()`. 892 | 893 |
894 | ```cpp 895 | void reset (pointer p, retain_object_t); 896 | ``` 897 |
898 | 899 | *Effects*: Assigns `p` to the stored pointer, and then if the old value of 900 | stored pointer `old_p`, was not equal to `nullptr`, calls 901 | `traits_type::decrement`. Then if `p` is not equal to `nullptr`, 902 | `traits_type::increment` is called. 903 | *Postconditions*: `get() == p` 904 | 905 |
906 | ```cpp 907 | void reset(pointer p, adopt_object_t); 908 | ``` 909 |
910 | 911 | *Effects*: Assigns `p` to the stored pointer, and then if the old value of 912 | stored pointer `old_p`, was not equal to `nullptr`, calls 913 | `traits_type::decrement`. 914 | *Postconditions*: `get() == p` 915 | 916 |
917 | ```cpp 918 | void reset (pointer p = pointer { }) 919 | ``` 920 |
921 | 922 | *Effects*: Delegates assignment of `p` to the stored pointer via `reset(p, 923 | traits_type::default_action())`. 924 | *Postconditions*: `get() == p` 925 | 926 |
927 | ```cpp 928 | void swap (retain_ptr& r) noexcept; 929 | ``` 930 |
931 | 932 | *Effects*: Invokes `swap` on the stored pointers of `*this` and `r`. 933 | 934 | 935 | ### `retain_ptr` specialized algorithms ### {#wording-pointer-algorithms} 936 | 937 |
938 | ```cpp 939 | template 940 | void swap (retain_ptr& x, retain_ptr& y) noexcept; 941 | ``` 942 |
943 | 944 | *Effects*: Calls `x.swap(y)` 945 | 946 |
947 | ```cpp 948 | template 949 | auto operator <=> (retain_ptr const&, retain_ptr const&) noexcept = default; 950 | ``` 951 |
952 | 953 | *Returns*: `x.get() <=> y.get()` 954 | 955 |
956 | ```cpp 957 | template 958 | auto operator <=> (retain_ptr const&, nullptr_t) noexcept = default; 959 | ``` 960 |
961 | 962 | *Returns*: `x.get() <=> nullptr` 963 | 964 |
965 | ```cpp 966 | template 967 | strong_ordering operator <=> (nullptr_t, retain_ptr const& y) noexcept; 968 | ``` 969 |
970 | 971 | *Returns*: `nullptr <=> y.get()` 972 | 973 |
974 | {
975 |   "implementation": {
976 |     "title": "retain_ptr",
977 |     "authors": [ "Isabella Muerte" ],
978 |     "href": "https://github.com/slurps-mad-rips/retain-ptr"
979 |   },
980 |   "P1132": {
981 |     "title": "out_ptr - a scalable output pointer abstraction",
982 |     "authors": [
983 |       "JeanHeyd Meneide",
984 |       "Todor Buyukliev",
985 |       "Isabella Muerte"
986 |     ],
987 |     "href": "https://wg21.link/p1132r0"
988 |   }
989 | }
990 | 
991 | -------------------------------------------------------------------------------- /src/sharing-is-caring.bs: -------------------------------------------------------------------------------- 1 | 14 | 18 | 19 | # Revision History # {#changelog} 20 | 21 | ## Revision 0 ## {#r0} 22 | 23 | Initial Release 🎉 24 | 25 | # Motivation # {#motivation} 26 | 27 | Generalized attributes were added in C++11 and for a brief period there was 28 | hope that all compiler vendors would be able to do away with their custom 29 | implementation specific syntax for attributes. Instead, we are still stuck 30 | having to use preprocessor macros just so we can have dynamically linked 31 | libraries work under multiple compilers. While the standard doesn't say 32 | anything about dynamically linked libraries, they do exist (whether we want 33 | them to or not). This paper does not seek to define this concept. It just makes 34 | a simple attribute required for all compiler vendors, especially those that 35 | have not placed their DLL export attribute into a namespace. One particular 36 | compiler vendor is extremely guilty of this. While the author doesn't want to 37 | name and shame, they will state that the name of the vendor in question starts 38 | with the letter "M" and ends in "icrosoft". 39 | 40 | This paper is extremely similar to, but not a recreation of, [[P0276]]. While 41 | that paper attempted to specify all the possible traits available for 42 | visibility in regards to a shared library, this paper focuses entirely on 43 | interfaces exported via shared libraries. Additionally, we go one step further 44 | and limit this "sharing" to functions only. There are many issues that can 45 | arise from sharing a type over program boundaries and this paper is avoiding 46 | them. A future paper can argue in favor of expanding what foundation this 47 | proposal lays down now. 48 | 49 | Note: The author is more than willing to pull this paper if every vendor would 50 | just behave and promise to namespace their compiler specific attributes so we 51 | don't have to go through something like this in the future. 52 | 53 | # Design # {#design} 54 | 55 | The name `shared` is recommended as it involves sharing interfaces across 56 | program boundaries. This differs from [[P0276]] which used GCC's existing 57 | `visibility` name as a base. There is no intended "import" vs "export" 58 | function declaration dichotomy. Platforms that might have required a difference 59 | are no longer supported by their vendors, and have instead added support for a 60 | single export. 61 | 62 | Note: These same platforms may still require an "import" vs "export" for 63 | non-function entities. 64 | 65 | # Wording # {#wording} 66 | 67 | Wording is relative to [[N4762]] 68 | 69 |
70 | 71 | **9.11.12 Shared attribute [dcl.attr.shared]** 72 | 73 | 1The *attribute-token* `shared` specifies that an entity with 74 | external linkage [**basic.link**] may be obtained: 75 | 76 | * (1.1) by current translation unit at program startup from a 77 | library file if the entity has no definition in current translation unit. 78 | [Note: attribute `shared` only provides information that the entity is 79 | available to obtain at runtime from a library file. – end note] 80 | 81 | * (1.2) by other translation units at runtime or at program 82 | startup if the entity has a definition in the current translation unit. 83 | [Note: In this case, `shared` attribute prevents optimizing compilers from 84 | removing or "stripping" the entity from the program and from changing the 85 | entity type or signature. &ndash end note] 86 | 87 | 2`shared` shall appear at most once in each *attribute-list* and no 88 | *attribute-argument-clause* shall be present. The attribute may be applied to 89 | the *declarator-id* in a non-inline function. 90 | 91 | 3The first declaration shall specify the `shared` attribute if any 92 | other declaration specifies the `shared` attribute. If an entity is declared 93 | with the `shared` attribute in one translation unit and the same entity is 94 | declared without the `shared` attribute in another translation unit, the 95 | program is ill-formed. 96 | 97 |
98 | 99 | ## Feature Testing ## {#feature-testing} 100 | 101 | Tests for the `shared` attribute can be completed via the current convention of 102 | using `__has_cpp_attribute` like so: `__has_cpp_attribute(shared)`. 103 | 104 | # Acknowldegment # {#acknowledgement} 105 | 106 | Special thanks to Antony Polukhin, who did a majority of the heavy lifting last 107 | time a paper like this was brought before the committee. 108 | 109 |
110 | {
111 |   "P0276": {
112 |     "authors": "Antony Polukhin",
113 |     "href": "https://wg21.link/p0276",
114 |     "title": "A Proposal to add Attribute [[visible]]"
115 |   }
116 | }
117 | 
118 | -------------------------------------------------------------------------------- /src/simplify-extern-template.bs: -------------------------------------------------------------------------------- 1 | 14 | 18 | 19 | # Revision History # {#changelog} 20 | 21 | ## Revision 0 ## {#r0} 22 | 23 | Initial Release 🎉 24 | 25 | # Motivation # {#motivation} 26 | 27 | Writing `extern template` statements has become so painful that it is one of 28 | the least used features in C++ outside of some vendors and extremely large C++ 29 | projects such as LLVM. When performing a search across all of GitHub's source 30 | code, all instances of `extern template` were found to be in test repositories 31 | for developers to play around with to understand how they worked. Most have not 32 | been touched since the first C++11 compliant compilers were released. 33 | 34 | To best display the primary issue at hand, let's look at what happens when we 35 | try to `extern template` `std::string`: 36 | 37 | ```cpp 38 | extern template class std::string; 39 | ``` 40 | 41 | Every compiler will reject the above, because we cannot `extern template` 42 | typedefs. Instead, we are required to know the actual definition of 43 | `std::string`, minus default template parameters. To actually `extern template` 44 | a class like `std::string`, one must write: 45 | 46 | ```cpp 47 | extern template class std::basic_string; 48 | ``` 49 | 50 | This can be surprising to many since `extern template class 51 | std::vector` works just fine. 52 | 53 | Modifying the standard to let us `extern template` aliases is desirable. 54 | 55 | Note: This paper does not attempt to permit specializations of alias templates, 56 | nor does it permit identifiers defined via a typedef. Instead an 57 | *alias-declaration* must be used. This is done to encourage the newer syntax 58 | when working with `extern template`. 59 | 60 | # Wording # {#wording} 61 | 62 | Wording is relative to [[N4762]] 63 | 64 | In Section 12.8.2 Explicit instantiation [**temp.explicit**] paragraph 3, 65 | insert: 66 | 67 |
68 | 3 If the explicit instantiation is for a class or member class, 69 | either the *elaborated-type-specifier* in the *declaration* shall include a 70 | *simple-template-id*, or the *declaratation* shall be a *template-name* 71 | that refers to an *alias template*; otherwise the *declaration* shall be 72 | a *simple-declaration* whose *init-declarator-list* comprises a single 73 | *init-declarator* that does not have an *initializer*. If the explicit 74 | instantiation is for a function or member function, the *unqualified-id* in 75 | the *declarator* shall be either a *template-id* or, where all template 76 | arguments can be deduced, a *template-name* or *operator-function-id*. [Note: 77 | The declaration may declare a *qualified-id*, in which case the 78 | *unqualified-id* of the *qualified-id* must be a *template-id*. —end note] If 79 | the explicit instantiation is for a member function, a member class or a static 80 | data member of a class template specialization, the name of the class template 81 | specialization in the *qualified-id* for the member name shall be a 82 | *simple-template-id*. If the explicit instantiation is for a variable template 83 | specialization, the *unqualified-id* in the *declarator* shall be a 84 | *simple-template-id*. An explicit instantiation shall appear in an enclosing 85 | namespace of its template. If the name declared in the explicit instantiation 86 | is an unqualified name, the explicit instantiation shall appear in the 87 | namespace where its template is declared or, if that namespace is inline 88 | (9.7.1), any namespace from its enclosing namespace set. [Note: Regarding 89 | qualified names in declarators, see 9.2.3. — end note] 90 |
91 | -------------------------------------------------------------------------------- /src/subscript-parade.bs: -------------------------------------------------------------------------------- 1 | 14 | 18 | 19 | # Revision History # {#changelog} 20 | 21 | ## Revision 0 ## {#r0} 22 | 23 | Initial Release 🎉 24 | 25 | # Motivation # {#motivation} 26 | 27 | As multidimensional types such as `std::mdspan` and other HPC types come about, 28 | it is becoming increasingly clear that the recommended convention of simply 29 | overloading `operator ()` to pass in multiple indices is not enough. This paper 30 | seeks to permit adding multiple arguments to `operator []` to permit 31 | expressions like `mdspan[1, 2, 3]`. This change is compatible with existing 32 | code as overloads for these multiple arguments need to be present on a given 33 | type for this to work in the first place. 34 | 35 | This paper works alongside [[P1161]], but takes the more bold statement that we 36 | need multiple arguments to the subscript operator *now* rather than later. 37 | After all, it has to start somewhere. It has to start sometime. What better 38 | place than here? What better time than *now*? 39 | 40 | # Design # {#design} 41 | 42 | The current design of this paper is to permit multiple arguments to be passed 43 | to `operator []`. This changes the proposed behavior seen in [[P1161]], where 44 | `array[x, y]` is in fact not deprecated, but instead attempts to call 45 | `array.operator [](x, y)`. This gives us the opportunity to have 46 | multidimensional array types that act like multidimensional arrays. This also 47 | saves us the pain of having to implement proxy types that are returned for the 48 | sole purpose of being indexed upon, or having to use `operator ()`, which while 49 | an existing convention is confusing when compared to other callable interfaces 50 | (that is, `mdspan` is not a Callable, yet it satisfies the interface for it). 51 | This also meas that the built-in comma operator is not permitted inside of 52 | subscript expressions. 53 | 54 | # Wording # {#wording} 55 | 56 | In 7.6.19 Comma operator [**expr.comma**] add a paragraph 57 | 58 |
59 | 60 | 3The built-in comma operator shall not be used within the subscript 61 | operator. 62 | 63 |
64 | 65 | In 11.5.5 Subscripting [**over.sub**] 66 | 67 |
68 | 1`operator []` shall be a non-static member function with 69 | exactly one parameteran arbitrary number of parameters. 70 | It implements the subscripting syntax: 71 | 72 | : 73 | :: *postfix-expression* **[** *expr-or-braced-init-list**expression-list**opt* **]** 74 | 75 | Thus, a subscripting expression `x[y]` is interpreted as `x.operator[](y)` 76 | for a class object `x` of type `T` if `T::operator[](T1)` 77 | `x[arg1,...]` is interpreted as `x.operator[](arg1, ...)` for a class 78 | object `x` of type `T` if `T::operator[](T1, T2, T3)` exists and if the 79 | operator is selected as the best match function by the overload resolution 80 | mechanism [*Example:* 81 | 82 | 83 | struct X { 84 | Z operator [] (std::initializer_list<int>); 85 | }; 86 | X x; 87 | x[{1, 2, 3}] = 7; // OK: meaning x.operator[]({1, 2, 3}) 88 | int a[10]; 89 | a[{1, 2, 3}] = 7; // error: built-in subscript operator 90 | 91 | 92 | 93 | 94 | struct X { 95 | Z operator [] (std::initializer_list<int>); 96 | Z operator [] (int x, int y, int z); 97 | }; 98 | X x; 99 | x[{1, 2, 3}] = 7; // OK: meaing x.operator[]({1, 2, 3}) 100 | x[1, 2, 3] = 7; // OK: meaning x.operator[](1, 2, 3) 101 | int a[10]; 102 | a[{1, 2, 3}] = 7; // error: built-in subscript operator 103 | a[1, 2, 3] = 7; // error: built-in subscript operator 104 | 105 | 106 | — *end example*] 107 | 108 |

109 | 110 | 2In subscript expressions, the built-in comma operator shall not be 111 | used 112 | 113 |

114 |
115 | 116 |
117 | {
118 |   "P1161": {
119 |     "title": "Deprecate uses of the comma operator in subscripting expressions",
120 |     "href": "https://wg21.link/p1161r0",
121 |     "authors": "Corentin Jabot"
122 |   }
123 | }
124 | 
125 | -------------------------------------------------------------------------------- /src/target-tuplets.bs: -------------------------------------------------------------------------------- 1 | 11 | 12 | # Revision History # {#changelog} 13 | 14 | ## Revision 0 ## {#r0} 15 | 16 | Initial release. 🎉 17 | 18 | # Motivation # {#motivation} 19 | 20 | One of the more difficult things in the vast breadth of build systems is 21 | declaring what system architecture, vendor, operating system, and environment 22 | is used when compiling for a given platform. In some cases, these names are 23 | chosen at random by various implementations and differ from each vendor. While 24 | some time ago these would have been called "Target Triples", in practice they 25 | have been "Target Tuplets". 26 | 27 | This paper aims to define and describe the concept of a target tuplet and how 28 | they can be consumed by build systems, toolchains, and users alike. This paper 29 | *does not* attempt to define or limit existing target tuplets and instead 30 | defers this "definition list" to a separate Standing Document. The reason for 31 | this is that updating target tuplets with a Technical Report or Standard 32 | requires several years to be updated and voted in. This model can't react to 33 | changes in existing practices, while a Standing Document can be updated at each 34 | plenary vote, or simply be updated by an editor, and published at will. 35 | 36 | This paper might give examples of existing target tuplets, but this is for 37 | demonstration purposes and is not to be taken as a guarantee that the tuplet 38 | will exist in such a form in the future. 39 | 40 | Lastly, this paper also does not attempt to define a *sysroot*. That may or may 41 | not be defined by a future separate paper. 42 | 43 | ## Why Tuplet instead of Triplet or Tuple? ## {#tuplet} 44 | 45 | Traditionally, a target tuplet has been called a *triplet*, as this referred to 46 | the *architecture*, the *operating system*, and the *environment*. However, as time 47 | has gone on, a 4th field was added, while the name *triplet* was kept. While 48 | the author is not a verified expert on the English language, *triple* and the 49 | *number 4* do not mean the same thing. 50 | 51 | Additionally, C++ has the concept of a `std::tuple`. The author opted to select 52 | the term *tuplet* to remove any possibly ambiguity regarding discussions of 53 | the definition found within this proposal. 54 | 55 | Lastly, the `-et` and `-ette` suffixes are used to describe something small 56 | (e.g., a cas*ette* is a small case). However, `-et` also means a group or 57 | grouping, such as in the definition of oct*et*. A target tuplet is a grouping 58 | of several elements regarding a target, hence the use of the term *tuplet* and 59 | not *tuplette*. 60 | 61 | # Design Considerations # {#design} 62 | 63 | A target tuplet is written (in utf-8) as a series of words (text), delimited 64 | via a "hyphen-minus" (-). The current layout of a target tuplet is borrowed 65 | from [[clang-cross-compile|clang]], as other compilers tend to support this 66 | layout but also provide alternative layouts that are incompatible with clang or 67 | each other. 68 | 69 | Typically, a target tuplet *does not* infer floating point ABI, processor 70 | specific intrinsics, or FPU specific intrinsics. These are more granular and 71 | defining them at a broad scope is, in the authors opinion, fruitless. 72 | 73 | Lastly, a target tuplet can be considered "ill-formed, no diagnostic required" 74 | if an incorrect sequence is put together and passed to the compiler. For 75 | example, specifying `mips-ibm-cuda-macho` as a target would most likely not 76 | work in any existing compiler (at the time of this writing). Instead, it is up 77 | to users and build systems to ensure that these incorrect sequences do not make 78 | their way to the compiler. This differs from clang's approach, which is to 79 | convert any unrecognized entries as `unknown`, and then infer specifics. 80 | 81 | # Wording # {#wording} 82 | 83 | ## Terms and Definitions ## {#terms-and-definitions} 84 | 85 | For the purposes of this document, the following terms apply 86 | 87 | : *build system* 88 | :: A program that will drive the execution of a C++ compiler on a set of 89 | translation units. They live a level above the compiler, and thus are 90 | technically out of scope for the C++ standard document itself. 91 | 92 | : *platform* 93 | :: The inverse of the C++ abstract machine. That is, a concrete set of 94 | compilers, tools, programs, and code necessary to execute a program. 95 | Colloquially referred to as a system in some spaces. 96 | 97 | : *vendor* 98 | :: An entity that provides an implementation of a C++ toolchain. 99 | 100 | : *host* 101 | :: A specific instance of a platform, upon which the C++ compiler executes. 102 | [*Note*: The C++ standard does not mandate that a C++ compiler must be a 103 | well-formed program itself -- 104 | *end note*] 105 | 106 | : *target* 107 | :: A specific instance of a platform, upon which a well-formed program is 108 | planned to execute 109 | 110 | : *machine* 111 | :: A specific instance of a target, upon which a well-formed program can run. 112 | 113 | : *object file format* 114 | :: The detailed file format a translation unit is in prior to linking. 115 | 116 | : *executable file format* 117 | :: The detailed file format a program is in once linked. 118 | 119 | ## Target Tuplet ## {#target-tuplet} 120 | 121 | ### In general ### {#general} 122 | 123 | 1 Target tuplets are a series of components used to represent a human 124 | friendly phrase for a compiler target. They are useful for discussing minimum 125 | feature sets for a code base, as well as what platform a program is expected to 126 | compile and execute on.To be able to create a well-formed program for a given 127 | target, a build system must know the details of how to invoke a compiler, 128 | invoke a linker, and produce a final program or collection of object files. 129 | 130 | 2 Since build systems live outside of the standard, they must be able 131 | to invoke one or more compilers, regardless of whether they are from the same 132 | vendor. This document defines 4 parts that make up a target tuplet. 133 | 134 | 3 The 4 parts that make up a target tuplet are 135 | : 136 | :: (3.1) — architecture 137 | :: (3.2) — vendor 138 | :: (3.2) — operating system 139 | :: (3.2) — environment 140 | 141 | respectively. The generic term *component* refers to any portion of a target 142 | tuplet that meets the above criteria. 143 | 144 | 4 Target tuplets are constructed by concatenating the content of 145 | all components with the hypen-minus character (-). Components are not 146 | permitted to contain the hyphen-minus themselves. 147 | 148 | 5 A target tuplet is considered well-formed if a compiler is able to 149 | understand the 4 components provided. Likewise, if a compiler is unable to 150 | understand the 4 components provided, the tuplet is considered ill-formed. 151 | While a build system is free to resolve the validity of a target tuplet, only a 152 | compiler can verify whether or not it is well formed. 153 | 154 | 6 In addition to the requirements above, a compiler or build system 155 | is free to take fewer than the 4 components required of a target tuplet. Such a 156 | sequence is called a *supplied target tuplet*. Each missing component in a 157 | supplied target tuplet can be replaced with the catch-all name `unknown`, 158 | however at least one component must be supplied by name. 159 | [*Note:* In most cases, the `vendor` component will be ignored or missing -- 160 | *end note*] 161 | 162 | ## Component Descriptions ## {#component-descriptions} 163 | 164 | ### Architecture ### {#component-architecture} 165 | 166 | 1 The architecture refers to the instruction set used for a given 167 | processor. 168 | 169 | 2 The architecture may not refer to specific releases of names of a 170 | given processor. 171 | 172 | 3 The architecture is permitted to express revisions to the 173 | instruction set itself. 174 | 175 | 4 [*Note:* `arm`, `armv6`, and `armv7a` are from the same family of 176 | processors, however the `v6` and `v7` refer to revisions made to the ISA -- 177 | *end note*] 178 | 179 | ### Vendor ### {#component-vendor} 180 | 181 | 1 A vendor is any entity that provides a C++ toolchain to a user. 182 | Whether this is an organization, a simple person, a PC, or even no one. 183 | 2 In the event that no one is marked as the vendor, the component 184 | will always resolve to the value `none`. 185 | 186 | ### Operating System ### {#component-operating-system} 187 | 188 | 1 An *operating system* is a superset of an environment, but is 189 | not responsible of how a user interacts with their toolchain. 190 | 191 | ### Environment ### {#component-environment} 192 | 193 | 1 An *environment* is any one of an executable file format, an 194 | ABI or a runtime. 195 | 196 | 2 Due to the nature of how an environment can be expressed, each 197 | type of environment can supercede another, with the exception of an executable 198 | file format. 199 | 200 | : 201 | :: (2.1) An executable file format implies that there is only 1 way 202 | to load and execute the program without virtualizing or converting to another 203 | environment. 204 | :: (2.2) An ABI implies the existence of an executable file format, 205 | an object file format, and 1 or more calling conventions for symbols within 206 | the object file format 207 | :: (2.3) A runtime is an object file format, an ABI for object 208 | files, and 1 or more libraries needed for a an executable to be produced 209 | 210 | ### Resolution ### {#component-resolution} 211 | 212 | 1 Resolving a supplied target tuplet into a well-formed target 213 | tuplet is not an intuitive process based on the written form. 214 | 215 |
216 |
217 | path: written-target-tuplet.svg
218 | 
219 |
Written Component Order
220 |
221 | 222 | 2 While the written component order is currently `architecture`, 223 | `vendor`, `operating system`, and `environment`, the actual priority differs 224 | based on the importance of each component. 225 | 226 |
227 |
228 | path: priority-target-tuplet.svg
229 | 
230 |
Component Priority Order
231 |
232 | 233 | 3 In the event that no operating system was provided, the component 234 | will be set to the *bare metal specifier* `none` and the resolution steps are 235 | to start again. 236 | 237 | # Informative Explanation # {#explanation} 238 | 239 | The road to get to the point where we can have a definition of a target tuplet 240 | has been a long one. There are many platforms, processors, toolchains, external 241 | tools, build systems, and configurations to consider and many of these are 242 | older than Standard C++ by almost a decade (The initial release of C++ in 1985 243 | was during the beginning of very early stages of this "wild west" of cross 244 | compilation). This section is intended to explain some of the decisions made 245 | with wording, as well as some of the history of how we ended up at the point we 246 | are currently at. Additionally, a lot of work was done to formalize some of the 247 | wording and to " 248 | 249 | ## A Brief History of Compiler Targets ## {#brief-history} 250 | 251 | Many older compilers are no longer available. Their source code, binaries, 252 | documentation, etc. are either lost to time, or require vast sums of money to 253 | gain access to. As such, the author only had GCC, a few non-triplet configured 254 | C compilers, and a handful of usenet postings to go off of for the information 255 | located here. 256 | 257 | Note: This portion of the document is not meant to be a "Howard Zinn-esque" 258 | retelling of how target tuplets came to be. 259 | 260 | Before autotools, GCC's early releases used simple shell scripts that were 261 | written by hand. The changelog was only a small file with a few notes, and 262 | some light information on how to configure the build for porting to new 263 | platforms. These files were labelled `config-`, as the ability to 264 | target a specific toolchain wasn't really a possibility at the time. As time 265 | went on, the number of targets that GCC could run on or target grew. This is 266 | when the first set of target triplets were created. At the time, the second 267 | field of a triplet was what is now the *third entry* in a target tuplet. At 268 | some point, an optional *vendor* field was created. In the wild, some might be 269 | familiar with the "triplet" of `x86_64-none-linux-gnu`, which is also sometimes 270 | represented as `x86_64-unknown-linux-gnu` or `x86_64-pc-linux-gnu`. A list of 271 | wildcard host/targets is [[gcc-host-target|provided by GCC]]. 272 | 273 | Because of the success of GCC, this form of targeting a platform became the *de 274 | facto* way of defining a target. GCC also added several aliases over the years 275 | (e.g., permitting `*-*-*-linux` to mean `*-*-*-elf` on some platforms), which 276 | led to a bit of confusion on how to target a platform. Once Clang supported 277 | targets, they did not provide aliases, which reduced the surface area for 278 | confusion. 279 | 280 | ## Why Vendor Over Machine ## {#vendor} 281 | 282 | At the Denver 2019-09 SG15 meeting, the author was recommended via a poll 283 | result to investigate naming conventions for the second entry as to whether it 284 | should be *vendor* or *machine*. After some time, it became clear that the 285 | *vendor* entry has always been for vendors. The use of `*-pc-*` was taken 286 | instead to mean "IBM Compatible PC", back when that used to mean something. 287 | 288 | However, when users have taken to think the second entry is a machine, 289 | operating system, or platform, the compiler has effectively *normalized* the 290 | second field to either be `unknown` or something else. Thus, using *machine* 291 | does not work well, as the wording above states that *vendor* is optional in a 292 | *supplied target tuplet*. 293 | 294 | ## Why environment over X ## {#environment} 295 | 296 | Some other names came up during discussion of the fourth field. These included, 297 | `runtime`, `ABI`, etc. In actuality, this last field has historically been 298 | where everything is thrown into one place to differentiate the compiler's 299 | target from effectively everything else. In practice, however, there is a 300 | hierarchy implied by *what* is in this field. 301 | 302 | If an object file format, such as `PE`, `COFF`, `a.out`, `Mach-O`, or `ELF` is 303 | given, it typically implies that there are 0 or 1 default vendor specific ABI 304 | or runtime available. In other words, these *could* be freestanding compiler 305 | targets, or there is only one possible runtime provided by the compiler. e.g., 306 | in the case of `*-apple-*-macho`, there is only ONE ABI and runtime available, 307 | and it is provided by Apple, with the object file format being `Mach-O`. In the 308 | case of a `*-apple-*-coff`, there is still only ONE ABI and runtime available, 309 | but the object file format is now different. 310 | 311 | A level above this, is an ABI. e.g., `gnu`, or `msvc`. These imply all of: 312 | 313 | * an object file format (e.g., ELF and PE/COFF) 314 | * 1 or more calling conventions for symbols within the object file format 315 | * multiple runtimes that can co-exist on a given platform. 316 | 317 | This is why, for the uninformed, MinGW and MSVC cannot link against each other 318 | unless it is done via C and unless the "symbols" agree upon calling convention. 319 | 320 | Above this, we have *runtimes*. These have all of: 321 | 322 | * A specific object file format 323 | * An ABI for storing precompiled object file formats 324 | * 1 or more libraries needed for a program to load without issue. 325 | 326 | As an example, `mingw` generates object files under the PE/COFF format, 327 | supports both the `__stdcall` and `__cdecl` calling conventions, and supplies 328 | a runtime that allows GCC compiled code to run directly on Windows. While the 329 | actual format of the PE/COFF files generated by MinGW are not compatible with 330 | MSVC, this doesn't change that MinGW has its own runtime, even if said runtime 331 | is simply to reorganize symbols so that it can safely link against the MSVC 332 | runtime. 333 | 334 | Likewise, `cygwin` generates object files under the PE/COFF format, but targets 335 | a POSIX-like environment. Thus it requires a library for its generated programs 336 | to execute correctly. 337 | 338 | Because of this tiered system, an "umbrella" term is needed. Our toolchains do 339 | not exist in a vacuum, and interact with our overall *environment*, hence the 340 | name *environment* was chosen. 341 | 342 | 343 |
344 | {
345 |   "clang-cross-compile": {
346 |     "href": "https://clang.llvm.org/docs/CrossCompilation.html",
347 |     "title": "Cross-compilation using Clang"
348 |   },
349 |   "gcc-host-target": {
350 |     "href": "https://gcc.gnu.org/install/specific.html",
351 |     "title": "Host/Target specific installation notes for GCC"
352 |   }
353 | }
354 | 
355 | -------------------------------------------------------------------------------- /src/void-main.bs: -------------------------------------------------------------------------------- 1 | 15 | 19 | 20 | # Revision History # {#changelog} 21 | 22 | ## Revision 0 ## {#r0} 23 | 24 | Initial Release 🎉 25 | 26 | # Motivation # {#motivation} 27 | 28 | Main is a sweet special little gremlin in C++. It has interesting properties 29 | regarding exceptions and noreturn functions, `argv` is actually deprecated on 30 | some unicode aware platforms and it even lets us ignore the return type 31 | entirely. And yet, this is the function we introduce to beginners from day one. 32 | This magic, bizarre, entry point to our programs is a nightmare. This proposal 33 | attempts to solve this, while also standardizing existing extensions *and* 34 | archaic literature and examples. 35 | 36 | This paper currently works in tandem with the [[p1275|Desert Sessions]] 37 | proposal, which attempts to solve issues regarding `argv` and the lack of 38 | modern support for reading from environment variables. However, it is not a 39 | prerequisite of adding the [[p1275]] API into the standard, nor does [[p1275]] 40 | require that this paper be added to the standard. 41 | 42 | # Design # {#design} 43 | 44 | The idea behind `void main()` is to simply state that a `main()` that takes no 45 | arguments can choose to return nothing as well. Given that we currently permit 46 | this *with* the `int` returning main signatures this will not break existing 47 | code. 48 | 49 | 50 | # Wording # {#wording} 51 | 52 | All wording is relative to [[N4762]] 53 | 54 | In Section 6.8.3.1 `main` function [**basic.start.main**] paragraph 2, insert 55 | 56 |
57 | 2An implementation shall not predefine the `main` function. This 58 | function shall not be overloaded. Its type shall have C++ language linkage and 59 | it have a declared return type of either type `int` or type 60 | `void`, but otherwise its type is implementation-defined. An 61 | implementation shall allow: 62 | 63 |
    64 |
  1. 65 | (2.1)— a function of `()` returning `void` and 66 |
  2. 67 |
  3. 68 | (2.1) 69 | (2.2) 70 | — a function of `()` returning `int` and 71 |
  4. 72 |
  5. 73 | (2.2) 74 | (2.3) 75 | — a function of `(int`, pointer to pointer to `char)` returning `int` 76 |
  6. 77 |
78 |
79 | 80 | In paragraph 5, insert: 81 | 82 |
83 | 5A `return` statement in `main` has the effect of leaving the main 84 | function (destroying any objects with automatic storage duration) and calling 85 | `std::exit` with the return value or `0` in the case of no return 86 | value as the argument. If control flows off the end of the 87 | *compound-statement* of `main`, the effect is equivalent to a `return` with 88 | operand `0` (see also 13.3) , or calling `std::exit(0)` once `main`'s 89 | scope has exited 90 |
91 | 92 |
 93 | {
 94 |   "p1275": {
 95 |     "authors": "Isabella Muerte",
 96 |     "href": "https://wg21.link/p1275r0",
 97 |     "title": "Desert Sessions: Improving Hostile Environment Interactions"
 98 |   }
 99 | }
100 | 
101 | -------------------------------------------------------------------------------- /src/workflow-operator.bs: -------------------------------------------------------------------------------- 1 | 16 | 20 | 21 | # Revision History # {#changelog} 22 | 23 | ## Revision 0 ## {#r0} 24 | 25 | Initial release. 🎉 26 | 27 | # Motivation # {#motivation} 28 | 29 | With the advent of Ranges, Executors, Coroutines, and monadic interfaces upon 30 | us for C++20, we have a unique opportunity to create a uniform workflow 31 | interface for expanding on each of the operations provided by the 32 | aforementioned types. 33 | 34 | This paper proposes two operators, hereby dubbed the "workflow operators", 35 | because they are not pipes, but for workflows. These workflow operators are 36 | represented with the characters `|>` and `<|`. If you are currently seeing two 37 | arrows on your screen, instead of a `|` and `>`/`<` combo, you probably are 38 | using a monospaced font with ligatures enabled. This paper does not recommend 39 | unicode specific characters. 40 | 41 | # Design # {#design} 42 | 43 | The plan is to define these operators' precedence such that they are above the 44 | comma operator, but below the compound assignment operators. This should, 45 | ideally, allow them to begin to be written in code with minimal interference 46 | needed. 47 | 48 | Currently, ranges and executors plan on overloading the `|` operator. This has 49 | unidirectional meaning, but more importantly has precedence in between the 50 | "bitwise xor" (`^`) and "logcal and" (`and`) operators. This can cause odd 51 | and unexpected behavior. One could argue that overloading `>>` and `<<` was a 52 | mistake at the time. Perhaps we should take the opportunity to not repeat the 53 | same type of mistake. 54 | 55 | Adding these operators has specific consequences, namely we don't need a paper 56 | everytime someone wants to add some kind of monadic operation to 57 | `std::optional`, `std::expected/outcome` or similar monadic types. In the past, 58 | people such as Ben Deane and Simon Brand, have spoken that the best operator we 59 | can get for proper binding is to use `>>`. However, its precedence means that 60 | we actually have to use `>>=`. Instead of overloading *that* operator, why 61 | don't we overload `|>` instead? 62 | 63 | We also would now have an operator that binds correctly when chaining 64 | coroutines. Because `co_await` (or whatever possible glyphs the committee 65 | chooses) binds so tightly, we are currently unable to safely chain coroutines 66 | in continuations without having to rely on member functions, or passing said 67 | coroutines into another function to be executed later. 68 | 69 | While it is asking *a lot* to add more operators into C++20, the author 70 | believes that this is an important requirement for composable and extensible 71 | interfaces regarding the aforementioned executors, ranges, coroutines, monads, 72 | and possibly even more interfaces. 73 | 74 | # FAQ # {#faq} 75 | 76 | ## Does this have use beyond the stated examples so far? ## {#faq-uses} 77 | 78 | Several people on the cpplang slack channel have mentioned that it would be 79 | useful for function composition and binding, as well as a possible "infix" 80 | operator. 81 | 82 | ## Does this work like the `|>` operator from `F#`? ## {#faq-fsharp} 83 | 84 | The syntax is taken from F#, however the semantics differ greatly due to F# 85 | being a garbage collected language, and an ML. C++ however is not either of 86 | those and more things must be taken into account. 87 | 88 | ## Are there any plans to add `<|>` to this set of operators? ## {#faq-more} 89 | 90 | At present there is no plan, however someone will probably find a use for it 🙂 91 | 92 | In all seriousness, there are possible uses for it *if* (and only if) C++ gets, 93 | perhaps, channels as found in languages such as Rust or golang. That said, the 94 | current `|>` and `<|` do not remove the need for, say, `when_all`, and 95 | `when_any` on futures or future-like types. 96 | 97 | ## Does this solve the same problem as UFCS? ## {#faq-ufcs} 98 | 99 | No. UFCS gets us one step closer to concept maps and uniform interfaces. This 100 | solves a different set of problems regarding operator precedence and extensible 101 | interfaces. 102 | 103 | ## Isn't this just UFCS? ## {#faq-ufcs2} 104 | 105 | No. UFCS has different semantics and requirements. This is just a regular old 106 | operator users and library implementors can utilize to create extensible 107 | interfaces with minimal interference into the way we write code. 108 | 109 | ## I dunno, seems like UFCS to me ## {#faq-ufcs3} 110 | 111 | It's not UFCS. We have a new operator in C++20 (`<=>`). We still don't have 112 | UFCS. This isn't UFCS. Stop saying it's UFCS. *It's not UFCS*. 113 | 114 | ## OK, it's just that it looks like its UFCS ## {#faq-ufcs4} 115 | 116 | No. 117 | 118 | # Examples # {#examples} 119 | 120 | The following examples show ranges, coroutines, and monad operations when 121 | chaining operations via the workflow operators. 122 | 123 | ## Ranges ## {#examples-range} 124 | 125 | The following is taken (and modified) from the original ranges v3 draft 126 | 127 | ```cpp 128 | int total = accumulate { 0 } <| view::iota(1) 129 | |> view::transform([](int x){return x*x;}) 130 | |> view::take(10); 131 | ``` 132 | 133 | In the above we create a range on the righthand side of `<|` and then pass this 134 | in to our imaginary accumulate type that takes a range after being initialized 135 | with its initial value. This could also technically be written as 136 | 137 | ```cpp 138 | int total = view::iota(1) 139 | |> view::transform([](int x){return x*x;}) 140 | |> view::take(10) 141 | |> accumulate(0); 142 | ``` 143 | 144 | ## Monads ## {#examples-monad} 145 | 146 | The following code was provided by Simon Brand via his CppCon 2018 talk on 147 | monads. It has been slightly modified to cut down on length. It is a 148 | demonstration of how we can extend existing interfaces without having to modify 149 | existing syntax or creating a new DSL when interacting with standard (or soon 150 | to be standard) types. 151 | 152 | Note: This code is, minus the use of `operator |>` valid C++17 code. With the 153 | addition of Concepts, we could theoretically further constrain what operations 154 | or interfaces can be flowed into a monadic type. 155 | 156 | ```cpp 157 | template 158 | auto map(std::expected const& ex, L const& fun) 159 | -> expected { 160 | if (ex) { return fun(*ex); } 161 | return std::make_unexpected(ex.error()); 162 | } 163 | 164 | template 165 | expected join (expected const& ex, L const& fun) { 166 | if (ex) { return *ex; } 167 | return std::make_unexpected(ex.error()); 168 | } 169 | 170 | template 171 | auto and_then( expected const& ex, L const& fun) { 172 | return join(map(ex,fun)); 173 | } 174 | 175 | namespace infix { 176 | 177 | template 178 | struct map { 179 | T t; 180 | map(T const& t) : t(t) {} 181 | }; 182 | 183 | template 184 | struct and_then { 185 | T t; 186 | and_then(T const& t) : t(t) {} 187 | }; 188 | 189 | template 190 | auto operator|>(std::expected const& ex, map const& fun) { 191 | return ::map(ex, fun.t); 192 | } 193 | 194 | template 195 | auto operator|>(std::expected const& ex, map const& fun) { 196 | return ::and_then(ex, fun.t); 197 | } 198 | 199 | } /* namespace infix */ 200 | 201 | std::expected divide (int numerator, int denominator) { 202 | if (denominator == 0) { return std::make_unexpected( "divide by zero" ); } 203 | return numerator / denominator; 204 | } 205 | 206 | std::expected to_int(std::string const& s) { 207 | if (s.empty()) { return std::make_unexpected("string was empty"s); } 208 | int i; 209 | auto result = from_chars(&s.front(), &s.back(), i); 210 | if (result.ec or result.ptr != &s.back()) { 211 | return std::make_unexpected("string did not contain an int"s); 212 | } 213 | return i; 214 | } 215 | 216 | int main() { 217 | using namespace infix; 218 | auto result = 219 | to_int("12") 220 | |> and_then([] (int i) { return divide(42, i ); }) 221 | |> map([] (double d) { return d * 2; }); 222 | ``` 223 | 224 | ## Coroutines And Executors ## {#examples-coroutine} 225 | 226 | The following code is modified regarding coroutines and its (possible) 227 | interactions with executors found in [[p1056r0]]. While this code may not seem 228 | very explanatory, the interaction between coroutines and executors is still not 229 | well defined. However, unlike the need to implement `.via` as a member function 230 | and having to "await" any changes if newer and better interfaces are found, 231 | users are free to implement their own interfaces on `task`, coroutine types, 232 | executors, or a combination of them. 233 | 234 | Recall, however, that a coroutine *starts* suspended and it is up to the user 235 | to decide how it is executed and where its continuation is placed. 236 | 237 | ### Case 2 ### {#examples-coroutine-2} 238 | 239 | ```cpp 240 | co_await f() |> via(e); 241 | ``` 242 | 243 | ### Case 3 ### {#examples-coroutine-3} 244 | 245 | Starts by an executor `ex`, resumes in the thread that triggered the completion 246 | of `f` 247 | 248 | ```cpp 249 | co_await spawn(ex) |> f; 250 | ``` 251 | 252 | ### Case 4 ### {#examples-coroutine-4} 253 | 254 | Starts on executor `ex1`, resumes on executor `ex2` 255 | 256 | ```cpp 257 | co_await spawn(ex1) |> f |> via(ex2) 258 | ``` 259 | 260 | # Acknowledgements # {#acknowledgements} 261 | 262 | Special thanks for Simon Brand for providing his source code. The encouragement 263 | of Simon Brand, Phil Nash, David Hollman, and countless others (I'm so sorry, 264 | there's a LOT of you and I can't remember all of you) to move forward with this 265 | paper after I originally suggested it in passing to Louis Dionne at CppCon 266 | 2017. 267 | -------------------------------------------------------------------------------- /src/written-target-tuplet.svg: -------------------------------------------------------------------------------- 1 |
Architecture
Vendor
Operating System
Environment
--------------------------------------------------------------------------------