aboutsummaryrefslogtreecommitdiff
path: root/include/SafeVector.hpp
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2024-08-22 21:44:06 +0200
committerdec05eba <dec05eba@protonmail.com>2024-08-23 18:53:19 +0200
commit54c60d9a18d103011a12939c5029dd35a8e9e200 (patch)
treefefba1d63a13df6c135a6b1f5e8640b12a9c0ff5 /include/SafeVector.hpp
parentba007c2b69dca6813c86115b2c1834de45c886be (diff)
Start on file chooser, page stack
Diffstat (limited to 'include/SafeVector.hpp')
-rw-r--r--include/SafeVector.hpp127
1 files changed, 127 insertions, 0 deletions
diff --git a/include/SafeVector.hpp b/include/SafeVector.hpp
new file mode 100644
index 0000000..cea9cb9
--- /dev/null
+++ b/include/SafeVector.hpp
@@ -0,0 +1,127 @@
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <functional>
+
+// A vector that can be modified while iterating
+template <typename T>
+class SafeVector {
+public:
+ using PointerType = typename std::pointer_traits<T>::element_type*;
+
+ void push_back(T item) {
+ data.push_back(std::move(item));
+ }
+
+ // Safe to call when vector is empty
+ void pop_back() {
+ if(!data.empty())
+ data.pop_back();
+ }
+
+ // Might not remove the data immediately if inside for_each loop.
+ // In that case the item is remove at the end of the loop.
+ void remove(PointerType item_to_remove) {
+ if(for_each_depth == 0)
+ remove_item(item_to_remove);
+ else
+ remove_queue.push_back(item_to_remove);
+ }
+
+ // Safe to call when vector is empty, in which case it returns nullptr
+ T* back() {
+ if(data.empty())
+ return nullptr;
+ else
+ return &data.back();
+ }
+
+ void clear() {
+ data.clear();
+ remove_queue.clear();
+ }
+
+ // Return true from |callback| to continue. This function returns false if |callback| returned false
+ bool for_each(std::function<bool(T &t)> callback) {
+ bool result = true;
+ ++for_each_depth;
+
+ for(size_t i = 0; i < data.size(); ++i) {
+ result = callback(data[i]);
+ if(!result)
+ break;
+ }
+
+ --for_each_depth;
+ if(for_each_depth == 0) {
+ for(PointerType item_to_remove : remove_queue) {
+ remove_item(item_to_remove);
+ }
+ remove_queue.clear();
+ }
+
+ return result;
+ }
+
+ // Return true from |callback| to continue. This function returns false if |callback| returned false
+ bool for_each_reverse(std::function<bool(T &t)> callback) {
+ bool result = true;
+ ++for_each_depth;
+
+ int i = (int)data.size() - 1;
+ while(true) {
+ // pop_back can be called while iterating so handle that case
+ if(i >= (int)data.size())
+ i = (int)data.size() - 1;
+
+ if(i < 0)
+ break;
+
+ result = callback(data[i]);
+ if(!result)
+ break;
+
+ --i;
+ }
+
+ --for_each_depth;
+ if(for_each_depth == 0) {
+ for(PointerType item_to_remove : remove_queue) {
+ remove_item(item_to_remove);
+ }
+ remove_queue.clear();
+ }
+
+ return result;
+ }
+
+ T& operator[](size_t index) {
+ return data[index];
+ }
+
+ const T& operator[](size_t index) const {
+ return data[index];
+ }
+
+ size_t size() const {
+ return data.size();
+ }
+
+ bool empty() const {
+ return data.empty();
+ }
+private:
+ void remove_item(PointerType item_to_remove) {
+ for(auto it = data.begin(), end = data.end(); it != end; ++it) {
+ if(&*(*it) == item_to_remove) {
+ data.erase(it);
+ return;
+ }
+ }
+ }
+private:
+ std::vector<T> data;
+ std::vector<PointerType> remove_queue;
+ int for_each_depth = 0;
+}; \ No newline at end of file