created retentive class family

This commit is contained in:
Lauchmelder 2021-12-16 15:38:52 +01:00
parent 4d59b8a53d
commit 75df9ad96d
4 changed files with 254 additions and 71 deletions

View file

@ -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})

View file

@ -1,8 +1,6 @@
#pragma once
#include <vector>
#include <array>
#include <functional>
#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<std::vector<Type>, 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<Type>& operator[](size_t index)
{
return data[index];
}
/**
* @brief Get the most up-to-date array
*
* @return The array with generation 0
*/
std::vector<Type>& 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<void(void)> 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<void(void)> rule)
{
this->rule = rule;
}
private:
std::array<std::vector<Type>, AttentionSpan + 1> data;
std::function<void(void)> rule = std::bind([]() {}); // Do nothing by default
};

99
lib/RetentiveEntity.hpp Normal file
View file

@ -0,0 +1,99 @@
#pragma once
#include <vector>
#include <array>
#include <functional>
/**
* @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<Type, AttentionSpan>& other) = delete;
RetentiveEntity(RetentiveEntity<Type, AttentionSpan>&& other) = delete;
RetentiveEntity<Type, AttentionSpan>& operator=(const RetentiveEntity<Type, AttentionSpan>& other) = delete;
RetentiveEntity<Type, AttentionSpan>& operator=(RetentiveEntity<Type, AttentionSpan>&& 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<void(void)> rule)
{
CycleGenerations();
rule();
}
/**
* @brief Set a general rule for evolving the data
*
* @param rule The new evolution rule
*/
void SetEvolutionRule(std::function<void(void)> rule)
{
this->rule = rule;
}
protected:
/**
* @brief Swaps the objects in the array
*/
virtual void CycleGenerations() = 0;
protected:
std::array<Type, AttentionSpan + 1> data;
std::function<void(void)> rule = std::bind([]() {}); // Do nothing by default
};

149
lib/RetentiveObject.hpp Normal file
View file

@ -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<Type*, AttentionSpan>
{
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<Type, AttentionSpan>& operator=(const RetentiveObject<Type, AttentionSpan>& 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<Type, AttentionSpan>& operator=(RetentiveObject<Type, AttentionSpan>&& 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<Type, AttentionSpan>& 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<Type, AttentionSpan>&& 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]);
}
}
};