1 | \chapter{\texorpdfstring{\CFA}{Cforall} Enumeration}
|
---|
2 |
|
---|
3 | \CFA extends C-Style enumeration by adding a number of new features that bring enumerations in line with other modern programming languages.
|
---|
4 | Any enumeration extensions must be intuitive to C programmers in syntax and semantics.
|
---|
5 | The following sections detail 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, \CFA introduces 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 built-in 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 | \section{Opaque Enumeration}
|
---|
57 | \label{s:OpaqueEnum}
|
---|
58 |
|
---|
59 | When an enumeration type is empty. it is an \newterm{opaque} enumeration.
|
---|
60 | \begin{cfa}
|
---|
61 | enum@()@ Mode { O_RDONLY, O_WRONLY, O_CREAT, O_TRUNC, O_APPEND };
|
---|
62 | \end{cfa}
|
---|
63 | Here, the compiler chooses the internal representation, which is hidden, so the enumerators cannot be initialized.
|
---|
64 | Compared to the C enum, opaque enums are more restrictive regarding typing and cannot be implicitly converted to integers.
|
---|
65 | \begin{cfa}
|
---|
66 | Mode mode = O_RDONLY;
|
---|
67 | int www @=@ mode; $\C{// disallowed}$
|
---|
68 | \end{cfa}
|
---|
69 | Opaque enumerations have only two attribute properties, @label@ and @posn@.
|
---|
70 | \begin{cfa}
|
---|
71 | char * s = label( O_TRUNC ); $\C{// "O\_TRUNC"}$
|
---|
72 | int open = posn( O_WRONLY ); $\C{// 1}$
|
---|
73 | s = label( mode ); $\C{// "O\_RDONLY"}$
|
---|
74 | int open = posn( mode ); $\C{// 0}$
|
---|
75 | \end{cfa}
|
---|
76 | Equality and relational operations are available.
|
---|
77 | \begin{cfa}
|
---|
78 | if ( mode @==@ O_CREAT ) ...
|
---|
79 | bool b = mode @<@ O_APPEND;
|
---|
80 | \end{cfa}
|
---|
81 |
|
---|
82 |
|
---|
83 | \section{Typed Enumeration}
|
---|
84 | \label{s:EnumeratorTyping}
|
---|
85 |
|
---|
86 | When an enumeration type is specified, all enumerators have that type and can be initialized with constants of that type or compile-time convertible to that type.
|
---|
87 | Figure~\ref{f:EumeratorTyping} shows a series of examples illustrating that all \CFA types can be used with an enumeration, and each type's values are used to set the enumerator constants.
|
---|
88 | Note the use of the synonyms @Liz@ and @Beth@ in the last declaration.
|
---|
89 | 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@.
|
---|
90 |
|
---|
91 | \begin{figure}
|
---|
92 | \begin{cfa}
|
---|
93 | // integral
|
---|
94 | enum( @char@ ) Currency { Dollar = '$\textdollar$', Cent = '$\textcent$', Yen = '$\textyen$', Pound = '$\textsterling$', Euro = 'E' };
|
---|
95 | enum( @signed char@ ) srgb { Red = -1, Green = 0, Blue = 1 };
|
---|
96 | enum( @long long int@ ) BigNum { X = 123_456_789_012_345, Y = 345_012_789_456_123 };
|
---|
97 | // non-integral
|
---|
98 | enum( @double@ ) Math { PI_2 = 1.570796, PI = 3.141597, E = 2.718282 };
|
---|
99 | enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i };
|
---|
100 | // pointer
|
---|
101 | enum( @char *@ ) Name { Fred = "FRED", Mary = "MARY", Jane = "JANE" };
|
---|
102 | int i, j, k;
|
---|
103 | enum( @int *@ ) ptr { I = &i, J = &j, K = &k };
|
---|
104 | enum( @int &@ ) ref { I = i, J = j, K = k };
|
---|
105 | // tuple
|
---|
106 | enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$
|
---|
107 | // function
|
---|
108 | void f() {...} void g() {...}
|
---|
109 | enum( @void (*)()@ ) funs { F = f, G = g };
|
---|
110 | // aggregate
|
---|
111 | struct Person { char * name; int age, height; };
|
---|
112 | enum( @Person@ ) friends { @Liz@ = { "ELIZABETH", 22, 170 }, @Beth@ = Liz,
|
---|
113 | Jon = { "JONATHAN", 35, 190 } };
|
---|
114 | \end{cfa}
|
---|
115 | % synonym feature unimplemented
|
---|
116 | \caption{Enumerator Typing}
|
---|
117 | \label{f:EumeratorTyping}
|
---|
118 | \end{figure}
|
---|
119 |
|
---|
120 | An advantage of the typed enumerations is eliminating the \emph{harmonizing} problem between an enumeration and companion data \see{\VRef{s:Usage}}:
|
---|
121 | \begin{cfa}
|
---|
122 | enum( char * ) integral_types {
|
---|
123 | chr = "char", schar = "signed char", uschar = "unsigned char",
|
---|
124 | sshort = "signed short int", ushort = "unsigned short int",
|
---|
125 | sint = "signed int", usint = "unsigned int",
|
---|
126 | ...
|
---|
127 | };
|
---|
128 | \end{cfa}
|
---|
129 | Note that 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.
|
---|
130 |
|
---|
131 | While the enumeration type can be any C aggregate, the aggregate's \CFA constructors are \emph{not} used to evaluate an enumerator's value.
|
---|
132 | \CFA enumeration constants are compile-time values (static);
|
---|
133 | calling constructors happens at runtime (dynamic).
|
---|
134 |
|
---|
135 |
|
---|
136 | \section{Implementation}
|
---|
137 |
|
---|
138 | \CFA-cc is a transpiler translating \CFA code into C, which is compiled by a C compiler.
|
---|
139 | During transpiling, \CFA-cc breaks a \CFA enumeration definition into a definition of a C enumeration with the same name and auxiliary arrays: a label and value array for a typed enumeration.
|
---|
140 | For example:
|
---|
141 | \begin{cfa}
|
---|
142 | enum( T ) E { E1 = t1, E2 = t2, E3 = t3 };
|
---|
143 | \end{cfa}
|
---|
144 | is compiled into:
|
---|
145 | \begin{cfa}
|
---|
146 | enum E { E1, E2, E3 };
|
---|
147 | const char * E_labels[3] = { "E1", "E2", "E3" };
|
---|
148 | const T E_values[3] = { t1, t2, t3 };
|
---|
149 | \end{cfa}
|
---|
150 | The generated C enumeration has enumerator values that match \CFA enumerator positions because of C's auto-initialization.
|
---|
151 | A \CFA enumeration variable definition is the same in \CFA as C, \eg:
|
---|
152 | \begin{cfa}
|
---|
153 | enum E e = E1;
|
---|
154 | e = E2;
|
---|
155 | \end{cfa}
|
---|
156 | so these expressions remain unchanged by \CFA-cc.
|
---|
157 | Therefore, a \CFA enumeration variable has the same underlying representation as its generated C enumeration.
|
---|
158 | This semantics implies a \CFA enumeration variable uses the same storage as a C enumeration variable, that @posn@ can use as its underlying representation, and the label and value arrays take little storage.
|
---|
159 | The arrays are annotated with @linkonce@, which are replaced later by gcc attribute @section(".gnu.linkonce.NAME")@, to prevent multiple definitions.
|
---|
160 | It should be possible to eliminate the two arrays if unused, either by \CFA if local to a translation unit and unused, or by the linker if global but unreferenced.
|
---|
161 | Also, the label and value arrays are declared @static@ and initialized with constants, so the arrays are allocated in the @.data@ section and initialized before program execution.
|
---|
162 | Hence, there is no additional execution cost unless new enumeration features are used, and storage usage is minimal as the number of enumerations in a program is small as is the number of enumerators in an enumeration.
|
---|
163 |
|
---|
164 | Along with the enumeration definition, \CFA-cc generates definitions of the attribute functions, @posn@, @label@ and @value@, for each enumeration:
|
---|
165 | \begin{cfa}
|
---|
166 | inline int posn( E e ) { return (int) e; }
|
---|
167 | inline const * label( E e ) { return E_labels[ (int) e ]; }
|
---|
168 | inline const * E_value( E e ) { return E_values[ (int) e ]; }
|
---|
169 | \end{cfa}
|
---|
170 | where the function calls are normally inlined by the backend C compiler into a few instructions.
|
---|
171 | These functions simplify the job of getting the enumerations types through the type system in the same way as normal functions and calls.
|
---|
172 | Note, the cast to @int@ is actually an internal reinterpreted cast added before type resolution to stop further reduction on the expression by the type resolver \see{\VRef{s:ValueConversion}} and removed in code generation.
|
---|
173 | Finally, to further mitigate \CFA enumeration costs, calls to @label@ and @value@ with an enumeration constant are unrolled into the appropriate constant expression, although this could be left to the backend C compiler.
|
---|
174 | Hence, in space and time costs, \CFA enumerations follow the C philosophy of only paying for what is used, modulo some future work to convince the linker to remove un-accessed @label@ and @value@ arrays, possibly with @weak@ attributes.
|
---|
175 |
|
---|
176 |
|
---|
177 | \section{Value Conversion}
|
---|
178 | \label{s:ValueConversion}
|
---|
179 | C has an implicit type conversion from an enumerator to its base type @int@.
|
---|
180 | 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
|
---|
181 | For example, using type @Currency@ in \VRef[Figure]{f:EumeratorTyping}:
|
---|
182 | \begin{cfa}
|
---|
183 | char currency = Dollar; $\C{// implicit conversion to base type}$
|
---|
184 | void foo( char );
|
---|
185 | foo( Dollar ); $\C{// implicit conversion to base type}$
|
---|
186 | \end{cfa}
|
---|
187 | 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.
|
---|
188 | \begin{cfa}
|
---|
189 | void baz( char ch ); $\C{// (1)}$
|
---|
190 | void baz( Currency cu ); $\C{// (2)}$
|
---|
191 | baz( Dollar );
|
---|
192 | \end{cfa}
|
---|
193 | 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.
|
---|
194 | Hence, \CFA chooses the exact match.
|
---|
195 | 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)@.
|
---|
196 | \begin{cfa}
|
---|
197 | void bar( @int@ );
|
---|
198 | Math x = PI; $\C{// (1)}$
|
---|
199 | double x = 5.5; $\C{// (2)}$
|
---|
200 | bar( x ); $\C{// costs (1, 0, 0, 0, 0, 0, 0, 0) or (0, 1, 0, 0, 0, 0, 0, 0)}$
|
---|
201 | \end{cfa}
|
---|
202 | Here, the candidate (1) has a @value@ conversion cost to convert to the base type, while the candidate (2) has an @unsafe@ conversion from @double@ to @int@,
|
---|
203 | which is a more expensive conversion.
|
---|
204 | Hence, @bar( x )@ resolves @x@ as type @Math@.
|
---|
205 |
|
---|
206 | % \begin{cfa}
|
---|
207 | % forall(T | @CfaEnum(T)@) void bar(T);
|
---|
208 | %
|
---|
209 | % bar(a); $\C{// (3), with cost (0, 0, 1, 0, 0, 0, 0, 0)}$
|
---|
210 | % \end{cfa}
|
---|
211 | % % @Value@ is designed to be less significant than @poly@ to allow function being generic over \CFA enumeration (see ~\ref{c:trait}).
|
---|
212 | % 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}.
|
---|
213 | % @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.
|
---|
214 |
|
---|
215 |
|
---|
216 | \section{Initialization}
|
---|
217 | \CFA extends C's auto-initialization scheme to \CFA enumeration. For an enumeration type with base type T, the initialization scheme is the following:
|
---|
218 | \begin{enumerate}
|
---|
219 | \item the first enumerator is initialized with @T@'s @zero_t@.
|
---|
220 | \item Every other enumerator is initialized with its previous enumerator's value "+1", where "+1" is defined in terms of overloaded operator @?+?(T, one_t)@.
|
---|
221 | \end{enumerate}
|
---|
222 |
|
---|
223 | \begin{cfa}
|
---|
224 | struct S { int i; };
|
---|
225 | S ?+?( S & s, one_t ) { return s.i++; }
|
---|
226 | void ?{}( S & s, zero_t ) { s.i = 0; }
|
---|
227 | enum(S) E { A, B, C, D };
|
---|
228 | \end{cfa}
|
---|
229 |
|
---|
230 | The restriction on C's enumeration initializers being constant expression is relaxed on \CFA enumeration.
|
---|
231 | Therefore, an enumerator initializer allows function calls like @?+?( S & s, one_t )@ and @?{}( S & s, zero_t )@.
|
---|
232 | It is because the values of \CFA enumerators are not stored in the compiled enumeration body but in the @value@ array, which
|
---|
233 | allows dynamic initialization.
|
---|
234 |
|
---|
235 | \section{Subset}
|
---|
236 |
|
---|
237 | An enumeration's type can be another enumeration.
|
---|
238 | \begin{cfa}
|
---|
239 | enum( char ) Letter { A = 'A', ..., Z = 'Z' };
|
---|
240 | enum( @Letter@ ) Greek { Alph = @A@, Beta = @B@, Gamma = @G@, ..., Zeta = @Z@ }; // alphabet intersection
|
---|
241 | \end{cfa}
|
---|
242 | Enumeration @Greek@ may have more or less enumerators than @Letter@, but its enumerator values \emph{must} be from @Letter@.
|
---|
243 | Therefore, the set of @Greek@ enumerator values in a subset of the @Letter@ enumerator values.
|
---|
244 | @Letter@ is type compatible with enumeration @Letter@ because value conversions are inserted whenever @Letter@ is used in place of @Greek@.
|
---|
245 | \begin{cfa}
|
---|
246 | Letter l = A; $\C{// allowed}$
|
---|
247 | Greek g = Alph; $\C{// allowed}$
|
---|
248 | l = Alph; $\C{// allowed, conversion to base type}$
|
---|
249 | g = A; $\C{// {\color{red}disallowed}}$
|
---|
250 | void foo( Letter );
|
---|
251 | foo( Beta ); $\C{// allowed, conversion to base type}$
|
---|
252 | void bar( Greek );
|
---|
253 | bar( A ); $\C{// {\color{red}disallowed}}$
|
---|
254 | \end{cfa}
|
---|
255 | Hence, @Letter@ enumerators are not type-compatible with the @Greek@ enumeration, but the reverse is true.
|
---|
256 |
|
---|
257 |
|
---|
258 | \section{Inheritance}
|
---|
259 | \label{s:CFAInheritance}
|
---|
260 |
|
---|
261 | \CFA Plan-9 inheritance may be used with \CFA enumerations, where Plan-9 inheritance is containment inheritance with implicit un-scoping (like a nested unnamed @struct@/@union@ in C).
|
---|
262 | Containment is nominative: an enumeration inherits all enumerators from another enumeration by declaring an @inline statement@ in its enumerator lists.
|
---|
263 | \begin{cfa}
|
---|
264 | enum( char * ) Names { /* $\see{\VRef[Figure]{f:EumeratorTyping}}$ */ };
|
---|
265 | enum( char * ) Names2 { @inline Names@, Jack = "JACK", Jill = "JILL" };
|
---|
266 | enum( char * ) Names3 { @inline Names2@, Sue = "SUE", Tom = "TOM" };
|
---|
267 | \end{cfa}
|
---|
268 | In the preceding example, @Names2@ is defined with five enumerators, three of which are from @Name@ through containment, and two are self-declared.
|
---|
269 | @Names3@ inherits all five members from @Names2@ and declares two additional enumerators.
|
---|
270 | Hence, enumeration inheritance forms a subset relationship.
|
---|
271 | Specifically, the inheritance relationship for the example above is:
|
---|
272 | \begin{cfa}
|
---|
273 | Names $\(\subset\)$ Names2 $\(\subset\)$ Names3 $\C{// enum type of Names}$
|
---|
274 | \end{cfa}
|
---|
275 |
|
---|
276 | Inheritance can be nested, and a \CFA enumeration can inline enumerators from more than one \CFA enumeration, forming a tree-like hierarchy.
|
---|
277 | However, the uniqueness of the enumeration name applies to enumerators, including those from supertypes, meaning an enumeration cannot name an enumerator with the same label as its subtype's members or inherits
|
---|
278 | from multiple enumeration that has overlapping enumerator labels. Consequently, a new type cannot inherit from an enumeration and its supertype or two enumerations with a
|
---|
279 | common supertype (the diamond problem) since such would unavoidably introduce duplicate enumerator labels.
|
---|
280 |
|
---|
281 | The base type must be consistent between subtype and supertype.
|
---|
282 | When an enumeration inherits enumerators from another enumeration, it copies the enumerators' @value@ and @label@, even if the @value@ is auto-initialized.
|
---|
283 | However, the underlying representation's position is the enumerator's order in the new enumeration.
|
---|
284 | \begin{cfa}
|
---|
285 | enum() E1 { B }; $\C{// B}$
|
---|
286 | enum() E2 { C, D }; $\C{// C D}$
|
---|
287 | enum() E3 { inline E1, inline E2, E }; $\C{// {\color{red}[\(_{E1}\)} B {\color{red}]} {\color{red}[\(_{E2}\)} C D {\color{red}]} E}$
|
---|
288 | 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}$
|
---|
289 | \end{cfa}
|
---|
290 | In this example, @B@ is at position 0 in @E1@ and @E3@, but position 1 in @E4@ as @A@ takes position 0 in @E4@.
|
---|
291 | @C@ is at position 0 in @E2@, 1 in @E3@, and 2 in @E4@.
|
---|
292 | @D@ is at position 1 in @E2@, 2 in @E3@, and 3 in @E4@.
|
---|
293 |
|
---|
294 | A subtype enumeration can be casted, or implicitly converted into its supertype, with a @safe@ cost, called \newterm{enumeration conversion}.
|
---|
295 | \begin{cfa}
|
---|
296 | enum E2 e2 = C;
|
---|
297 | posn( e2 ); $\C[1.75in]{// 0}$
|
---|
298 | enum E3 e3 = e2; $\C{// Assignment with enumeration conversion E2 to E3}$
|
---|
299 | posn( e2 ); $\C{// 1 cost}$
|
---|
300 | void foo( E3 e );
|
---|
301 | foo( e2 ); $\C{// Type compatible with enumeration conversion E2 to E3}$
|
---|
302 | posn( (E3)e2 ); $\C{// Explicit cast with enumeration conversion E2 to E3}$
|
---|
303 | E3 e31 = B; $\C{// No conversion: E3.B}$
|
---|
304 | posn( e31 ); $\C{// 0 cost}\CRT$
|
---|
305 | \end{cfa}
|
---|
306 | The last expression is unambiguous.
|
---|
307 | While both @E2.B@ and @E3.B@ are valid candidates, @E2.B@ has an associated safe cost and @E3.B@ needs no conversion (@zero@ cost).
|
---|
308 | \CFA selects the lowest cost candidate @E3.B@.
|
---|
309 |
|
---|
310 | For the given function prototypes, the following calls are valid.
|
---|
311 | \begin{cquote}
|
---|
312 | \begin{tabular}{ll}
|
---|
313 | \begin{cfa}
|
---|
314 | void f( Names );
|
---|
315 | void g( Names2 );
|
---|
316 | void h( Names3 );
|
---|
317 | void j( const char * );
|
---|
318 | \end{cfa}
|
---|
319 | &
|
---|
320 | \begin{cfa}
|
---|
321 | f( Fred );
|
---|
322 | g( Fred ); g( Jill );
|
---|
323 | h( Fred ); h( Jill ); h( Sue );
|
---|
324 | j( Fred ); j( Jill ); j( Sue ); j( "WILL" );
|
---|
325 | \end{cfa}
|
---|
326 | \end{tabular}
|
---|
327 | \end{cquote}
|
---|
328 | 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.
|
---|
329 |
|
---|
330 |
|
---|
331 | \subsection{Offset Calculation}
|
---|
332 |
|
---|
333 | As discussed in \VRef{s:OpaqueEnum}, \CFA chooses position as a representation of a \CFA enumeration variable.
|
---|
334 | When a cast or implicit conversion moves an enumeration from subtype to supertype, the position can be unchanged or increased.
|
---|
335 | \CFA determines the position offset with an \newterm{offset calculation} function.
|
---|
336 |
|
---|
337 | \begin{figure}
|
---|
338 | \begin{cfa}
|
---|
339 | struct Enumerator;
|
---|
340 | struct CFAEnum { vector<variant<CFAEnum, Enumerator>> members; string name; };
|
---|
341 | inline static bool operator==(CFAEnum& lhs, CFAEnum& rhs) { return lhs.name == rhs.name; }
|
---|
342 | pair<bool, int> calculateEnumOffset(CFAEnum src, CFAEnum dst) {
|
---|
343 | int offset = 0;
|
---|
344 | if ( src == dst ) return make_pair(true, 0);
|
---|
345 | for ( auto v : dst.members ) {
|
---|
346 | if ( holds_alternative<Enumerator>(v) ) {
|
---|
347 | offset++;
|
---|
348 | } else {
|
---|
349 | auto m = get<CFAEnum>(v);
|
---|
350 | if ( m == src ) @return@ make_pair( true, offset );
|
---|
351 | auto dist = calculateEnumOffset( src, m );
|
---|
352 | if ( dist.first ) {
|
---|
353 | @return@ make_pair( true, offset + dist.second );
|
---|
354 | } else {
|
---|
355 | offset += dist.second;
|
---|
356 | }
|
---|
357 | }
|
---|
358 | }
|
---|
359 | @return@ make_pair( false, offset );
|
---|
360 | }
|
---|
361 | \end{cfa}
|
---|
362 | \caption{Compute Offset from Subtype Enumeration to a Supertype}
|
---|
363 | \label{s:OffsetSubtypeSuperType}
|
---|
364 | \end{figure}
|
---|
365 |
|
---|
366 | Figure~\ref{s:OffsetSubtypeSuperType} shows an outline of the offset calculation in \CC.
|
---|
367 | Structure @CFAEnum@ represents the \CFA enumeration with a vector of variants of @CFAEnum@ or @Enumerator@.
|
---|
368 | The algorithm takes two @CFAEnums@ parameters, @src@ and @dst@, with @src@ being the type of expression the conversion applies to, and @dst@ being the type the expression is cast to.
|
---|
369 | The algorithm iterates over the members in @dst@ to find @src@.
|
---|
370 | If a member is an enumerator of @dst@, the positions of all subsequent members are incremented by one.
|
---|
371 | If the current member is @dst@, the function returns true, indicating \emph{found} and the accumulated offset.
|
---|
372 | Otherwise, the algorithm recurses into the current @CFAEnum@ @m@ to check if its @src@ is convertible to @m@.
|
---|
373 | If @src@ is convertible to the current member @m@, this means @src@ is a subtype-of-subtype of @dst@.
|
---|
374 | The offset between @src@ and @dst@ is the sum of the offset of @m@ in @dst@ and the offset of @src@ in @m@.
|
---|
375 | If @src@ is not a subtype of @m@, the loop continues but with the offset shifted by the size of @m@.
|
---|
376 | If the loop ends, than @src@ is not convertible to @dst@, and false is returned.
|
---|
377 |
|
---|
378 |
|
---|
379 | \section{Control Structures}
|
---|
380 |
|
---|
381 | Enumerators are used in various contexts.
|
---|
382 | In most programming languages, an enumerator is implicitly converted to its value (like a typed macro substitution).
|
---|
383 | However, in some contexts, enumerator synonyms and typed enumerations make this implicit conversion to value incorrect.
|
---|
384 | A programmer's intuition assumes an implicit conversion to position in these contexts.
|
---|
385 |
|
---|
386 | 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.
|
---|
387 | (For this discussion, ignore the fact that @case@ requires a compile-time constant.)
|
---|
388 | \begin{cfa}[belowskip=0pt]
|
---|
389 | enum Count { First, Second, Third, Fourth };
|
---|
390 | Count e;
|
---|
391 | \end{cfa}
|
---|
392 | \begin{cquote}
|
---|
393 | \setlength{\tabcolsep}{15pt}
|
---|
394 | \noindent
|
---|
395 | \begin{tabular}{@{}ll@{}}
|
---|
396 | \begin{cfa}[aboveskip=0pt]
|
---|
397 |
|
---|
398 | choose( e ) {
|
---|
399 | case @First@: ...;
|
---|
400 | case @Second@: ...;
|
---|
401 | case @Third@: ...;
|
---|
402 | case @Fourth@: ...;
|
---|
403 | }
|
---|
404 | \end{cfa}
|
---|
405 | &
|
---|
406 | \begin{cfa}[aboveskip=0pt]
|
---|
407 | // rewrite
|
---|
408 | choose( @value@( e ) ) {
|
---|
409 | case @value@( First ): ...;
|
---|
410 | case @value@( Second ): ...;
|
---|
411 | case @value@( Third ): ...;
|
---|
412 | case @value@( Fourth ): ...;
|
---|
413 | }
|
---|
414 | \end{cfa}
|
---|
415 | \end{tabular}
|
---|
416 | \end{cquote}
|
---|
417 | 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.
|
---|
418 | However, this implementation is fragile, \eg if the enumeration is changed to:
|
---|
419 | \begin{cfa}
|
---|
420 | enum Count { First, Second, Third @= First@, Fourth };
|
---|
421 | \end{cfa}
|
---|
422 | making @Third == First@ and @Fourth == Second@, causing a compilation error because of duplicate @case@ clauses.
|
---|
423 | To better match with programmer intuition, \CFA toggles between value and position semantics depending on the language context.
|
---|
424 | For conditional clauses and switch statements, \CFA uses the robust position implementation.
|
---|
425 | \begin{cfa}
|
---|
426 | if ( @posn@( e ) < posn( Third ) ) ...
|
---|
427 | choose( @posn@( e ) ) {
|
---|
428 | case @posn@( First ): ...;
|
---|
429 | case @posn@( Second ): ...;
|
---|
430 | case @posn@( Third ): ...;
|
---|
431 | case @posn@( Fourth ): ...;
|
---|
432 | }
|
---|
433 | \end{cfa}
|
---|
434 |
|
---|
435 | \CFA provides a special form of for-control for enumerating through an enumeration, where the range is a type.
|
---|
436 | \begin{cfa}
|
---|
437 | for ( cx; @Count@ ) { sout | cx | nonl; } sout | nl;
|
---|
438 | for ( cx; ~= Count ) { sout | cx | nonl; } sout | nl;
|
---|
439 | for ( cx; -~= Count ) { sout | cx | nonl; } sout | nl;
|
---|
440 | First Second Third Fourth
|
---|
441 | First Second Third Fourth
|
---|
442 | Fourth Third Second First
|
---|
443 | \end{cfa}
|
---|
444 | 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.
|
---|
445 | The prefix @+~=@ or @-~=@ iterate forward or backward through the inclusive enumeration range, where no prefix defaults to @+~=@.
|
---|
446 |
|
---|
447 | C has an idiom for @if@ and loop predicates of comparing the predicate result ``not equal to 0''.
|
---|
448 | \begin{cfa}
|
---|
449 | if ( x + y /* != 0 */ ) ...
|
---|
450 | while ( p /* != 0 */ ) ...
|
---|
451 | \end{cfa}
|
---|
452 | This idiom extends to enumerations because there is a boolean conversion regarding the enumeration value if and only if such a conversion is available.
|
---|
453 | For example, such a conversion exists for all numerical types (integral and floating-point).
|
---|
454 | It is possible to explicitly extend this idiom to any typed enumeration by overloading the @!=@ operator.
|
---|
455 | \begin{cfa}
|
---|
456 | bool ?!=?( Name n, zero_t ) { return n != Fred; }
|
---|
457 | Name n = Mary;
|
---|
458 | if ( n ) ... // result is true
|
---|
459 | \end{cfa}
|
---|
460 | Specialize meanings are also possible.
|
---|
461 | \begin{cfa}
|
---|
462 | enum(int) ErrorCode { Normal = 0, Slow = 1, Overheat = 1000, OutOfResource = 1001 };
|
---|
463 | bool ?!=?( ErrorCode ec, zero_t ) { return ec >= Overheat; }
|
---|
464 | ErrorCode code = ...;
|
---|
465 | if ( code ) { problem(); }
|
---|
466 | \end{cfa}
|
---|
467 |
|
---|
468 |
|
---|
469 | \section{Dimension}
|
---|
470 |
|
---|
471 | \VRef{s:EnumeratorTyping} introduces the harmonizing problem between an enumeration and secondary information.
|
---|
472 | When possible, using a typed enumeration for the secondary information is the best approach.
|
---|
473 | However, there are times when combining these two types is not possible.
|
---|
474 | For example, the secondary information might precede the enumeration and/or its type is needed directly to declare parameters of functions.
|
---|
475 | In these cases, having secondary arrays of the enumeration size are necessary.
|
---|
476 |
|
---|
477 | 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.
|
---|
478 | \begin{cfa}
|
---|
479 | enum E1 { A, B, C, N }; // possibly predefined
|
---|
480 | enum(int) E2 { A, B, C };
|
---|
481 | float H1[N] = { [A] :$\footnotemark$ 3.4, [B] : 7.1, [C] : 0.01 }; // C
|
---|
482 | float H2[@E2@] = { [A] : 3.4, [B] : 7.1, [C] : 0.01 }; // CFA
|
---|
483 | \end{cfa}
|
---|
484 | \footnotetext{C uses symbol \lstinline{'='} for designator initialization, but \CFA changes it to \lstinline{':'} because of problems with tuple syntax.}
|
---|
485 | This approach is also necessary for a predefined typed enumeration (unchangeable) when additional secondary information needs to be added.
|
---|
486 | 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.
|
---|
487 | This behaviour can be reverted by explicit overloading:
|
---|
488 | \begin{cfa}
|
---|
489 | float ?[?]( float * arr, E2 index ) { return arr[ value( index ) ]; }
|
---|
490 | \end{cfa}
|
---|
491 | While enumerator labels @A@, @B@ and @C@ are being defined twice in different enumerations, they are unambiguous within the context.
|
---|
492 | Designators in @H1@ are unambiguous becasue @E2@ has a @value@ cost to @int@, which is more expensive than @safe@ cost from C-Enum @E1@ to @int@.
|
---|
493 | Designators in @H2@ are resolved as @E2@ because when a \CFA enumeration type is being used as an array dimension, \CFA adds the enumeration type to the initializer's resolution context.
|
---|
494 |
|
---|
495 |
|
---|
496 | \section{I/O}
|
---|
497 |
|
---|
498 | As seen in multiple examples, \CFA enumerations can be printed and the default property printed is the enumerator's label, which is similar in other programming languages.
|
---|
499 | However, very few programming languages provide a mechanism to read in enumerator values.
|
---|
500 | Even the @boolean@ type in many languages does not have a mechanism for input using the enumerators @true@ or @false@.
|
---|
501 | \VRef[Figure]{f:EnumerationI/O} show \CFA enumeration input based on the enumerator labels.
|
---|
502 | When the enumerator labels are packed together in the input stream, the input algorithm scans for the longest matching string.
|
---|
503 | 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 string constants can be quoted or unquoted.
|
---|
504 |
|
---|
505 | \begin{figure}
|
---|
506 | \begin{cquote}
|
---|
507 | \setlength{\tabcolsep}{15pt}
|
---|
508 | \begin{tabular}{@{}ll@{}}
|
---|
509 | \begin{cfa}
|
---|
510 | int main() {
|
---|
511 | enum(int ) E { BBB = 3, AAA, AA, AB, B };
|
---|
512 | E e;
|
---|
513 |
|
---|
514 | try {
|
---|
515 | for () {
|
---|
516 | try {
|
---|
517 | @sin | e@;
|
---|
518 | } catch( missing_data * ) {
|
---|
519 | sout | "missing data";
|
---|
520 | continue; // try again
|
---|
521 | }
|
---|
522 | sout | e | "= " | value( e );
|
---|
523 | }
|
---|
524 | } catch( end_of_file ) {}
|
---|
525 | }
|
---|
526 | \end{cfa}
|
---|
527 | &
|
---|
528 | \begin{cfa}
|
---|
529 | $\rm input$
|
---|
530 | BBBABAAAAB
|
---|
531 | BBB AAA AA AB B
|
---|
532 |
|
---|
533 | $\rm output$
|
---|
534 | BBB = 3
|
---|
535 | AB = 6
|
---|
536 | AAA = 4
|
---|
537 | AB = 6
|
---|
538 | BBB = 3
|
---|
539 | AAA = 4
|
---|
540 | AA = 5
|
---|
541 | AB = 6
|
---|
542 | B = 7
|
---|
543 |
|
---|
544 | \end{cfa}
|
---|
545 | \end{tabular}
|
---|
546 | \end{cquote}
|
---|
547 | \caption{Enumeration I/O}
|
---|
548 | \label{f:EnumerationI/O}
|
---|
549 | \end{figure}
|
---|
550 |
|
---|
551 |
|
---|
552 | \section{Planet Example}
|
---|
553 |
|
---|
554 | \VRef[Figure]{f:PlanetExample} shows an archetypal enumeration example illustrating most of the \CFA enumeration features.
|
---|
555 | @Planet@ is an enumeration of type @MR@.
|
---|
556 | Each planet enumerator is initialized to a specific mass/radius, @MR@, value.
|
---|
557 | The unnamed enumeration provides the gravitational-constant enumerator @G@.
|
---|
558 | Function @surfaceGravity@ uses the @with@ clause to remove @p@ qualification from fields @mass@ and @radius@.
|
---|
559 | 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@.
|
---|
560 | The resulting random orbital-body is used in a @choose@ statement.
|
---|
561 | The enumerators in the @case@ clause use the enumerator position for testing.
|
---|
562 | The prints use @label@ to print an enumerator's name.
|
---|
563 | Finally, a loop enumerates through the planets computing the weight on each planet for a given Earth mass.
|
---|
564 | The print statement does an equality comparison with an enumeration variable and enumerator (@p == MOON@).
|
---|
565 |
|
---|
566 | \begin{figure}
|
---|
567 | \small
|
---|
568 | \begin{cfa}
|
---|
569 | struct MR { double mass, radius; }; $\C[3.5in]{// planet definition}$
|
---|
570 | enum( @MR@ ) Planet { $\C{// typed enumeration}$
|
---|
571 | // mass (kg) radius (km)
|
---|
572 | MERCURY = { 0.330_E24, 2.4397_E6 },
|
---|
573 | VENUS = { 4.869_E24, 6.0518_E6 },
|
---|
574 | EARTH = { 5.976_E24, 6.3781_E6 },
|
---|
575 | MOON = { 7.346_E22, 1.7380_E6 }, $\C{// not a planet}$
|
---|
576 | MARS = { 0.642_E24, 3.3972_E6 },
|
---|
577 | JUPITER = { 1898._E24, 71.492_E6 },
|
---|
578 | SATURN = { 568.8_E24, 60.268_E6 },
|
---|
579 | URANUS = { 86.86_E24, 25.559_E6 },
|
---|
580 | NEPTUNE = { 102.4_E24, 24.746_E6 },
|
---|
581 | PLUTO = { 1.303_E22, 1.1880_E6 }, $\C{// not a planet}$
|
---|
582 | };
|
---|
583 | enum( double ) { G = 6.6743_E-11 }; $\C{// universal gravitational constant (m3 kg-1 s-2)}$
|
---|
584 | static double surfaceGravity( Planet p ) @with( p )@ {
|
---|
585 | return G * mass / ( radius @\@ 2 ); $\C{// no qualification, exponentiation}$
|
---|
586 | }
|
---|
587 | static double surfaceWeight( Planet p, double otherMass ) {
|
---|
588 | return otherMass * surfaceGravity( p );
|
---|
589 | }
|
---|
590 | int main( int argc, char * argv[] ) {
|
---|
591 | if ( argc != 2 ) @exit@ | "Usage: " | argv[0] | "earth-weight"; // terminate program
|
---|
592 | double earthWeight = convert( argv[1] );
|
---|
593 | double earthMass = earthWeight / surfaceGravity( EARTH );
|
---|
594 | Planet rp = @fromInt@( prng( @countof@( Planet ) ) ); $\C{// select random orbiting body}$
|
---|
595 | @choose( rp )@ { $\C{// implicit breaks}$
|
---|
596 | case MERCURY, VENUS, EARTH, MARS:
|
---|
597 | sout | @rp@ | "is a rocky planet";
|
---|
598 | case JUPITER, SATURN, URANUS, NEPTUNE:
|
---|
599 | sout | rp | "is a gas-giant planet";
|
---|
600 | default:
|
---|
601 | sout | rp | "is not a planet";
|
---|
602 | }
|
---|
603 | for ( @p; Planet@ ) { $\C{// enumerate}\CRT$
|
---|
604 | sout | "Your weight on" | ( @p == MOON@ ? "the" : " " ) | p
|
---|
605 | | "is" | wd( 1,1, surfaceWeight( p, earthMass ) ) | "kg";
|
---|
606 | }
|
---|
607 | }
|
---|
608 | $\$$ planet 100
|
---|
609 | JUPITER is a gas-giant planet
|
---|
610 | Your weight on MERCURY is 37.7 kg
|
---|
611 | Your weight on VENUS is 90.5 kg
|
---|
612 | Your weight on EARTH is 100.0 kg
|
---|
613 | Your weight on the MOON is 16.6 kg
|
---|
614 | Your weight on MARS is 37.9 kg
|
---|
615 | Your weight on JUPITER is 252.8 kg
|
---|
616 | Your weight on SATURN is 106.6 kg
|
---|
617 | Your weight on URANUS is 90.5 kg
|
---|
618 | Your weight on NEPTUNE is 113.8 kg
|
---|
619 | Your weight on PLUTO is 6.3 kg
|
---|
620 | \end{cfa}
|
---|
621 | \caption{Planet Example}
|
---|
622 | \label{f:PlanetExample}
|
---|
623 | \end{figure}
|
---|