Index: doc/bibliography/cfa.bib
===================================================================
--- doc/bibliography/cfa.bib	(revision 4fc45ff2c77dee81fe985eb3414cbd30cf4778e7)
+++ doc/bibliography/cfa.bib	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -3057,4 +3057,14 @@
     pages	= {147-148},
     note	= {Reprinted in \cite{Yourdon79} pp. 29--36.},
+}
+
+@online{GObject,
+    keywords = {GObject},
+    contributor = {a3moss@uwaterloo.ca},
+    author = {{The GNOME Project}},
+    title = {{GObject} Reference Manual},
+    year = 2014,
+    url = {https://developer.gnome.org/gobject/stable/},
+    urldate = {2017-04-04}
 }
 
@@ -4553,4 +4563,22 @@
 }
 
+@book{obj-c-book,
+    keywords = {objective-c},
+    contributor = {a3moss@uwaterloo.ca},
+    author = {{Apple Computer Inc.}},
+    title = {The {Objective-C} Programming Language},
+    year = 2002
+}
+
+@online{xcode7,
+    keywords = {objective-c},
+    contributor = {a3moss@uwaterloo.ca},
+    author = {{Apple Computer Inc.}},
+    title = {{Xcode} 7 Release Notes},
+    year = 2015,
+    url = {https://developer.apple.com/library/content/documentation/Xcode/Conceptual/RN-Xcode-Archive/Chapters/xc7_release_notes.html},
+    urldate = {2017-04-04}
+}
+
 @book{Beta,
     keywords	= {Beta, object oriented, concurrency, exceptions},
@@ -6870,4 +6898,14 @@
 }
 
+@online{Vala,
+    keywords = {GObject, Vala},
+    contributor = {a3moss@uwaterloo.ca},
+    author = {{The GNOME Project}},
+    title = {Vala Reference Manual},
+    year = 2017,
+    url = {https://wiki.gnome.org/Projects/Vala/Manual},
+    urldate = {2017-04-04}
+}
+
 @inproceedings{Amdahl67,
     author	= {Gene M. Amdahl},
Index: doc/generic_types/evaluation/Makefile
===================================================================
--- doc/generic_types/evaluation/Makefile	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/Makefile	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,22 @@
+CFA=cfa
+
+# %.o : %.cf
+#	$(CFA) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+cfa-stack.o: cfa-stack.c cfa-stack.h
+	$(CFA) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+c-bench: c-bench.c bench.h c-stack.o
+	$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $< c-stack.o
+
+cpp-bench: cpp-bench.cpp bench.h cpp-stack.h
+	$(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $<
+
+cfa-bench: cfa-bench.c bench.h cfa-stack.o
+	$(CFA) $(CFLAGS) $(CPPFLAGS) -o $@ $< cfa-stack.o
+
+clean:
+	-rm *.o
+	-rm c-bench
+	-rm cpp-bench
+	-rm cfa-bench
Index: doc/generic_types/evaluation/bench.h
===================================================================
--- doc/generic_types/evaluation/bench.h	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/bench.h	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include <time.h>
+
+#define N 200000000
+
+long ms_between(clock_t start, clock_t end) {
+	return (end - start) / (CLOCKS_PER_SEC / 1000);
+}
+
+#define TIMED(name, code) { \
+	clock_t start, end; \
+	start = clock(); \
+	code \
+	end = clock(); \
+	printf("%s:\t%7ld ms\n", name, ms_between(start, end)); \
+}
+
+#define REPEAT_TIMED(name, code) TIMED( name, for (int i = 0; i < N; ++i) { code } )
Index: doc/generic_types/evaluation/c-bench.c
===================================================================
--- doc/generic_types/evaluation/c-bench.c	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/c-bench.c	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+#include "bench.h"
+#include "c-stack.h"
+
+int main(int argc, char** argv) {
+	srand(20171025);
+
+	struct stack s = new_stack();
+
+	REPEAT_TIMED( "push_int",
+		int* x = malloc(sizeof(int));
+		*x = rand();
+		push_stack(&s, x);
+	)
+
+	clear_stack(&s);
+}
Index: doc/generic_types/evaluation/c-stack.c
===================================================================
--- doc/generic_types/evaluation/c-stack.c	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/c-stack.c	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,39 @@
+#include <stdlib.h>
+#include "c-stack.h"
+
+struct stack_node {
+	void* value;
+	struct stack_node* next;
+};
+
+struct stack new_stack() {
+	return (struct stack){ NULL };
+}
+
+void clear_stack(struct stack* s) {
+	struct stack_node* next = s->head;
+	while ( next ) {
+		struct stack_node* crnt = next;
+		next = crnt->next;
+		free(crnt->value);
+		free(crnt);
+	}
+}
+
+_Bool stack_empty(const struct stack* s) {
+	return s->head == NULL;
+}
+
+void push_stack(struct stack* s, void* value) {
+	struct stack_node* n = malloc(sizeof(struct stack_node));
+	*n = (struct stack_node){ value, s->head };
+	s->head = n;
+}
+
+void* pop_stack(struct stack* s) {
+	struct stack_node* n = s->head;
+	s->head = n->next;
+	void* x = n->value;
+	free(n);
+	return x;
+}
Index: doc/generic_types/evaluation/c-stack.h
===================================================================
--- doc/generic_types/evaluation/c-stack.h	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/c-stack.h	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,15 @@
+struct stack_node;
+
+struct stack {
+	struct stack_node* head;
+};
+
+struct stack new_stack();
+
+void clear_stack(struct stack* s);
+
+_Bool stack_empty(const struct stack* s);
+
+void push_stack(struct stack* s, void* value);
+
+void* pop_stack(struct stack* s);
Index: doc/generic_types/evaluation/cfa-bench.c
===================================================================
--- doc/generic_types/evaluation/cfa-bench.c	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/cfa-bench.c	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,13 @@
+#include <stdlib.h>
+#include "bench.h"
+#include "cfa-stack.h"
+
+int main(int argc, char** argv) {
+	srand(20171025);
+
+	stack(int) s;
+
+	REPEAT_TIMED( "push_int",
+		push( &s, rand() );
+	)
+}
Index: doc/generic_types/evaluation/cfa-stack.c
===================================================================
--- doc/generic_types/evaluation/cfa-stack.c	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/cfa-stack.c	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,36 @@
+#include <stdlib>
+#include "cfa-stack.h"
+
+forall(otype T) struct stack_node {
+	T value;
+	stack_node(T)* next;
+};
+
+forall(otype T) void ?{}(stack(T)* s) {
+	?{}( s->head, 0 );
+}
+
+forall(otype T) void ^?{}(stack(T)* s) {
+	stack_node(T)* next = s->head;
+	while ( next ) {
+		stack_node(T)* crnt = next;
+		next = crnt->next;
+		delete(crnt);
+	}
+}
+
+forall(otype T) _Bool empty(const stack(T)* s) {
+	return s->head == 0;
+}
+
+forall(otype T) void push(stack(T)* s, T value) {
+	s->head = new( value, s->head );
+}
+
+forall(otype T) T pop(stack(T)* s) {
+	stack_node(T)* n = s->head;
+	s->head = n->next;
+	T x = n->value;
+	delete(n);
+	return x;
+}
Index: doc/generic_types/evaluation/cfa-stack.h
===================================================================
--- doc/generic_types/evaluation/cfa-stack.h	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/cfa-stack.h	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,15 @@
+forall(otype T) struct stack_node;
+
+forall(otype T) struct stack {
+	stack_node(T)* head;
+};
+
+forall(otype T) void ?{}(stack(T)* s);
+
+forall(otype T) void ^?{}(stack(T)* s);
+
+forall(otype T) _Bool empty(const stack(T)* s);
+
+forall(otype T) void push(stack(T)* s, T value);
+
+forall(otype T) T pop(stack(T)* s);
Index: doc/generic_types/evaluation/cpp-bench.cpp
===================================================================
--- doc/generic_types/evaluation/cpp-bench.cpp	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/cpp-bench.cpp	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,13 @@
+#include <stdlib.h>
+#include "bench.h"
+#include "cpp-stack.h"
+
+int main(int argc, char** argv) {
+	srand(20171025);
+
+	stack<int> s;
+
+	REPEAT_TIMED( "push_int",
+		s.push( rand() );
+	)
+}
Index: doc/generic_types/evaluation/cpp-stack.h
===================================================================
--- doc/generic_types/evaluation/cpp-stack.h	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
+++ doc/generic_types/evaluation/cpp-stack.h	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -0,0 +1,40 @@
+#include <utility>
+
+template<typename T> class stack {
+	struct node {
+		T value;
+		node* next;
+
+		node( T&& v, node* n ) : value(std::move(v)), next(n) {}
+	};
+	
+	node* head;
+
+public:
+	stack() : head(nullptr) {}
+
+	~stack() {
+		node* next = head;
+		while ( next ) {
+			node* crnt = next;
+			next = crnt->next;
+			delete crnt;
+		}
+	}
+
+	bool empty() const {
+		return head == nullptr;
+	}
+
+	void push(T&& value) {
+		head = new node{ std::move(value), head };
+	}
+
+	T pop() {
+		node* n = head;
+		head = n->next;
+		T x = std::move(n->value);
+		delete n;
+		return x;
+	}
+};
Index: doc/generic_types/generic_types.tex
===================================================================
--- doc/generic_types/generic_types.tex	(revision 4fc45ff2c77dee81fe985eb3414cbd30cf4778e7)
+++ doc/generic_types/generic_types.tex	(revision 8f5bf6da7cff3f5b4f5453aaaedc6793f02d7c64)
@@ -860,5 +860,5 @@
 Cyclone also provides capabilities for polymorphic functions and existential types~\citep{Grossman06}, similar in concept to \CFA's @forall@ functions and generic types. Cyclone existential types can include function pointers in a construct similar to a virtual function table, but these pointers must be explicitly initialized at some point in the code, a tedious and potentially error-prone process. Furthermore, Cyclone's polymorphic functions and types are restricted in that they may only abstract over types with the same layout and calling convention as @void*@, in practice only pointer types and @int@ - in \CFA terms, all Cyclone polymorphism must be dtype-static. This design provides the efficiency benefits discussed in Section~\ref{sec:generic-apps} for dtype-static polymorphism, but is more restrictive than \CFA's more general model.
 
-\TODO{Talk about GObject, other object-oriented frameworks for C (Objective-C)?}
+Apple's Objective-C \citep{obj-c-book} is another industrially successful set of extensions to C. The Objective-C language model is a fairly radical departure from C, adding object-orientation and message-passing. Objective-C implements variadic functions using the C @va_arg@ mechanism, and did not support type-checked generics until recently \citep{xcode7}, historically using less-efficient and more error-prone runtime checking of object types instead. The GObject framework \citep{GObject} also adds object-orientation with runtime type-checking and reference-counting garbage-collection to C; these are much more intrusive feature additions than those provided by \CFA, in addition to the runtime overhead of reference-counting. The Vala programming language \citep{Vala} compiles to GObject-based C, and so adds the burden of learning a separate language syntax to the aforementioned demerits of GObject as a modernization path for existing C code-bases.
 
 Go \citep{Go} and Rust \citep{Rust} are both modern, compiled languages with abstraction features similar to \CFA traits, \emph{interfaces} in Go and \emph{traits} in Rust. However, both languages represent dramatic departures from C in terms of language model, and neither has the same level of compatibility with C as \CFA. Go is a garbage-collected language, imposing the associated runtime overhead, and complicating foreign-function calls with the necessity of accounting for data transfer between the managed Go runtime and the unmanaged C runtime. Furthermore, while generic types and functions are available in Go, they are limited to a small fixed set provided by the compiler, with no language facility to define more. Rust is not garbage-collected, and thus has a lighter-weight runtime that is more easily interoperable with C. It also possesses much more powerful abstraction capabilities for writing generic code than Go. On the other hand, Rust's borrow-checker, while it does provide strong safety guarantees, is complex and difficult to learn, and imposes a distinctly idiomatic programming style on Rust. \CFA, with its more modest safety features, is significantly easier to port C code to, while maintaining the idiomatic style of the original source.
