Skip to content

Commit 5571100

Browse files
committed
Complete the metafunctions section
1 parent 9629009 commit 5571100

File tree

1 file changed

+266
-28
lines changed

1 file changed

+266
-28
lines changed

docs/cpp2/metafunctions.md

Lines changed: 266 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A metafunction is a compile-time function that can participate in interpreting t
1111

1212
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.
1313

14-
## <a id="applying-metafunctions"></a> Applying metafunctions
14+
## <a id="applying-metafunctions"></a> Applying metafunctions using `@`
1515

1616
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:
1717

@@ -28,46 +28,211 @@ point2d: @value type = {
2828

2929
## <a id="generating-source"></a>Generating source code at compile time
3030

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'"
68+
class shape {
69+
public: virtual auto draw() const -> void = 0;
70+
public: virtual auto move_by(cpp2::in<double> dx, cpp2::in<double> dy) const -> void = 0;
71+
public: virtual ~shape() noexcept;
72+
73+
public: shape() = default;
74+
public: shape(shape const&) = delete; /* No 'that' constructor, suppress copy */
75+
public: auto operator=(shape const&) -> void = delete;
76+
77+
};
78+
79+
shape::~shape() noexcept{}
80+
```
3281
3382
3483
## <a id="built-in-metafunctions"></a>Built-in metafunctions
3584
3685
The following metafunctions are provided in the box with cppfront.
3786
38-
### interface
3987
40-
TODO
88+
### For regular value-like types (copyable, comparable)
89+
90+
91+
#### <a id="ordered"></a>`ordered`, `weakly_ordered`, `partially_ordered`
92+
93+
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)`
123+
124+
125+
#### <a id="value"></a>`basic_value`, `value`, `weakly_ordered_value`, `partially_ordered_value`
126+
127+
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:
41132
133+
- any function is protected or virtual
42134
43-
### polymorphic_base
135+
- the type has a destructor that is not public
44136
45-
TODO
46137
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)."
142+
>
143+
> — P0707R4, section 3
47144
48-
### <a id="ordered"></a>ordered, weakly_ordered, partially_ordered
145+
#### `struct`
49146
50-
TODO
147+
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.
51148
149+
`struct` is implemented in terms of [`cpp1_rule_of_zero`](#cpp1_rule_of_zero).
52150
53-
### copyable
151+
`struct` will emit a compile-time error if:
54152
55-
TODO
153+
- any member is non-public
56154
155+
- any function is virtual
57156
58-
### <a id="value"></a>basic_value, value, weakly_ordered_value, partially_ordered_value
157+
- there is a user-written `operator=`
59158
60-
TODO
159+
> Notes:
160+
>
161+
> "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)
61173
62174
63-
### struct
175+
### For polymorphic types (interfaces, base classes)
64176
65-
TODO
66177
178+
#### `interface`
67179
68-
### `enum`
180+
An `interface` type is an abstract base class having only pure virtual functions.
69181
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:
71236
72237
``` cpp title="Using the @enum metafunction when writing a type" hl_lines="14"
73238
// skat_game is declaratively a safe enumeration type: it has
@@ -111,9 +276,34 @@ janus: @enum type = {
111276
}
112277
```
113278

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 default signed 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
115305

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:
117307

118308
``` cpp title="Using the @flag_enum metafunction when writing a type" hl_lines="11"
119309
// file_attributes is declaratively a safe flag enum type:
@@ -134,10 +324,32 @@ file_attributes: @flag_enum<u8> type = {
134324
}
135325
```
136326

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
137336
138-
### `union`
139337

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:
141353

142354
``` cpp title="Using the @union metafunction when writing a type" hl_lines="10 18-20 25 26"
143355
// name_or_number is declaratively a safe union/variant type:
@@ -200,23 +412,49 @@ main: () = {
200412
}
201413
```
202414

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
204434

205-
TODO
206435

207-
### print
436+
#### `cpp1_rule_of_zero`
208437

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

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)
211449
212-
## <a id="writing-metafunctions"></a> Writing your own metafunctions
213450

214-
TODO
451+
#### `print`
215452

453+
`print` prints a pretty-printed visualization of the type to the console.
216454

217-
## <a id="reflection-api"></a> Reflection API reference
455+
This is most useful for debugging metafunctions, and otherwise seeing the results of applying previous metafunctions.
218456

219-
TODO
457+
For a detailed example, see [the `shape` example above](#generating-source-code-at-compile-time).
220458

221459

222460
[^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

Comments
 (0)