| 1 | \chapter{\CFA Enumeration}
|
|---|
| 2 |
|
|---|
| 3 | % \CFA supports C enumeration using the same syntax and semantics for backwards compatibility.
|
|---|
| 4 | % \CFA also extends C-Style enumeration by adding a number of new features that bring enumerations inline with other modern programming languages.
|
|---|
| 5 | % Any enumeration extensions must be intuitive to C programmers both in syntax and semantics.
|
|---|
| 6 | % The following sections detail all of my new contributions to enumerations in \CFA.
|
|---|
| 7 | \CFA extends the enumeration declaration by parameterizing with a type (like a generic type).
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 | \begin{cfa}[caption={CFA Enum},captionpos=b,label={l:CFAEnum}]
|
|---|
| 11 | $\it enum$-specifier:
|
|---|
| 12 | enum @(type-specifier$\(_{opt}\)$)@ identifier$\(_{opt}\)$ { cfa-enumerator-list }
|
|---|
| 13 | enum @(type-specifier$\(_{opt}\)$)@ identifier$\(_{opt}\)$ { cfa-enumerator-list , }
|
|---|
| 14 | enum @(type-specifier$\(_{opt}\)$)@ identifier
|
|---|
| 15 | cfa-enumerator-list:
|
|---|
| 16 | cfa-enumerator
|
|---|
| 17 | cfa-enumerator, cfa-enumerator-list
|
|---|
| 18 | cfa-enumerator:
|
|---|
| 19 | enumeration-constant
|
|---|
| 20 | $\it inline$ identifier
|
|---|
| 21 | enumeration-constant = expression
|
|---|
| 22 | \end{cfa}
|
|---|
| 23 |
|
|---|
| 24 | A \newterm{\CFA enumeration}, or \newterm{\CFA enum}, has an optional type declaration in the bracket next to the @enum@ keyword.
|
|---|
| 25 | Without optional type declarations, the syntax defines \newterm{opaque enums}.
|
|---|
| 26 | Otherwise, \CFA enum with type declaration are \newterm{typed enums}.
|
|---|
| 27 |
|
|---|
| 28 | \section{Opaque Enum}
|
|---|
| 29 | \label{s:OpaqueEnum}
|
|---|
| 30 | Opaque enum is a special CFA enumeration type, where the internal representation is chosen by the compiler and hidden from users.
|
|---|
| 31 | Compared C enum, opaque enums are more restrictive in terms of typing, and cannot be implicitly converted to integers.
|
|---|
| 32 | Enumerators of opaque enum cannot have initializer. Declaring initializer in the body of opaque enum results in a compile time error.
|
|---|
| 33 | \begin{cfa}
|
|---|
| 34 | enum@()@ Planets { MERCURY, VENUS, EARTH, MARS, JUPITER, SATURN, URANUS, NEPTUNE };
|
|---|
| 35 |
|
|---|
| 36 | Planet p = URANUS;
|
|---|
| 37 | int i = VENUS; @// Error, VENUS cannot be converted into an integral type
|
|---|
| 38 | \end{cfa}
|
|---|
| 39 | % Each opaque enum has two @attributes@: @position@ and @label@. \CFA auto-generates @attribute functions@ @posn()@ and @label()@ for every \CFA enum to returns the respective attributes.
|
|---|
| 40 | Opaque enumerations have two defining properties: @label@ (name) and @order@ (position), exposed to users by predefined @attribute functions@ , with the following signatures:
|
|---|
| 41 | \begin{cfa}
|
|---|
| 42 | forall( E ) {
|
|---|
| 43 | unsigned posn(E e);
|
|---|
| 44 | const char * s label(E e);
|
|---|
| 45 | };
|
|---|
| 46 | \end{cfa}
|
|---|
| 47 | With polymorphic type parameter E being substituted by enumeration types such as @Planet@.
|
|---|
| 48 |
|
|---|
| 49 | \begin{cfa}
|
|---|
| 50 | unsigned i = posn(VENUS); // 1
|
|---|
| 51 | char * s = label(MARS); // "MARS"
|
|---|
| 52 | \end{cfa}
|
|---|
| 53 |
|
|---|
| 54 | \subsection{Representation}
|
|---|
| 55 | The underlying representation of \CFA enumeration object is its order, saved as an integral type. Therefore, the size of a \CFA enumeration is consistent with C enumeration.
|
|---|
| 56 | Attribute function @posn@ performs type substitution on an expression from \CFA type to integral type.
|
|---|
| 57 | Names of enumerators are stored in a global data structure, with @label@ maps \CFA enumeration object to corresponding data.
|
|---|
| 58 |
|
|---|
| 59 | \section{Typed Enum}
|
|---|
| 60 | \label{s:EnumeratorTyping}
|
|---|
| 61 |
|
|---|
| 62 | \CFA extends the enumeration declaration by parameterizing with a type (like a generic type), allowing enumerators to be assigned any values from the declared type.
|
|---|
| 63 | 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.
|
|---|
| 64 | Note, the synonyms @Liz@ and @Beth@ in the last declaration.
|
|---|
| 65 | 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@.
|
|---|
| 66 |
|
|---|
| 67 | \begin{figure}
|
|---|
| 68 | \begin{cfa}
|
|---|
| 69 | // integral
|
|---|
| 70 | enum( @char@ ) Currency { Dollar = '$\textdollar$', Cent = '$\textcent$', Yen = '$\textyen$', Pound = '$\textsterling$', Euro = 'E' };
|
|---|
| 71 | enum( @signed char@ ) srgb { Red = -1, Green = 0, Blue = 1 };
|
|---|
| 72 | enum( @long long int@ ) BigNum { X = 123_456_789_012_345, Y = 345_012_789_456_123 };
|
|---|
| 73 | // non-integral
|
|---|
| 74 | enum( @double@ ) Math { PI_2 = 1.570796, PI = 3.141597, E = 2.718282 };
|
|---|
| 75 | enum( @_Complex@ ) Plane { X = 1.5+3.4i, Y = 7+3i, Z = 0+0.5i };
|
|---|
| 76 | // pointer
|
|---|
| 77 | enum( @char *@ ) Name { Fred = "FRED", Mary = "MARY", Jane = "JANE" };
|
|---|
| 78 | int i, j, k;
|
|---|
| 79 | enum( @int *@ ) ptr { I = &i, J = &j, K = &k };
|
|---|
| 80 | enum( @int &@ ) ref { I = i, J = j, K = k };
|
|---|
| 81 | // tuple
|
|---|
| 82 | enum( @[int, int]@ ) { T = [ 1, 2 ] }; $\C{// new \CFA type}$
|
|---|
| 83 | // function
|
|---|
| 84 | void f() {...} void g() {...}
|
|---|
| 85 | enum( @void (*)()@ ) funs { F = f, G = g };
|
|---|
| 86 | // aggregate
|
|---|
| 87 | struct Person { char * name; int age, height; };
|
|---|
| 88 | @***@enum( @Person@ ) friends { @Liz@ = { "ELIZABETH", 22, 170 }, @Beth@ = Liz,
|
|---|
| 89 | Jon = { "JONATHAN", 35, 190 } };
|
|---|
| 90 | \end{cfa}
|
|---|
| 91 | \caption{Enumerator Typing}
|
|---|
| 92 | \label{f:EumeratorTyping}
|
|---|
| 93 | \end{figure}
|
|---|
| 94 |
|
|---|
| 95 | An advantage of the typed enumerations is eliminating the \emph{harmonizing} problem between an enumeration and companion data \see{\VRef{s:Usage}}:
|
|---|
| 96 | \begin{cfa}
|
|---|
| 97 | enum( char * ) integral_types {
|
|---|
| 98 | chr = "char", schar = "signed char", uschar = "unsigned char",
|
|---|
| 99 | sshort = "signed short int", ushort = "unsigned short int",
|
|---|
| 100 | sint = "signed int", usint = "unsigned int",
|
|---|
| 101 | ...
|
|---|
| 102 | };
|
|---|
| 103 | \end{cfa}
|
|---|
| 104 | 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.
|
|---|
| 105 |
|
|---|
| 106 | While the enumeration type can be any C aggregate, the aggregate's \CFA constructors are not used to evaluate an enumerator's value.
|
|---|
| 107 | \CFA enumeration constants are compile-time values (static);
|
|---|
| 108 | calling constructors happens at runtime (dynamic).
|
|---|
| 109 |
|
|---|
| 110 | @value@ is an @attribute@ that defined for typed enum along with position and label. @values@ of a typed enum are stored in a global array of declared typed, initialized with
|
|---|
| 111 | value of enumerator initializers. @value()@ functions maps an enum to an elements of the array.
|
|---|
| 112 |
|
|---|
| 113 |
|
|---|
| 114 | \subsection{Value Conversion}
|
|---|
| 115 | C has an implicit type conversion from an enumerator to its base type @int@.
|
|---|
| 116 | Correspondingly, \CFA has an implicit conversion from a typed enumerator to its base type, allowing typed enumeration to be seemlyless used as
|
|---|
| 117 | a value of its base type.
|
|---|
| 118 | \begin{cfa}
|
|---|
| 119 | char currency = Dollar;
|
|---|
| 120 | void foo( char * );
|
|---|
| 121 | foo( Fred );
|
|---|
| 122 | \end{cfa}
|
|---|
| 123 |
|
|---|
| 124 | % During the resolution of expression e with \CFA enumeration type, \CFA adds @value(e)@ as an additional candidate with an extra \newterm{value} cost.
|
|---|
| 125 | % For expression @char currency = Dollar@, the is no defined conversion from Dollar (\CFA enumeration) type to basic type and the conversion cost is @infinite@,
|
|---|
| 126 | % thus the only valid candidate is @value(Dollar)@.
|
|---|
| 127 | The implicit conversion induces a \newterm{value cost}, which is a new category in \CFA's conversion cost model to disambiguate function overloading over for both \CFA enumeration and its base type.
|
|---|
| 128 | \begin{cfa}
|
|---|
| 129 | void baz( char ch ); $\C{// (1)}$
|
|---|
| 130 | void baz( Currency cu ); $\C{// (2)}$
|
|---|
| 131 |
|
|---|
| 132 | baz( Cent );
|
|---|
| 133 | \end{cfa}
|
|---|
| 134 | While both baz are applicable to \CFA enumeration, using Cent as a char in @candiate (1)@ comes with a @value@ cost,
|
|---|
| 135 | while @candidate (2)@ has @zero@ cost. \CFA always choose a overloaded candidate implemented for a \CFA enumeration itself over a candidate applies on a base type.
|
|---|
| 136 |
|
|---|
| 137 | Value cost is defined to be a more significant factor than an @unsafe@ but weight less than @poly@.
|
|---|
| 138 | With @value@ being an additional category, the current \CFA conversion cost is a 8-tuple:
|
|---|
| 139 | @@(unsafe, value, poly, safe, sign, vars, specialization, reference)@@.
|
|---|
| 140 |
|
|---|
| 141 | \begin{cfa}
|
|---|
| 142 | void bar(int);
|
|---|
| 143 | enum(int) Month !{
|
|---|
| 144 | January=31, February=29, March=31, April=30, May=31, June-30,
|
|---|
| 145 | July=31, August=31, September=30, October=31, November=30, December=31
|
|---|
| 146 | };
|
|---|
| 147 |
|
|---|
| 148 | Month a = Februrary; $\C{// (1), with cost (0, 1, 0, 0, 0, 0, 0, 0)}$
|
|---|
| 149 | double a = 5.5; $\C{// (2), with cost (1, 0, 0, 0, 0, 0, 0, 0)}$
|
|---|
| 150 |
|
|---|
| 151 | bar(a);
|
|---|
| 152 | \end{cfa}
|
|---|
| 153 | In the previous example, candidate (1) has an value cost to parameter type int, with is lower than (2) as an unsafe conversion from double to int.
|
|---|
| 154 | \CFA chooses value cost over unsafe cost and therefore @a@ of @bar(a)@ is resolved as an @Month@.
|
|---|
| 155 |
|
|---|
| 156 | \begin{cfa}
|
|---|
| 157 | forall(T | @CfaEnum(T)@) void bar(T);
|
|---|
| 158 |
|
|---|
| 159 | bar(a); $\C{// (3), with cost (0, 0, 1, 0, 0, 0, 0, 0)}$
|
|---|
| 160 | \end{cfa}
|
|---|
| 161 | % @Value@ is designed to be less significant than @poly@ to allow function being generic over \CFA enumeration (see ~\ref{c:trait}).
|
|---|
| 162 | 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}.
|
|---|
| 163 | @Value@ is a being a more significant cost than @poly@ implies if a overloaeded function defined for @CfaEnum@ (and other generic type), \CFA always
|
|---|
| 164 | try to resolve it as a @CfaEnum@, rather to insert a @value@ conversion.
|
|---|
| 165 |
|
|---|
| 166 | \subsection{Explicit Conversion}
|
|---|
| 167 | Explicit conversion is allowed on \CFA enumeration to an integral type, in which case \CFA converts \CFA enumeration into its underlying representation,
|
|---|
| 168 | which is its @position@.
|
|---|
| 169 |
|
|---|
| 170 | \section{Auto Initialization}
|
|---|
| 171 |
|
|---|
| 172 | C auto-initialization works for the integral type @int@ with constant expressions.
|
|---|
| 173 | \begin{cfa}
|
|---|
| 174 | enum Alphabet ! {
|
|---|
| 175 | A = 'A', B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
|---|
| 176 | a = 'a', b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z
|
|---|
| 177 | };
|
|---|
| 178 | \end{cfa}
|
|---|
| 179 | The complexity of the constant expression depends on the level of runtime 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.
|
|---|
| 180 |
|
|---|
| 181 | % The notion of auto-initialization is generalized in \CFA enumertation E with base type T in the following way:
|
|---|
| 182 | When an enumerator @e@ does not have a initializer, if @e@ has enumeration type @E@ with base type @T@, \CFA auto-initialize @e@ with the following scheme:
|
|---|
| 183 | \begin{enumerate}
|
|---|
| 184 | % \item Enumerator e is the first enumerator of \CFA enumeration E with base type T. If e declares no no initializer, e is auto-initialized by the $zero\_t$ constructor of T.
|
|---|
| 185 | \item if e is first enumerator, e is initialized with T's @zero_t@.
|
|---|
| 186 | \item otherwise, if d is the enumerator defined just before e, with d has has been initialized with expression @l@ (@l@ can also be an auto-generated), e is initialized with @l++@.
|
|---|
| 187 | % \CFA reports a compile time error if T has no $zero\_t$ constructor.
|
|---|
| 188 | % Enumerator e is an enumerator of base-type T enumeration E that position i, where $i \neq 0$. And d is the enumerator with position @i-1@, e is auto-initialized with
|
|---|
| 189 | % the result of @value(d)++@. If operator @?++@ is not defined for type T, \CFA reports a compile time error.
|
|---|
| 190 |
|
|---|
| 191 | % Unfortunately, auto-initialization is not implemented because \CFA is only a transpiler, relying on generated C code to perform the detail work.
|
|---|
| 192 | % C does not have the equivalent of \CC \lstinline[language={[GNU]C++}]{constexpr}, and it is currently beyond the scope of the \CFA project to implement a complex runtime interpreter in the transpiler.
|
|---|
| 193 | % Nevertheless, the necessary language concepts exist to support this feature.
|
|---|
| 194 | \end{enumerate}
|
|---|
| 195 | while @?++( T )@ can be explicitly overloaded or implicitly overloaded with properly defined @one_t@ and @?+?(T, T)@.
|
|---|
| 196 |
|
|---|
| 197 | Unfortunately, auto-initialization with only constant expression is not enforced because \CFA is only a transpiler, relying on generated C code to perform the detail work.
|
|---|
| 198 | C does not have the equivalent of \CC \lstinline[language={[GNU]C++}]{constexpr}, and it is currently beyond the scope of the \CFA project to implement a complex runtime interpreter in the transpiler.
|
|---|
| 199 | Nevertheless, the necessary language concepts exist to support this feature.
|
|---|
| 200 |
|
|---|
| 201 | \section{Enumeration Inheritance}
|
|---|
| 202 |
|
|---|
| 203 | \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).
|
|---|
| 204 | Inheritance can be nested, and a \CFA enumeration can inline enumerators from more than one \CFA enumeration, forming a tree-like structure.
|
|---|
| 205 | Howver, the uniqueness of enumeration label applies to enumerators from supertypes, meaning an enumeration cannot name enumerator with the same label as its subtype's members, or inherits
|
|---|
| 206 | from multiple enumeration that has overlapping enumerator label. As a consequence, a new type cannot inherits both an enumeration and its supertype, or inherit two enumerations with a
|
|---|
| 207 | common supertype (the diamond problem), since such would unavoidably introduce duplicate enumerator labels.
|
|---|
| 208 |
|
|---|
| 209 | \begin{cfa}
|
|---|
| 210 | enum( char * ) Names { /* as above */ };
|
|---|
| 211 | enum( char * ) Names2 { @inline Names@, Jack = "JACK", Jill = "JILL" };
|
|---|
| 212 | enum( char * ) Names3 { @inline Names2@, Sue = "SUE", Tom = "TOM" };
|
|---|
| 213 | \end{cfa}
|
|---|
| 214 |
|
|---|
| 215 | % Enumeration @Name2@ inherits all the enumerators and their values from enumeration @Names@ by containment, and a @Names@ enumeration is a @subtype@ of enumeration @Name2@.
|
|---|
| 216 | % Note, that enumerators must be unique in inheritance but enumerator values may be repeated.
|
|---|
| 217 |
|
|---|
| 218 | @Names2@ is defined with five enumerators, three of which are from @Name@ through containment, and two are self-declared.
|
|---|
| 219 | @Names3@ inherits all five members from @Names2@ and declare two additional enumerators.
|
|---|
| 220 |
|
|---|
| 221 | % The enumeration type for the inheriting type must be the same as the inherited type;
|
|---|
| 222 | % hence the enumeration type may be omitted for the inheriting enumeration and it is inferred from the inherited enumeration, as for @Name3@.
|
|---|
| 223 | % When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important.
|
|---|
| 224 | Specifically, the inheritance relationship for @Names@ is:
|
|---|
| 225 | \begin{cfa}
|
|---|
| 226 | Names $\(\subset\)$ Names2 $\(\subset\)$ Names3 $\C{// enum type of Names}$
|
|---|
| 227 | \end{cfa}
|
|---|
| 228 |
|
|---|
| 229 | The enumeration type for the inheriting type must be the same as the inherited type.
|
|---|
| 230 | When an enumeration inherits enumerators from another enumeration, it copies the enumerators' @value@ and @label@, even if the @value@ was auto initialized. However, the @position@ as the underlying
|
|---|
| 231 | representation will be the order of the enumerator in new enumeration.
|
|---|
| 232 | % new enumeration @N@ copies all enumerators from @O@, including those @O@ obtains through inheritance. Enumerators inherited from @O@
|
|---|
| 233 | % keeps same @label@ and @value@, but @position@ may shift to the right if other enumerators or inline enumeration declared in prior of @inline A@.
|
|---|
| 234 | % hence the enumeration type may be omitted for the inheriting enumeration and it is inferred from the inherited enumeration, as for @Name3@.
|
|---|
| 235 | % When inheriting from integral types, automatic numbering may be used, so the inheritance placement left to right is important.
|
|---|
| 236 |
|
|---|
| 237 | \begin{cfa}
|
|---|
| 238 | enum() Phynchocephalia { Tuatara };
|
|---|
| 239 | enum() Squamata { Snake, Lizard };
|
|---|
| 240 | enum() Lepidosauromorpha { inline Phynchocephalia, inline Squamata, Kuehneosauridae };
|
|---|
| 241 | \end{cfa}
|
|---|
| 242 | Snake, for example, has the position 0 in Squamata, but 1 in Lepidosauromorpha as Tuatara inherited from Phynchocephalia is position 0 in Lepidosauromorpha.
|
|---|
| 243 |
|
|---|
| 244 | A subtype enumeration can be casted, or implicitly converted into its supertype, with a @safe@ cost.
|
|---|
| 245 | \begin{cfa}
|
|---|
| 246 | enum Squamata squamata_lizard = Lizard;
|
|---|
| 247 | posn(quamata_lizard); // 1
|
|---|
| 248 | enum Lepidosauromorpha lepidosauromorpha_lizard = squamata_lizard;
|
|---|
| 249 | posn(lepidosauromorpha_lizard); // 2
|
|---|
| 250 | void foo( Lepidosauromorpha l );
|
|---|
| 251 | foo( squamata_lizard );
|
|---|
| 252 | posn( (Lepidosauromorpha) squamata_lizard ); // 2
|
|---|
| 253 |
|
|---|
| 254 | Lepidosauromorpha s = Snake;
|
|---|
| 255 | \end{cfa}
|
|---|
| 256 | The last expression in the preceding example is umabigious. While both @Squamata.Snake@ and @Lepidosauromorpha.Snake@ are valid candidate, @Squamata.Snake@ has
|
|---|
| 257 | an associated safe cost and \CFA select the zero cost candidate @Lepidosauromorpha.Snake@.
|
|---|
| 258 |
|
|---|
| 259 | As discussed in \VRef{s:OpaqueEnum}, \CFA chooses position as a representation of \CFA enum. Conversion involves both change of typing
|
|---|
| 260 | and possibly @position@.
|
|---|
| 261 |
|
|---|
| 262 | When converting a subtype to a supertype, the position can only be a larger value. The difference between the position in subtype and in supertype is an "offset".
|
|---|
| 263 | \CFA runs a the following algorithm to determine the offset for an enumerator to a super type.
|
|---|
| 264 | % In a summary, \CFA loops over members (include enumerators and inline enums) of the supertype.
|
|---|
| 265 | % If the member is the matching enumerator, the algorithm returns its position.
|
|---|
| 266 | % If the member is a inline enumeration, the algorithm trys to find the enumerator in the inline enumeration. If success, it returns the position of enumerator in the inline enumeration, plus
|
|---|
| 267 | % the position in the current enumeration. Otherwises, it increase the offset by the size of inline enumeration.
|
|---|
| 268 |
|
|---|
| 269 | \begin{cfa}
|
|---|
| 270 | struct Enumerator;
|
|---|
| 271 | struct CFAEnum {
|
|---|
| 272 | vector<variant<CFAEnum, Enumerator>> members;
|
|---|
| 273 | };
|
|---|
| 274 | pair<bool, int> calculateEnumOffset( CFAEnum dst, Enumerator e ) {
|
|---|
| 275 | int offset = 0;
|
|---|
| 276 | for( auto v: dst.members ) {
|
|---|
| 277 | if ( v.holds_alternative<Enumerator>() ) {
|
|---|
| 278 | auto m = v.get<Enumerator>();
|
|---|
| 279 | if ( m == e ) return make_pair( true, 0 );
|
|---|
| 280 | offset++;
|
|---|
| 281 | } else {
|
|---|
| 282 | auto p = calculateEnumOffset( v, e );
|
|---|
| 283 | if ( p.first ) return make_pair( true, offset + p.second );
|
|---|
| 284 | offset += p.second;
|
|---|
| 285 | }
|
|---|
| 286 | }
|
|---|
| 287 | return make_pair( false, offset );
|
|---|
| 288 | }
|
|---|
| 289 | \end{cfa}
|
|---|
| 290 |
|
|---|
| 291 | % \begin{cfa}
|
|---|
| 292 | % Names fred = Name.Fred;
|
|---|
| 293 | % (Names2) fred; (Names3) fred; (Name3) Names.Jack; $\C{// cast to super type}$
|
|---|
| 294 | % Names2 fred2 = fred; Names3 fred3 = fred2; $\C{// assign to super type}$
|
|---|
| 295 | % \end{cfa}
|
|---|
| 296 | For the given function prototypes, the following calls are valid.
|
|---|
| 297 | \begin{cquote}
|
|---|
| 298 | \begin{tabular}{ll}
|
|---|
| 299 | \begin{cfa}
|
|---|
| 300 | void f( Names );
|
|---|
| 301 | void g( Names2 );
|
|---|
| 302 | void h( Names3 );
|
|---|
| 303 | void j( const char * );
|
|---|
| 304 | \end{cfa}
|
|---|
| 305 | &
|
|---|
| 306 | \begin{cfa}
|
|---|
| 307 | f( Fred );
|
|---|
| 308 | g( Fred ); g( Jill );
|
|---|
| 309 | h( Fred ); h( Jill ); h( Sue );
|
|---|
| 310 | j( Fred ); j( Jill ); j( Sue ); j( "WILL" );
|
|---|
| 311 | \end{cfa}
|
|---|
| 312 | \end{tabular}
|
|---|
| 313 | \end{cquote}
|
|---|
| 314 | 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.
|
|---|
| 315 |
|
|---|
| 316 | \section{Enumerator Control Structures}
|
|---|
| 317 |
|
|---|
| 318 | Enumerators can be used in multiple contexts.
|
|---|
| 319 | In most programming languages, an enumerator is implicitly converted to its value (like a typed macro substitution).
|
|---|
| 320 | However, enumerator synonyms and typed enumerations make this implicit conversion to value incorrect in some contexts.
|
|---|
| 321 | In these contexts, a programmer's intuition assumes an implicit conversion to position.
|
|---|
| 322 |
|
|---|
| 323 | 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.
|
|---|
| 324 | (For this discussion, ignore the fact that @case@ requires a compile-time constant.)
|
|---|
| 325 | \begin{cfa}[belowskip=0pt]
|
|---|
| 326 | enum Count { First, Second, Third, Fourth };
|
|---|
| 327 | Count e;
|
|---|
| 328 | \end{cfa}
|
|---|
| 329 | \begin{cquote}
|
|---|
| 330 | \setlength{\tabcolsep}{15pt}
|
|---|
| 331 | \noindent
|
|---|
| 332 | \begin{tabular}{@{}ll@{}}
|
|---|
| 333 | \begin{cfa}[aboveskip=0pt]
|
|---|
| 334 |
|
|---|
| 335 | choose( e ) {
|
|---|
| 336 | case @First@: ...;
|
|---|
| 337 | case @Second@: ...;
|
|---|
| 338 | case @Third@: ...;
|
|---|
| 339 | case @Fourth@: ...;
|
|---|
| 340 | }
|
|---|
| 341 | \end{cfa}
|
|---|
| 342 | &
|
|---|
| 343 | \begin{cfa}[aboveskip=0pt]
|
|---|
| 344 | // rewrite
|
|---|
| 345 | choose( @value@( e ) ) {
|
|---|
| 346 | case @value@( First ): ...;
|
|---|
| 347 | case @value@( Second ): ...;
|
|---|
| 348 | case @value@( Third ): ...;
|
|---|
| 349 | case @value@( Fourth ): ...;
|
|---|
| 350 | }
|
|---|
| 351 | \end{cfa}
|
|---|
| 352 | \end{tabular}
|
|---|
| 353 | \end{cquote}
|
|---|
| 354 | 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.
|
|---|
| 355 | However, this implementation is fragile, \eg if the enumeration is changed to:
|
|---|
| 356 | \begin{cfa}
|
|---|
| 357 | enum Count { First, Second, Third @= First@, Fourth };
|
|---|
| 358 | \end{cfa}
|
|---|
| 359 | making @Third == First@ and @Fourth == Second@, causing a compilation error because of duplicate @case@ clauses.
|
|---|
| 360 | To better match with programmer intuition, \CFA toggles between value and position semantics depending on the language context.
|
|---|
| 361 | For conditional clauses and switch statements, \CFA uses the robust position implementation.
|
|---|
| 362 | \begin{cfa}
|
|---|
| 363 | if ( @posn@( e ) < posn( Third ) ) ...
|
|---|
| 364 | choose( @posn@( e ) ) {
|
|---|
| 365 | case @posn@( First ): ...;
|
|---|
| 366 | case @posn@( Second ): ...;
|
|---|
| 367 | case @posn@( Third ): ...;
|
|---|
| 368 | case @posn@( Fourth ): ...;
|
|---|
| 369 | }
|
|---|
| 370 | \end{cfa}
|
|---|
| 371 |
|
|---|
| 372 | \CFA provides a special form of for-control for enumerating through an enumeration, where the range is a type.
|
|---|
| 373 | \begin{cfa}
|
|---|
| 374 | for ( cx; @Count@ ) { sout | cx | nonl; } sout | nl;
|
|---|
| 375 | for ( cx; +~= Count ) { sout | cx | nonl; } sout | nl;
|
|---|
| 376 | for ( cx; -~= Count ) { sout | cx | nonl; } sout | nl;
|
|---|
| 377 | First Second Third Fourth
|
|---|
| 378 | First Second Third Fourth
|
|---|
| 379 | Fourth Third Second First
|
|---|
| 380 | \end{cfa}
|
|---|
| 381 | 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.
|
|---|
| 382 | The prefix @+~=@ or @-~=@ iterate forward or backwards through the inclusive enumeration range, where no prefix defaults to @+~=@.
|
|---|
| 383 |
|
|---|
| 384 | C has an idiom for @if@ and loop predicates of comparing the predicate result ``not equal to 0''.
|
|---|
| 385 | \begin{cfa}
|
|---|
| 386 | if ( x + y /* != 0 */ ) ...
|
|---|
| 387 | while ( p /* != 0 */ ) ...
|
|---|
| 388 | \end{cfa}
|
|---|
| 389 | 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.
|
|---|
| 390 | For example, such a conversion exists for all numerical types (integral and floating-point).
|
|---|
| 391 | It is possible to explicitly extend this idiom to any typed enumeration by overloading the @!=@ operator.
|
|---|
| 392 | \begin{cfa}
|
|---|
| 393 | bool ?!=?( Name n, zero_t ) { return n != Fred; }
|
|---|
| 394 | Name n = Mary;
|
|---|
| 395 | if ( n ) ... // result is true
|
|---|
| 396 | \end{cfa}
|
|---|
| 397 | Specialize meanings are also possible.
|
|---|
| 398 | \begin{cfa}
|
|---|
| 399 | enum(int) ErrorCode { Normal = 0, Slow = 1, Overheat = 1000, OutOfResource = 1001 };
|
|---|
| 400 | bool ?!=?( ErrorCode ec, zero_t ) { return ec >= Overheat; }
|
|---|
| 401 | ErrorCode code = ...;
|
|---|
| 402 | if ( code ) { problem(); }
|
|---|
| 403 | \end{cfa}
|
|---|
| 404 |
|
|---|
| 405 |
|
|---|
| 406 | \section{Enumeration Dimension}
|
|---|
| 407 |
|
|---|
| 408 | \VRef{s:EnumeratorTyping} introduced the harmonizing problem between an enumeration and secondary information.
|
|---|
| 409 | When possible, using a typed enumeration for the secondary information is the best approach.
|
|---|
| 410 | However, there are times when combining these two types is not possible.
|
|---|
| 411 | For example, the secondary information might precede the enumeration and/or its type is needed directly to declare parameters of functions.
|
|---|
| 412 | In these cases, having secondary arrays of the enumeration size are necessary.
|
|---|
| 413 |
|
|---|
| 414 | 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.
|
|---|
| 415 | \begin{cfa}
|
|---|
| 416 | enum E { A, B, C, N }; // possibly predefined
|
|---|
| 417 | float H1[N] = { [A] : 3.4, [B] : 7.1, [C] : 0.01 }; // C
|
|---|
| 418 | float H2[@E@] = { [A] : 3.4, [B] : 7.1, [C] : 0.01 }; // CFA
|
|---|
| 419 | \end{cfa}
|
|---|
| 420 | (Note, C uses the symbol, @'='@ for designator initialization, but \CFA had to change to @':'@ because of problems with tuple syntax.)
|
|---|
| 421 | This approach is also necessary for a predefined typed enumeration (unchangeable), when additional secondary-information need to be added.
|
|---|
| 422 |
|
|---|
| 423 |
|
|---|
| 424 | \section{Enumeration I/O}
|
|---|
| 425 |
|
|---|
| 426 | 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.
|
|---|
| 427 | However, very few programming languages provide a mechanism to read in enumerator values.
|
|---|
| 428 | Even the @boolean@ type in many languages does not have a mechanism for input using the enumerators @true@ or @false@.
|
|---|
| 429 | \VRef[Figure]{f:EnumerationI/O} show \CFA enumeration input based on the enumerator labels.
|
|---|
| 430 | When the enumerator labels are packed together in the input stream, the input algorithm scans for the longest matching string.
|
|---|
| 431 | For basic types in \CFA, the constants use to initialize a variable in a program are available to initialize a variable using input, where strings constants can be quoted or unquoted.
|
|---|
| 432 |
|
|---|
| 433 | \begin{figure}
|
|---|
| 434 | \begin{cquote}
|
|---|
| 435 | \setlength{\tabcolsep}{15pt}
|
|---|
| 436 | \begin{tabular}{@{}ll@{}}
|
|---|
| 437 | \begin{cfa}
|
|---|
| 438 | int main() {
|
|---|
| 439 | enum(int ) E { BBB = 3, AAA, AA, AB, B };
|
|---|
| 440 | E e;
|
|---|
| 441 |
|
|---|
| 442 | for () {
|
|---|
| 443 | try {
|
|---|
| 444 | @sin | e@;
|
|---|
| 445 | } catch( missing_data * ) {
|
|---|
| 446 | sout | "missing data";
|
|---|
| 447 | continue; // try again
|
|---|
| 448 | }
|
|---|
| 449 | if ( eof( sin ) ) break;
|
|---|
| 450 | sout | e | "= " | value( e );
|
|---|
| 451 | }
|
|---|
| 452 | }
|
|---|
| 453 | \end{cfa}
|
|---|
| 454 | &
|
|---|
| 455 | \begin{cfa}
|
|---|
| 456 | $\rm input$
|
|---|
| 457 | BBBABAAAAB
|
|---|
| 458 | BBB AAA AA AB B
|
|---|
| 459 |
|
|---|
| 460 | $\rm output$
|
|---|
| 461 | BBB = 3
|
|---|
| 462 | AB = 6
|
|---|
| 463 | AAA = 4
|
|---|
| 464 | AB = 6
|
|---|
| 465 | BBB = 3
|
|---|
| 466 | AAA = 4
|
|---|
| 467 | AA = 5
|
|---|
| 468 | AB = 6
|
|---|
| 469 | B = 7
|
|---|
| 470 |
|
|---|
| 471 | \end{cfa}
|
|---|
| 472 | \end{tabular}
|
|---|
| 473 | \end{cquote}
|
|---|
| 474 | \caption{Enumeration I/O}
|
|---|
| 475 | \label{f:EnumerationI/O}
|
|---|
| 476 | \end{figure}
|
|---|
| 477 |
|
|---|
| 478 |
|
|---|
| 479 |
|
|---|
| 480 | \section{Planet Example}
|
|---|
| 481 |
|
|---|
| 482 | \VRef[Figure]{f:PlanetExample} shows an archetypal enumeration example illustrating most of the \CFA enumeration features.
|
|---|
| 483 | @Planet@ is an enumeration of type @MR@.
|
|---|
| 484 | Each planet enumerator is initialized to a specific mass/radius, @MR@, value.
|
|---|
| 485 | The unnamed enumeration provides the gravitational-constant enumerator @G@.
|
|---|
| 486 | Function @surfaceGravity@ uses the @with@ clause to remove @p@ qualification from fields @mass@ and @radius@.
|
|---|
| 487 | 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@.
|
|---|
| 488 | The resulting random orbital-body is used in a @choose@ statement.
|
|---|
| 489 | The enumerators in the @case@ clause use the enumerator position for testing.
|
|---|
| 490 | The prints use @label@ to print an enumerator's name.
|
|---|
| 491 | Finally, a loop enumerates through the planets computing the weight on each planet for a given earth mass.
|
|---|
| 492 | The print statement does an equality comparison with an enumeration variable and enumerator (@p == MOON@).
|
|---|
| 493 |
|
|---|
| 494 | \begin{figure}
|
|---|
| 495 | \small
|
|---|
| 496 | \begin{cfa}
|
|---|
| 497 | struct MR { double mass, radius; }; $\C{// planet definition}$
|
|---|
| 498 | enum( @MR@ ) Planet { $\C{// typed enumeration}$
|
|---|
| 499 | // mass (kg) radius (km)
|
|---|
| 500 | MERCURY = { 0.330_E24, 2.4397_E6 },
|
|---|
| 501 | VENUS = { 4.869_E24, 6.0518_E6 },
|
|---|
| 502 | EARTH = { 5.976_E24, 6.3781_E6 },
|
|---|
| 503 | MOON = { 7.346_E22, 1.7380_E6 }, $\C{// not a planet}$
|
|---|
| 504 | MARS = { 0.642_E24, 3.3972_E6 },
|
|---|
| 505 | JUPITER = { 1898._E24, 71.492_E6 },
|
|---|
| 506 | SATURN = { 568.8_E24, 60.268_E6 },
|
|---|
| 507 | URANUS = { 86.86_E24, 25.559_E6 },
|
|---|
| 508 | NEPTUNE = { 102.4_E24, 24.746_E6 },
|
|---|
| 509 | PLUTO = { 1.303_E22, 1.1880_E6 }, $\C{// not a planet}$
|
|---|
| 510 | };
|
|---|
| 511 | enum( double ) { G = 6.6743_E-11 }; $\C{// universal gravitational constant (m3 kg-1 s-2)}$
|
|---|
| 512 | static double surfaceGravity( Planet p ) @with( p )@ {
|
|---|
| 513 | return G * mass / ( radius @\@ 2 ); $\C{// no qualification, exponentiation}$
|
|---|
| 514 | }
|
|---|
| 515 | static double surfaceWeight( Planet p, double otherMass ) {
|
|---|
| 516 | return otherMass * surfaceGravity( p );
|
|---|
| 517 | }
|
|---|
| 518 | int main( int argc, char * argv[] ) {
|
|---|
| 519 | if ( argc != 2 ) @exit@ | "Usage: " | argv[0] | "earth-weight"; // terminate program
|
|---|
| 520 | double earthWeight = convert( argv[1] );
|
|---|
| 521 | double earthMass = earthWeight / surfaceGravity( EARTH );
|
|---|
| 522 | Planet rp = @fromInt@( prng( @countof@( Planet ) ) ); $\C{// select random orbiting body}$
|
|---|
| 523 | @choose( rp )@ { $\C{// implicit breaks}$
|
|---|
| 524 | case MERCURY, VENUS, EARTH, MARS:
|
|---|
| 525 | sout | @rp@ | "is a rocky planet";
|
|---|
| 526 | case JUPITER, SATURN, URANUS, NEPTUNE:
|
|---|
| 527 | sout | rp | "is a gas-giant planet";
|
|---|
| 528 | default:
|
|---|
| 529 | sout | rp | "is not a planet";
|
|---|
| 530 | }
|
|---|
| 531 | for ( @p; Planet@ ) { $\C{// enumerate}$
|
|---|
| 532 | sout | "Your weight on" | ( @p == MOON@ ? "the" : " " ) | p
|
|---|
| 533 | | "is" | wd( 1,1, surfaceWeight( p, earthMass ) ) | "kg";
|
|---|
| 534 | }
|
|---|
| 535 | }
|
|---|
| 536 | $\$$ planet 100
|
|---|
| 537 | JUPITER is a gas-giant planet
|
|---|
| 538 | Your weight on MERCURY is 37.7 kg
|
|---|
| 539 | Your weight on VENUS is 90.5 kg
|
|---|
| 540 | Your weight on EARTH is 100.0 kg
|
|---|
| 541 | Your weight on the MOON is 16.6 kg
|
|---|
| 542 | Your weight on MARS is 37.9 kg
|
|---|
| 543 | Your weight on JUPITER is 252.8 kg
|
|---|
| 544 | Your weight on SATURN is 106.6 kg
|
|---|
| 545 | Your weight on URANUS is 90.5 kg
|
|---|
| 546 | Your weight on NEPTUNE is 113.8 kg
|
|---|
| 547 | Your weight on PLUTO is 6.3 kg
|
|---|
| 548 | \end{cfa}
|
|---|
| 549 | \caption{Planet Example}
|
|---|
| 550 | \label{f:PlanetExample}
|
|---|
| 551 | \end{figure}
|
|---|