1 | % ====================================================================== |
---|
2 | % ====================================================================== |
---|
3 | \chapter{Introduction to \CFA}\label{s:cfa} |
---|
4 | % ====================================================================== |
---|
5 | % ====================================================================== |
---|
6 | |
---|
7 | \section{Overview} |
---|
8 | The following serves as an introduction to \CFA. \CFA is a layer over C, is transpiled to C and is largely considered to be an extension of C. Beyond C, it adds productivity features, libraries, a type system, and many other language constructions. However, \CFA stays true to C as a language, with most code revolving around \code{struct}'s and routines, and respects the same rules as C. \CFA is not object oriented as it has no notion of \code{this} and no classes or methods, but supports some object oriented adjacent ideas including costructors, destructors, and limited inheritance. \CFA is rich with interesting features, but a subset that is pertinent to this work will be discussed. |
---|
9 | |
---|
10 | \section{References} |
---|
11 | References in \CFA are similar to references in \CC, however in \CFA references are rebindable, and support multi-level referencing. References in \CFA are a layer of syntactic sugar over pointers to reduce the number of ref/deref operations needed with pointer usage. Some examples of references in \CFA are shown in Listing~\ref{l:cfa_ref}. Another related item to note is that the \CFA equivalent of \CC's \code{nullptr} is \code{0p}. |
---|
12 | |
---|
13 | \begin{cfacode}[tabsize=3,caption={Example of \CFA references},label={l:cfa_ref}] |
---|
14 | int i = 2; |
---|
15 | int & ref_i = i; // declare ref to i |
---|
16 | int * ptr_i = &i; // ptr to i |
---|
17 | |
---|
18 | // address of ref_i is the same as address of i |
---|
19 | assert( &ref_i == ptr_i ); |
---|
20 | |
---|
21 | int && ref_ref_i = ref_i; // can have a ref to a ref |
---|
22 | ref_i = 3; // set i to 3 |
---|
23 | int new_i = 4; |
---|
24 | |
---|
25 | // syntax to rebind ref_i (must cancel implicit deref) |
---|
26 | &ref_i = &new_i; // (&*)ref_i = &new_i; (sets underlying ptr) |
---|
27 | \end{cfacode} |
---|
28 | |
---|
29 | |
---|
30 | \section{Overloading} |
---|
31 | In \CFA routines can be overloaded on parameter type, number of parameters, and return type. Variables can also be overloaded on type, meaning that two variables can have the same name so long as they have different types. The variables will be disambiguated via type, sometimes requiring a cast. The code snippet in Listing~\ref{l:cfa_overload} contains examples of overloading. |
---|
32 | |
---|
33 | |
---|
34 | \begin{cfacode}[tabsize=3,caption={Example of \CFA function overloading},label={l:cfa_overload}] |
---|
35 | int foo() { printf("A\n"); return 0;} |
---|
36 | int foo( int bar ) { printf("B\n"); return 1; } |
---|
37 | int foo( double bar ) { printf("C\n"); return 2; } |
---|
38 | double foo( double bar ) { printf("D\n"); return 3;} |
---|
39 | void foo( double bar ) { printf("%.0f\n", bar); } |
---|
40 | |
---|
41 | int main() { |
---|
42 | foo(); // prints A |
---|
43 | foo( 0 ); // prints B |
---|
44 | int a = foo( 0.0 ); // prints C |
---|
45 | double a = foo( 0.0 ); // prints D |
---|
46 | foo( a ); // prints 3 |
---|
47 | } |
---|
48 | \end{cfacode} |
---|
49 | |
---|
50 | |
---|
51 | \section{With Statement} |
---|
52 | The with statement is a tool for exposing members of aggregate types within a scope in \CFA. It allows users to use fields of aggregate types without using their fully qualified name. This feature is also implemented in Pascal. It can exist as a stand-alone statement or it can be used on routines to expose fields in the body of the routine. An example is shown in Listing~\ref{l:cfa_with}. |
---|
53 | |
---|
54 | |
---|
55 | \begin{cfacode}[tabsize=3,caption={Usage of \CFA with statement},label={l:cfa_with}] |
---|
56 | struct obj { |
---|
57 | int a, b, c; |
---|
58 | }; |
---|
59 | struct pair { |
---|
60 | double x, y; |
---|
61 | }; |
---|
62 | |
---|
63 | // Stand-alone with stmt: |
---|
64 | pair p; |
---|
65 | with( p ) { |
---|
66 | x = 6.28; |
---|
67 | y = 1.73; |
---|
68 | } |
---|
69 | |
---|
70 | // Can be used on routines: |
---|
71 | void foo( obj o, pair p ) with( o, p ) { |
---|
72 | a = 1; |
---|
73 | b = 2; |
---|
74 | c = 3; |
---|
75 | x = 3.14; |
---|
76 | y = 2.71; |
---|
77 | } |
---|
78 | |
---|
79 | // routine foo is equivalent to routine bar: |
---|
80 | void bar( obj o, pair p ) { |
---|
81 | o.a = 1; |
---|
82 | o.b = 2; |
---|
83 | o.c = 3; |
---|
84 | p.x = 3.14; |
---|
85 | p.y = 2.71; |
---|
86 | } |
---|
87 | \end{cfacode} |
---|
88 | |
---|
89 | |
---|
90 | \section{Operators} |
---|
91 | Operators can be overloaded in \CFA with operator routines. Operators in \CFA are named using the operator symbol and '?' to respresent operands. |
---|
92 | An example is shown in Listing~\ref{l:cfa_operate}. |
---|
93 | |
---|
94 | |
---|
95 | \begin{cfacode}[tabsize=3,caption={Example of \CFA operators},label={l:cfa_operate}] |
---|
96 | struct coord { |
---|
97 | double x; |
---|
98 | double y; |
---|
99 | double z; |
---|
100 | }; |
---|
101 | coord ++?( coord & c ) with(c) { |
---|
102 | x++; |
---|
103 | y++; |
---|
104 | z++; |
---|
105 | return c; |
---|
106 | } |
---|
107 | coord ?<=?( coord op1, coord op2 ) with( op1 ) { |
---|
108 | return (x*x + y*y + z*z) <= |
---|
109 | (op2.x*op2.x + op2.y*op2.y + op2.z*op2.z); |
---|
110 | } |
---|
111 | \end{cfacode} |
---|
112 | |
---|
113 | |
---|
114 | \section{Constructors and Destructors} |
---|
115 | Constructors and destructors in \CFA are two special operator routines that are used for creation and destruction of objects. The default constructor and destructor for a type are called implicitly upon creation and deletion respectively if they are defined. An example is shown in Listing~\ref{l:cfa_ctor}. |
---|
116 | |
---|
117 | |
---|
118 | \begin{cfacode}[tabsize=3,caption={Example of \CFA constructors and destructors},label={l:cfa_ctor}] |
---|
119 | struct discrete_point { |
---|
120 | int x; |
---|
121 | int y; |
---|
122 | }; |
---|
123 | void ?{}( discrete_point & this ) with(this) { // ctor |
---|
124 | x = 0; |
---|
125 | y = 0; |
---|
126 | } |
---|
127 | void ?{}( discrete_point & this, int x, int y ) { // ctor |
---|
128 | this.x = x; |
---|
129 | this.y = y; |
---|
130 | } |
---|
131 | void ^?{}( discrete_point & this ) with(this) { // dtor |
---|
132 | x = 0; |
---|
133 | y = 0; |
---|
134 | } |
---|
135 | |
---|
136 | int main() { |
---|
137 | discrete_point d; // implicit call to ?{} |
---|
138 | discrete_point p{}; // same call as line above |
---|
139 | discrete_point dp{ 2, -4 }; // specialized ctor |
---|
140 | } // ^d{}, ^p{}, ^dp{} all called as they go out of scope |
---|
141 | \end{cfacode} |
---|
142 | |
---|
143 | |
---|
144 | \section{Polymorphism}\label{s:poly} |
---|
145 | C does not natively support polymorphism, and requires users to implement polymorphism themselves if they want to use it. \CFA extends C with two styles of polymorphism that it supports, parametric polymorphism and nominal inheritance. |
---|
146 | |
---|
147 | \subsection{Parametric Polymorphism} |
---|
148 | \CFA provides parametric polymorphism in the form of \code{forall}, and \code{trait}s. A \code{forall} takes in a set of types and a list of constraints. The declarations that follow the \code{forall} are parameterized over the types listed that satisfy the constraints. Sometimes the list of constraints can be long, which is where a \code{trait} can be used. A \code{trait} is a collection of constraints that is given a name and can be reused in foralls. An example of the usage of parametric polymorphism in \CFA is shown in Listing~\ref{l:cfa_poly}. |
---|
149 | |
---|
150 | \begin{cfacode}[tabsize=3,caption={Example of \CFA polymorphism},label={l:cfa_poly}] |
---|
151 | // sized() is a trait that means the type has a size |
---|
152 | forall( V & | sized(V) ) // type params for trait |
---|
153 | trait vector_space { |
---|
154 | V add( V, V ); // vector addition |
---|
155 | V scalar_mult( int, V ); // scalar multiplication |
---|
156 | |
---|
157 | // dtor and copy ctor needed in constraints to pass by copy |
---|
158 | void ?{}( V &, V & ); // copy ctor for return |
---|
159 | void ^?{}( V & ); // dtor |
---|
160 | }; |
---|
161 | |
---|
162 | forall( V & | vector_space( V )) { |
---|
163 | V get_inverse( V v1 ) { |
---|
164 | return scalar_mult( -1, v1 ); // can use ?*? routine defined in trait |
---|
165 | } |
---|
166 | V add_and_invert( V v1, V v2 ) { |
---|
167 | return get_inverse( add( v1, v2 ) ); // can use ?*? routine defined in trait |
---|
168 | } |
---|
169 | } |
---|
170 | struct Vec1 { int x; }; |
---|
171 | void ?{}( Vec1 & this, Vec1 & other ) { this.x = other.x; } |
---|
172 | void ?{}( Vec1 & this, int x ) { this.x = x; } |
---|
173 | void ^?{}( Vec1 & this ) {} |
---|
174 | Vec1 add( Vec1 v1, Vec1 v2 ) { v1.x += v2.x; return v1; } |
---|
175 | Vec1 scalar_mult( int c, Vec1 v1 ) { v1.x = v1.x * c; return v1; } |
---|
176 | |
---|
177 | struct Vec2 { int x; int y; }; |
---|
178 | void ?{}( Vec2 & this, Vec2 & other ) { this.x = other.x; this.y = other.y; } |
---|
179 | void ?{}( Vec2 & this, int x ) { this.x = x; this.y = x; } |
---|
180 | void ^?{}( Vec2 & this ) {} |
---|
181 | Vec2 add( Vec2 v1, Vec2 v2 ) { v1.x += v2.x; v1.y += v2.y; return v1; } |
---|
182 | Vec2 scalar_mult( int c, Vec2 v1 ) { v1.x = v1.x * c; v1.y = v1.y * c; return v1; } |
---|
183 | |
---|
184 | int main() { |
---|
185 | Vec1 v1{ 1 }; // create Vec1 and call ctor |
---|
186 | Vec2 v2{ 2 }; // create Vec2 and call ctor |
---|
187 | |
---|
188 | // can use forall defined routines since types satisfy trait |
---|
189 | add_and_invert( get_inverse( v1 ), v1 ); |
---|
190 | add_and_invert( get_inverse( v2 ), v2 ); |
---|
191 | } |
---|
192 | |
---|
193 | \end{cfacode} |
---|
194 | |
---|
195 | \subsection{Inheritance} |
---|
196 | Inheritance in \CFA copies its style from Plan-9 C nominal inheritance. In \CFA structs can \code{inline} another struct type to gain its fields and to be able to be passed to routines that require a parameter of the inlined type. An example of \CFA inheritance is shown in Listing~\ref{l:cfa_inherit}. |
---|
197 | |
---|
198 | \begin{cfacode}[tabsize=3,caption={Example of \CFA inheritance},label={l:cfa_inherit}] |
---|
199 | struct one_d { double x; }; |
---|
200 | struct two_d { |
---|
201 | inline one_d; |
---|
202 | double y; |
---|
203 | }; |
---|
204 | struct three_d { |
---|
205 | inline two_d; |
---|
206 | double z; |
---|
207 | }; |
---|
208 | double get_x( one_d & d ){ return d.x; } |
---|
209 | |
---|
210 | struct dog {}; |
---|
211 | struct dog_food { |
---|
212 | int count; |
---|
213 | }; |
---|
214 | struct pet { |
---|
215 | inline dog; |
---|
216 | inline dog_food; |
---|
217 | }; |
---|
218 | void pet_dog( dog & d ){printf("woof\n");} |
---|
219 | void print_food( dog_food & f ){printf("%d\n", f.count);} |
---|
220 | |
---|
221 | int main() { |
---|
222 | one_d x; |
---|
223 | two_d y; |
---|
224 | three_d z; |
---|
225 | x.x = 1; |
---|
226 | y.x = 2; |
---|
227 | z.x = 3; |
---|
228 | get_x( x ); // returns 1; |
---|
229 | get_x( y ); // returns 2; |
---|
230 | get_x( z ); // returns 3; |
---|
231 | pet p; |
---|
232 | p.count = 5; |
---|
233 | pet_dog( p ); // prints woof |
---|
234 | print_food( p ); // prints 5 |
---|
235 | } |
---|
236 | \end{cfacode} |
---|
237 | |
---|
238 | |
---|