1 | \chapter{\CFA Enumeration} |
---|
2 | |
---|
3 | \CFA extends C-Style enumeration by adding a number of new features that bring enumerations inline with other modern programming languages. |
---|
4 | Any enumeration extensions must be intuitive to C programmers both in syntax and semantics. |
---|
5 | The following sections detail all of my new contributions to enumerations in \CFA. |
---|
6 | |
---|
7 | |
---|
8 | \section{Syntax} |
---|
9 | |
---|
10 | \CFA extends the C enumeration declaration \see{\VRef{s:CEnumeration}} by parameterizing with a type (like a generic type), and adding Plan-9 inheritance \see{\VRef{s:CFAInheritance}} using an @inline@ to another enumeration type. |
---|
11 | \begin{cfa}[identifierstyle=\linespread{0.9}\it] |
---|
12 | $\it enum$-specifier: |
---|
13 | enum @(type-specifier$\(_{opt}\)$)@ identifier$\(_{opt}\)$ { cfa-enumerator-list } |
---|
14 | enum @(type-specifier$\(_{opt}\)$)@ identifier$\(_{opt}\)$ { cfa-enumerator-list , } |
---|
15 | enum @(type-specifier$\(_{opt}\)$)@ identifier |
---|
16 | cfa-enumerator-list: |
---|
17 | cfa-enumerator |
---|
18 | cfa-enumerator-list, cfa-enumerator |
---|
19 | cfa-enumerator: |
---|
20 | enumeration-constant |
---|
21 | @inline $\color{red}enum$-type-name@ |
---|
22 | enumeration-constant = constant-expression |
---|
23 | \end{cfa} |
---|
24 | |
---|
25 | |
---|
26 | \section{Operations} |
---|
27 | |
---|
28 | \CFA enumerations have access to the three enumerations properties \see{\VRef{s:Terminology}}: label, order (position), and value via three overloaded functions @label@, @posn@, and @value@ \see{\VRef{c:trait} for details}. |
---|
29 | \CFA auto-generates these functions for every \CFA enumeration. |
---|
30 | \begin{cfa} |
---|
31 | enum(int) E { A = 3 } e = A; |
---|
32 | sout | A | @label@( A ) | @posn@( A ) | @value@( A ); |
---|
33 | sout | e | @label@( e ) | @posn@( e ) | @value@( e ); |
---|
34 | A A 0 3 |
---|
35 | A A 0 3 |
---|
36 | \end{cfa} |
---|
37 | For output, the default is to print the label. |
---|
38 | An alternate way to get an enumerator's position is to cast it to @int@. |
---|
39 | \begin{cfa} |
---|
40 | sout | A | label( A ) | @(int)A@ | value( A ); |
---|
41 | sout | A | label( A ) | @(int)A@ | value( A ); |
---|
42 | A A @0@ 3 |
---|
43 | A A @0@ 3 |
---|
44 | \end{cfa} |
---|
45 | Finally, there is an additional enumeration pseudo-function @countof@ (like @sizeof@, @typeof@) that returns the number of enumerators in an enumeration. |
---|
46 | \begin{cfa} |
---|
47 | enum(int) E { A, B, C, D } e; |
---|
48 | countof( E ); // 4, type argument |
---|
49 | countof( e ); // 4, variable argument |
---|
50 | \end{cfa} |
---|
51 | This buildin function replaces the C idiom for automatically computing the number of enumerators \see{\VRef{s:Usage}}. |
---|
52 | \begin{cfa} |
---|
53 | enum E { A, B, C, D, @N@ }; // N == 4 |
---|
54 | \end{cfa} |
---|
55 | |
---|
56 | The underlying representation of \CFA enumeration object is its position, saved as an integral type. |
---|
57 | Therefore, the size of a \CFA enumeration is consistent with a C enumeration. |
---|
58 | Attribute function @posn@ performs type substitution on an expression from \CFA type to integral type. |
---|
59 | The label and value of an enumerator is stored in a global data structure for each enumeration, where attribute functions @label@/@value@ map an \CFA enumeration object to the corresponding data. |
---|
60 | These operations do not apply to C Enums because backwards compatibility means the necessary backing data structures cannot be supplied. |
---|
61 | |
---|
62 | \section{Opaque Enumeration} |
---|
63 | \label{s:OpaqueEnum} |
---|
64 | |
---|
65 | When an enumeration type is empty is it an \newterm{opaque} enumeration. |
---|
66 | \begin{cfa} |
---|
67 | enum@()@ Mode { O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC, O_APPEND }; |
---|
68 | \end{cfa} |
---|
69 | Here, the internal representation is chosen by the compiler and hidden, so the enumerators cannot be initialized. |
---|
70 | Compared to the C enum, opaque enums are more restrictive in terms of typing and cannot be implicitly converted to integers. |
---|
71 | \begin{cfa} |
---|
72 | Mode mode = O_RDONLY; |
---|
73 | int www @=@ mode; $\C{// disallowed}$ |
---|
74 | \end{cfa} |
---|
75 | Opaque enumerations have only two attribute properties @label@ and @posn@. |
---|
76 | \begin{cfa} |
---|
77 | char * s = label( O_TRUNC ); $\C{// "O\_TRUNC"}$ |
---|
78 | int open = posn( O_WRONLY ); $\C{// 1}$ |
---|
79 | \end{cfa} |
---|
80 | The equality and relational operations are available. |
---|
81 | \begin{cfa} |
---|
82 | if ( mode @==@ O_CREAT ) ... |
---|
83 | bool b = mode @<@ O_APPEND; |
---|
84 | \end{cfa} |
---|
85 | |
---|
86 | |
---|
87 | \section{Typed Enumeration} |
---|
88 | \label{s:EnumeratorTyping} |
---|
89 | |
---|
90 | When an enumeration type is specified, all enumerators have that type and can be initialized with constants of that type or compile-time convertable to that type. |
---|
91 | Figure~\ref{f:EumeratorTyping} shows a series of examples illustrating that all \CFA types can be use with an enumeration and each type's values used to set the enumerator constants. |
---|
92 | Note, the use of the synonyms @Liz@ and @Beth@ in the last declaration. |
---|
93 | Because enumerators are constants, the enumeration type is implicitly @const@, so all the enumerator types in Figure~\ref{f:EumeratorTyping} are logically rewritten with @const@. |
---|
94 | |
---|
95 | \begin{figure} |
---|
96 | \begin{cfa} |
---|
97 | // integral |
---|
98 | enum( @char@ ) Currency { Dollar = '$\textdollar$', Cent = '$\textcent$', Yen = '$\textyen$', Pound = '$\textsterling$', Euro = 'E' }; |
---|
99 | enum( @signed char@ ) srgb { Red = -1, Green = 0, Blue = 1 }; |
---|
100 | enum( @long long int@ ) BigNum { X = 123_456_789_012_345, Y = 345_012_789_456_123 }; |
---|
101 | // non-integral |
---|
102 | enum( @double@ ) Math { PI_2 = 1.570796, PI = 3.141597, E = 2.718282 }; |
---|
103 | enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i }; |
---|
104 | // pointer |
---|
105 | enum( @char *@ ) Name { Fred = "FRED", Mary = "MARY", Jane = "JANE" }; |
---|
106 | int i, j, k; |
---|
107 | enum( @int *@ ) ptr { I = &i, J = &j, K = &k }; |
---|
108 | enum( @int &@ ) ref { I = i, J = j, K = k }; |
---|
109 | // tuple |
---|
110 | enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$ |
---|
111 | // function |
---|
112 | void f() {...} void g() {...} |
---|
113 | enum( @void (*)()@ ) funs { F = f, G = g }; |
---|
114 | // aggregate |
---|
115 | struct Person { char * name; int age, height; }; |
---|
116 | enum( @Person@ ) friends { @Liz@ = { "ELIZABETH", 22, 170 }, @Beth@ = Liz, |
---|
117 | Jon = { "JONATHAN", 35, 190 } }; |
---|
118 | \end{cfa} |
---|
119 | % synonym feature unimplemented |
---|
120 | \caption{Enumerator Typing} |
---|
121 | \label{f:EumeratorTyping} |
---|
122 | \end{figure} |
---|
123 | |
---|
124 | An advantage of the typed enumerations is eliminating the \emph{harmonizing} problem between an enumeration and companion data \see{\VRef{s:Usage}}: |
---|
125 | \begin{cfa} |
---|
126 | enum( char * ) integral_types { |
---|
127 | chr = "char", schar = "signed char", uschar = "unsigned char", |
---|
128 | sshort = "signed short int", ushort = "unsigned short int", |
---|
129 | sint = "signed int", usint = "unsigned int", |
---|
130 | ... |
---|
131 | }; |
---|
132 | \end{cfa} |
---|
133 | Note, the enumeration type can be a structure (see @Person@ in Figure~\ref{f:EumeratorTyping}), so it is possible to have the equivalent of multiple arrays of companion data using an array of structures. |
---|
134 | |
---|
135 | While the enumeration type can be any C aggregate, the aggregate's \CFA constructors are \emph{not} used to evaluate an enumerator's value. |
---|
136 | \CFA enumeration constants are compile-time values (static); |
---|
137 | calling constructors happens at runtime (dynamic). |
---|
138 | |
---|
139 | |
---|
140 | \section{Value Conversion} |
---|
141 | |
---|
142 | C has an implicit type conversion from an enumerator to its base type @int@. |
---|
143 | Correspondingly, \CFA has an implicit conversion from a typed enumerator to its base type, allowing typed enumeration to be seamlessly used as the value of its base type |
---|
144 | For example, using type @Currency@ in \VRef[Figure]{f:EumeratorTyping}: |
---|
145 | \begin{cfa} |
---|
146 | char currency = Dollar; $\C{// implicit conversion to base type}$ |
---|
147 | void foo( char ); |
---|
148 | foo( Dollar ); $\C{// implicit conversion to base type}$ |
---|
149 | \end{cfa} |
---|
150 | The implicit conversion induces a \newterm{value cost}, which is a new category (8 tuple) in \CFA's conversion cost model \see{\VRef{s:ConversionCost}} to disambiguate function overloading over a \CFA enumeration and its base type. |
---|
151 | \begin{cfa} |
---|
152 | void baz( char ch ); $\C{// (1)}$ |
---|
153 | void baz( Currency cu ); $\C{// (2)}$ |
---|
154 | baz( Dollar ); |
---|
155 | \end{cfa} |
---|
156 | While both @baz@ functions are applicable to the enumerator @Dollar@, @candidate (1)@ comes with a @value@ cost for the conversion to the enumeration's base type, while @candidate (2)@ has @zero@ cost. |
---|
157 | Hence, \CFA chooses the exact match. |
---|
158 | Value cost is defined to be a more significant factor than an @unsafe@ but less than the other conversion costs: @(unsafe,@ {\color{red}@value@}@, poly, safe, sign, vars, specialization,@ @reference)@. |
---|
159 | \begin{cfa} |
---|
160 | void bar( @int@ ); |
---|
161 | Math x = PI; $\C{// (1)}$ |
---|
162 | double x = 5.5; $\C{// (2)}$ |
---|
163 | bar( x ); $\C{// costs (1, 0, 0, 0, 0, 0, 0, 0) or (0, 1, 0, 0, 0, 0, 0, 0)}$ |
---|
164 | \end{cfa} |
---|
165 | Here, candidate (1) has a value conversion cost to convert to the base type, while candidate (2) has an unsafe conversion from @double@ to @int@. |
---|
166 | Hence, @bar( x )@ resolves @x@ as type @Math@. |
---|
167 | |
---|
168 | % \begin{cfa} |
---|
169 | % forall(T | @CfaEnum(T)@) void bar(T); |
---|
170 | % |
---|
171 | % bar(a); $\C{// (3), with cost (0, 0, 1, 0, 0, 0, 0, 0)}$ |
---|
172 | % \end{cfa} |
---|
173 | % % @Value@ is designed to be less significant than @poly@ to allow function being generic over \CFA enumeration (see ~\ref{c:trait}). |
---|
174 | % Being generic over @CfaEnum@ traits (a pre-defined interface for \CFA enums) is a practice in \CFA to implement functions over \CFA enumerations, as will see in chapter~\ref{c:trait}. |
---|
175 | % @Value@ is a being a more significant cost than @poly@ implies if a overloaeded function defined for @CfaEnum@ (and other generic type), \CFA always try to resolve it as a @CfaEnum@, rather to insert a @value@ conversion. |
---|
176 | |
---|
177 | |
---|
178 | \section{Auto Initialization} |
---|
179 | |
---|
180 | A partially implemented feature is auto-initialization, which works for the C integral type with constant expressions. |
---|
181 | \begin{cfa} |
---|
182 | enum Week { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun }; // 0-2, 10-13 |
---|
183 | \end{cfa} |
---|
184 | The complexity of the constant expression depends on the level of computation the compiler implements, \eg \CC \lstinline[language={[GNU]C++}]{constexpr} provides complex compile-time computation across multiple types, which blurs the compilation/runtime boundary. |
---|
185 | |
---|
186 | If \CFA had powerful compilation expression evaluation, auto initialization would be implemented as follows. |
---|
187 | \begin{cfa} |
---|
188 | enum E(T) { A, B, C }; |
---|
189 | \end{cfa} |
---|
190 | \begin{enumerate} |
---|
191 | \item the first enumerator, @A@, is initialized with @T@'s @zero_t@. |
---|
192 | \item otherwise, the next enumerator is initialized with the previous enumerator's value using operator @?++@, where @?++( T )@ can be overloaded for any type @T@. |
---|
193 | \end{enumerate} |
---|
194 | |
---|
195 | Unfortunately, constant expressions in C are not powerful and \CFA is only a transpiler, relying on generated C code to perform the detail work. |
---|
196 | It is currently beyond the scope of the \CFA project to implement a complex runtime interpreter in the transpiler to evaluate complex expressions across multiple builtin and user-defined type. |
---|
197 | Nevertheless, the necessary language concepts exist to support this feature. |
---|
198 | |
---|
199 | |
---|
200 | \section{Subset} |
---|
201 | |
---|
202 | An enumeration's type can be another enumeration. |
---|
203 | \begin{cfa} |
---|
204 | enum( char ) Letter { A = 'A', ... }; |
---|
205 | enum( @Letter@ ) Greek { Alph = A, Beta = B, ... }; // alphabet intersection |
---|
206 | |
---|
207 | void foo(Letter l); |
---|
208 | foo(Beta); $\C{// foo(value(Beta))}$ |
---|
209 | \end{cfa} |
---|
210 | Enumeration @Greek@ may have more or less enumerators than @Letter@, but the enum values \emph{must} be of a member of @Letter@. |
---|
211 | Therefore, the set of @Greek@ enum value in a subset of the value set of type @Letter@. |
---|
212 | @Letter@ is type compatible with enumeration @Letter@ thanks to \CFA inserts value conversion whenever @Letter@ be used |
---|
213 | in place of @Greek@. On the other hand, @Letter@ enums are not type compatible with enumeration @Greek@. |
---|
214 | As a result, @Greek@ becomes a logical subtype of @Letter@. |
---|
215 | |
---|
216 | Subset defines an implicit subtyping relationship between two \CFA enumerations. \CFA also has |
---|
217 | containment inheritance for \CFA enumerations where subtyping is explicitly structured. |
---|
218 | |
---|
219 | \section{Inheritance} |
---|
220 | \label{s:CFAInheritance} |
---|
221 | |
---|
222 | \CFA Plan-9 inheritance may be used with \CFA enumerations, where Plan-9 inheritance is containment inheritance with implicit unscoping (like a nested unnamed @struct@/@union@ in C). |
---|
223 | Containment is nominative: an enumeration inherits all enumerators from another enumeration by declaring an @inline statement@ in its enumerator lists. |
---|
224 | \begin{cfa} |
---|
225 | enum( char * ) Names { /* $\see{\VRef[Figure]{f:EumeratorTyping}}$ */ }; |
---|
226 | enum( char * ) Names2 { @inline Names@, Jack = "JACK", Jill = "JILL" }; |
---|
227 | enum( char * ) Names3 { @inline Names2@, Sue = "SUE", Tom = "TOM" }; |
---|
228 | \end{cfa} |
---|
229 | In the preceding example, @Names2@ is defined with five enumerators, three of which are from @Name@ through containment, and two are self-declared. |
---|
230 | @Names3@ inherits all five members from @Names2@ and declares two additional enumerators. |
---|
231 | Hence, enumeration inheritance forms a subset relationship. |
---|
232 | Specifically, the inheritance relationship for the example above is: |
---|
233 | \begin{cfa} |
---|
234 | Names $\(\subset\)$ Names2 $\(\subset\)$ Names3 $\C{// enum type of Names}$ |
---|
235 | \end{cfa} |
---|
236 | |
---|
237 | Inheritance can be nested, and a \CFA enumeration can inline enumerators from more than one \CFA enumeration, forming a tree-like hierarchy. |
---|
238 | However, the uniqueness of enumeration name applies to enumerators, including those from supertypes, meaning an enumeration cannot name enumerator with the same label as its subtype's members, or inherits |
---|
239 | from multiple enumeration that has overlapping enumerator label. As a consequence, a new type cannot inherits from both an enumeration and its supertype, or two enumerations with a |
---|
240 | common supertype (the diamond problem), since such would unavoidably introduce duplicate enumerator labels. |
---|
241 | |
---|
242 | The base type must be consistent between subtype and supertype. |
---|
243 | When an enumeration inherits enumerators from another enumeration, it copies the enumerators' @value@ and @label@, even if the @value@ is auto initialized. |
---|
244 | However, the position of the underlying representation is the order of the enumerator in the new enumeration. |
---|
245 | \begin{cfa} |
---|
246 | enum() E1 { B }; $\C{// B}$ |
---|
247 | enum() E2 { C, D }; $\C{// C D}$ |
---|
248 | enum() E3 { inline E1, inline E2, E }; $\C{// {\color{red}[\(_{E1}\)} B {\color{red}]} {\color{red}[\(_{E2}\)} C D {\color{red}]} E}$ |
---|
249 | enum() E4 { A, inline E3, F}; $\C{// A {\color{blue}[\(_{E3}\)} {\color{red}[\(_{E1}\)} B {\color{red}]} {\color{red}[\(_{E2}\)} C D {\color{red}]} E {\color{blue}]} F }$ |
---|
250 | \end{cfa} |
---|
251 | In the example above, @B@ has the position 0 in @E1@ and @E3@, but it at the position 1 in @E4@ as @A@ taking the 0 in @E4@. |
---|
252 | @C@ is at the position 0 in @E2@, 1 in @E3@ and 2 in E4. @D@ is at the position 1 in @E2@, 2 in @E3@ and 3 in @E4@. |
---|
253 | |
---|
254 | A subtype enumeration can be casted, or implicitly converted into its supertype, with a @safe@ cost. Such conversion is an @enumeration conversion@. |
---|
255 | \begin{cfa} |
---|
256 | enum E2 e2 = C; |
---|
257 | posn( e2 ); $\C[1.75in]{// 0}$ |
---|
258 | enum E3 e3 = e2; |
---|
259 | posn( e2 ); $\C{// 1}$ |
---|
260 | void foo( E3 e ); |
---|
261 | foo( e2 ); |
---|
262 | posn( (E3)e2 ); $\C{// 1}$ |
---|
263 | E3 e31 = B; |
---|
264 | posn( e31 ); $\C{// 0}\CRT$ |
---|
265 | \end{cfa} |
---|
266 | The last expression is unambiguous. |
---|
267 | While both @E2.B@ and @E3.B@ are valid candidate, @E2.B@ has an associated safe cost and \CFA selects the zero cost candidate @E3.B@. |
---|
268 | |
---|
269 | For the given function prototypes, the following calls are valid. |
---|
270 | \begin{cquote} |
---|
271 | \begin{tabular}{ll} |
---|
272 | \begin{cfa} |
---|
273 | void f( Names ); |
---|
274 | void g( Names2 ); |
---|
275 | void h( Names3 ); |
---|
276 | void j( const char * ); |
---|
277 | \end{cfa} |
---|
278 | & |
---|
279 | \begin{cfa} |
---|
280 | f( Fred ); |
---|
281 | g( Fred ); g( Jill ); |
---|
282 | h( Fred ); h( Jill ); h( Sue ); |
---|
283 | j( Fred ); j( Jill ); j( Sue ); j( "WILL" ); |
---|
284 | \end{cfa} |
---|
285 | \end{tabular} |
---|
286 | \end{cquote} |
---|
287 | Note, the validity of calls is the same for call-by-reference as for call-by-value, and @const@ restrictions are the same as for other types. |
---|
288 | |
---|
289 | \subsection{Offset Calculation} |
---|
290 | As discussed in \VRef{s:OpaqueEnum}, \CFA chooses position as a representation of a \CFA enumeration variable. |
---|
291 | Because enumerators has different @position@ between subtype and supertype, \CFA might need to manipulate the representation whenever a cast or |
---|
292 | implicit conversion involves two \CFA enums. \CFA determine how a position is going to change with an @offset calculation@ function, reflects to |
---|
293 | enumerator as an arithmetic expression. Casting an enumeration of subtype to a supertype, the position can be unchanged or increase. The change |
---|
294 | of position is an @offset@. |
---|
295 | |
---|
296 | \begin{figure} |
---|
297 | \begin{cfa} |
---|
298 | struct Enumerator; |
---|
299 | struct CFAEnum { vector<variant<CFAEnum, Enumerator>> members; string name; }; |
---|
300 | inline static bool operator==(CFAEnum& lhs, CFAEnum& rhs) { return lhs.name == rhs.name; } |
---|
301 | pair<bool, int> calculateEnumOffset(CFAEnum src, CFAEnum dst) { |
---|
302 | int offset = 0; |
---|
303 | if (src == dst) return make_pair(true, 0); |
---|
304 | for (auto v : dst.members) { |
---|
305 | if (holds_alternative<Enumerator>(v)) { |
---|
306 | offset++; |
---|
307 | } else { |
---|
308 | auto m = get<CFAEnum>(v); |
---|
309 | if (m == src) return make_pair(true, offset); |
---|
310 | auto dist = calculateEnumOffset(src, m); |
---|
311 | if (dist.first) { |
---|
312 | return make_pair(true, offset + dist.second); |
---|
313 | } else { |
---|
314 | offset += dist.second; |
---|
315 | } |
---|
316 | } |
---|
317 | } |
---|
318 | return make_pair(false, offset); |
---|
319 | } |
---|
320 | \end{cfa} |
---|
321 | \caption{Compute Offset from Subtype Enumeration to a Supertype} |
---|
322 | \label{s:OffsetSubtypeSuperType} |
---|
323 | \end{figure} |
---|
324 | |
---|
325 | Figure~\ref{s:OffsetSubtypeSuperType} shows a minimum of @offset calculation@, written in \CC. CFAEnum represents \CFA enumeration |
---|
326 | which has a vector of variants of Enumerator or CFAEnum. Two CFAEnums are differentiable by their unquie name. |
---|
327 | The algorithm takes two CFAEnums as parameters @src@ and @dst@, with @src@ being type of expression the conversion applies on, |
---|
328 | and @dst@ being type that the expression cast into. The algorithm returns a pair of value: when @src@ is convertible to @dst@ (@src@ is a subtype of @dst@), |
---|
329 | it returns boolean true and the offset. Otherwise, it returns false and the size of @src@ (total number of enumerators in @src@). The offset between a type and itself is defined |
---|
330 | as 0. |
---|
331 | |
---|
332 | The algorithm iterates over members in @dst@ to find @src@. If a member is an enumerator of @dst@, the positions of all subsequent members |
---|
333 | increment by one. If the current member is @dst@, the function returns true indicating "found" and the accumulated offset. Otherwise, the algorithm recurse |
---|
334 | into the current CFAEnum @m@ and find out if the @src@ is convertible to @m@. The @src@ being convertible to the current member @m@ means @src@ |
---|
335 | is a "subtype of subtype" of @dst@. The offset between @src@ and @dst@ is the sum of the offset of @m@ in @dst@ and the offset of |
---|
336 | @src@ in @m@. If @src@ is not a subtype of @m@, the loop continue but with offset shifted by the size of @m@. The procedure reaches the end |
---|
337 | of the loop proves @src@ is not convertible to @dst@. It returns size of @src@ in case it is in a recurse innvocation and size is needed for offset update. |
---|
338 | |
---|
339 | \section{Control Structures} |
---|
340 | |
---|
341 | Enumerators can be used in multiple contexts. |
---|
342 | In most programming languages, an enumerator is implicitly converted to its value (like a typed macro substitution). |
---|
343 | However, enumerator synonyms and typed enumerations make this implicit conversion to value incorrect in some contexts. |
---|
344 | In these contexts, a programmer's intuition assumes an implicit conversion to position. |
---|
345 | |
---|
346 | For example, an intuitive use of enumerations is with the \CFA @switch@/@choose@ statement, where @choose@ performs an implicit @break@ rather than a fall-through at the end of a @case@ clause. |
---|
347 | (For this discussion, ignore the fact that @case@ requires a compile-time constant.) |
---|
348 | \begin{cfa}[belowskip=0pt] |
---|
349 | enum Count { First, Second, Third, Fourth }; |
---|
350 | Count e; |
---|
351 | \end{cfa} |
---|
352 | \begin{cquote} |
---|
353 | \setlength{\tabcolsep}{15pt} |
---|
354 | \noindent |
---|
355 | \begin{tabular}{@{}ll@{}} |
---|
356 | \begin{cfa}[aboveskip=0pt] |
---|
357 | |
---|
358 | choose( e ) { |
---|
359 | case @First@: ...; |
---|
360 | case @Second@: ...; |
---|
361 | case @Third@: ...; |
---|
362 | case @Fourth@: ...; |
---|
363 | } |
---|
364 | \end{cfa} |
---|
365 | & |
---|
366 | \begin{cfa}[aboveskip=0pt] |
---|
367 | // rewrite |
---|
368 | choose( @value@( e ) ) { |
---|
369 | case @value@( First ): ...; |
---|
370 | case @value@( Second ): ...; |
---|
371 | case @value@( Third ): ...; |
---|
372 | case @value@( Fourth ): ...; |
---|
373 | } |
---|
374 | \end{cfa} |
---|
375 | \end{tabular} |
---|
376 | \end{cquote} |
---|
377 | Here, the intuitive code on the left is implicitly transformed into the standard implementation on the right, using the value of the enumeration variable and enumerators. |
---|
378 | However, this implementation is fragile, \eg if the enumeration is changed to: |
---|
379 | \begin{cfa} |
---|
380 | enum Count { First, Second, Third @= First@, Fourth }; |
---|
381 | \end{cfa} |
---|
382 | making @Third == First@ and @Fourth == Second@, causing a compilation error because of duplicate @case@ clauses. |
---|
383 | To better match with programmer intuition, \CFA toggles between value and position semantics depending on the language context. |
---|
384 | For conditional clauses and switch statements, \CFA uses the robust position implementation. |
---|
385 | \begin{cfa} |
---|
386 | if ( @posn@( e ) < posn( Third ) ) ... |
---|
387 | choose( @posn@( e ) ) { |
---|
388 | case @posn@( First ): ...; |
---|
389 | case @posn@( Second ): ...; |
---|
390 | case @posn@( Third ): ...; |
---|
391 | case @posn@( Fourth ): ...; |
---|
392 | } |
---|
393 | \end{cfa} |
---|
394 | |
---|
395 | \CFA provides a special form of for-control for enumerating through an enumeration, where the range is a type. |
---|
396 | \begin{cfa} |
---|
397 | for ( cx; @Count@ ) { sout | cx | nonl; } sout | nl; |
---|
398 | for ( cx; ~= Count ) { sout | cx | nonl; } sout | nl; |
---|
399 | for ( cx; -~= Count ) { sout | cx | nonl; } sout | nl; |
---|
400 | First Second Third Fourth |
---|
401 | First Second Third Fourth |
---|
402 | Fourth Third Second First |
---|
403 | \end{cfa} |
---|
404 | The enumeration type is syntax sugar for looping over all enumerators and assigning each enumerator to the loop index, whose type is inferred from the range type. |
---|
405 | The prefix @+~=@ or @-~=@ iterate forward or backwards through the inclusive enumeration range, where no prefix defaults to @+~=@. |
---|
406 | |
---|
407 | C has an idiom for @if@ and loop predicates of comparing the predicate result ``not equal to 0''. |
---|
408 | \begin{cfa} |
---|
409 | if ( x + y /* != 0 */ ) ... |
---|
410 | while ( p /* != 0 */ ) ... |
---|
411 | \end{cfa} |
---|
412 | This idiom extends to enumerations because there is a boolean conversion in terms of the enumeration value, if and only if such a conversion is available. |
---|
413 | For example, such a conversion exists for all numerical types (integral and floating-point). |
---|
414 | It is possible to explicitly extend this idiom to any typed enumeration by overloading the @!=@ operator. |
---|
415 | \begin{cfa} |
---|
416 | bool ?!=?( Name n, zero_t ) { return n != Fred; } |
---|
417 | Name n = Mary; |
---|
418 | if ( n ) ... // result is true |
---|
419 | \end{cfa} |
---|
420 | Specialize meanings are also possible. |
---|
421 | \begin{cfa} |
---|
422 | enum(int) ErrorCode { Normal = 0, Slow = 1, Overheat = 1000, OutOfResource = 1001 }; |
---|
423 | bool ?!=?( ErrorCode ec, zero_t ) { return ec >= Overheat; } |
---|
424 | ErrorCode code = ...; |
---|
425 | if ( code ) { problem(); } |
---|
426 | \end{cfa} |
---|
427 | |
---|
428 | |
---|
429 | \section{Dimension} |
---|
430 | |
---|
431 | \VRef{s:EnumeratorTyping} introduces the harmonizing problem between an enumeration and secondary information. |
---|
432 | When possible, using a typed enumeration for the secondary information is the best approach. |
---|
433 | However, there are times when combining these two types is not possible. |
---|
434 | For example, the secondary information might precede the enumeration and/or its type is needed directly to declare parameters of functions. |
---|
435 | In these cases, having secondary arrays of the enumeration size are necessary. |
---|
436 | |
---|
437 | To support some level of harmonizing in these cases, an array dimension can be defined using an enumerator type, and the enumerators used as subscripts. |
---|
438 | \begin{cfa} |
---|
439 | enum E1 { A, B, C, N }; // possibly predefined |
---|
440 | enum(int) E2 { A, B, C }; |
---|
441 | float H1[N] = { [A] :$\footnotemark$ 3.4, [B] : 7.1, [C] : 0.01 }; // C |
---|
442 | float H2[@E2@] = { [A] : 3.4, [B] : 7.1, [C] : 0.01 }; // CFA |
---|
443 | \end{cfa} |
---|
444 | \footnotetext{C uses symbol \lstinline{'='} for designator initialization, but \CFA changes it to \lstinline{':'} because of problems with tuple syntax.} |
---|
445 | This approach is also necessary for a predefined typed enumeration (unchangeable), when additional secondary-information need to be added. |
---|
446 | |
---|
447 | The array subscript operator, namely @?[?]@, is overloaded so that when a \CFA enumerator is used as an array index, it implicitly converts to its position over value to sustain data harmonization. |
---|
448 | This behaviour can be reverted by explicit overloading: |
---|
449 | \begin{cfa} |
---|
450 | float ?[?]( float * arr, E2 index ) { return arr[ value( index ) ]; } |
---|
451 | \end{cfa} |
---|
452 | When an enumeration type is being used as an array dimension, \CFA adds the enumeration type to the initializer's context. |
---|
453 | As a result, @H2@'s array destinators @A@, @B@ and @C@ are resolved unambiguously to type @E2@. |
---|
454 | (@H1@'s destinators are also resolved unambiguously to @E1@ because @E2@ has a @value@ cost.) |
---|
455 | |
---|
456 | |
---|
457 | \section{I/O} |
---|
458 | |
---|
459 | As seen in multiple examples, enumerations can be printed and the default property printed is the enumerator's label, which is similar in other programming languages. |
---|
460 | However, very few programming languages provide a mechanism to read in enumerator values. |
---|
461 | Even the @boolean@ type in many languages does not have a mechanism for input using the enumerators @true@ or @false@. |
---|
462 | \VRef[Figure]{f:EnumerationI/O} show \CFA enumeration input based on the enumerator labels. |
---|
463 | When the enumerator labels are packed together in the input stream, the input algorithm scans for the longest matching string. |
---|
464 | For basic types in \CFA, the rule is that the same constants used to initialize a variable in a program are available to initialize a variable using input, where strings constants can be quoted or unquoted. |
---|
465 | |
---|
466 | \begin{figure} |
---|
467 | \begin{cquote} |
---|
468 | \setlength{\tabcolsep}{15pt} |
---|
469 | \begin{tabular}{@{}ll@{}} |
---|
470 | \begin{cfa} |
---|
471 | int main() { |
---|
472 | enum(int ) E { BBB = 3, AAA, AA, AB, B }; |
---|
473 | E e; |
---|
474 | |
---|
475 | for () { |
---|
476 | try { |
---|
477 | @sin | e@; |
---|
478 | } catch( missing_data * ) { |
---|
479 | sout | "missing data"; |
---|
480 | continue; // try again |
---|
481 | } |
---|
482 | if ( eof( sin ) ) break; |
---|
483 | sout | e | "= " | value( e ); |
---|
484 | } |
---|
485 | } |
---|
486 | \end{cfa} |
---|
487 | & |
---|
488 | \begin{cfa} |
---|
489 | $\rm input$ |
---|
490 | BBBABAAAAB |
---|
491 | BBB AAA AA AB B |
---|
492 | |
---|
493 | $\rm output$ |
---|
494 | BBB = 3 |
---|
495 | AB = 6 |
---|
496 | AAA = 4 |
---|
497 | AB = 6 |
---|
498 | BBB = 3 |
---|
499 | AAA = 4 |
---|
500 | AA = 5 |
---|
501 | AB = 6 |
---|
502 | B = 7 |
---|
503 | |
---|
504 | \end{cfa} |
---|
505 | \end{tabular} |
---|
506 | \end{cquote} |
---|
507 | \caption{Enumeration I/O} |
---|
508 | \label{f:EnumerationI/O} |
---|
509 | \end{figure} |
---|
510 | |
---|
511 | |
---|
512 | \section{Planet Example} |
---|
513 | |
---|
514 | \VRef[Figure]{f:PlanetExample} shows an archetypal enumeration example illustrating most of the \CFA enumeration features. |
---|
515 | @Planet@ is an enumeration of type @MR@. |
---|
516 | Each planet enumerator is initialized to a specific mass/radius, @MR@, value. |
---|
517 | The unnamed enumeration provides the gravitational-constant enumerator @G@. |
---|
518 | Function @surfaceGravity@ uses the @with@ clause to remove @p@ qualification from fields @mass@ and @radius@. |
---|
519 | The program main uses the pseudo function @countof@ to obtain the number of enumerators in @Planet@, and safely converts the random value into a @Planet@ enumerator using @fromInt@. |
---|
520 | The resulting random orbital-body is used in a @choose@ statement. |
---|
521 | The enumerators in the @case@ clause use the enumerator position for testing. |
---|
522 | The prints use @label@ to print an enumerator's name. |
---|
523 | Finally, a loop enumerates through the planets computing the weight on each planet for a given earth mass. |
---|
524 | The print statement does an equality comparison with an enumeration variable and enumerator (@p == MOON@). |
---|
525 | |
---|
526 | \begin{figure} |
---|
527 | \small |
---|
528 | \begin{cfa} |
---|
529 | struct MR { double mass, radius; }; $\C[3.5in]{// planet definition}$ |
---|
530 | enum( @MR@ ) Planet { $\C{// typed enumeration}$ |
---|
531 | // mass (kg) radius (km) |
---|
532 | MERCURY = { 0.330_E24, 2.4397_E6 }, |
---|
533 | VENUS = { 4.869_E24, 6.0518_E6 }, |
---|
534 | EARTH = { 5.976_E24, 6.3781_E6 }, |
---|
535 | MOON = { 7.346_E22, 1.7380_E6 }, $\C{// not a planet}$ |
---|
536 | MARS = { 0.642_E24, 3.3972_E6 }, |
---|
537 | JUPITER = { 1898._E24, 71.492_E6 }, |
---|
538 | SATURN = { 568.8_E24, 60.268_E6 }, |
---|
539 | URANUS = { 86.86_E24, 25.559_E6 }, |
---|
540 | NEPTUNE = { 102.4_E24, 24.746_E6 }, |
---|
541 | PLUTO = { 1.303_E22, 1.1880_E6 }, $\C{// not a planet}$ |
---|
542 | }; |
---|
543 | enum( double ) { G = 6.6743_E-11 }; $\C{// universal gravitational constant (m3 kg-1 s-2)}$ |
---|
544 | static double surfaceGravity( Planet p ) @with( p )@ { |
---|
545 | return G * mass / ( radius @\@ 2 ); $\C{// no qualification, exponentiation}$ |
---|
546 | } |
---|
547 | static double surfaceWeight( Planet p, double otherMass ) { |
---|
548 | return otherMass * surfaceGravity( p ); |
---|
549 | } |
---|
550 | int main( int argc, char * argv[] ) { |
---|
551 | if ( argc != 2 ) @exit@ | "Usage: " | argv[0] | "earth-weight"; // terminate program |
---|
552 | double earthWeight = convert( argv[1] ); |
---|
553 | double earthMass = earthWeight / surfaceGravity( EARTH ); |
---|
554 | Planet rp = @fromInt@( prng( @countof@( Planet ) ) ); $\C{// select random orbiting body}$ |
---|
555 | @choose( rp )@ { $\C{// implicit breaks}$ |
---|
556 | case MERCURY, VENUS, EARTH, MARS: |
---|
557 | sout | @rp@ | "is a rocky planet"; |
---|
558 | case JUPITER, SATURN, URANUS, NEPTUNE: |
---|
559 | sout | rp | "is a gas-giant planet"; |
---|
560 | default: |
---|
561 | sout | rp | "is not a planet"; |
---|
562 | } |
---|
563 | for ( @p; Planet@ ) { $\C{// enumerate}\CRT$ |
---|
564 | sout | "Your weight on" | ( @p == MOON@ ? "the" : " " ) | p |
---|
565 | | "is" | wd( 1,1, surfaceWeight( p, earthMass ) ) | "kg"; |
---|
566 | } |
---|
567 | } |
---|
568 | $\$$ planet 100 |
---|
569 | JUPITER is a gas-giant planet |
---|
570 | Your weight on MERCURY is 37.7 kg |
---|
571 | Your weight on VENUS is 90.5 kg |
---|
572 | Your weight on EARTH is 100.0 kg |
---|
573 | Your weight on the MOON is 16.6 kg |
---|
574 | Your weight on MARS is 37.9 kg |
---|
575 | Your weight on JUPITER is 252.8 kg |
---|
576 | Your weight on SATURN is 106.6 kg |
---|
577 | Your weight on URANUS is 90.5 kg |
---|
578 | Your weight on NEPTUNE is 113.8 kg |
---|
579 | Your weight on PLUTO is 6.3 kg |
---|
580 | \end{cfa} |
---|
581 | \caption{Planet Example} |
---|
582 | \label{f:PlanetExample} |
---|
583 | \end{figure} |
---|