diff options
author | HampusM <hampus@hampusmat.com> | 2022-03-19 13:21:54 +0100 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2022-06-13 17:56:56 +0200 |
commit | 55c93fe609888be73677317978959040cf35b2ff (patch) | |
tree | 41ee53961eb5667136432b69773ef7acf3ad8259 | |
parent | 020303df1410d10546f53d0bfee4f48797d4f067 (diff) |
refactor: implement matrix iterator
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/engine/graphics/string_matrix.cpp | 117 | ||||
-rw-r--r-- | src/engine/graphics/string_matrix.hpp | 61 | ||||
-rw-r--r-- | src/engine/matrix_iterator.hpp | 65 | ||||
-rw-r--r-- | src/engine/matrix_iterator.tpp | 112 | ||||
-rw-r--r-- | src/interfaces/matrix.hpp | 42 | ||||
-rw-r--r-- | test/CMakeLists.txt | 45 | ||||
-rw-r--r-- | test/main.cpp | 2 | ||||
-rw-r--r-- | test/string_matrix.test.cpp | 53 |
9 files changed, 430 insertions, 71 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 66bad3a..1dfce91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,3 +9,7 @@ project(game-of-life CXX) add_subdirectory(lib) add_subdirectory(src) + +if("${TESTING}") + add_subdirectory(test) +endif() diff --git a/src/engine/graphics/string_matrix.cpp b/src/engine/graphics/string_matrix.cpp index ae06755..3920b8e 100644 --- a/src/engine/graphics/string_matrix.cpp +++ b/src/engine/graphics/string_matrix.cpp @@ -1,30 +1,59 @@ #include "string_matrix.hpp" +#include <iostream> +#include <ranges> + StringMatrix::StringMatrix(const Bounds &bounds) noexcept - : _rows(bounds.get_height()), _columns(bounds.get_width()) + : _matrix(new std::string_view *[bounds.get_height()]), + _row_cnt(bounds.get_height()), + _column_cnt(bounds.get_width()) { - _matrix.reserve(bounds.get_height()); - _matrix.assign(_matrix.capacity(), std::vector<std::string_view>(bounds.get_width())); + for (gsl::owner<std::string_view **> row = _matrix; row != _matrix + _row_cnt; row++) + { + *row = static_cast<gsl::owner<std::string_view *>>( + new std::string_view[bounds.get_width()]); + } }; +StringMatrix::StringMatrix(const StringMatrix &string_matrix) noexcept + : _matrix(new std::string_view *[string_matrix._row_cnt]), + _row_cnt(string_matrix._row_cnt), + _column_cnt(string_matrix._column_cnt) +{ + _copy_matrix_from(string_matrix); +} + +StringMatrix::StringMatrix(StringMatrix &&string_matrix) noexcept + : _matrix(string_matrix._matrix), + _row_cnt(string_matrix._row_cnt), + _column_cnt(string_matrix._column_cnt) +{ + string_matrix._matrix = nullptr; +} + +StringMatrix::~StringMatrix() noexcept +{ + _delete_matrix(); +} + void StringMatrix::fill(std::string_view element) noexcept { - for (uint32_t row = 0U; row < _matrix.capacity(); row++) + for (auto row : *this) { - std::vector<std::string_view> row_vector = _matrix[row]; - - for (uint32_t column = 0U; column < row_vector.capacity(); column++) + for (auto &col : row) { - _matrix[row][column] = element; + col = element; } } } std::string_view StringMatrix::get(const Vector2 &pos) const noexcept { + auto x = static_cast<std::size_t>(pos.get_x()); auto y = static_cast<std::size_t>(pos.get_y()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) return _matrix[y][x]; } @@ -33,15 +62,79 @@ void StringMatrix::set(const Vector2 &pos, std::string_view element) noexcept auto x = static_cast<std::size_t>(pos.get_x()); auto y = static_cast<std::size_t>(pos.get_y()); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) _matrix[y][x] = element; } -uint32_t StringMatrix::rows() const noexcept +uint32_t StringMatrix::get_row_cnt() const noexcept +{ + return _row_cnt; +} + +uint32_t StringMatrix::get_column_cnt() const noexcept +{ + return _column_cnt; +} + +MatrixIterator<std::string_view> StringMatrix::begin() const noexcept +{ + return MatrixIterator(_matrix, _column_cnt); +} + +MatrixIterator<std::string_view> StringMatrix::end() const noexcept { - return _rows; + return MatrixIterator(_matrix + _row_cnt, _column_cnt); } -uint32_t StringMatrix::columns() const noexcept +StringMatrix &StringMatrix::operator=(const StringMatrix &rhs) noexcept { - return _columns; + if (&rhs != this) + { + _delete_matrix(); + _matrix = nullptr; + + _matrix = new std::string_view *[rhs._row_cnt]; + _copy_matrix_from(rhs); + } + + return *this; +} + +StringMatrix &StringMatrix::operator=(StringMatrix &&rhs) noexcept +{ + if (&rhs != this) + { + _delete_matrix(); + _matrix = rhs._matrix; + rhs._matrix = nullptr; + } + + return *this; +} + +void StringMatrix::_delete_matrix() noexcept +{ + for (gsl::owner<std::string_view **> row = _matrix; row != _matrix + _row_cnt; row++) + { + delete[](*row); + } + + delete[] _matrix; +} + +void StringMatrix::_copy_matrix_from(const StringMatrix &source) noexcept +{ + gsl::owner<std::string_view **> source_row = source._matrix; + + for (gsl::owner<std::string_view **> row = _matrix; row != _matrix + _row_cnt; + row++, source_row++) + { + *row = static_cast<gsl::owner<std::string_view *>>( + new std::string_view[_column_cnt]); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto *end = *source_row + _column_cnt; + + std::copy(*source_row, end, *row); + } } diff --git a/src/engine/graphics/string_matrix.hpp b/src/engine/graphics/string_matrix.hpp index 40fbb11..ff0939c 100644 --- a/src/engine/graphics/string_matrix.hpp +++ b/src/engine/graphics/string_matrix.hpp @@ -4,58 +4,49 @@ #include "engine/data/bounds.hpp" #include "engine/data/vector2.hpp" +#include "engine/matrix_iterator.hpp" +#include <gsl/pointers> +#include <memory> #include <string_view> #include <vector> -/** - * A Matrix. - */ class StringMatrix : public IMatrix<std::string_view> { public: - /** - * Creates a matrix. - * - * @param bounds The bounds of the matrix - */ explicit StringMatrix(const Bounds &bounds) noexcept; - /** - * Fills the matrix with a element. - * - * @param element A element - */ + StringMatrix(const StringMatrix &string_matrix) noexcept; + + StringMatrix(StringMatrix &&string_matrix) noexcept; + + ~StringMatrix() noexcept override; + void fill(std::string_view element) noexcept override; - /** - * Returns a element of the matrix. - * - * @param pos The position of a element - */ [[nodiscard]] std::string_view get(const Vector2 &pos) const noexcept override; - /** - * Sets a element of the matrix. - * - * @param pos The position of a element - * @param element A new element - */ void set(const Vector2 &pos, std::string_view element) noexcept override; - /** - * Returns the number of rows the matrix has. - */ - [[nodiscard]] uint32_t rows() const noexcept override; + [[nodiscard]] uint32_t get_row_cnt() const noexcept override; + + [[nodiscard]] uint32_t get_column_cnt() const noexcept override; + + [[nodiscard]] MatrixIterator<std::string_view> begin() const noexcept override; - /** - * Returns the number of columns the matrix has. - */ - [[nodiscard]] uint32_t columns() const noexcept override; + [[nodiscard]] MatrixIterator<std::string_view> end() const noexcept override; + + StringMatrix &operator=(const StringMatrix &rhs) noexcept; + + StringMatrix &operator=(StringMatrix &&rhs) noexcept; private: - std::vector<std::vector<std::string_view>> _matrix; + gsl::owner<std::string_view **> _matrix; + + uint32_t _row_cnt; + uint32_t _column_cnt; + + void _delete_matrix() noexcept; - uint32_t _rows; - uint32_t _columns; + void _copy_matrix_from(const StringMatrix &source) noexcept; }; diff --git a/src/engine/matrix_iterator.hpp b/src/engine/matrix_iterator.hpp new file mode 100644 index 0000000..5d35052 --- /dev/null +++ b/src/engine/matrix_iterator.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include <gsl/pointers> + +template <typename Element> +class MatrixRowIterator +{ +public: + using ColumnPtr = gsl::owner<Element *>; + + explicit MatrixRowIterator(ColumnPtr column_ptr) noexcept; + + MatrixRowIterator &operator++() noexcept; + MatrixRowIterator operator++(int) noexcept; + + Element &operator*() const noexcept; + + bool operator==(const MatrixRowIterator &rhs) const noexcept; + bool operator!=(const MatrixRowIterator &rhs) const noexcept; + +private: + ColumnPtr _column_ptr; +}; + +template <typename Element> +class MatrixRow +{ +public: + using RowPtr = gsl::owner<Element *>; + + explicit MatrixRow(RowPtr row_ptr, const uint32_t &column_cnt) noexcept; + + [[nodiscard]] MatrixRowIterator<Element> begin() const noexcept; + + [[nodiscard]] MatrixRowIterator<Element> end() const noexcept; + +private: + RowPtr _row_ptr; + + const uint32_t &_column_cnt; +}; + +template <typename Element> +class MatrixIterator +{ +public: + using RowPtr = gsl::owner<Element **>; + + explicit MatrixIterator(RowPtr row_ptr, const uint32_t &column_cnt) noexcept; + + MatrixIterator &operator++() noexcept; + MatrixIterator operator++(int) noexcept; + + MatrixRow<Element> operator*() const noexcept; + + bool operator==(const MatrixIterator &rhs) const noexcept; + bool operator!=(const MatrixIterator &rhs) const noexcept; + +private: + RowPtr _row_ptr; + + const uint32_t &_column_cnt; +}; + +#include "matrix_iterator.tpp" diff --git a/src/engine/matrix_iterator.tpp b/src/engine/matrix_iterator.tpp new file mode 100644 index 0000000..52952c9 --- /dev/null +++ b/src/engine/matrix_iterator.tpp @@ -0,0 +1,112 @@ +#pragma once + +#include "matrix_iterator.hpp" + +// Matrix row iterator + +template <typename Element> +MatrixRowIterator<Element>::MatrixRowIterator(ColumnPtr column_ptr) noexcept + : _column_ptr(column_ptr) +{ +} + +template <typename Element> +MatrixRowIterator<Element> &MatrixRowIterator<Element>::operator++() noexcept +{ + ++_column_ptr; + + return *this; +} + +template <typename Element> +MatrixRowIterator<Element> MatrixRowIterator<Element>::operator++(int) noexcept +{ + auto copy = *this; + + ++(*this); + + return copy; +} + +template <typename Element> +Element &MatrixRowIterator<Element>::operator*() const noexcept +{ + return *_column_ptr; +} + +template <typename Element> +bool MatrixRowIterator<Element>::operator==(const MatrixRowIterator &rhs) const noexcept +{ + return _column_ptr == rhs._column_ptr; +} + +template <typename Element> +bool MatrixRowIterator<Element>::operator!=(const MatrixRowIterator &rhs) const noexcept +{ + return _column_ptr != rhs._column_ptr; +} + +// Matrix row + +template <typename Element> +MatrixRow<Element>::MatrixRow(RowPtr row_ptr, const uint32_t &column_cnt) noexcept + : _row_ptr(row_ptr), _column_cnt(column_cnt) +{ +} + +template <typename Element> +MatrixRowIterator<Element> MatrixRow<Element>::begin() const noexcept +{ + return MatrixRowIterator<Element>(_row_ptr); +} + +template <typename Element> +MatrixRowIterator<Element> MatrixRow<Element>::end() const noexcept +{ + return MatrixRowIterator<Element>(_row_ptr + _column_cnt); +} + +// Matrix iterator + +template <typename Element> +MatrixIterator<Element>::MatrixIterator(RowPtr row_ptr, + const uint32_t &column_cnt) noexcept + : _row_ptr(row_ptr), _column_cnt(column_cnt) +{ +} + +template <typename Element> +MatrixIterator<Element> &MatrixIterator<Element>::operator++() noexcept +{ + ++_row_ptr; + + return *this; +} + +template <typename Element> +MatrixIterator<Element> MatrixIterator<Element>::operator++(int) noexcept +{ + auto copy = *this; + + ++(*this); + + return copy; +} + +template <typename Element> +MatrixRow<Element> MatrixIterator<Element>::operator*() const noexcept +{ + return MatrixRow(*_row_ptr, _column_cnt); +} + +template <typename Element> +bool MatrixIterator<Element>::operator==(const MatrixIterator &rhs) const noexcept +{ + return _row_ptr == rhs._row_ptr; +} + +template <typename Element> +bool MatrixIterator<Element>::operator!=(const MatrixIterator &rhs) const noexcept +{ + return _row_ptr != rhs._row_ptr; +} diff --git a/src/interfaces/matrix.hpp b/src/interfaces/matrix.hpp index 44a26ed..c22c0a7 100644 --- a/src/interfaces/matrix.hpp +++ b/src/interfaces/matrix.hpp @@ -3,45 +3,39 @@ #include "engine/data/bounds.hpp" #include "engine/data/vector2.hpp" +#include "engine/matrix_iterator.hpp" + #include <memory> template <typename Element> class IMatrix { public: + IMatrix() noexcept = default; + + IMatrix(const IMatrix &matrix) noexcept = default; + + IMatrix(IMatrix &&matrix) noexcept = default; + virtual ~IMatrix() noexcept = default; - /** - * Fills the matrix with a element. - * - * @param element A element - */ virtual void fill(Element element) noexcept = 0; - /** - * Returns a element of the matrix. - * - * @param pos The position of a element - */ [[nodiscard]] virtual Element get(const Vector2 &pos) const noexcept = 0; - /** - * Sets a element of the matrix. - * - * @param pos The position of a element - * @param element A new element - */ virtual void set(const Vector2 &pos, Element element) noexcept = 0; - /** - * Returns the number of rows the matrix has. - */ - [[nodiscard]] virtual uint32_t rows() const noexcept = 0; + [[nodiscard]] virtual uint32_t get_row_cnt() const noexcept = 0; + + [[nodiscard]] virtual uint32_t get_column_cnt() const noexcept = 0; + + [[nodiscard]] virtual MatrixIterator<std::string_view> begin() const noexcept = 0; + + [[nodiscard]] virtual MatrixIterator<std::string_view> end() const noexcept = 0; + + IMatrix &operator=(const IMatrix &matrix) noexcept = default; - /** - * Returns the number of columns the matrix has. - */ - [[nodiscard]] virtual uint32_t columns() const noexcept = 0; + IMatrix &operator=(IMatrix &&matrix) noexcept = default; }; template <typename Element> diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..ac84f42 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,45 @@ +file(GLOB SOURCES + main.cpp + string_matrix.test.cpp + ${CMAKE_SOURCE_DIR}/src/engine/data/vector2.cpp + ${CMAKE_SOURCE_DIR}/src/engine/data/bounds.cpp + ${CMAKE_SOURCE_DIR}/src/engine/graphics/string_matrix.cpp +) + +add_executable(tests ${SOURCES}) + +target_compile_features(tests PUBLIC cxx_std_20) + +target_compile_options( + tests + PRIVATE + -Wall -Wextra -Wpedantic -Wshadow + -Wold-style-cast -Wcast-align -Wno-unused + -Wconversion -Wcast-qual -Wctor-dtor-privacy + -Wdisabled-optimization -Wformat=2 -Winit-self + -Wmissing-declarations -Wmissing-include-dirs + -Woverloaded-virtual -Wredundant-decls + -Wsign-conversion -Wsign-promo + -Wstrict-overflow=5 -Wswitch-default + -Wundef -Werror + -pedantic -fsanitize=address -fno-exceptions +) + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + target_compile_options( + tests + PRIVATE + -Wlogical-op -Wnoexcept -Wstrict-null-sentinel + ) +endif() + +target_include_directories( + tests + PRIVATE + "${CMAKE_SOURCE_DIR}/src" + "${CMAKE_SOURCE_DIR}/test" +) + +target_link_libraries(tests GSL doctest) + +target_link_options(tests PRIVATE -fsanitize=address) diff --git a/test/main.cpp b/test/main.cpp new file mode 100644 index 0000000..0a3f254 --- /dev/null +++ b/test/main.cpp @@ -0,0 +1,2 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> diff --git a/test/string_matrix.test.cpp b/test/string_matrix.test.cpp new file mode 100644 index 0000000..522a274 --- /dev/null +++ b/test/string_matrix.test.cpp @@ -0,0 +1,53 @@ +#include "engine/graphics/string_matrix.hpp" +#include "engine/data/bounds.hpp" + +#include <doctest/doctest.h> +#include <type_traits> + +constexpr uint32_t MATRIX_WIDTH = 76; +constexpr uint32_t MATRIX_HEIGHT = 31; + +TEST_CASE("String matrix") +{ + auto string_matrix = + StringMatrix(Bounds({.width = MATRIX_WIDTH, .height = MATRIX_HEIGHT})); + + SUBCASE("Can set & get elements") + { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + const auto position = Vector2({.x = 56, .y = 20}); + + string_matrix.set(position, "#"); + + CHECK(string_matrix.get(position) == "#"); + } + + SUBCASE("Can iterate") + { + CHECK(std::is_same_v<decltype(string_matrix.begin()), + MatrixIterator<std::string_view>>); + + CHECK(std::is_same_v<decltype(string_matrix.end()), + MatrixIterator<std::string_view>>); + + uint32_t row_iter_cnt = 0; + + for (auto row : string_matrix) + { + row_iter_cnt++; + + CHECK(std::is_same_v<decltype(row), MatrixRow<std::string_view>>); + + uint32_t col_iter_cnt = 0; + + for (auto &col : row) + { + col_iter_cnt++; + } + + CHECK(col_iter_cnt == MATRIX_WIDTH); + } + + CHECK(row_iter_cnt == MATRIX_HEIGHT); + } +} |