1
This commit is contained in:
2022-12-08 02:14:04 +01:00
parent 1455757e24
commit f5ee5f6942
12 changed files with 536 additions and 501 deletions

View File

@ -91,7 +91,7 @@ bool VESA::initGraphicMode(uint16_t mode) {
return false; return false;
} }
// Kernel::kout << "TotalVideoMemory: " << ((ib->TotalMemory*65536) / (1024*1024)) << " MB" << endl; // Util::System::out << "TotalVideoMemory: " << ((ib->TotalMemory*65536) / (1024*1024)) << " MB" << endl;
// Gewuenschten Grafikmodus aus Antwort suchen // Gewuenschten Grafikmodus aus Antwort suchen
auto *modePtr = reinterpret_cast<uint16_t *>((ib->VideoModePtr[1] << 4) + ib->VideoModePtr[0]); auto *modePtr = reinterpret_cast<uint16_t *>((ib->VideoModePtr[1] << 4) + ib->VideoModePtr[0]);

View File

@ -5,7 +5,7 @@
*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*
* Beschreibung: Ein Bluescreen, falls eine x86 Exception auftritt. Evt. * * Beschreibung: Ein Bluescreen, falls eine x86 Exception auftritt. Evt. *
* ist der Stack und oder Heap kaputt, weswegen hier nicht * * ist der Stack und oder Heap kaputt, weswegen hier nicht *
* Kernel::kout etc. verwendet wird. * * Util::System::out etc. verwendet wird. *
* * * *
* Autor: Michael Schoettner, 11.12.2018 * * Autor: Michael Schoettner, 11.12.2018 *
*****************************************************************************/ *****************************************************************************/

View File

@ -5,7 +5,7 @@
*---------------------------------------------------------------------------* *---------------------------------------------------------------------------*
* Beschreibung: Ein Bluescreen, falls eine x86 Exception auftritt. Evt. * * Beschreibung: Ein Bluescreen, falls eine x86 Exception auftritt. Evt. *
* ist der Stack und oder Heap kaputt, weswegen hier nicht * * ist der Stack und oder Heap kaputt, weswegen hier nicht *
* Kernel::kout etc. verwendet wird. * * Util::System::out etc. verwendet wird. *
* * * *
* Autor: Michael Schoettner, 2.2.2017 * * Autor: Michael Schoettner, 2.2.2017 *
*****************************************************************************/ *****************************************************************************/

View File

@ -31,7 +31,7 @@ public:
} }
while (true) { while (true) {
// Kernel::kout << "Idle!" << endl; // Util::System::out << "Idle!" << endl;
scheduler.yield(); scheduler.yield();
} }
} }

View File

@ -29,14 +29,6 @@ namespace Kernel {
constexpr const bool INSANE_TRACE = false; constexpr const bool INSANE_TRACE = false;
/*****************************************************************************
* Methode: Dispatcher::dispatch *
*---------------------------------------------------------------------------*
* Beschreibung: Auf den active thread wechseln. *
* *
* Parameter: *
* next Thread der die CPU::erhalten soll. *
*****************************************************************************/
void Scheduler::start(Container::Vector<Memory::unique_ptr<Thread>>::iterator next) { void Scheduler::start(Container::Vector<Memory::unique_ptr<Thread>>::iterator next) {
active = next; active = next;
if (active >= ready_queue.end()) { if (active >= ready_queue.end()) {
@ -139,7 +131,8 @@ void Scheduler::kill(uint32_t tid, Memory::unique_ptr<Thread> *ptr) {
uint32_t prev_tid = (*active)->tid; uint32_t prev_tid = (*active)->tid;
// Block queue, can always kill // Block queue, can always kill
for (Container::Vector<Memory::unique_ptr<Thread>>::iterator it = block_queue.begin(); it != block_queue.end(); ++it) { for (Container::Vector<Memory::unique_ptr<Thread>>::iterator it = block_queue.begin();
it != block_queue.end(); ++it) {
if ((*it)->tid == tid) { if ((*it)->tid == tid) {
// Found thread to kill // Found thread to kill
@ -165,7 +158,8 @@ void Scheduler::kill(uint32_t tid, Memory::unique_ptr<Thread> *ptr) {
return; return;
} }
for (Container::Vector<Memory::unique_ptr<Thread>>::iterator it = ready_queue.begin(); it != ready_queue.end(); ++it) { for (Container::Vector<Memory::unique_ptr<Thread>>::iterator it = ready_queue.begin();
it != ready_queue.end(); ++it) {
if ((*it)->tid == tid) { if ((*it)->tid == tid) {
// Found thread to kill // Found thread to kill
@ -322,7 +316,8 @@ void Scheduler::deblock(uint32_t tid) {
Device::CPU::disable_int(); Device::CPU::disable_int();
for (Container::Vector<Memory::unique_ptr<Thread>>::iterator it = block_queue.begin(); it != block_queue.end(); ++it) { for (Container::Vector<Memory::unique_ptr<Thread>>::iterator it = block_queue.begin();
it != block_queue.end(); ++it) {
if ((*it)->tid == tid) { if ((*it)->tid == tid) {
// Found thread with correct tid // Found thread with correct tid

View File

@ -13,7 +13,7 @@
#define Scheduler_include__ #define Scheduler_include__
#include "Thread.h" #include "Thread.h"
#include "lib/mem/UniquePointer.h" #include "lib/memory/UniquePointer.h"
#include "lib/stream/Logger.h" #include "lib/stream/Logger.h"
#include "lib/container/Vector.h" #include "lib/container/Vector.h"
@ -26,17 +26,20 @@ private:
Container::Vector<Memory::unique_ptr<Thread>> ready_queue; Container::Vector<Memory::unique_ptr<Thread>> ready_queue;
Container::Vector<Memory::unique_ptr<Thread>> block_queue; Container::Vector<Memory::unique_ptr<Thread>> block_queue;
// NOTE: It makes sense to keep track of the active thread through this as it makes handling the // It makes sense to keep track of the active thread through this as it makes handling the
// unique_ptr easier and reduces the copying in the vector when cycling through the threads // unique_ptr easier and reduces the copying in the vector when cycling through the threads
// as we don't have to keep the active thread at the front (would only make sense with a queue)
Container::Vector<Memory::unique_ptr<Thread>>::iterator active = nullptr; Container::Vector<Memory::unique_ptr<Thread>>::iterator active = nullptr;
// Scheduler wird evt. von einer Unterbrechung vom Zeitgeber gerufen, // Scheduler wird evt. von einer Unterbrechung vom Zeitgeber gerufen,
// bevor er initialisiert wurde // bevor er initialisiert wurde
uint32_t idle_tid = 0U; uint32_t idle_tid = 0;
// Roughly the old dispatcher functionality // Roughly the old dispatcher functionality
void start(Container::Vector<Memory::unique_ptr<Thread>>::iterator next); // Start next without prev void
void switch_to(Thread *prev_raw, Container::Vector<Memory::unique_ptr<Thread>>::iterator next); // Switch from prev to next start(Container::Vector<Memory::unique_ptr<Thread>>::iterator next); // Start next without prev
void switch_to(Thread *prev_raw,
Container::Vector<Memory::unique_ptr<Thread>>::iterator next); // Switch from prev to next
// Kann nur vom Idle-Thread aufgerufen werden (erster Thread der vom Scheduler gestartet wird) // Kann nur vom Idle-Thread aufgerufen werden (erster Thread der vom Scheduler gestartet wird)
void enable_preemption(uint32_t tid) { idle_tid = tid; } void enable_preemption(uint32_t tid) { idle_tid = tid; }
@ -56,14 +59,14 @@ public:
block_queue.reserve(); block_queue.reserve();
} }
uint32_t get_active() const { [[nodiscard]] uint32_t get_active() const {
return (*active)->tid; return (*active)->tid;
} }
// Scheduler initialisiert? // Scheduler initialisiert?
// Zeitgeber-Unterbrechung kommt evt. bevor der Scheduler fertig // Zeitgeber-Unterbrechung kommt evt. bevor der Scheduler fertig
// intiialisiert wurde! // intiialisiert wurde!
bool preemption_enabled() const { return idle_tid != 0U; } [[nodiscard]] bool preemption_enabled() const { return idle_tid != 0U; }
// Scheduler starten // Scheduler starten
void schedule(); void schedule();

View File

@ -6,17 +6,26 @@
namespace Container { namespace Container {
template<typename T, const std::size_t N> /**
class Array { * This class implements a stack allocated array with bounds checking
public: * and iterator support.
*
* @tparam T The type of the objects
* @tparam N The number of elements the array can store
*/
template<typename T, const std::size_t N>
class Array {
public:
using iterator = ContinuousIterator<T>; using iterator = ContinuousIterator<T>;
private: private:
T buf[N]; T buf[N];
public: public:
Array() = default; // If i write default something like bse::array<int, 10> arr; is not initialized... Array() = default; // If i write default something like Container::Array<int, 10> arr; is not initialized...
// TODO: This doesn't account for initializer lists of the wrong length, last value should be repeated
// Only increment iterator when it < list.end() - 1?
// Construct like this: bse::array<int, 5> {1, 2, 3, 4, 5}; // Construct like this: bse::array<int, 5> {1, 2, 3, 4, 5};
Array(std::initializer_list<T> list) { Array(std::initializer_list<T> list) {
typename std::initializer_list<T>::iterator it = list.begin(); typename std::initializer_list<T>::iterator it = list.begin();
@ -26,18 +35,25 @@ namespace Container {
} }
} }
// TODO: Rest of constructors
iterator begin() { return iterator(&buf[0]); } iterator begin() { return iterator(&buf[0]); }
iterator begin() const { return iterator(&buf[0]); } iterator begin() const { return iterator(&buf[0]); }
iterator end() { return iterator(&buf[N]); } iterator end() { return iterator(&buf[N]); }
iterator end() const { return iterator(&buf[N]); } iterator end() const { return iterator(&buf[N]); }
constexpr T& operator[](std::size_t i) { return buf[i]; } constexpr T &operator[](std::size_t i) { return buf[i]; }
constexpr const T& operator[](std::size_t i) const { return buf[i]; }
T* data() { return &buf[0]; } constexpr const T &operator[](std::size_t i) const { return buf[i]; }
const T* data() const { return &buf[0]; }
void swap(Array<T, N>& other) { T *data() { return &buf[0]; }
const T *data() const { return &buf[0]; }
void swap(Array<T, N> &other) {
for (std::size_t i = 0; i < N; ++i) { for (std::size_t i = 0; i < N; ++i) {
std::swap(buf[i], other[i]); std::swap(buf[i], other[i]);
} }
@ -46,17 +62,17 @@ namespace Container {
// Array& other has to have size n: // Array& other has to have size n:
// arr1.swap_n<5>(arr2) => arr2 has size 5, arr1 has size >= 5 // arr1.swap_n<5>(arr2) => arr2 has size 5, arr1 has size >= 5
template<std::size_t n> template<std::size_t n>
void swap_n(Array<T, n>& other) { void swap_n(Array<T, n> &other) {
for (std::size_t i = 0; i < n; ++i) { for (std::size_t i = 0; i < n; ++i) {
std::swap(buf[i], other[i]); std::swap(buf[i], other[i]);
} }
} }
constexpr std::size_t size() const { [[nodiscard]] constexpr std::size_t size() const {
return N; return N;
} }
}; };
} // namespace bse } // namespace Container
#endif #endif

View File

@ -3,24 +3,27 @@
namespace Container { namespace Container {
// This iterator works for structures where the elements are adjacent in memory. // This iterator works for structures where the elements are adjacent in memory.
template<typename T> template<typename T>
class ContinuousIterator { class ContinuousIterator {
private: private:
T* ptr = nullptr; T *ptr = nullptr;
public: public:
ContinuousIterator() = delete; ContinuousIterator() = delete;
// Use const_cast as the iterator has to increment the pointer // Use const_cast as the iterator has to increment the pointer
ContinuousIterator(const T* ptr) : ptr(const_cast<T*>(ptr)) {} // Don't make this explicit: Want to write Container::Vector<int>::iterator = nullptr;
ContinuousIterator(const T *ptr) : ptr(const_cast<T *>(ptr)) {}
ContinuousIterator& operator++() { // TODO: Rest of constructors
ContinuousIterator &operator++() {
++ptr; ++ptr;
return *this; return *this;
} }
ContinuousIterator& operator--() { ContinuousIterator &operator--() {
--ptr; --ptr;
return *this; return *this;
} }
@ -34,26 +37,34 @@ namespace Container {
} }
// Convenience // Convenience
T* operator->() { return ptr; } T *operator->() { return ptr; }
const T* operator->() const { return ptr; }
T& operator*() { return *ptr; }
const T& operator*() const { return *ptr; }
bool operator<(const ContinuousIterator& other) const { return ptr < other.ptr; } const T *operator->() const { return ptr; }
bool operator<=(const ContinuousIterator& other) const { return ptr <= other.ptr; }
bool operator>(const ContinuousIterator& other) const { return ptr > other.ptr; } T &operator*() { return *ptr; }
bool operator>=(const ContinuousIterator& other) const { return ptr >= other.ptr; }
bool operator==(const ContinuousIterator& other) const { return ptr == other.ptr; } const T &operator*() const { return *ptr; }
bool operator!=(const ContinuousIterator& other) const { return ptr != other.ptr; }
bool operator<(const ContinuousIterator &other) const { return ptr < other.ptr; }
bool operator<=(const ContinuousIterator &other) const { return ptr <= other.ptr; }
bool operator>(const ContinuousIterator &other) const { return ptr > other.ptr; }
bool operator>=(const ContinuousIterator &other) const { return ptr >= other.ptr; }
bool operator==(const ContinuousIterator &other) const { return ptr == other.ptr; }
bool operator!=(const ContinuousIterator &other) const { return ptr != other.ptr; }
template<typename t> template<typename t>
friend unsigned int distance(const ContinuousIterator<t>& first, const ContinuousIterator<t>& last); friend unsigned int distance(const ContinuousIterator<t> &first, const ContinuousIterator<t> &last);
}; };
template<typename T> template<typename T>
unsigned int distance(const ContinuousIterator<T>& first, const ContinuousIterator<T>& last) { unsigned int distance(const ContinuousIterator<T> &first, const ContinuousIterator<T> &last) {
return last.ptr - first.ptr; return last.ptr - first.ptr;
} }
} // namespace bse } // namespace bse

View File

@ -11,16 +11,16 @@
// https://en.cppreference.com/w/cpp/container/vector // https://en.cppreference.com/w/cpp/container/vector
namespace Container { namespace Container {
template<typename T> template<typename T>
class Vector { class Vector {
public: public:
using iterator = ContinuousIterator<T>; using iterator = ContinuousIterator<T>;
private: private:
static constexpr const std::size_t default_cap = 10; // Arbitrary but very small because this isn't a real OS :( static constexpr const std::size_t default_cap = 10; // Arbitrary but very small because this isn't a real OS :(
static constexpr const std::size_t min_cap = 5; // Slots to allocate extra when array full static constexpr const std::size_t min_cap = 5; // Slots to allocate extra when array full
T* buf = nullptr; // Heap allocated as size needs to change during runtime T *buf = nullptr; // Heap allocated as size needs to change during runtime
// Can't use Array for the same reason so we use a C Style array // Can't use Array for the same reason so we use a C Style array
std::size_t buf_pos = 0; std::size_t buf_pos = 0;
std::size_t buf_cap = 0; std::size_t buf_cap = 0;
@ -57,7 +57,7 @@ namespace Container {
// 4. Sets new pos/cap // 4. Sets new pos/cap
void switch_buf(std::size_t cap) { void switch_buf(std::size_t cap) {
// Alloc new array // Alloc new array
T* new_buf = new T[cap]; T *new_buf = new T[cap];
// Swap current elements to new array // Swap current elements to new array
for (std::size_t i = 0; i < size(); ++i) { for (std::size_t i = 0; i < size(); ++i) {
@ -99,7 +99,7 @@ namespace Container {
} }
} }
public: public:
explicit Vector(bool lazy = false) { explicit Vector(bool lazy = false) {
if (!lazy) { // I added this as a work around, the scheduler can't initialize the queues right if (!lazy) { // I added this as a work around, the scheduler can't initialize the queues right
// away because when the scheduler is started the allocator is not ready. // away because when the scheduler is started the allocator is not ready.
@ -107,6 +107,8 @@ namespace Container {
} }
}; };
// TODO: This doesn't account for initializer lists of the wrong length, last value should be repeated
// Only increment iterator when it < list.end() - 1?
// Initialize like this: bse::vector<int> vec {1, 2, 3, 4, 5}; // Initialize like this: bse::vector<int> vec {1, 2, 3, 4, 5};
Vector(std::initializer_list<T> list) : buf_cap(list.size()), buf(new T[buf_cap]) { Vector(std::initializer_list<T> list) : buf_cap(list.size()), buf(new T[buf_cap]) {
typename std::initializer_list<T>::iterator it = list.begin(); typename std::initializer_list<T>::iterator it = list.begin();
@ -116,14 +118,13 @@ namespace Container {
} }
} }
Vector(const Vector &copy) : buf_pos(copy.buf_pos), buf_cap(copy.buf_cap), buf(new T[buf_cap]) {
Vector(const Vector& copy) : buf_pos(copy.buf_pos), buf_cap(copy.buf_cap), buf(new T[buf_cap]) {
for (unsigned int i = 0; i < buf_pos; ++i) { for (unsigned int i = 0; i < buf_pos; ++i) {
buf[i] = copy[i]; // Does a copy since copy is marked const reference buf[i] = copy[i]; // Does a copy since copy is marked const reference
} }
} }
Vector& operator=(const Vector& copy) { Vector &operator=(const Vector &copy) {
if (this != &copy) { if (this != &copy) {
~Vector(); ~Vector();
@ -137,13 +138,13 @@ namespace Container {
return *this; return *this;
} }
Vector(Vector&& move) noexcept : buf(move.buf), buf_pos(move.buf_pos), buf_cap(move.buf_cap) { Vector(Vector &&move) noexcept: buf(move.buf), buf_pos(move.buf_pos), buf_cap(move.buf_cap) {
move.buf_cap = 0; move.buf_cap = 0;
move.buf_pos = 0; move.buf_pos = 0;
move.buf = nullptr; move.buf = nullptr;
} }
Vector& operator=(Vector&& move) noexcept { Vector &operator=(Vector &&move) noexcept {
if (this != &move) { if (this != &move) {
buf_cap = move.buf_cap; buf_cap = move.buf_cap;
buf_pos = move.buf_pos; buf_pos = move.buf_pos;
@ -169,19 +170,22 @@ namespace Container {
// Iterator // Iterator
iterator begin() { return iterator(&buf[0]); } iterator begin() { return iterator(&buf[0]); }
iterator begin() const { return iterator(&buf[0]); } iterator begin() const { return iterator(&buf[0]); }
iterator end() { return iterator(&buf[size()]); } iterator end() { return iterator(&buf[size()]); }
iterator end() const { return iterator(&buf[size()]); } iterator end() const { return iterator(&buf[size()]); }
// Add elements // Add elements
// https://en.cppreference.com/w/cpp/container/vector/push_back // https://en.cppreference.com/w/cpp/container/vector/push_back
void push_back(const T& copy) { void push_back(const T &copy) {
buf[size()] = copy; buf[size()] = copy;
++buf_pos; ++buf_pos;
min_expand(); min_expand();
} }
void push_back(T&& move) { void push_back(T &&move) {
buf[size()] = std::move(move); buf[size()] = std::move(move);
++buf_pos; ++buf_pos;
min_expand(); min_expand();
@ -189,7 +193,7 @@ namespace Container {
// https://en.cppreference.com/w/cpp/container/vector/insert // https://en.cppreference.com/w/cpp/container/vector/insert
// The element will be inserted before the pos iterator, pos can be the end() iterator // The element will be inserted before the pos iterator, pos can be the end() iterator
iterator insert(iterator pos, const T& copy) { iterator insert(iterator pos, const T &copy) {
std::size_t idx = distance(begin(), pos); // begin() does init if necessary std::size_t idx = distance(begin(), pos); // begin() does init if necessary
copy_right(idx); // nothing will be done if pos == end() copy_right(idx); // nothing will be done if pos == end()
buf[idx] = copy; buf[idx] = copy;
@ -198,7 +202,7 @@ namespace Container {
return iterator(&buf[idx]); return iterator(&buf[idx]);
} }
iterator insert(iterator pos, T&& move) { iterator insert(iterator pos, T &&move) {
std::size_t idx = distance(begin(), pos); // begin() does init if necessary std::size_t idx = distance(begin(), pos); // begin() does init if necessary
copy_right(idx); copy_right(idx);
buf[idx] = std::move(move); buf[idx] = std::move(move);
@ -219,27 +223,27 @@ namespace Container {
} }
// Access // Access
T& front() { T &front() {
return buf[0]; return buf[0];
} }
const T& front() const { const T &front() const {
return buf[0]; return buf[0];
} }
T& back() { T &back() {
return buf[size() - 1]; return buf[size() - 1];
} }
const T& back() const { const T &back() const {
return buf[size() - 1]; return buf[size() - 1];
} }
T& operator[](std::size_t pos) { T &operator[](std::size_t pos) {
return buf[pos]; return buf[pos];
} }
const T& operator[](std::size_t pos) const { const T &operator[](std::size_t pos) const {
return buf[pos]; return buf[pos];
} }
@ -278,13 +282,13 @@ namespace Container {
[[nodiscard]] bool initialized() const { [[nodiscard]] bool initialized() const {
return buf != nullptr; return buf != nullptr;
} }
}; };
// Erase all elements that match a predicate // Erase all elements that match a predicate
// NOTE: pred is no real predicate as one would need closures for this, but we don't have <functional> available // NOTE: pred is no real predicate as one would need closures for this, but we don't have <functional> available
// This means the result has to be passed separately and the function differs from the c++20 std::erase_if // This means the result has to be passed separately and the function differs from the c++20 std::erase_if
template<typename T, typename arg> template<typename T, typename arg>
std::size_t erase_if(Vector<T>& vec, arg (*pred)(const T&), arg result) { std::size_t erase_if(Vector<T> &vec, arg (*pred)(const T &), arg result) {
std::size_t erased_els = 0; std::size_t erased_els = 0;
for (typename Vector<T>::Iterator it = vec.begin(); it != vec.end(); /*Do nothing*/) { for (typename Vector<T>::Iterator it = vec.begin(); it != vec.end(); /*Do nothing*/) {
if (pred(*it) == result) { if (pred(*it) == result) {
@ -295,8 +299,8 @@ namespace Container {
} }
} }
return erased_els; return erased_els;
} }
} // namespace bse } // namespace Container
#endif #endif

View File

@ -1,132 +0,0 @@
#ifndef UniquePointer_Include_H_
#define UniquePointer_Include_H_
#include <utility>
// https://en.cppreference.com/w/cpp/memory/unique_ptr
// NOTE: Because of the way the scheduling works our functions are not executed completely.
// This means that object destructors are not called if the objects live in a scope
// that is left because of thread switching (e.g. a threads run function)...
namespace Memory {
// T is the type make_unique is called with, meaning int or int[] for example
// T_ is the bare type without extents (int in both cases), so we have a
// int* pointer type for both unique_ptr<int> and unique_ptr<int[]>
template<typename T>
class unique_ptr {
private:
using T_ = std::remove_extent_t<T>;
T_* ptr = nullptr;
// Only use make_unique or reset for construction
unique_ptr(T_* ptr) : ptr(ptr) {}
// I didn't want to introduce custom deleters for my small needs
void del() {
if constexpr (std::is_array_v<T>) {
delete[] ptr;
} else {
delete ptr;
}
ptr = nullptr;
}
public:
// Forbid copying
unique_ptr(const unique_ptr& copy) = delete;
unique_ptr& operator=(const unique_ptr& copy) = delete;
// Construction
unique_ptr() = default; // Allow declaration without explicit definition
template<typename t, typename... Args>
friend typename std::enable_if_t<!std::is_array_v<t>, unique_ptr<t>>
make_unique(Args&&... args);
template<typename t>
friend typename std::enable_if_t<std::is_array_v<t>, unique_ptr<t>>
make_unique(std::size_t size);
// Deletion
~unique_ptr() {
del();
}
// Moving
unique_ptr(unique_ptr&& move) noexcept { reset(move.release()); };
// Implicit upcasting is needed: for sth like
// unique_ptr<Thread> ptr = make_unique<IdleThread>();
// IdleThread is derived from Thread so the assert passes
template<typename t>
unique_ptr(unique_ptr<t>&& move) noexcept {
static_assert(std::is_base_of_v<T, t>, "Has to be derived type");
reset(move.release());
}
unique_ptr& operator=(unique_ptr&& move) noexcept {
reset(move.release());
return *this;
}
// Resetting: Replaces managed object, deleting the old one
void reset() { del(); }
void reset(T_* pt) {
del();
ptr = pt;
}
// Release: Releases ownership without deletion
T_* release() {
// T* old = ptr;
// ptr = nullptr;
// return old;
return std::exchange(ptr, nullptr);
}
// Get: Access the raw pointer without taking ownership
T_* get() const {
return ptr;
}
// Pointer operators
T_* operator->() { return ptr; }
const T_* operator->() const { return ptr; }
T_& operator*() { return *ptr; }
const T_& operator*() const { return *ptr; }
explicit operator void*() const { return ptr; }
explicit operator bool() const { return (ptr != nullptr); }
bool operator==(const unique_ptr& other) const { return ptr == other.ptr; }
// These are only for array unique_ptr but I didn't enforce that
T_& operator[](std::size_t i) { return ptr[i]; }
const T_& operator[](std::size_t i) const { return ptr[i]; }
};
// make_unique implementation =======================================
// Allow initialization of unique_ptr<int> with optional constructor arguments
// and unique_ptr<int[]> without constructor arguments
template<typename T, typename... Args>
// We make the return type dependent on whether T is an array type or not
typename std::enable_if_t<!std::is_array_v<T>, unique_ptr<T>>
make_unique(Args&&... args) {
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<typename T>
typename std::enable_if_t<std::is_array_v<T>, unique_ptr<T>>
make_unique(std::size_t size) {
using T_ = typename std::remove_extent_t<T>;
return unique_ptr<T>(new T_[size]);
}
} // namespace bse
#endif

View File

@ -0,0 +1,140 @@
#ifndef UniquePointer_Include_H_
#define UniquePointer_Include_H_
#include <utility>
// https://en.cppreference.com/w/cpp/memory/unique_ptr
// NOTE: Because of the way the scheduling works our functions are not executed completely.
// This means that object destructors are not called if the objects live in a scope
// that is left because of thread switching (e.g. a threads run function)...
namespace Memory {
// T is the type make_unique is called with, meaning int or int[] for example
// T_ is the bare type without extents (int in both cases), so we have a
// int* pointer type for both unique_ptr<int> and unique_ptr<int[]>
template<typename T>
class unique_ptr {
private:
using T_ = std::remove_extent_t<T>;
T_ *ptr = nullptr;
// TODO: Was there a reason I didn't mark this explicit?
// Only use make_unique or reset for construction
unique_ptr(T_ *ptr) : ptr(ptr) {}
// I didn't want to introduce custom deleters for my small needs
void del() {
if constexpr (std::is_array_v<T>) {
delete[] ptr;
} else {
delete ptr;
}
ptr = nullptr;
}
public:
// Forbid copying
unique_ptr(const unique_ptr &copy) = delete;
unique_ptr &operator=(const unique_ptr &copy) = delete;
// Construction
unique_ptr() = default; // Allow declaration without explicit definition
template<typename t, typename... Args>
friend typename std::enable_if_t<!std::is_array_v<t>, unique_ptr<t>>
make_unique(Args &&... args);
template<typename t>
friend typename std::enable_if_t<std::is_array_v<t>, unique_ptr<t>>
make_unique(std::size_t size);
// Deletion
~unique_ptr() {
del();
}
// Moving
unique_ptr(unique_ptr &&move) noexcept { reset(move.release()); };
// Implicit upcasting is needed: for sth like
// unique_ptr<Thread> ptr = make_unique<IdleThread>();
// IdleThread is derived from Thread so the assert passes
template<typename t>
unique_ptr(unique_ptr<t> &&move) noexcept {
static_assert(std::is_base_of_v<T, t>, "Has to be derived type");
reset(move.release());
}
unique_ptr &operator=(unique_ptr &&move) noexcept {
reset(move.release());
return *this;
}
// Resetting: Replaces managed object, deleting the old one
void reset() { del(); }
void reset(T_ *pt) {
del();
ptr = pt;
}
// Release: Releases ownership without deletion
T_ *release() {
// T* old = ptr;
// ptr = nullptr;
// return old;
return std::exchange(ptr, nullptr);
}
// Get: Access the raw pointer without taking ownership
T_ *get() const {
return ptr;
}
// Pointer operators
T_ *operator->() { return ptr; }
const T_ *operator->() const { return ptr; }
T_ &operator*() { return *ptr; }
const T_ &operator*() const { return *ptr; }
explicit operator void *() const { return ptr; }
explicit operator bool() const { return (ptr != nullptr); }
bool operator==(const unique_ptr &other) const { return ptr == other.ptr; }
// These are only for array unique_ptr but I didn't enforce that
T_ &operator[](std::size_t i) { return ptr[i]; }
const T_ &operator[](std::size_t i) const { return ptr[i]; }
};
// make_unique implementation =======================================
// Allow initialization of unique_ptr<int> with optional constructor arguments
// and unique_ptr<int[]> without constructor arguments
template<typename T, typename... Args>
// We make the return type dependent on whether T is an array type or not
typename std::enable_if_t<!std::is_array_v<T>, unique_ptr<T>>
make_unique(Args &&... args) {
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<typename T>
typename std::enable_if_t<std::is_array_v<T>, unique_ptr<T>>
make_unique(std::size_t size) {
using T_ = typename std::remove_extent_t<T>;
return unique_ptr<T>(new T_[size]);
}
} // namespace bse
#endif

View File

@ -51,8 +51,6 @@ private:
CGA::color color_bg; CGA::color color_bg;
bool blink; bool blink;
friend class Logger; // Give access to the color
public: public:
CGA_Stream(CGA_Stream &copy) = delete; // Verhindere Kopieren CGA_Stream(CGA_Stream &copy) = delete; // Verhindere Kopieren
@ -60,9 +58,9 @@ public:
pos = 0; pos = 0;
} }
// CAn't make singleton because atexit // Can't make singleton because atexit
// ~CGA_Stream() override = default; // ~CGA_Stream() override = default;
void lock() { sem.p(); } void lock() { sem.p(); }