Index: doc/generic_types/evaluation/.gitignore
===================================================================
--- doc/generic_types/evaluation/.gitignore	(revision d919f471068ed87347669886083a44de531c1614)
+++ doc/generic_types/evaluation/.gitignore	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -1,8 +1,6 @@
 c-bench
 cpp-bench
+cpp-vbench
 cfa-bench
-c2-bench
-cpp2-bench
-cfa2-bench
 *.o
 *.d
Index: doc/generic_types/evaluation/Makefile
===================================================================
--- doc/generic_types/evaluation/Makefile	(revision d919f471068ed87347669886083a44de531c1614)
+++ doc/generic_types/evaluation/Makefile	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -1,9 +1,8 @@
 CFA = my-cfa
 DEPFLAGS = -MMD -MP
-OPT = -O2
-CFLAGS = $(OPT)
-CXXFLAGS = $(OPT)
+CFLAGS = -O2 -flto
+CXXFLAGS = $(CFLAGS) --std=c++14
 
-.PHONY: all clean distclean bench
+.PHONY: all clean distclean run-c run-cpp run-cfa run
 
 all: c-bench cpp-bench cfa-bench
@@ -28,4 +27,5 @@
 COBJS = c-stack.o
 CPPOBJS = 
+CPPVOBJS = cpp-vstack.o
 CFAOBJS = cfa-stack.o
 
@@ -36,4 +36,7 @@
 	$(COMPILE.cpp) -o $@ $< $(CPPOBJS) $(LDFLAGS)
 
+cpp-vbench: cpp-vbench.cpp cpp-vbench.d $(CPPVOBJS)
+	$(COMPILE.cpp) -o $@ $< $(CPPVOBJS) $(LDFLAGS)
+
 cfa-bench: cfa-bench.c cfa-bench.d $(CFAOBJS)
 	$(COMPILE.cfa) -o $@ $< $(CFAOBJS) $(LDFLAGS)
@@ -42,4 +45,5 @@
 	-rm $(COBJS) c-bench
 	-rm $(CPPOBJS) cpp-bench
+	-rm $(CPPVOBJS) cpp-vbench
 	-rm $(CFAOBJS) cfa-bench
 
@@ -47,13 +51,32 @@
 	-rm $(COBJS:.o=.d) c-bench.d
 	-rm $(CPPOBJS:.o=.d) cpp-bench.d
+	-rm $(CPPVOBJS:.o=.d) cpp-vbench.d
 	-rm $(CFAOBJS:.o=.d) cfa-bench.d
 
-bench: c-bench cpp-bench cfa-bench
+run-c: c-bench
 	@echo '## C ##'
 	@./c-bench
+	@printf 'source_size:\t%7d lines\n' `cat c-bench.c bench.h c-stack.h c-stack.c | wc -l`
+	@printf 'binary_size:\t%7d bytes\n' `wc -c < c-bench`
+
+run-cfa: cfa-bench
+	@echo '## Cforall ##'
+	@./cfa-bench
+	@printf 'source_size:\t%7d lines\n' `cat cfa-bench.c bench.h cfa-stack.h cfa-stack.c | wc -l`
+	@printf 'binary_size:\t%7d bytes\n' `wc -c < cfa-bench`
+
+run-cpp: cpp-bench
 	@echo '## C++ ##'
 	@./cpp-bench
-	@echo '## Cforall ##'
-	@./cfa-bench
+	@printf 'source_size:\t%7d lines\n' `cat cpp-bench.cpp bench.hpp cpp-stack.hpp | wc -l`
+	@printf 'binary_size:\t%7d bytes\n' `wc -c < cpp-bench`
+
+run-cppv: cpp-vbench
+	@echo '## C++ virtual ##'
+	@./cpp-vbench
+	@printf 'source_size:\t%7d lines\n' `cat cpp-vbench.cpp bench.hpp object.hpp cpp-vstack.hpp cpp-vstack.cpp | wc -l`
+	@printf 'binary_size:\t%7d bytes\n' `wc -c < cpp-vbench`
+
+run: run-c run-cfa run-cpp run-cppv
 
 # so make doesn't fail without dependency files
Index: doc/generic_types/evaluation/bench.h
===================================================================
--- doc/generic_types/evaluation/bench.h	(revision d919f471068ed87347669886083a44de531c1614)
+++ doc/generic_types/evaluation/bench.h	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -4,5 +4,5 @@
 #include <time.h>
 
- #define N 50000000
+ #define N 100000000
  
 
@@ -12,5 +12,5 @@
 
 #define TIMED(name, code) { \
-	clock_t _start, _end; \
+	volatile clock_t _start, _end; \
 	_start = clock(); \
 	code \
Index: doc/generic_types/evaluation/bench.hpp
===================================================================
--- doc/generic_types/evaluation/bench.hpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
+++ doc/generic_types/evaluation/bench.hpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <iomanip>
+#include <iostream>
+#include <time.h>
+
+ #define N 100000000
+ 
+
+long ms_between(clock_t start, clock_t end) {
+	return (end - start) / (CLOCKS_PER_SEC / 1000);
+}
+
+#define TIMED(name, code) { \
+	volatile clock_t _start, _end; \
+	_start = clock(); \
+	code \
+	_end = clock(); \
+	std::cout << name << ":\t" << std::setw(7) << ms_between(_start, _end) << std::setw(0) << " ms" << std::endl; \
+}
+
+#define REPEAT_TIMED(name, code) TIMED( name, for (int _i = 0; _i < N; ++_i) { code } )
Index: doc/generic_types/evaluation/cpp-bench.cpp
===================================================================
--- doc/generic_types/evaluation/cpp-bench.cpp	(revision d919f471068ed87347669886083a44de531c1614)
+++ doc/generic_types/evaluation/cpp-bench.cpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -1,5 +1,5 @@
 #include <stdlib.h>
-#include "bench.h"
-#include "cpp-stack.h"
+#include "bench.hpp"
+#include "cpp-stack.hpp"
 
 int main(int argc, char** argv) {
Index: c/generic_types/evaluation/cpp-stack.h
===================================================================
--- doc/generic_types/evaluation/cpp-stack.h	(revision d919f471068ed87347669886083a44de531c1614)
+++ 	(revision )
@@ -1,74 +1,0 @@
-#pragma once
-
-#include <utility>
-
-template<typename T> class stack {
-	struct node {
-		T value;
-		node* next;
-
-		node( T& v ) : value(v), next(nullptr) {}
-		node( T&& v, node* n ) : value(std::move(v)), next(n) {}
-	};
-	
-	node* head;
-
-	void copy(const stack<T>& o) {
-		node** crnt = &head;
-		node* next = o.head;
-		while ( next ) {
-			*crnt = new node{ next->value };
-			crnt = &(*crnt)->next;
-			next = next->next;
-		}
-		*crnt = nullptr;
-	}
-
-public:
-	void clear() {
-		node* next = head;
-		while ( next ) {
-			node* crnt = next;
-			next = crnt->next;
-			delete crnt;
-		}
-	}
-
-	stack() : head(nullptr) {}
-
-	stack(const stack<T>& o) { copy(o); }
-
-	stack(stack<T>&& o) : head(o.head) { o.head = nullptr; }
-
-	~stack() { clear(); }
-
-	stack& operator= (const stack<T>& o) {
-		if ( this == &o ) return *this;
-		clear();
-		copy(o);
-		return *this;if ( this == &o ) return *this;
-	}
-
-	stack& operator= (stack<T>&& o) {
-		if ( this == &o ) return *this;
-		head = o.head;
-		o.head = nullptr;
-		return *this;
-	}
-
-	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/evaluation/cpp-stack.hpp
===================================================================
--- doc/generic_types/evaluation/cpp-stack.hpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
+++ doc/generic_types/evaluation/cpp-stack.hpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -0,0 +1,74 @@
+#pragma once
+
+#include <utility>
+
+template<typename T> class stack {
+	struct node {
+		T value;
+		node* next;
+
+		node( T& v ) : value(v), next(nullptr) {}
+		node( T&& v, node* n ) : value(std::move(v)), next(n) {}
+	};
+	
+	node* head;
+
+	void copy(const stack<T>& o) {
+		node** crnt = &head;
+		node* next = o.head;
+		while ( next ) {
+			*crnt = new node{ next->value };
+			crnt = &(*crnt)->next;
+			next = next->next;
+		}
+		*crnt = nullptr;
+	}
+
+public:
+	void clear() {
+		node* next = head;
+		while ( next ) {
+			node* crnt = next;
+			next = crnt->next;
+			delete crnt;
+		}
+	}
+
+	stack() : head(nullptr) {}
+
+	stack(const stack<T>& o) { copy(o); }
+
+	stack(stack<T>&& o) : head(o.head) { o.head = nullptr; }
+
+	~stack() { clear(); }
+
+	stack& operator= (const stack<T>& o) {
+		if ( this == &o ) return *this;
+		clear();
+		copy(o);
+		return *this;
+	}
+
+	stack& operator= (stack<T>&& o) {
+		if ( this == &o ) return *this;
+		head = o.head;
+		o.head = nullptr;
+		return *this;
+	}
+
+	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/evaluation/cpp-vbench.cpp
===================================================================
--- doc/generic_types/evaluation/cpp-vbench.cpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
+++ doc/generic_types/evaluation/cpp-vbench.cpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+#include "bench.hpp"
+#include "cpp-vstack.hpp"
+
+int main(int argc, char** argv) {
+	srand(20171025);
+
+	stack s;
+	REPEAT_TIMED( "push_int",
+		s.push( std::make_unique<integer>( rand() ) );
+	)
+
+	stack t;
+	TIMED( "copy_int", 
+		t = s;
+	)
+
+	TIMED( "clear_int", 
+		s.clear();
+	)
+
+	integer sum;
+	REPEAT_TIMED( "pop_int",
+		sum += t.pop()->as<integer>();
+	)
+}
Index: doc/generic_types/evaluation/cpp-vstack.cpp
===================================================================
--- doc/generic_types/evaluation/cpp-vstack.cpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
+++ doc/generic_types/evaluation/cpp-vstack.cpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -0,0 +1,65 @@
+#include "cpp-vstack.hpp"
+
+#include <utility>
+
+stack::node::node( const object& v ) : value( v.new_copy() ), next( nullptr ) {}
+
+stack::node::node( std::unique_ptr<object>&& v, node* n ) : value( std::move(v) ), next( n ) {}
+
+void stack::copy(const stack& o) {
+	node** crnt = &head;
+	node* next = o.head;
+	while ( next ) {
+		*crnt = new node{ *next->value };
+		crnt = &(*crnt)->next;
+		next = next->next;
+	}
+	*crnt = nullptr;
+}
+
+void stack::clear() {
+	node* next = head;
+	while ( next ) {
+		node* crnt = next;
+		next = crnt->next;
+		delete crnt;
+	}
+}
+
+stack::stack() : head(nullptr) {}
+
+stack::stack(const stack& o) { copy(o); }
+
+stack::stack(stack&& o) : head(o.head) { o.head = nullptr; }
+
+stack::~stack() { clear(); }
+
+stack& stack::operator= (const stack& o) {
+	if ( this == &o ) return *this;
+	clear();
+	copy(o);
+	return *this;
+}
+
+stack& stack::operator= (stack&& o) {
+	if ( this == &o ) return *this;
+	head = o.head;
+	o.head = nullptr;
+	return *this;
+}
+
+bool stack::empty() const {
+	return head == nullptr;
+}
+
+void stack::push(std::unique_ptr<object>&& value) {
+	head = new node{ std::move(value), head };
+}
+
+std::unique_ptr<object> stack::pop() {
+	node* n = head;
+	head = n->next;
+	std::unique_ptr<object> x = std::move(n->value);
+	delete n;
+	return x;
+}
Index: doc/generic_types/evaluation/cpp-vstack.hpp
===================================================================
--- doc/generic_types/evaluation/cpp-vstack.hpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
+++ doc/generic_types/evaluation/cpp-vstack.hpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "object.hpp"
+
+class stack {
+	struct node {
+		std::unique_ptr<object> value;
+		node* next;
+
+		node( const object& v );
+
+		node( std::unique_ptr<object>&& v, node* n );
+	};
+
+	node* head;
+
+	void copy(const stack& o);
+
+public:
+	void clear();
+
+	stack();
+
+	stack(const stack& o);
+
+	stack(stack&& o);
+
+	~stack();
+
+	stack& operator= (const stack& o);
+	
+	stack& operator= (stack&& o);
+
+	bool empty() const;
+
+	void push(std::unique_ptr<object>&& value);
+
+	std::unique_ptr<object> pop();
+};
Index: doc/generic_types/evaluation/object.hpp
===================================================================
--- doc/generic_types/evaluation/object.hpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
+++ doc/generic_types/evaluation/object.hpp	(revision b276be54f19d3e800f3e97e609d486c6f53f3bf5)
@@ -0,0 +1,78 @@
+#pragma once
+
+#include <exception>
+#include <memory>
+#include <string>
+#include <typeinfo>
+#include <typeindex>
+
+class bad_cast : public std::exception {
+	std::string why;
+public:
+	bad_cast( const std::type_index& f, const std::type_index& t ) : std::exception() {
+		why = std::string{"bad cast of "} + f.name() + " to " + t.name();
+	}
+
+	~bad_cast() override = default;
+	
+	const char* what() const noexcept override { return why.c_str(); }
+};
+
+class object {
+public:
+	std::type_index get_class() const { return { typeid(*this) }; }
+
+	template<typename T>
+	T& as() {
+		std::type_index from = get_class(), to = { typeid(T) };
+		if ( from != to ) throw bad_cast{ from, to };
+		return reinterpret_cast<T&>(*this);
+	}
+
+	template<typename T>
+	const T& as() const {
+		std::type_index from = get_class(), to = { typeid(T) };
+		if ( from != to ) throw bad_cast{ from, to };
+		return reinterpret_cast<const T&>(*this);
+	}
+
+	virtual std::unique_ptr<object> new_inst() const = 0;
+	
+	virtual std::unique_ptr<object> new_copy() const = 0;
+	
+	virtual object& operator= (const object&) = 0;
+	
+	virtual ~object() = default;
+};
+
+class integer : public object {
+private:
+	int x;
+
+public:
+	integer() = default;
+
+	integer(int x) : x(x) {}
+
+	std::unique_ptr<object> new_inst() const override { return std::make_unique<integer>(); }
+	
+	std::unique_ptr<object> new_copy() const override { return std::make_unique<integer>(*this); }
+
+	integer& operator= (const integer& that) {
+		x = that.x;
+		return *this;	
+	}
+
+	object& operator= (const object& that) override {
+		std::type_index from = that.get_class(), to = get_class();
+		if ( from != to ) throw bad_cast{ from, to };
+		return *this = reinterpret_cast<const integer&>(that);
+	}
+
+	~integer() override = default;
+
+	integer& operator+= (const integer& that) {
+		x += that.x;
+		return *this;
+	}
+};
