typetui 0.2.0

A terminal-based typing test.
Documentation
#include <iostream>
#include <memory>
#include <vector>

class FileHandle {
    FILE* file;
public:
    explicit FileHandle(const char* filename, const char* mode = "r")
        : file(std::fopen(filename, mode)) {
        if (!file) throw std::runtime_error("Failed to open file");
    }
    
    ~FileHandle() {
        if (file) std::fclose(file);
    }
    
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
    
    FileHandle(FileHandle&& other) noexcept : file(other.file) {
        other.file = nullptr;
    }
    
    FILE* get() const { return file; }
};

template<typename T>
class SharedBuffer {
    std::shared_ptr<std::vector<T>> data;
    size_t offset;
    size_t length;
    
public:
    explicit SharedBuffer(size_t size)
        : data(std::make_shared<std::vector<T>>(size)),
          offset(0), length(size) {}
    
    SharedBuffer slice(size_t start, size_t len) const {
        SharedBuffer result(*this);
        result.offset = offset + start;
        result.length = len;
        return result;
    }
    
    T& operator[](size_t index) {
        return (*data)[offset + index];
    }
    
    size_t size() const { return length; }
    
    size_t use_count() const { return data.use_count(); }
};

template<typename T>
class UniqueArray {
    std::unique_ptr<T[]> data;
    size_t len;
    
public:
    explicit UniqueArray(size_t size) : data(std::make_unique<T[]>(size)), len(size) {}
    
    T& operator[](size_t index) { return data[index]; }
    const T& operator[](size_t index) const { return data[index]; }
    size_t size() const { return len; }
    
    T* begin() { return data.get(); }
    T* end() { return data.get() + len; }
    
    std::unique_ptr<T[]> release() { return std::move(data); }
};

class Observable {
    std::vector<std::weak_ptr<class Observer>> observers;
    
public:
    void add_observer(std::shared_ptr<Observer> observer) {
        observers.push_back(observer);
    }
    
    void notify(const std::string& message) {
        observers.erase(
            std::remove_if(observers.begin(), observers.end(),
                [](const std::weak_ptr<Observer>& wp) { return wp.expired(); }),
            observers.end()
        );
        
        for (auto& wp : observers) {
            if (auto sp = wp.lock()) {
                sp->on_notify(message);
            }
        }
    }
};

class Observer : public std::enable_shared_from_this<Observer> {
public:
    virtual ~Observer() = default;
    virtual void on_notify(const std::string& message) = 0;
};

class ConsoleObserver : public Observer {
public:
    void on_notify(const std::string& message) override {
        std::cout << "Console: " << message << "\n";
    }
};

class TreeNode {
public:
    int value;
    std::shared_ptr<TreeNode> left;
    std::shared_ptr<TreeNode> right;
    std::weak_ptr<TreeNode> parent;
    
    TreeNode(int v) : value(v) {}
    
    void set_children(std::shared_ptr<TreeNode> l, std::shared_ptr<TreeNode> r) {
        left = l;
        right = r;
        if (left) left->parent = shared_from_this();
        if (right) right->parent = shared_from_this();
    }
};

template<typename T>
class Factory {
public:
    template<typename... Args>
    std::shared_ptr<T> create(Args&&... args) {
        return std::make_shared<T>(std::forward<Args>(args)...);
    }
    
    template<typename... Args>
    std::unique_ptr<T> create_unique(Args&&... args) {
        return std::make_unique<T>(std::forward<Args>(args)...);
    }
};

void demonstrate_custom_deleter() {
    auto file_deleter = [](FILE* f) {
        if (f) {
            std::cout << "Closing file\n";
            std::fclose(f);
        }
    };
    
    std::shared_ptr<FILE> file(std::fopen("test.txt", "w"), file_deleter);
    if (file) {
        std::fprintf(file.get(), "Hello from custom deleter\n");
    }
}

void demonstrate_enable_shared_from_this() {
    auto node = std::make_shared<TreeNode>(10);
    auto left = std::make_shared<TreeNode>(5);
    auto right = std::make_shared<TreeNode>(15);
    
    node->set_children(left, right);
    
    if (auto p = left->parent.lock()) {
        std::cout << "Left child's parent value: " << p->value << "\n";
    }
}

void demonstrate_circular_reference() {
    struct Node {
        int value;
        std::shared_ptr<Node> next;
        std::weak_ptr<Node> prev;
    };
    
    auto a = std::make_shared<Node>();
    auto b = std::make_shared<Node>();
    
    a->value = 1;
    b->value = 2;
    
    a->next = b;
    b->prev = a;
    
    std::cout << "a use_count: " << a.use_count() << "\n";
    std::cout << "b use_count: " << b.use_count() << "\n";
}

template<typename T>
std::shared_ptr<T> make_shared_array(size_t size) {
    return std::shared_ptr<T>(new T[size], std::default_delete<T[]>());
}

int main() {
    SharedBuffer<int> buffer(100);
    for (size_t i = 0; i < buffer.size(); ++i) {
        buffer[i] = static_cast<int>(i);
    }
    
    auto slice = buffer.slice(10, 20);
    std::cout << "Slice size: " << slice.size() << "\n";
    std::cout << "Slice[0]: " << slice[0] << "\n";
    std::cout << "Buffer use_count: " << buffer.use_count() << "\n";
    
    demonstrate_circular_reference();
    demonstrate_custom_deleter();
    
    return 0;
}