Merge branch 'GC-Master'
* GC-Master: Updated with README and stuff. Added the Mapbox files. Initial check-in.
This commit is contained in:
commit
1b7c81e469
|
@ -0,0 +1,18 @@
|
||||||
|
*#
|
||||||
|
.#*
|
||||||
|
*~
|
||||||
|
*.orig
|
||||||
|
npm-debug.log
|
||||||
|
package.yml
|
||||||
|
node_modules/*
|
||||||
|
tmp/
|
||||||
|
bin/_mocha
|
||||||
|
bin/mocha
|
||||||
|
bin/escodegen
|
||||||
|
bin/esgenerate
|
||||||
|
test-reports.xml
|
||||||
|
LisperatorLanguage
|
||||||
|
src/test.js
|
||||||
|
src/test.coffee
|
||||||
|
notes/
|
||||||
|
build/
|
|
@ -0,0 +1,6 @@
|
||||||
|
project("Collector")
|
||||||
|
|
||||||
|
list(APPEND CMAKE_CXX_FLAGS "${CXXMAKE_C_FLAGS} -std=c++1y -I../src/include/ -g")
|
||||||
|
|
||||||
|
add_executable(collector src/collector.cpp)
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
---
|
||||||
|
**WHAT:**
|
||||||
|
|
||||||
|
This is an example of Bob Nystrom's
|
||||||
|
"[Baby's First Garbage Collector](http://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/),"
|
||||||
|
which I've been wanting to implement for a while in order to understand
|
||||||
|
it better. To make the problem harder (as I always do), I decided to
|
||||||
|
write it in C++, and to make it even more fun, I've implemented it using
|
||||||
|
the new Variant container from C++17.
|
||||||
|
|
||||||
|
**WHY:**
|
||||||
|
|
||||||
|
I've never written a garbage collector before. Now I know what it is
|
||||||
|
and how it works.
|
||||||
|
|
||||||
|
**DETAILS:**
|
||||||
|
|
||||||
|
The collector Nystrom wrote is a simple bi-color mark-and-sweep
|
||||||
|
collector for a singly threaded process with distinct pauses based upon
|
||||||
|
a straightforward memory usage heuristic. That heuristic is simply, "Is
|
||||||
|
the amount of memory currently in use twice as much as the last time we
|
||||||
|
garbage collected?" If the answer is yes, the collector runs and sweeps
|
||||||
|
up the stack.
|
||||||
|
|
||||||
|
Nystrom's code is highly readable, and I hope mine is as well. Because
|
||||||
|
I used Variant, my Object class has an internal Pair class, and then the
|
||||||
|
Variant is just \<int, Pair\>, where "Pair" is a pair of pointers to
|
||||||
|
other objects. The entirety of the VM is basically a stack of
|
||||||
|
singly-linked lists which either represents integers or collections of
|
||||||
|
integers in a Lisp-like structure.
|
||||||
|
|
||||||
|
The allocator creates two kinds of objects, then: pairs, and lists. A
|
||||||
|
pair is created by pushing two other objects onto the stack, then
|
||||||
|
calling `push()`, which pops them off the stack and replaces them with a
|
||||||
|
Pair object. The VM class has two methods, both named `push()`, one of
|
||||||
|
which pushes an integer, the other a pair. Since a pair is built from
|
||||||
|
objects on the stack, the Pair version takes no arguments, and since
|
||||||
|
C++14 and beyond have move semantics that Variant honors,
|
||||||
|
Variant\<Pair\> only constructs a single pair. Pretty nice. I was also
|
||||||
|
able to use both lambda-style and constructor-style visitors in my
|
||||||
|
Variant, which was a fun little bonus.
|
||||||
|
|
||||||
|
**NOTE:**
|
||||||
|
|
||||||
|
I have included the header files for the
|
||||||
|
[Mapbox version of Variant](https://github.com/mapbox/variant) since the
|
||||||
|
C++17 committee's standards haven't quite reached the general public and
|
||||||
|
the Variant implementation is still a subject of some debate. This
|
||||||
|
implementation looks straightforward enough and is a header-only
|
||||||
|
release. It works with both GCC 4.8.5 and Clang 3.8, and that's good
|
||||||
|
enough for me.
|
||||||
|
|
||||||
|
The Mapbox variant is BSD licensed, and a copy of the license is
|
||||||
|
included in the Include directory.
|
||||||
|
|
||||||
|
**BUILDING:**
|
||||||
|
|
||||||
|
From the base directory of the project:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
|
||||||
|
And you should be able to run the basic tests. It's just one file.
|
|
@ -0,0 +1,238 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define MAX_STACK 256
|
||||||
|
#define MAX_BARRIER 8
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OBJ_INT,
|
||||||
|
OBJ_PAIR
|
||||||
|
} ObjectType;
|
||||||
|
|
||||||
|
typedef struct sObject {
|
||||||
|
unsigned char marked;
|
||||||
|
struct sObject* next;
|
||||||
|
ObjectType type;
|
||||||
|
union {
|
||||||
|
int value;
|
||||||
|
struct {
|
||||||
|
struct sObject* head;
|
||||||
|
struct sObject* tail;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
} Object;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Object* stack[MAX_STACK];
|
||||||
|
Object* root;
|
||||||
|
int stackSize;
|
||||||
|
int numObjects;
|
||||||
|
int maxObjects;
|
||||||
|
} VM;
|
||||||
|
|
||||||
|
void assert(int condition, const char* message) {
|
||||||
|
if (!condition) {
|
||||||
|
printf("%s\n", message);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VM* newVM() {
|
||||||
|
VM* vm = malloc(sizeof(VM));
|
||||||
|
vm->stackSize = 0;
|
||||||
|
vm->numObjects = 0;
|
||||||
|
vm->maxObjects = MAX_BARRIER;
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(VM* vm, Object* value) {
|
||||||
|
assert(vm->stackSize < MAX_STACK, "Stack overflow!");
|
||||||
|
vm->stack[vm->stackSize++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* pop(VM* vm) {
|
||||||
|
assert(vm->stackSize > 0, "Stack underflow!");
|
||||||
|
return vm->stack[--(vm->stackSize)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark(Object* object) {
|
||||||
|
if (object->marked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object->marked = 1;
|
||||||
|
if (object->type == OBJ_PAIR) {
|
||||||
|
mark(object->head);
|
||||||
|
mark(object->tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void markAll(VM* vm) {
|
||||||
|
for(int i = 0; i < vm->stackSize; i++) {
|
||||||
|
mark(vm->stack[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sweep(VM* vm) {
|
||||||
|
Object** object = &vm->root;
|
||||||
|
while (*object) {
|
||||||
|
if (!(*object)->marked) {
|
||||||
|
Object* unreached = *object;
|
||||||
|
*object = unreached->next;
|
||||||
|
vm->numObjects--;
|
||||||
|
free(unreached);
|
||||||
|
} else {
|
||||||
|
(*object)->marked = 0;
|
||||||
|
object = &(*object)->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc(VM* vm) {
|
||||||
|
int numObjects = vm->numObjects;
|
||||||
|
markAll(vm);
|
||||||
|
sweep(vm);
|
||||||
|
vm->maxObjects = vm->numObjects * 2;
|
||||||
|
printf("Collected %d objects, %d remaining.\n", numObjects - vm->numObjects,
|
||||||
|
vm->numObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *newObject(VM* vm, ObjectType type) {
|
||||||
|
if (vm->numObjects == vm->maxObjects) {
|
||||||
|
gc(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* object = malloc(sizeof(Object));
|
||||||
|
object->marked = 0;
|
||||||
|
object->type = type;
|
||||||
|
object->next = vm->root;
|
||||||
|
vm->root = object;
|
||||||
|
vm->numObjects++;
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* pushInt(VM* vm, int value) {
|
||||||
|
Object* object = newObject(vm, OBJ_INT);
|
||||||
|
object->value = value;
|
||||||
|
push(vm, object);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* pushPair(VM* vm) {
|
||||||
|
Object* object = newObject(vm, OBJ_PAIR);
|
||||||
|
object->head = pop(vm);
|
||||||
|
object->tail = pop(vm);
|
||||||
|
push(vm, object);
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeVM(VM *vm) {
|
||||||
|
vm->stackSize = 0;
|
||||||
|
gc(vm);
|
||||||
|
free(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void objectPrint(Object* object) {
|
||||||
|
switch(object->type) {
|
||||||
|
case OBJ_INT:
|
||||||
|
printf("%d\n", object->value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBJ_PAIR:
|
||||||
|
printf("(");
|
||||||
|
objectPrint(object->head);
|
||||||
|
printf(", ");
|
||||||
|
objectPrint(object->tail);
|
||||||
|
printf(")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test1() {
|
||||||
|
printf("Test 1: Objects on stack are preserved.\n");
|
||||||
|
VM* vm = newVM();
|
||||||
|
pushInt(vm, 1);
|
||||||
|
pushInt(vm, 2);
|
||||||
|
|
||||||
|
gc(vm);
|
||||||
|
assert(vm->numObjects == 2, "Should have preserved objects.");
|
||||||
|
freeVM(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test2() {
|
||||||
|
printf("Test 2: Unreached objects are collected.\n");
|
||||||
|
VM* vm = newVM();
|
||||||
|
pushInt(vm, 1);
|
||||||
|
pushInt(vm, 2);
|
||||||
|
pop(vm);
|
||||||
|
pop(vm);
|
||||||
|
|
||||||
|
gc(vm);
|
||||||
|
assert(vm->numObjects == 0, "Should have collected objects.");
|
||||||
|
freeVM(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test3() {
|
||||||
|
printf("Test 3: Reach nested objects.\n");
|
||||||
|
VM* vm = newVM();
|
||||||
|
pushInt(vm, 1);
|
||||||
|
pushInt(vm, 2);
|
||||||
|
pushPair(vm);
|
||||||
|
pushInt(vm, 3);
|
||||||
|
pushInt(vm, 4);
|
||||||
|
pushPair(vm);
|
||||||
|
pushPair(vm);
|
||||||
|
|
||||||
|
gc(vm);
|
||||||
|
assert(vm->numObjects == 7, "Should have reached objects.");
|
||||||
|
freeVM(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test4() {
|
||||||
|
printf("Test 4: Handle cycles.\n");
|
||||||
|
VM* vm = newVM();
|
||||||
|
pushInt(vm, 1);
|
||||||
|
pushInt(vm, 2);
|
||||||
|
Object* a = pushPair(vm);
|
||||||
|
pushInt(vm, 3);
|
||||||
|
pushInt(vm, 4);
|
||||||
|
Object* b = pushPair(vm);
|
||||||
|
|
||||||
|
/* Set up a cycle, and also make 2 and 4 unreachable and collectible. */
|
||||||
|
a->tail = b;
|
||||||
|
b->tail = a;
|
||||||
|
|
||||||
|
gc(vm);
|
||||||
|
assert(vm->numObjects == 4, "Should have collected objects.");
|
||||||
|
freeVM(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void perfTest() {
|
||||||
|
printf("Performance Test.\n");
|
||||||
|
VM* vm = newVM();
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
for (int j = 0; j < 20; j++) {
|
||||||
|
pushInt(vm, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = 0; k < 20; k++) {
|
||||||
|
pop(vm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeVM(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char * argv[]) {
|
||||||
|
test1();
|
||||||
|
test2();
|
||||||
|
test3();
|
||||||
|
test4();
|
||||||
|
perfTest();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,252 @@
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/* Requires the mapbox header-only variant found at
|
||||||
|
https://github.com/mapbox/variant
|
||||||
|
|
||||||
|
Compiles with:
|
||||||
|
clang++ -std=c++14 -I./include/ -o collector collector.cpp
|
||||||
|
… or
|
||||||
|
g++ -std=c++1y -I./include/ -g -o collector collector.cpp
|
||||||
|
… where 'include' has the variant headers.
|
||||||
|
|
||||||
|
clang version 3.9.1-svn288847-1~exp1 (branches/release_39)
|
||||||
|
g++ version (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
|
||||||
|
|
||||||
|
*/
|
||||||
|
#include <mapbox/variant.hpp>
|
||||||
|
|
||||||
|
#define MAX_STACK 256
|
||||||
|
#define MAX_BARRIER 8
|
||||||
|
|
||||||
|
void my_assert(int condition, const char* message) {
|
||||||
|
if (!condition) {
|
||||||
|
std::cout << message << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An implementation of Bob Nystrom's "Baby's First Garbage Collector"
|
||||||
|
http://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/,
|
||||||
|
only in C++, and with some educational stuff along the way about
|
||||||
|
the new Variant (automagical discriminated unions) coming in
|
||||||
|
Libstdc++ version 4, part of the C++ 2017 standard.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Object {
|
||||||
|
public:
|
||||||
|
unsigned char marked;
|
||||||
|
Object *next;
|
||||||
|
Object(int v): marked(0), value(v) {}
|
||||||
|
// Variant<Pair> uses move semantics; this doesn't result in Pair being built twice.
|
||||||
|
Object(Object* head, Object* tail): marked(0), value(Pair(head, tail)) {}
|
||||||
|
|
||||||
|
class Pair {
|
||||||
|
public:
|
||||||
|
Pair(Object* h, Object* t): head(h), tail(t) {};
|
||||||
|
Object* head;
|
||||||
|
Object* tail;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This is mostly an exploration of a discriminated union, and
|
||||||
|
making one work in the context of a primitive but functional
|
||||||
|
garbage collector. */
|
||||||
|
mapbox::util::variant<int, Pair> value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VM {
|
||||||
|
public:
|
||||||
|
/* Imagine my surprise when I learned that clang doesn't bother to
|
||||||
|
zero out memory allocated on the threadstack. */
|
||||||
|
VM(): stackSize(0), numObjects(0), maxObjects(MAX_BARRIER), root(NULL) {};
|
||||||
|
|
||||||
|
Object* pop() {
|
||||||
|
my_assert(stackSize > 0, "Stack underflow!");
|
||||||
|
return stack[--stackSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is basically the interface for a very primitive reverse
|
||||||
|
polish notation calculator of some kind. A garbage-collected
|
||||||
|
Forth interpreter, perhaps. */
|
||||||
|
|
||||||
|
Object* push(int v) {
|
||||||
|
return _push(insert(new Object(v)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* push() {
|
||||||
|
return _push(insert(new Object(pop(), pop())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lambda-style visitors, enabling descent. */
|
||||||
|
void mark(Object *o) {
|
||||||
|
auto marker = mapbox::util::make_visitor(
|
||||||
|
[this](int) {},
|
||||||
|
[this](Object::Pair p) {
|
||||||
|
this->mark(p.head);
|
||||||
|
this->mark(p.tail);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (o->marked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
o->marked = 1;
|
||||||
|
return mapbox::util::apply_visitor(marker, o->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* So named because each scope resembles a collection of objects
|
||||||
|
leading horizontally from the vertical stack, creating a spine. */
|
||||||
|
void markSpine() {
|
||||||
|
for(auto i = 0; i < stackSize; i++) {
|
||||||
|
mark(stack[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void collect() {
|
||||||
|
int num = numObjects;
|
||||||
|
markSpine();
|
||||||
|
sweep();
|
||||||
|
maxObjects = numObjects * 2;
|
||||||
|
#ifdef DEBUG
|
||||||
|
std::cout << "Collected " << (num - numObjects) << " objects, "
|
||||||
|
<< numObjects << " remain." << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The saddest fact: I went with using NULL as our end-of-stack
|
||||||
|
discriminator rather than something higher-level, like an
|
||||||
|
Optional or Either-variant, because to use those I'd have to use
|
||||||
|
recursion to sweep the interpreter's stack, which means I'm at
|
||||||
|
the mercy of the C stack, complete with the cost of the unwind at
|
||||||
|
the end. Bummer. */
|
||||||
|
|
||||||
|
/* I look at this and ask, WWHSD? What Would Herb Sutter Do? */
|
||||||
|
|
||||||
|
void sweep() {
|
||||||
|
Object** o = &root;
|
||||||
|
while(*o) {
|
||||||
|
if (!(*o)->marked) {
|
||||||
|
Object* unreached = *o;
|
||||||
|
*o = unreached->next;
|
||||||
|
numObjects--;
|
||||||
|
delete unreached;
|
||||||
|
} else {
|
||||||
|
(*o)->marked = 0;
|
||||||
|
o = &(*o)->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int numObjects;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/* Heh. Typo, "Stark overflow." I'll just leave Tony right there anyway... */
|
||||||
|
Object* _push(Object *o) {
|
||||||
|
my_assert(stackSize < MAX_STACK, "Stark overflow");
|
||||||
|
stack[stackSize++] = o;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* insert(Object *o) {
|
||||||
|
if (numObjects >= maxObjects) {
|
||||||
|
collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
o->marked = 0;
|
||||||
|
o->next = root;
|
||||||
|
root = o;
|
||||||
|
numObjects++;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object* stack[MAX_STACK];
|
||||||
|
Object* root;
|
||||||
|
int stackSize;
|
||||||
|
int maxObjects;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void test1() {
|
||||||
|
std::cout << "Test 1: Objects on stack are preserved." << std::endl;
|
||||||
|
VM vm;
|
||||||
|
vm.push(1);
|
||||||
|
vm.push(2);
|
||||||
|
vm.collect();
|
||||||
|
my_assert(vm.numObjects == 2, "Should have preserved objects.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test2() {
|
||||||
|
std::cout << "Test 2: Unreached objects are collected." << std::endl;
|
||||||
|
VM vm;
|
||||||
|
vm.push(1);
|
||||||
|
vm.push(2);
|
||||||
|
vm.pop();
|
||||||
|
vm.pop();
|
||||||
|
vm.collect();
|
||||||
|
my_assert(vm.numObjects == 0, "Should have collected objects.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void test3() {
|
||||||
|
std::cout << "Test 3: Reach nested objects." << std::endl;
|
||||||
|
VM vm;
|
||||||
|
vm.push(1);
|
||||||
|
vm.push(2);
|
||||||
|
vm.push();
|
||||||
|
vm.push(3);
|
||||||
|
vm.push(4);
|
||||||
|
vm.push();
|
||||||
|
vm.push();
|
||||||
|
vm.collect();
|
||||||
|
my_assert(vm.numObjects == 7, "Should have reached objects.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void test4() {
|
||||||
|
std::cout << "Test 4: Handle cycles." << std::endl;
|
||||||
|
VM vm;
|
||||||
|
vm.push(1);
|
||||||
|
vm.push(2);
|
||||||
|
Object* a = vm.push();
|
||||||
|
vm.push(3);
|
||||||
|
vm.push(4);
|
||||||
|
Object* b = vm.push();
|
||||||
|
|
||||||
|
/* Constructor-based variant visitor. */
|
||||||
|
struct tail_setter {
|
||||||
|
Object* tail;
|
||||||
|
tail_setter(Object *t) : tail(t) {}
|
||||||
|
inline void operator()(int &i) {}
|
||||||
|
inline void operator()(Object::Pair &p) { p.tail = tail; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Set up a cycle, and also make 2 and 4 unreachable and collectible. */
|
||||||
|
mapbox::util::apply_visitor(tail_setter(b), a->value);
|
||||||
|
mapbox::util::apply_visitor(tail_setter(a), b->value);
|
||||||
|
vm.collect();
|
||||||
|
my_assert(vm.numObjects == 4, "Should have collected objects.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void perfTest() {
|
||||||
|
std::cout << "Performance Test." << std::endl;
|
||||||
|
VM vm;
|
||||||
|
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
for (int j = 0; j < 20; j++) {
|
||||||
|
vm.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int k = 0; k < 20; k++) {
|
||||||
|
vm.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char * argv[]) {
|
||||||
|
test1();
|
||||||
|
test2();
|
||||||
|
test3();
|
||||||
|
test4();
|
||||||
|
perfTest();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
Copyright (c) MapBox
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
- Neither the name "MapBox" nor the names of its contributors may be
|
||||||
|
used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,74 @@
|
||||||
|
#ifndef MAPBOX_UTIL_OPTIONAL_HPP
|
||||||
|
#define MAPBOX_UTIL_OPTIONAL_HPP
|
||||||
|
|
||||||
|
#pragma message("This implementation of optional is deprecated. See https://github.com/mapbox/variant/issues/64.")
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <mapbox/variant.hpp>
|
||||||
|
|
||||||
|
namespace mapbox {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class optional
|
||||||
|
{
|
||||||
|
static_assert(!std::is_reference<T>::value, "optional doesn't support references");
|
||||||
|
|
||||||
|
struct none_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
variant<none_type, T> variant_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
optional() = default;
|
||||||
|
|
||||||
|
optional(optional const& rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs)
|
||||||
|
{ // protect against invalid self-assignment
|
||||||
|
variant_ = rhs.variant_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
optional(T const& v) { variant_ = v; }
|
||||||
|
|
||||||
|
explicit operator bool() const noexcept { return variant_.template is<T>(); }
|
||||||
|
|
||||||
|
T const& get() const { return variant_.template get<T>(); }
|
||||||
|
T& get() { return variant_.template get<T>(); }
|
||||||
|
|
||||||
|
T const& operator*() const { return this->get(); }
|
||||||
|
T operator*() { return this->get(); }
|
||||||
|
|
||||||
|
optional& operator=(T const& v)
|
||||||
|
{
|
||||||
|
variant_ = v;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional& operator=(optional const& rhs)
|
||||||
|
{
|
||||||
|
if (this != &rhs)
|
||||||
|
{
|
||||||
|
variant_ = rhs.variant_;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void emplace(Args&&... args)
|
||||||
|
{
|
||||||
|
variant_ = T{std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() { variant_ = none_type{}; }
|
||||||
|
|
||||||
|
}; // class optional
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace mapbox
|
||||||
|
|
||||||
|
#endif // MAPBOX_UTIL_OPTIONAL_HPP
|
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||||
|
#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||||
|
|
||||||
|
// Based on variant/recursive_wrapper.hpp from boost.
|
||||||
|
//
|
||||||
|
// Original license:
|
||||||
|
//
|
||||||
|
// Copyright (c) 2002-2003
|
||||||
|
// Eric Friedman, Itay Maman
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See
|
||||||
|
// accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace mapbox {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class recursive_wrapper
|
||||||
|
{
|
||||||
|
|
||||||
|
T* p_;
|
||||||
|
|
||||||
|
void assign(T const& rhs)
|
||||||
|
{
|
||||||
|
this->get() = rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using type = T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor default initializes the internally stored value.
|
||||||
|
* For POD types this means nothing is done and the storage is
|
||||||
|
* uninitialized.
|
||||||
|
*
|
||||||
|
* @throws std::bad_alloc if there is insufficient memory for an object
|
||||||
|
* of type T.
|
||||||
|
* @throws any exception thrown by the default constructur of T.
|
||||||
|
*/
|
||||||
|
recursive_wrapper()
|
||||||
|
: p_(new T){}
|
||||||
|
|
||||||
|
~recursive_wrapper() noexcept { delete p_; }
|
||||||
|
|
||||||
|
recursive_wrapper(recursive_wrapper const& operand)
|
||||||
|
: p_(new T(operand.get())) {}
|
||||||
|
|
||||||
|
recursive_wrapper(T const& operand)
|
||||||
|
: p_(new T(operand)) {}
|
||||||
|
|
||||||
|
recursive_wrapper(recursive_wrapper&& operand)
|
||||||
|
: p_(new T(std::move(operand.get()))) {}
|
||||||
|
|
||||||
|
recursive_wrapper(T&& operand)
|
||||||
|
: p_(new T(std::move(operand))) {}
|
||||||
|
|
||||||
|
inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
|
||||||
|
{
|
||||||
|
assign(rhs.get());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline recursive_wrapper& operator=(T const& rhs)
|
||||||
|
{
|
||||||
|
assign(rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(recursive_wrapper& operand) noexcept
|
||||||
|
{
|
||||||
|
T* temp = operand.p_;
|
||||||
|
operand.p_ = p_;
|
||||||
|
p_ = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept
|
||||||
|
{
|
||||||
|
swap(rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
recursive_wrapper& operator=(T&& rhs)
|
||||||
|
{
|
||||||
|
get() = std::move(rhs);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& get()
|
||||||
|
{
|
||||||
|
assert(p_);
|
||||||
|
return *get_pointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
T const& get() const
|
||||||
|
{
|
||||||
|
assert(p_);
|
||||||
|
return *get_pointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
T* get_pointer() { return p_; }
|
||||||
|
|
||||||
|
const T* get_pointer() const { return p_; }
|
||||||
|
|
||||||
|
operator T const&() const { return this->get(); }
|
||||||
|
|
||||||
|
operator T&() { return this->get(); }
|
||||||
|
|
||||||
|
}; // class recursive_wrapper
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
|
||||||
|
{
|
||||||
|
lhs.swap(rhs);
|
||||||
|
}
|
||||||
|
} // namespace util
|
||||||
|
} // namespace mapbox
|
||||||
|
|
||||||
|
#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef MAPBOX_UTIL_VARIANT_IO_HPP
|
||||||
|
#define MAPBOX_UTIL_VARIANT_IO_HPP
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
#include <mapbox/variant.hpp>
|
||||||
|
|
||||||
|
namespace mapbox {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
// operator<< helper
|
||||||
|
template <typename Out>
|
||||||
|
class printer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit printer(Out& out)
|
||||||
|
: out_(out) {}
|
||||||
|
printer& operator=(printer const&) = delete;
|
||||||
|
|
||||||
|
// visitor
|
||||||
|
template <typename T>
|
||||||
|
void operator()(T const& operand) const
|
||||||
|
{
|
||||||
|
out_ << operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Out& out_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator<<
|
||||||
|
template <typename CharT, typename Traits, typename... Types>
|
||||||
|
VARIANT_INLINE std::basic_ostream<CharT, Traits>&
|
||||||
|
operator<<(std::basic_ostream<CharT, Traits>& out, variant<Types...> const& rhs)
|
||||||
|
{
|
||||||
|
detail::printer<std::basic_ostream<CharT, Traits>> visitor(out);
|
||||||
|
apply_visitor(visitor, rhs);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
} // namespace util
|
||||||
|
} // namespace mapbox
|
||||||
|
|
||||||
|
#endif // MAPBOX_UTIL_VARIANT_IO_HPP
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef MAPBOX_UTIL_VARIANT_VISITOR_HPP
|
||||||
|
#define MAPBOX_UTIL_VARIANT_VISITOR_HPP
|
||||||
|
|
||||||
|
namespace mapbox {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
template <typename... Fns>
|
||||||
|
struct visitor;
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
struct visitor<Fn> : Fn
|
||||||
|
{
|
||||||
|
using type = Fn;
|
||||||
|
using Fn::operator();
|
||||||
|
|
||||||
|
visitor(Fn fn) : Fn(fn) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Fn, typename... Fns>
|
||||||
|
struct visitor<Fn, Fns...> : Fn, visitor<Fns...>
|
||||||
|
{
|
||||||
|
using type = visitor;
|
||||||
|
using Fn::operator();
|
||||||
|
using visitor<Fns...>::operator();
|
||||||
|
|
||||||
|
visitor(Fn fn, Fns... fns) : Fn(fn), visitor<Fns...>(fns...) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Fns>
|
||||||
|
visitor<Fns...> make_visitor(Fns... fns)
|
||||||
|
{
|
||||||
|
return visitor<Fns...>(fns...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace mapbox
|
||||||
|
|
||||||
|
#endif // MAPBOX_UTIL_VARIANT_VISITOR_HPP
|
Loading…
Reference in New Issue