You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -11,7 +11,7 @@ A metafunction is a compile-time function that can participate in interpreting t
11
11
12
12
The most important thing about metafunctions is that they are not hardwired language features — they are compile-time library code that uses the reflection and code generation API, that lets the author of an ordinary type easily opt into a named set of defaults, requirements, and generated contents. This approach is essential to making the language simpler, because it lets us avoid hardwiring special "extra" types into the language and compiler.
## <aid="applying-metafunctions"></a> Applying metafunctions using `@`
15
15
16
16
Metafunctions provide an easy way for a type author to opt into a group of defaults, constraints, and generated functions: Just write `@name` afer the `:` of a declaration, where `name` is the name of the metafunction. This lets the type author declare (and the human reader see) the intent up front: "This isn't just any `type`, this is a `@value type`" which automatically gives the type default/copy/move construction and assignment, `<=>` with `std::strong_ordering` comparisons, and guarantees that it has a public destructor and no protected or virtual functions:
17
17
@@ -28,46 +28,211 @@ point2d: @value type = {
28
28
29
29
## <aid="generating-source"></a>Generating source code at compile time
30
30
31
-
TODO
31
+
A metafunction applied to a definition using `@` gets to participate in interpreting the meaning of the definition by inspecting and manipulating the definition's parse tree. For example
32
+
33
+
```cpp title="shape.cpp2: Using @interface @print" hl_lines="1"
34
+
shape: @interface @print type = {
35
+
draw : (this);
36
+
move_by: (this, dx: double, dy: double);
37
+
}
38
+
```
39
+
40
+
The above code:
41
+
42
+
- applies `@interface`, which makes functions pure virtual by default and defines a virtual destructor with a do-nothing body if there isn't already a virtual destructor (among other things), and
43
+
44
+
- then applies `@print`, which pretty-prints the resulting parse tree as source code to the console so that we can see the results of what the first metafunction did.
45
+
46
+
The result of compiling this is the following cppfront output, which is the `@interface`-modified Cpp2 source code as printed by `@print`:
47
+
48
+
```cpp title="'cppfront shape.cpp2' output to the console, from @print" hl_lines="1"
49
+
shape:/* @interface @print */ type =
50
+
{
51
+
public draw:(virtual in this);
52
+
53
+
public move_by:(
54
+
virtual in this,
55
+
in dx: double,
56
+
in dy: double
57
+
);
58
+
59
+
operator=:(virtual move this) =
60
+
{
61
+
}
62
+
}
63
+
```
64
+
65
+
Finally, cppfront also emits the following in `shape.cpp`:
66
+
67
+
```cpp title="'cppfront shape.cpp' output to 'shape.cpp'"
An `ordered` (or `weakly_ordered` or `partially_ordered`) type has an `#!cpp operator<=>` three-way comparison operator that returns `std::strong_ordering` (or `std::weak_ordering` or `std::partial_ordering`, respectively). This means objects of this type can be used in all binary comparisons: `<`, `<=`, `==`, `!=`, `>=`, and `>`.
94
+
95
+
If the user explicitly writes `operator<=>`, its return type must be the same as the one implied by the metafunction they chose.
96
+
97
+
If the user doesn't explicitly write `operator<=>`, a default memberwise `operator<=>: (this, that) -> /* appropriate _ordering */;` will be generated for the type.
98
+
99
+
These metafunctions will emit a compile-time error if:
100
+
101
+
- a user-written `operator<=>` returns a different type than the one implied by the metafunction they chose
102
+
103
+
> Notes:
104
+
>
105
+
> "... A totally ordered type ... requires `#!cpp operator<=>` that returns `std::strong_ordering`. If the function is not user-written, a lexicographical memberwise implementation is generated by default..."
106
+
>
107
+
> — P0707R4, section 3
108
+
109
+
> Note: This feature derived from Cpp2 was already adopted into Standard C++ via paper [P0515](https://wg21.link/p0515), so most of the heavy lifting is done by the Cpp1 C++20/23 compiler, including the memberwise default semantics. In contrast, cppfront has to do the work itself for default memberwise semantics for operator= assignment as those aren't yet part of Standard C++.
110
+
111
+
112
+
#### `copyable`
113
+
114
+
A `copyable` type has (copy and move) x (construction and assignment).
115
+
116
+
If the user explicitly writes any of the copy/move `operator=` functions, they must also write the most general one that takes `(out this, that)`.
117
+
118
+
If the user doesn't write any of the copy/move `operator=` functions, a default general memberwise `operator=: (out this, that) = { }` will be generated for the type.
119
+
120
+
`copyable` will emit a compile-time error if:
121
+
122
+
- there is a user-written `operator=` but no user-written `operator=: (out this, that)`
A `basic_value` type is a regular type: [`copyable`](#copyable), default constructible, and not polymorphic (no protected or virtual functions).
128
+
129
+
A `value` (or `weakly_ordered_value` or `partially_ordered_value`) is a `basic_value` that is also [`ordered`](#ordered) (or `weakly_ordered` or `partially_ordered`, respectively).
130
+
131
+
These metafunctions will emit a compile-time error if:
41
132
133
+
- any function is protected or virtual
42
134
43
-
### polymorphic_base
135
+
- the type has a destructor that is not public
44
136
45
-
TODO
46
137
138
+
> Notes:
139
+
>
140
+
> "A value is ... a regular type. It must have all public default construction, copy/move construction/assignment,
141
+
and destruction, all of which are generated by default if not user-written; and it must not have any protected or virtual functions (including the destructor)."
A `struct` is a type with only public bases, objects, and functions, with no virtual functions, and with no user-defined constructors (i.e., no invariants) or assignment or destructors.
51
148
149
+
`struct` is implemented in terms of [`cpp1_rule_of_zero`](#cpp1_rule_of_zero).
> "By definition, a `struct` is a `class` in which members are by default `public`; that is,
162
+
>
163
+
> struct s { ...
164
+
>
165
+
> is simply shorthand for
166
+
>
167
+
> class s { public: ...
168
+
>
169
+
> ... Which style you use depends on circumstances and taste. I usually prefer to use `struct` for classes that have all
170
+
> data `public`."
171
+
>
172
+
> — Stroustrup (The C++ Programming Language, 3rd ed., p. 234)
61
173
62
174
63
-
### struct
175
+
### For polymorphic types (interfaces, base classes)
64
176
65
-
TODO
66
177
178
+
#### `interface`
67
179
68
-
### `enum`
180
+
An `interface` type is an abstract base class having only pure virtual functions.
69
181
70
-
Cpp2 has no `enum` feature hardwired into the language. Instead you apply the `@enum` metafunction when writing an ordinary `type`:
182
+
Cpp2 has no `interface` feature hardwired into the language, as C# and Java do. Instead you apply the `@interface` metafunction when writing an ordinary `type`. For a detailed example, see [the `shape` example above](#generating-source-code-at-compile-time).
183
+
184
+
`interface` will emit a compile-time error if:
185
+
186
+
- the type contains a data object
187
+
188
+
- the type has a copy or move function (the diagnostic message will suggest a virtual `clone` function instead)
189
+
190
+
- any function has body
191
+
192
+
- any function is nonpublic
193
+
194
+
> Notes:
195
+
>
196
+
> "... an abstract base class defines an interface ..."
197
+
>
198
+
> — Stroustrup (The Design and Evolution of C++, 12.3.1)
199
+
200
+
201
+
#### `polymorphic_base`
202
+
203
+
A `polymorphic_base` type is a pure polymorphic base type that is not copyable, and whose destructor is either public and virtual or protected and nonvirtual.
204
+
205
+
Unlike an [interface](#interface), it can have nonpublic and nonvirtual functions.
206
+
207
+
`polymorphic_base` will emit a compile-time error if:
208
+
209
+
- the type has a copy or move function (the diagnostic message will suggest a virtual `clone` function instead)
210
+
211
+
- the type has a destructor that is not public and virtual, and also not protected and nonvirtual
212
+
213
+
> Notes:
214
+
>
215
+
> "C.35: A base class destructor should be either public and virtual, or protected and non-virtual."
216
+
>
217
+
> "[C.43] ... a base class should not be copyable, and so does not necessarily need a default constructor."
218
+
>
219
+
> — Stroustrup, Sutter, et al. (C++ Core Guidelines)
220
+
221
+
222
+
### For enumeration types
223
+
224
+
225
+
#### `enum`
226
+
227
+
Cpp2 has no `enum` feature hardwired into the language. Instead you apply the `@enum` metafunction when writing an ordinary `type`.
228
+
229
+
`enum` will emit a compile-time error if:
230
+
231
+
- any member has the reserved name `operator=` or `operator<=>`, as these will be generated by the metafunction
232
+
233
+
- an enumerator is not public or does not have a deduced type
234
+
235
+
For example:
71
236
72
237
``` cpp title="Using the @enum metafunction when writing a type" hl_lines="14"
73
238
// skat_game is declaratively a safe enumeration type: it has
@@ -111,9 +276,34 @@ janus: @enum type = {
111
276
}
112
277
```
113
278
114
-
### `flag_enum`
279
+
> Notes:
280
+
>
281
+
> "C enumerations constitute a curiously half-baked concept. ... the cleanest way out was to deem each enumeration a separate type."
282
+
>
283
+
> — Stroustrup (The Design and Evolution of C++, 11.7)
284
+
>
285
+
> "An enumeration is a distinct type ... with named constants"
286
+
>
287
+
> — ISO C++ Standard
288
+
>
289
+
> "An `enum`[...] is a totally ordered value type that stores a value of its enumerators's type, and otherwise has only public member variables of its enumerator's type, all of which are naturally scoped because they are members of a type."
290
+
>
291
+
> — P0707R4, section 3
292
+
293
+
294
+
#### `flag_enum`
295
+
296
+
`flag_enum` is a variation on `enum` that has power-of-two default enumerator values, a defaultsigned underlying type that is large enough to hold the values, and supports bitwise operators.
297
+
298
+
`flag_enum` will emit a compile-time error if:
299
+
300
+
- any member has the reserved name `operator=`, `operator<=>`, `has`, `set`, `clear`, `to_string`, `get_raw_value`, or `none`, as these will be generated by the metafunction
301
+
302
+
- an enumerator is not public or does not have a deduced type
303
+
304
+
- the values are outside the range that can be represented by the largest default underlying type
115
305
116
-
`flag_enum` is a variation on `enum` that has power-of-two default enumerator values, a default unsigned underlying type, and supports bitwise operators:
306
+
For example:
117
307
118
308
``` cpp title="Using the @flag_enum metafunction when writing a type" hl_lines="11"
119
309
// file_attributes is declaratively a safe flag enum type:
@@ -134,10 +324,32 @@ file_attributes: @flag_enum<u8> type = {
134
324
}
135
325
```
136
326
327
+
> Notes:
328
+
>
329
+
> "`flag_enum` expresses an enumeration that stores values
330
+
> corresponding to bitwise-or'd enumerators. The enumerators must
331
+
> be powers of two, and are automatically generated [...] A none
332
+
> value is provided [...] Operators `|` and `&` are provided to
333
+
> combine and extract values."
334
+
>
335
+
> — P0707R4, section 3
137
336
138
-
### `union`
139
337
140
-
`@union` declaratively opts into writing a safe discriminated union/variant dynamic type. For example:
338
+
### For dynamic types
339
+
340
+
341
+
#### `union`
342
+
343
+
`@union` declaratively opts into writing a safe discriminated union/variant dynamic type.
344
+
345
+
`union` will emit a compile-time error if:
346
+
347
+
- any alternative is not public or has an initializer
348
+
349
+
- any member starts with the reserved name prefix `is_` or `set_`, as these will be generated by the metafunction
350
+
351
+
352
+
For example:
141
353
142
354
```cpp title="Using the @union metafunction when writing a type" hl_lines="10 18-20 25 26"
143
355
// name_or_number is declaratively a safe union/variant type:
@@ -200,23 +412,49 @@ main: () = {
200
412
}
201
413
```
202
414
203
-
### cpp1_rule_of_zero
415
+
> Notes:
416
+
>
417
+
> "As with void*, programmers should know that unions [...] are
418
+
> inherently dangerous, should be avoided wherever possible,
419
+
> and should be handled with special care when actually needed."
420
+
>
421
+
> — Stroustrup (The Design and Evolution of C++, 14.3.4.1)
422
+
>
423
+
> "C++17 needs a type-safe `union`... The implications of the
424
+
> consensus `variant` design are well understood and have been
425
+
> explored over several LEWG discussions, over a thousand emails,
426
+
> a joint LEWG/EWG session, and not to mention 12 years of
427
+
> experience with Boost and other libraries."
428
+
>
429
+
> — Axel Naumann, in [P0088](https://wg21.link/p0088),
430
+
> the adopted proposal for C++17 `std::variant`
431
+
432
+
433
+
### Helpers and utilities
204
434
205
-
TODO
206
435
207
-
###print
436
+
#### `cpp1_rule_of_zero`
208
437
209
-
TODO
438
+
A `cpp1_rule_of_zero` type is one that has no user-written copy/move/destructor functions, and for which Cpp2 should generate nothing so that the Cpp1 defaults for generated special member functions are accepted.
210
439
440
+
> Notes:
441
+
>
442
+
> C.20: If you can avoid defining default operations, do
443
+
>
444
+
> Reason: It's the simplest and gives the cleanest semantics.
445
+
>
446
+
> This is known as "the rule of zero".
447
+
>
448
+
> — Stroustrup, Sutter, et al. (C++ Core Guidelines)
211
449
212
-
## <aid="writing-metafunctions"></a> Writing your own metafunctions
213
450
214
-
TODO
451
+
#### `print`
215
452
453
+
`print` prints a pretty-printed visualization of the type to the console.
216
454
217
-
## <aid="reflection-api"></a> Reflection API reference
455
+
This is most useful for debugging metafunctions, and otherwise seeing the results of applying previous metafunctions.
218
456
219
-
TODO
457
+
For a detailed example, see [the `shape` example above](#generating-source-code-at-compile-time).
220
458
221
459
222
460
[^variant]: With `variant`, there's no way to distinguish in the type system between a `variant<int,string>` that stores either an employee id or employee name, and a `variant<int,string>` that stores either a lucky number or a pet unicorn's dominant color.
0 commit comments