From 75df9ad96dd877f7ab72b537e7435144af61c2a5 Mon Sep 17 00:00:00 2001 From: Lauchmelder Date: Thu, 16 Dec 2021 15:38:52 +0100 Subject: [PATCH] created retentive class family --- lib/CMakeLists.txt | 2 +- lib/RetentiveArray.hpp | 75 ++------------------ lib/RetentiveEntity.hpp | 99 ++++++++++++++++++++++++++ lib/RetentiveObject.hpp | 149 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 254 insertions(+), 71 deletions(-) create mode 100644 lib/RetentiveEntity.hpp create mode 100644 lib/RetentiveObject.hpp diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d4c3b4a..9b2560d 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,7 +1,7 @@ add_library(nm_utils STATIC "Window.cpp" - "RetentiveArray.hpp") + "RetentiveArray.hpp" "RetentiveObject.hpp" "RetentiveEntity.hpp") target_include_directories(nm_utils PUBLIC ${SDL2_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(nm_utils PUBLIC ${SDL2_LIBRARIES}) diff --git a/lib/RetentiveArray.hpp b/lib/RetentiveArray.hpp index bba74dd..c2b5b43 100644 --- a/lib/RetentiveArray.hpp +++ b/lib/RetentiveArray.hpp @@ -1,8 +1,6 @@ #pragma once -#include -#include -#include +#include "RetentiveEntity.hpp" /** * @brief An array that remembers its past. @@ -24,7 +22,7 @@ template< typename Type, unsigned int AttentionSpan, typename std::enable_if_t<(AttentionSpan > 0), bool> = true> -class RetentiveArray +class RetentiveArray : public RetentiveEntity, AttentionSpan> { public: /** @@ -104,77 +102,14 @@ public: // Do nothing } - /** - * @brief Get the array from `index` generations ago - * - * @param index Amount of generations to go backwards in time - * @return The array from before `index` generations - */ - std::vector& operator[](size_t index) - { - return data[index]; - } - - /** - * @brief Get the most up-to-date array - * - * @return The array with generation 0 - */ - std::vector& Current() - { - return data[0]; - } - - /** - * @brief Evolve the data in the array - * - * Simply calls the member function pointer. The called function is then - * supposed to perform whatever is needed to compute the new array contents. - * - * The data is swapped BEFORE calling this function, so the evolution function should - * modify the data in the Current() vector (index 0) - */ - void Evolve() - { - // Evolve the array - Evolve(rule); - } - - /** - * @brief Evolve the data in the array - * - * Simply calls the provided function pointer. The called function is then - * supposed to perform whatever is needed to compute the new array contents. - * - * The data is swapped BEFORE calling this function, so the evolution function should - * modify the data in the Current() vector (index 0) - * - * @param rule A function that evolves the data in the array - */ - void Evolve(std::function rule) +private: + void CycleGenerations() override { // Cycle the array contents, so that the previous "current" array is now the 2nd. // The 2nd becomes the 3rd etc, and the former last array becomes the new current for (int n = 1; n <= AttentionSpan; n++) { - data[0].swap(data[1]); + data[0].swap(data[n]); } - - rule(); } - - /** - * @brief Set a general rule for evolving the data - * - * @param rule The new evolution rule - */ - void SetEvolutionRule(std::function rule) - { - this->rule = rule; - } - - -private: - std::array, AttentionSpan + 1> data; - std::function rule = std::bind([]() {}); // Do nothing by default }; diff --git a/lib/RetentiveEntity.hpp b/lib/RetentiveEntity.hpp new file mode 100644 index 0000000..79203f0 --- /dev/null +++ b/lib/RetentiveEntity.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include + +/** + * @brief Abstract base type for all retentive things + * + * @tparam Type The object to give a memory to + * @tparam AttentionSpan How many generations of the data will be remembered by the object + */ +template< + typename Type, + unsigned int AttentionSpan, + typename std::enable_if_t<(AttentionSpan > 0), bool> = true> +class RetentiveEntity +{ +public: + RetentiveEntity() {} + RetentiveEntity(const RetentiveEntity& other) = delete; + RetentiveEntity(RetentiveEntity&& other) = delete; + RetentiveEntity& operator=(const RetentiveEntity& other) = delete; + RetentiveEntity& operator=(RetentiveEntity&& other) = delete; + + /** + * @brief Get the entity from `index` generations ago + * + * @param index Amount of generations to go backwards in time + * @return The entity from before `index` generations + */ + virtual Type& operator[](size_t index) + { + return data[index]; + } + + /** + * @brief Get the most up-to-date entity + * + * @return The entity with generation 0 + */ + virtual Type& Current() + { + return data[0]; + } + + /** + * @brief Evolve the data in the entity + * + * Simply calls the member function pointer. The called function is then + * supposed to perform whatever is needed to compute the new object contents. + * + * The data is swapped BEFORE calling this function, so the evolution function should + * modify the data in the Current() vector (index 0) + */ + void Evolve() + { + // Evolve the object + CycleGenerations(); + rule(); + } + + /** + * @brief Evolve the data in the object + * + * Simply calls the provided function pointer. The called function is then + * supposed to perform whatever is needed to compute the new object contents. + * + * The data is swapped BEFORE calling this function, so the evolution function should + * modify the data in the Current() vector (index 0) + * + * @param rule A function that evolves the data in the object + */ + void Evolve(std::function rule) + { + CycleGenerations(); + rule(); + } + + /** + * @brief Set a general rule for evolving the data + * + * @param rule The new evolution rule + */ + void SetEvolutionRule(std::function rule) + { + this->rule = rule; + } + +protected: + /** + * @brief Swaps the objects in the array + */ + virtual void CycleGenerations() = 0; + +protected: + std::array data; + std::function rule = std::bind([]() {}); // Do nothing by default +}; diff --git a/lib/RetentiveObject.hpp b/lib/RetentiveObject.hpp new file mode 100644 index 0000000..397ebf9 --- /dev/null +++ b/lib/RetentiveObject.hpp @@ -0,0 +1,149 @@ +#pragma once + +#include "RetentiveEntity.hpp" + +/** + * @brief An object that remembers its past. + * + * You most likely want to use a RetentiveArray instead. + * This structure is only really useful if you have a custom container class + * that you need to evolve over time. + * + * A retentive object is a kind of buffered object that "remembers" + * the state of the object from the last n generations. + * + * This structure handles the evolution of these kinds of objects and takes + * care of the memory allocation/freeing as well as properly swapping the + * object pointers. + * + * @tparam Type The object to give a memory to + * @tparam AttentionSpan How many generations of the data will be remembered by the object + */ +template< + typename Type, + unsigned int AttentionSpan, + typename std::enable_if_t<(AttentionSpan > 0), bool> = true> +class RetentiveObject : public RetentiveEntity +{ +public: + /** + * @brief Constructs a new, empty retentive object + */ + RetentiveObject() + { + // Create new vectors for every generation that needs to be remembered + for (int n = 0; n <= AttentionSpan; n++) + { + data[n] = new Type(); + } + } + + /** + * @brief Constructs a new, empty retentive object + * + * @param size The size of the data contained in the object + */ + RetentiveObject(const Type& initVal) + { + // Create new vectors for every generation that needs to be remembered + for (int n = 0; n <= AttentionSpan; n++) + { + data[n] = new Type(initVal); + } + } + + /** + * @brief Constructs a new retentive object with the data of another object + * + * @param other The retentive object to copy from + */ + RetentiveObject& operator=(const RetentiveObject& other) + { + for (int n = 0; n <= AttentionSpan; n++) + { + *data[n] = *other.data[n]; + } + + return *this; + } + + /** + * @brief Constructs a new retentive object pointing to the data of an rvalue + * + * @param other The retentive object to set its data to + */ + RetentiveObject& operator=(RetentiveObject&& other) + { + for (int n = 0; n <= AttentionSpan; n++) + { + data[n] = other.data[n]; + other.data[n] = nullptr; + } + + return *this; + } + + /** + * @brief Constructs a new retentive object with the data of another object + * + * @param other The retentive object to copy from + */ + RetentiveObject(const RetentiveObject& other) + { + *this = other; + } + + /** + * @brief Constructs a new retentive object pointing to the data of an rvalue + * + * @param other The retentive object to set its data to + */ + RetentiveObject(RetentiveObject&& other) + { + *this = other; + } + + ~RetentiveObject() + { + for (int n = 0; n <= AttentionSpan; n++) + { + if (data[n] != nullptr) + delete data[n]; + } + } + + /** + * @brief Get the object from `index` generations ago + * + * @param index Amount of generations to go backwards in time + * @return The object from before `index` generations + */ + Type& operator[](size_t index) override + { + return *(data[index]); + } + + /** + * @brief Get the most up-to-date object + * + * @return The object with generation 0 + */ + Type& Current() override + { + return *(data[0]); + } + +private: + /** + * @brief Swaps the objects in the array + */ + void CycleGenerations() override + { + // Cycle the object contents, so that the previous "current" object is now the 2nd. + // The 2nd becomes the 3rd etc, and the former last object becomes the new current + for (int n = 1; n <= AttentionSpan; n++) + { + std::swap(data[0], data[n]); + } + } +};