Kajetan Johannes Hammerle 3 years ago
commit
21c1b726b2
3 changed files with 361 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 352 0
      Main.cpp
  3. 8 0
      Makefile

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+vector

+ 352 - 0
Main.cpp

@@ -0,0 +1,352 @@
+#include <cassert>
+#include <iostream>
+#include <vector>
+
+struct A {
+    static int instances;
+    int a;
+    A(int a) : a(a) {
+        // std::cout << "construct A " << a << "\n";
+        instances += a;
+    }
+
+    A(const A& other) : a(other.a) {
+        // std::cout << "copy construct A " << a << "\n";
+        instances += a;
+    }
+
+    A(A&& other) : a(other.a) {
+        // std::cout << "move construct A " << a << "\n";
+        instances += a;
+    }
+
+    A& operator=(const A& other) {
+        instances -= a;
+        a = other.a;
+        instances += a;
+        // std::cout << "copy assignment A " << a << "\n";
+        return *this;
+    }
+
+    A& operator=(A&& other) {
+        instances -= a;
+        a = other.a;
+        instances += a;
+        // std::cout << "move assignment A " << a << "\n";
+        return *this;
+    }
+
+    ~A() {
+        // std::cout << "destruct A " << a << "\n";
+        instances -= a;
+    }
+};
+
+int A::instances = 0;
+
+template<typename T>
+class Vector {
+    char* data;
+    int capacity;
+    int elements;
+
+public:
+    Vector() : data(nullptr), capacity(0), elements(0) {
+    }
+
+    ~Vector() {
+        destroy();
+    }
+
+    // allocate new storage for copies
+    Vector(const Vector& other) : data(allocate(other.capacity)), capacity(other.capacity), elements(other.elements) {
+        // copy data into new storage
+        other.copyTo(data);
+    }
+
+    Vector& operator=(const Vector& other) {
+        if(this != &other) {
+            // clear current resources
+            destroy();
+            // allocate new storage for copies
+            data = allocate(other.capacity);
+            capacity = other.capacity;
+            elements = other.elements;
+            // copy data into new storage
+            other.copyTo(data);
+        }
+        return *this;
+    }
+
+    // acquire the given other vector
+    Vector(Vector&& other) : data(other.data), capacity(other.capacity), elements(other.elements) {
+        // remove everything from the other vector
+        other.reset();
+    }
+
+    Vector& operator=(Vector&& other) {
+        if(this != &other) {
+            // clear current resources
+            destroy();
+            // acquire the given other vector
+            data = other.data;
+            capacity = other.capacity;
+            elements = other.elements;
+            // remove everything from the other vector
+            other.reset();
+        }
+        return *this;
+    }
+
+    void reserve(int size) {
+        if(size <= capacity) {
+            return;
+        }
+        capacity = size;
+        char* newData = allocate(capacity);
+        // nothing todo without any old data
+        if(data == nullptr) {
+            data = newData;
+            return;
+        }
+        // there are elements which need to be moved
+        moveTo(newData);
+        destroy();
+        data = newData;
+    }
+
+    void resize(int size, const T& t) {
+        if(size > elements) {
+            // move existing object to new storage
+            char* newData = allocate(size);
+            moveTo(newData);
+            destroy();
+            data = newData;
+            capacity = size;
+            // fill the rest with the given object
+            for(int i = elements; i < size; i++) {
+                push_back(t);
+            }
+        } else if(size < elements) {
+            // remove objects until the size matches
+            for(int i = size; i < elements; i++) {
+                (*pointer(data, i)).~T();
+            }
+            elements = size;
+        }
+    }
+
+    void resize(int size) {
+        resize(size, T());
+    }
+
+    void push_back(const T& t) {
+        ensureCapacity();
+        new(pointer(data, elements++)) T(t);
+    }
+
+    void push_back(T&& t) {
+        ensureCapacity();
+        // && would be lost without std::move
+        new(pointer(data, elements++)) T(std::move(t));
+    }
+
+    T& operator[](int index) {
+        return *pointer(data, index);
+    }
+
+    const T& operator[](int index) const {
+        return *pointer(data, index);
+    }
+
+    T& at(int index) {
+        // std states "at" is [] with range check
+        assert(index >= 0 && index < elements);
+        return (*this)[index];
+    }
+
+    const T& at(int index) const {
+        // std states "at" is [] with range check
+        assert(index >= 0 && index < elements);
+        return (*this)[index];
+    }
+
+    int size() const {
+        return elements;
+    }
+
+    T* begin() {
+        return pointer(data, 0);
+    }
+
+    T* end() {
+        return pointer(data, elements);
+    }
+
+    const T* begin() const {
+        return pointer(data, 0);
+    }
+
+    const T* end() const {
+        return pointer(data, elements);
+    }
+
+    void erase(T* from, T* to) {
+        T* remove = from;
+        T* move = to;
+        T* stop = end();
+        // fill the hole by moving following objects as long as possible
+        while(remove != to && move != stop) {
+            *remove = std::move(*move);
+            remove++;
+            move++;
+        }
+        // remove left over objects
+        while(remove != stop) {
+            remove->~T();
+            remove++;
+            elements--;
+        }
+    }
+
+    void erase(T* start) {
+        erase(start, start + 1);
+    }
+
+    T* as_array() {
+        return begin();
+    }
+
+    const T* as_array() const {
+        return begin();
+    }
+
+private:
+    static char* allocate(int length) {
+        return new char[sizeof(T) * length];
+    }
+
+    static T* pointer(char* data, int index) {
+        return reinterpret_cast<T*>(data) + index;
+    }
+
+    static const T* pointer(const char* data, int index) {
+        return reinterpret_cast<const T*>(data) + index;
+    }
+
+    void ensureCapacity() {
+        if(elements >= capacity) {
+            // doubling the size amortizes costs
+            reserve(capacity == 0 ? 1 : capacity * 2);
+        }
+    }
+
+    void copyTo(char* newData) const {
+        for(int i = 0; i < elements; i++) {
+            new(pointer(newData, i)) T(*pointer(data, i));
+        }
+    }
+
+    void moveTo(char* newData) {
+        for(int i = 0; i < elements; i++) {
+            new(pointer(newData, i)) T(std::move(*pointer(data, i)));
+        }
+    }
+
+    void destroy() {
+        callDestructors();
+        delete[] data;
+    }
+
+    void reset() {
+        data = nullptr;
+        capacity = 0;
+        elements = 0;
+    }
+
+    void callDestructors() {
+        // placement new needs explicit destructor calling
+        for(int i = 0; i < elements; i++) {
+            (*pointer(data, i)).~T();
+        }
+    }
+};
+
+void printError(int number) {
+    std::cout << "\033[0;31mError " << number << "\033[0m\n";
+}
+
+template<typename V>
+void test() {
+    {
+        const int elements = 2;
+        V v;
+        for(int i = 0; i < elements; i++) {
+            v.push_back(A(i));
+        }
+        const V& cv = v;
+        for(int i = 0; i < elements; i++) {
+            if(v[i].a != i || cv[i].a != i || v.at(i).a != i || cv.at(i).a != i) {
+                printError(1);
+            }
+        }
+        if(v.size() != elements) {
+            printError(2);
+        }
+    }
+    {
+        V v1;
+        v1.push_back(A(10));
+        V v2 = v1;
+        V v3;
+        v3.push_back(A(20));
+        v3 = v1;
+        if(v1[0].a != 10 || v2[0].a != 10 || v3[0].a != 10) {
+            printError(3);
+        }
+
+        V v4 = std::move(v1);
+        V v5(std::move(v2));
+        V v6;
+        v6 = std::move(v3);
+
+        if(v1.size() != 0 || v2.size() != 0 || v3.size() != 0 || v4.size() != 1 || v5.size() != 1 || v6.size() != 1) {
+            printError(4);
+        }
+    }
+    {
+        V v;
+        v.resize(1, A(8));
+        v.resize(3, A(9));
+        if(v.size() != 3 || v[0].a != 8 || v[1].a != 9 || v[2].a != 9) {
+            printError(5);
+        }
+        v.resize(1, A(10));
+        if(v.size() != 1 || v[0].a != 8) {
+            printError(6);
+        }
+    }
+    {
+        V v;
+        v.push_back(A(20));
+        v.push_back(A(21));
+        v.push_back(A(22));
+        v.erase(v.begin() + 1);
+        if(v.size() != 2 || v[0].a != 20 || v[1].a != 22) {
+            printError(7);
+        }
+        v.erase(v.begin(), v.end());
+        if(v.size() != 0) {
+            printError(8);
+        }
+    }
+    if(A::instances != 0) {
+        std::cout << "object counter is not 0: " << A::instances << "\n";
+    }
+}
+
+int main() {
+    test<Vector<A>>();
+    std::cout << "--------------------------\n";
+    test<std::vector<A>>();
+}

+ 8 - 0
Makefile

@@ -0,0 +1,8 @@
+all: vector
+	./vector
+
+vector: Main.cpp
+	g++ -std=c++14 -o $@ Main.cpp -Wall -Wextra -Werror -pedantic
+
+clean:
+	rm -f vector