Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
add_subdirectory(Struct)
add_subdirectory(Matrix)
2 changes: 1 addition & 1 deletion apps/Struct/CMakeLists.txt → apps/Matrix/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
add_and_install_project_app(matrix
DEPEND
lib_Struct
lib_Matrix
)
35 changes: 35 additions & 0 deletions apps/Matrix/src/main_Matrix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* \brief Main application
*/
#include <iostream>
#include <Matrix/Matrix.h>



int main() {
constexpr int def_value = 0;
Matrix::Matrix<int, def_value, 2> matrix;
const int N = 10;

auto a = matrix[0][0];

for (int i = 0; i < N; ++i)
matrix[i][i] = matrix[N-1-i][i] = i;

for (int row = 1; row <= 8; ++row) {
for (int col = 1; col <= 8; ++col){
std::cout << matrix[row][col];
col == 8 ? std::cout << std::endl : std::cout << " ";
}
}
std::cout << matrix.size() << std::endl;

for (const auto& [key, value]: matrix){
std::string t;
for (const auto& c: key) {
t += std::to_string(c) + " ";
}
t = t.substr(0, t.size()-1);
std::cout << "[" << t << "] : " << value << std::endl;
}
}
8 changes: 0 additions & 8 deletions apps/Struct/src/main_Struct.cpp

This file was deleted.

2 changes: 1 addition & 1 deletion library/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
add_subdirectory(Struct)
add_subdirectory(Matrix)
File renamed without changes.
File renamed without changes.
158 changes: 158 additions & 0 deletions library/Matrix/include/Matrix/Matrix.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* \brief main library header for Matrix
* \code
* Matrix<int, -2, 5> m; // create 5D matrix of int with default value -2;
* \endcode
* Idea:
* 1. Use <map> to represent cell with value. It takes
* 2. Overload operator[] by using matrixes with lower dimention so that it can be used in this manner
* \code
* Matrix <int,-1, 3> m;
* m [1][2][3][4] = 5;
* std::cout << m[1][2][3][4] << std::endl; // print "5";
* std::cout << m[1][2][3][3] << std::endl; // print "-1";
* \endcode
*/
#pragma once

#include <map>
#include <vector>

namespace Matrix {

//----------------------------------
#pragma mark Matrix Implementation
//----------------------------------
template<typename ValueType, ValueType DefaultValue, size_t MainDimension = 2>
class Matrix
{
using KeyType = std::vector<size_t>;
using DataType = std::map<KeyType, ValueType>;
using MatrixType = Matrix<ValueType, DefaultValue, MainDimension>;

public:

/**
* \brief Use pattern `Proxy` to get access to element
*
* \tparam ProxyDimention
*/
template<size_t ProxyDimention>
class Proxy {
public:
Proxy(MatrixType* matrix, KeyType key)
: matrix_(matrix),
const_matrix_(nullptr),
key_(key) {}

Proxy(const MatrixType* matrix, KeyType key)
: matrix_(nullptr),
const_matrix_(matrix),
key_(key) {}

auto operator[](const size_t idx) {
key_[MainDimension - ProxyDimention] = idx;
if (matrix_)
return Proxy<ProxyDimention - 1>{matrix_, key_};
else
return Proxy<ProxyDimention - 1>{const_matrix_, key_};
}

private:
MatrixType* matrix_;
const MatrixType* const_matrix_;
KeyType key_;
};

/// \brief Zero-dimention proxy to contain actual value of the element
template<>
class Proxy<0> {
public:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

У меня вопрос.
Как у Вас получилось использовать полную специализацию шаблонного класса внутри скоупа класса?
У меня на g++ выходит ошибка:
error: explicit specialization in non-namespace scope 'class Matrix<T, defaultValue, Rank>.
А Вы каким компилятором пользовались?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я пользую clang. Думаю, g++ тоже должно быть тоже просто. Попробуй либо вынести определение в cpp (авось сработает), либо создай stand-alone Proxy и сделай композицию внутри матрицы

Copy link

@azbyx azbyx Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Чтобы реализовать данную функциональность оставаясь внутри класса, я просто добавил дополнительный шаблонный параметр(можно неполный тип), что позволило избежать полной специализации шаблонного класса внутри объемлющего класса. Такое ухищрение необходимо только для g++ и MSVC, так как clang, как оказалось, позволяет полную специализацию конкретно в таких случаях.
Спасибо, за помощь.

Proxy(MatrixType* matrix, KeyType key)
: matrix_(matrix),
const_matrix_(nullptr),
key_(key) {}

Proxy(const MatrixType* matrix, KeyType key)
: matrix_(nullptr),
const_matrix_(matrix),
key_(key) {}


operator ValueType() const {
if (matrix_)
return matrix_->get_value(key_);
else
return const_matrix_->get_value(key_);
}

auto operator=(const ValueType& value) {
return matrix_->set_value(key_, value);
}

private:
MatrixType* matrix_;
const MatrixType* const_matrix_;
KeyType key_;
};

Matrix() = default;
virtual ~Matrix() = default;

// copy
Matrix(const Matrix&) = default;
Matrix& operator=(const Matrix&) = default;

//move
Matrix(Matrix&&) = default;
Matrix& operator=(Matrix&&) = default;

auto operator[](const size_t idx) {
KeyType key(MainDimension, 0);
key[0] = idx;
return Proxy<MainDimension - 1>{this, key};
};

auto operator[](const size_t idx) const {
KeyType key(MainDimension, 0);
key[0] = idx;
return Proxy<MainDimension - 1>{this, key};
};


size_t size() const {
return data_.size();
}

auto begin() const {
return data_.begin();
}

auto end() const {
return data_.end();
}

private:
DataType data_;

auto get_value(const KeyType& key) const -> const ValueType{
auto it = data_.find(key);
if (it == data_.end()) {
return DefaultValue;
}
else {
return it->second;
}
}
auto set_value(const KeyType& key, const ValueType& value) -> ValueType {
if (value == DefaultValue){
data_.erase(key);
}
else
data_[key] = value;
return value;
}

};

} // namespace Matrix
1 change: 1 addition & 0 deletions library/Matrix/src/Matrix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include <Matrix/Matrix.h>
File renamed without changes.
107 changes: 107 additions & 0 deletions library/Matrix/test/src/test_Matrix.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#include <Matrix/Matrix.h>

#include <gtest/gtest.h>
#include <string>
#include <sstream>

TEST(Matrix, OneDimentionalMatrix)
{
constexpr int DefaultValue = -1;

// non-const matrix
Matrix::Matrix<int, DefaultValue, 1> m;

EXPECT_EQ(m.size(), 0);
auto a = m[0];
EXPECT_EQ(m.size(), 0);
EXPECT_EQ(a, DefaultValue);
m[0] = 0;
EXPECT_EQ(m.size(), 1);
EXPECT_EQ(m[0], 0);
EXPECT_EQ(m[1], DefaultValue);

m[9] = 9;
EXPECT_EQ(m[0], 0);
EXPECT_EQ(m[1], DefaultValue);
EXPECT_EQ(m[9], 9);
EXPECT_EQ(m.size(), 2);

std::string expected = "[ 0 ] : 0\n[ 9 ] : 9\n";
std::stringstream str;
for (const auto& item: m) {
str << "[";
for (const auto coord : item.first)
str << " " << coord;
str <<" ] : " << item.second << '\n';
}
EXPECT_EQ(expected, str.str());


// const matrix -- no change available;
const Matrix::Matrix<int, DefaultValue, 1> m_const;
EXPECT_EQ(m_const.size(), 0);
EXPECT_EQ(m_const[0], DefaultValue);
EXPECT_EQ(m_const[1], DefaultValue);

// Const matrix: construct from existing one (to pass to functions, for instance)
const Matrix::Matrix<int, DefaultValue, 1> m_const2(m);
EXPECT_EQ(m_const2.size(), 2);
EXPECT_EQ(m_const2[0], 0);
EXPECT_EQ(m_const2[1], DefaultValue);
EXPECT_EQ(m_const2[9], 9);

// const ref
m[2] = 42;
const auto& m_ref = m;
EXPECT_EQ(m_ref.size(), 3);
EXPECT_EQ(m_ref[0], 0);
EXPECT_EQ(m_ref[1], DefaultValue);
EXPECT_EQ(m_ref[9], 9);
EXPECT_EQ(m_ref[2], 42);
}

TEST(Matix, TwoDimentionalMatrix) {
constexpr int DefaultValue = -10;
Matrix::Matrix<int, DefaultValue> m;

EXPECT_EQ(m.size(), 0);

auto a = m[0][0];
EXPECT_EQ(a, DefaultValue);
EXPECT_EQ(m.size(), 0); // expect still empty matrix

m[100][100] = 314;
EXPECT_EQ(m[100][100], 314);
EXPECT_EQ(m.size(), 1);

std::string expected = "[ 100 100 ] : 314\n";
std::stringstream str;
for (const auto& item: m) {
str << "[";
for (const auto coord : item.first)
str << " " << coord;
str <<" ] : " << item.second << '\n';
}
EXPECT_EQ(expected, str.str());


// const matrix
const Matrix::Matrix<int, DefaultValue> m_const;
EXPECT_EQ(m_const.size(), 0);
EXPECT_EQ(m_const[0][0], DefaultValue);
EXPECT_EQ(m_const[0][1], DefaultValue);

// Const matrix: construct from existing one (to pass to functions, for instance)
const Matrix::Matrix<int, DefaultValue> m_const2(m);
EXPECT_EQ(m_const2.size(), 1);
EXPECT_EQ(m_const2[0][0], DefaultValue);
EXPECT_EQ(m_const2[100][100], 314);
}


int main(int argc, char** argv) {

testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();

}
2 changes: 0 additions & 2 deletions library/Struct/include/Struct/Struct.h

This file was deleted.

1 change: 0 additions & 1 deletion library/Struct/src/Struct.cpp

This file was deleted.

13 changes: 0 additions & 13 deletions library/Struct/test/src/test_Struct.cpp

This file was deleted.