source: doc/proposals/flags.md@ 1cf2a9b

ADT arm-eh ast-experimental enum forall-pointer-decay jacob/cs343-translation new-ast new-ast-unique-expr pthread-emulation qualifiedEnum
Last change on this file since 1cf2a9b was 200fcb3, checked in by Peter A. Buhr <pabuhr@…>, 7 years ago

add auto newline to sout, change endl to nl

  • Property mode set to 100644
File size: 5.0 KB
Line 
1## Flag Enums ##
2
3A common programming problem is to represent a value from a set of boolean flags, each of which can be either on or off. C already has enums and bitfields, which can be naturally used to represent the individual flags, but are un-ergonomic to combine together. This proposal introduces "flag enums", a variant of the usual enums specialized to represent flags in a more ergonomic way.
4
5As an example, a flag enum for the TCP control bits could be defined as follows:
6
7 ```
8 enum TCP_Flags {
9 FIN,
10 SYN,
11 RST,
12 PSH,
13 ACK,
14 URG,
15 ECE,
16 CWR,
17 NS
18 } __attribute__((flag));
19 ```
20
21The `__attribute__` syntax is ugly, but represents the smallest backwards compatibility break; a new SUE for enum flags (e.g. `flag enum TCP_Flags { ... };` or even `flag TCP_Flags { ... };`) might also be reasonable.
22
23A flag enum would be different than a normal enum in two ways: it would auto-generate discriminant values differently, and it would have a number of bitwise operators defined on it by default.
24
25Normal enums generate their discriminant values sequentially starting at zero (`0, 1, 2, 3, ...`), while a flag enum would generate its discriminant values as successive powers of two starting at `1`. E.g. the `TCP_Flags` declaration above would codegen to an enum like below:
26
27 ```
28 enum TCP_Flags {
29 FIN = 0x1,
30 SYN = 0x2,
31 RST = 0x4,
32 PSH = 0x8,
33 ACK = 0x10,
34 URG = 0x20,
35 ECE = 0x40,
36 CWR = 0x80,
37 NS = 0x100
38 };
39 ```
40
41The precise rule used would be that if no enum discriminant is given, the discriminant is the smallest power of two larger than the previous discriminant (`1` if there is no previous discriminant). This would allow some flexibility for cases like these:
42
43 ```
44 enum FunFlags {
45 NONE = 0, // Named empty value
46 FOO, // == 0x1
47 BAZ = 0x6, // Multi-bit flag: 0x4 | 0x2
48 BAR, // == 0x8
49 FOOBAR = FOO | BAR // Named combination flag
50 } __attribute__((flag));
51 ```
52
53Secondly, we would auto-generate a number of useful operators for any flag enum, as follows:
54* The default constructor for any flag enum would be defined, and would produce a flag with an underlying value of 0.
55* Assignment from and equality/inequality to `zero_t` should also be defined based on the underlying enum value.
56* The bitwise operators `?&?, ?|?, ?^?, ~?` and their assignment variants `?&=?, ?|=?, ?^=?` shall be defined with the semantics of the underlying enum value; `?-?` and `?-=?` should also be defined such that `a - b == a & ~b` (a set difference operation).
57
58With these operations defined, flag enums would support a full set of useful flag operations, using existing, known syntax, as follows:
59
60 ```
61 FunFlags f = some_val();
62 if ( f ) { sout | "f has some flag(s) set"; }
63 if ( f & FOO ) { sout | "f has FOO set"; }
64 f |= FOO; // set FOO
65 f -= FOO; // unset FOO
66 f ^= FOO; // toggle FOO
67 ```
68
69In each of the cases above, `FOO` could be replaced by `(BAR | BAZ)` to do the same operation or test on multiple flags at once.
70
71### Alternative/Additional Features ###
72
73#### User-defined enum discriminant iterator ####
74It may be useful to provide a more general method for changing the enum discriminant assignment function, e.g. the flag enum discriminants could be defined by something like the following:
75
76 ```
77 enum(@ << 1) TCP_Flags { // each discriminant is left-shifted by 1 from the previous
78 FIN = 0x1, // first flag is 1
79 SYN,
80 ACK,
81 ...
82 }
83 ```
84
85#### Member expression for enums ####
86As a more ergonomic way to set and unset enum flags, we could define a member expression for flags enums. Since only unions and structs can have member expressions now, this change would be backwards compatible. Basically, given a `FunFlags f`, `f.FOO` would return a proxy object which could be implicitly converted to `bool` (with semantics `f & FOO`, i.e. "check if `FOO` is set on `f`"), as well as having `bool` assigned to it (with semantics `f |= FOO` on true or `f -= FOO` on false, i.e. "set or unset `FOO` on `f` as appropriate"). With this member function, the operations above can be expressed as follows (possibly more ergonomically):
87
88 ```
89 FunFlags f = some_val();
90 if ( f.FOO ) { sout | "f has FOO set"; }
91 f.FOO = true; // set FOO
92 f.FOO = false; // unset FOO
93 f.FOO = ! f.FOO; // toggle FOO
94 ```
95
96### Related Work ###
97C# has the [`[Flags]`][1] enum attribute, but their proposal does not go as far; specifically, the flag discriminants must be manually specified, and they do not automatically implement the bitwise operators on the flags.
98
99Java has [`EnumSet`][2] which represents the set of flags for a given enum (C++ [`bitset`][3] can be used similarly). The main disadvantage of applying this approach to Cforall is that C enum types already implicitly convert to int, and the bitwise operators already have interpretations on enums with `int` results based on this conversion. As such, all flags need to be wrapped in a set to be used type-safely with the bitwise operators.
100
101[1]: https://msdn.microsoft.com/en-us/library/system.enum.hasflag(v=vs.110).aspx
102[2]: http://docs.oracle.com/javase/7/docs/api/java/util/EnumSet.html
103[3]: http://en.cppreference.com/w/cpp/utility/bitset
Note: See TracBrowser for help on using the repository browser.