Related
I need to perform some manipulation on images. The images can be color/greyscale and 8-bit/16-bit. I want to do it without using any third party library (opencv, IPP etc).
I have:
The image data as void *.
Width and height of the image.
Number of channels
Bit Resolution of each color channel.
I was thinking of having following structures to represent Color and Image.
Color structure
template<typename ColorDataType, std::size_t channelCount = 3, std::size_t bitResolution = 8>
struct Color
{
using DataType = ColorDataType;
std::array<DataType, channelCount> colorData;
};
Image structure
template<typename ColorType>
class Image
{
std::size_t width;
std::size_t height;
ColorType::DataType * data; // Or a unique_ptr<DataType[]> haven't decided on the ownership yet.
public:
Image(std::size_t inWidth, std::size_t inHeight, ColorType::DataType * inData)
: width(inWidth), height(inHeight), data(inData)
{}
Color & GetColor(std::size_t row, std::size_t col)
{
// How can I return a color element here that could be manipulated from the receiving side?
// The change should be reflected in the memory addressed by data pointer.
}
};
What would be the best way to return a section of the image data so that I can manipulate it later? I would also like to have a const mechanism for the same, in case my Image is const.
Additional information:
The requirement is more about treating the given data stream as a 3-D matrix whose dimensions are:
Row of the image
Column of the image
Color of the image
The 3rd dimension, Color, can have 1 or 3 elements depending on whether the image is a gray-scale image or RGB image. I would like to have a way to access the Color element of an image based on the row and column provided and be able to edit the real data in the data stream based on the Color element received.
By doing so, I was hoping to represent Color as a separate entity, so that I can say "Color has a DataType, channelCount and bitResolution" And Image is made of a particular Color Type
I'm assuming your Color class knows everything about itself to be constructable just from a pointer to your memory. In order for it to actually manipulate your image, pass iterators into data to the constructor of Color and store these iterators. Every time you manipulate your color object, make sure (beware thread-safety!) to also manipulate the data behind the iterators.
If you don't want to force your receiver to accept references, you can also return decltype(auto) from GetColor.
I used something similar in a 2D-array class that I wanted to have something similar to a operator[][]:
#include <cassert>
#include <vector>
template <typename T> class Matrix {
std::vector<T> _matrix;
const int _rows;
const int _cols;
public:
Matrix(int rows, int cols, T def) : _matrix(rows * cols, def), _rows(rows), _cols(cols) {}
Matrix(int rows, int cols) : Matrix(rows, cols, T()) {}
class Row {
private:
Matrix<T> &_m;
const int _row;
public:
Row(Matrix<T> &m, int row) : _m(m), _row(row) {}
decltype(auto) operator[](int col) {
assert(col >= 0);
assert(col < _m.cols());
return _m._matrix[_row * _m.cols() + col];
}
};
friend class Row;
Row operator[](int row) {
assert(row >= 0);
assert(row < _rows);
return Row(*this, row);
}
int rows() const { return _rows; }
int cols() const { return _cols; }
};
EDIT: This should be more like what you are looking for:
#include <array>
#include <cassert>
#include <iostream>
#include <vector>
template <std::size_t channelCount = 3, std::size_t bitResolution = 8> struct Color {
private:
std::vector<int>::iterator begin;
public:
Color(std::vector<int>::iterator begin) : begin(begin) {}
std::string toString() const {
std::string rep = "(";
for (std::size_t i = 0; i < channelCount; ++i) {
rep += std::to_string(*(begin + i));
if (i + 1 < channelCount)
rep += ", ";
}
rep += ")";
return rep;
}
template <std::size_t channel> void setChannelColor(int color) {
static_assert(channel < channelCount);
*(begin + channel) = color;
}
static constexpr std::size_t getChannelCount() { return channelCount; }
static constexpr std::size_t getBitResolution() { return bitResolution; }
};
template <class ColorTypeT> class Image {
using ColorType = Color<ColorTypeT::getChannelCount(), ColorTypeT::getBitResolution()>;
static constexpr std::size_t channelCount = ColorTypeT::getChannelCount();
static constexpr std::size_t bitResolution = ColorTypeT::getBitResolution();
std::size_t width;
std::size_t height;
std::vector<int> data;
public:
Image(std::size_t inWidth, std::size_t inHeight, std::vector<int> inData) : width(inWidth), height(inHeight), data(std::move(inData)) {}
decltype(auto) GetColor(std::size_t row, std::size_t col) { return ColorType(data.begin() + (row * width + col) * channelCount); }
std::size_t getWidth() const { return width; }
std::size_t getHeight() const { return height; }
};
template <class Image> void printImage(Image &image) {
for (std::size_t i = 0; i < image.getHeight(); ++i) {
for (std::size_t j = 0; j < image.getWidth(); ++j) {
std::cout << image.GetColor(i, j).toString() << " ";
}
std::cout << std::endl;
}
}
int main(void) {
std::vector<int> data = {255, 0, 0, 0, 255, 255, 0, 0, 0, 128, 0, 0, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 0, 255};
using Color1 = Color<3, 8>;
using Color2 = Color<1, 8>;
Image<Color1> i1{3, 3, data};
Image<Color2> i2{9, 3, data};
printImage(i1);
std::cout << std::endl;
printImage(i2);
std::cout << std::endl;
i1.GetColor(0, 0).setChannelColor<1>(128);
// i2.GetColor(0, 0).setChannelColor<2>(128); // static_assert fails
i2.GetColor(0, 0).setChannelColor<0>(128);
printImage(i1);
std::cout << std::endl;
printImage(i2);
return 0;
}
It does not take the resolution into account yet, but that should not be much work.
EDIT:
The two classes now don't have a "connection", you can plug in anything that has the methods getChannelCount and getBitResolution. To enforce this, you can use a concept:
template<typename T>
concept bool ColorType = requires(T a) {
{ a.getChannelCount() } -> std::size_t { a.getBitResolution() } -> std::size_t;
};
And then change the definition of the Image class to:
template <ColorType ColorTypeT> class Image
clang does not support this yet, however, GCC 8 does.
I am trying to make class that acts as multidimensional vector. It doesn't have to do anything fancy. I basically want to have a "container" class foo where I can access elements by foo[x][y][z]. Now I would also need similar classes for foo[x][y] and foo[x]. Which lead me to ponder about the following (more general) question, is there a way to make something like this where you can just initialize as foo A(a,b,c,...) for any n number of arguments and get a n-dimensional vector with elements accessible by [][][]...? Below the class I have for (in example) the four-dimensional case.
First the header
#ifndef FCONTAINER_H
#define FCONTAINER_H
#include <iostream>
using namespace std;
class Fcontainer
{
private:
unsigned dim1, dim2, dim3, dim4 ;
double* data;
public:
Fcontainer(unsigned const dims1, unsigned const dims2, unsigned const dims3, unsigned const dims4);
~Fcontainer();
Fcontainer(const Fcontainer& m);
Fcontainer& operator= (const Fcontainer& m);
double& operator() (unsigned const dim1, unsigned const dim2, unsigned const dim3, unsigned const dim4);
double const& operator() (unsigned const dim1, unsigned const dim2, unsigned const dim3, unsigned const dim4) const;
};
#endif // FCONTAINER_H
Now the cpp:
#include "fcontainer.hpp"
Fcontainer::Fcontainer(unsigned const dims1, unsigned const dims2, unsigned const dims3, unsigned const dims4)
{
dim1 = dims1; dim2 = dims2; dim3 = dims3; dim4 = dims4;
if (dims1 == 0 || dims2 == 0 || dims3 == 0 || dims4 == 0)
throw std::invalid_argument("Container constructor has 0 size");
data = new double[dims1 * dims2 * dims3 * dims4];
}
Fcontainer::~Fcontainer()
{
delete[] data;
}
double& Fcontainer::operator() (unsigned const dims1, unsigned const dims2, unsigned const dims3, unsigned const dims4)
{
if (dims1 >= dim1 || dims2 >= dim2 || dims3 >= dim3 || dims4 >= dim4)
throw std::invalid_argument("Container subscript out of bounds");
return data[dims1*dim2*dims3*dim4 + dims2*dim3*dim4 + dim3*dim4 + dims4];
}
double const& Fcontainer::operator() (unsigned const dims1, unsigned const dims2, unsigned const dims3, unsigned const dims4) const
{
if(dims1 >= dim1 || dims2 >= dim2 || dims3 >= dim3 || dims4 >= dim4)
throw std::invalid_argument("Container subscript out of bounds");
return data[dims1*dim2*dims3*dim4 + dims2*dim3*dim4 + dim3*dim4 + dims4];
}
So I want to expand this to an arbitrary amount of dimensions. I suppose it will take something along the lines of a variadic template or an std::initializer_list but I am not clear on how to approach this( for this problem).
Messing around in Visual Studio for a little while, I came up with this nonsense:
template<typename T>
class Matrix {
std::vector<size_t> dimensions;
std::unique_ptr<T[]> _data;
template<typename ... Dimensions>
size_t apply_dimensions(size_t dim, Dimensions&& ... dims) {
dimensions.emplace_back(dim);
return dim * apply_dimensions(std::forward<Dimensions>(dims)...);
}
size_t apply_dimensions(size_t dim) {
dimensions.emplace_back(dim);
return dim;
}
public:
Matrix(std::vector<size_t> dims) : dimensions(std::move(dims)) {
size_t size = flat_size();
_data = std::make_unique<T[]>(size);
}
template<typename ... Dimensions>
Matrix(size_t dim, Dimensions&&... dims) {
size_t size = apply_dimensions(dim, std::forward<Dimensions>(dims)...);
_data = std::make_unique<T[]>(size);
}
T & operator()(std::vector<size_t> const& indexes) {
if(indexes.size() != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
return _data[get_flat_index(indexes)];
}
T const& operator()(std::vector<size_t> const& indexes) const {
if (indexes.size() != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
return _data[get_flat_index(indexes)];
}
template<typename ... Indexes>
T & operator()(size_t idx, Indexes&& ... indexes) {
if (sizeof...(indexes)+1 != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
return at(flat_index);
}
template<typename ... Indexes>
T const& operator()(size_t idx, Indexes&& ... indexes) const {
if (sizeof...(indexes)+1 != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
return at(flat_index);
}
T & at(size_t flat_index) {
return _data[flat_index];
}
T const& at(size_t flat_index) const {
return _data[flat_index];
}
size_t dimension_size(size_t dim) const {
return dimensions[dim];
}
size_t num_of_dimensions() const {
return dimensions.size();
}
size_t flat_size() const {
size_t size = 1;
for (size_t dim : dimensions)
size *= dim;
return size;
}
private:
size_t get_flat_index(std::vector<size_t> const& indexes) const {
size_t dim = 0;
size_t flat_index = 0;
for (size_t index : indexes) {
flat_index += get_offset(index, dim++);
}
return flat_index;
}
template<typename ... Indexes>
size_t get_flat_index(size_t dim, size_t index, Indexes&& ... indexes) const {
return get_offset(index, dim) + get_flat_index(dim + 1, std::forward<Indexes>(indexes)...);
}
size_t get_flat_index(size_t dim, size_t index) const {
return get_offset(index, dim);
}
size_t get_offset(size_t index, size_t dim) const {
if (index >= dimensions[dim])
throw std::runtime_error("Index out of Bounds");
for (size_t i = dim + 1; i < dimensions.size(); i++) {
index *= dimensions[i];
}
return index;
}
};
Let's talk about what this code accomplishes.
//private:
template<typename ... Dimensions>
size_t apply_dimensions(size_t dim, Dimensions&& ... dims) {
dimensions.emplace_back(dim);
return dim * apply_dimensions(std::forward<Dimensions>(dims)...);
}
size_t apply_dimensions(size_t dim) {
dimensions.emplace_back(dim);
return dim;
}
public:
Matrix(std::vector<size_t> dims) : dimensions(std::move(dims)) {
size_t size = flat_size();
_data = std::make_unique<T[]>(size);
}
template<typename ... Dimensions>
Matrix(size_t dim, Dimensions&&... dims) {
size_t size = apply_dimensions(dim, std::forward<Dimensions>(dims)...);
_data = std::make_unique<T[]>(size);
}
What this code enables us to do is write an initializer for this matrix that takes an arbitrary number of dimensions.
int main() {
Matrix<int> mat{2, 2}; //Yields a 2x2 2D Rectangular Matrix
mat = Matrix<int>{4, 6, 5};//mat is now a 4x6x5 3D Rectangular Matrix
mat = Matrix<int>{9};//mat is now a 9-length 1D array.
mat = Matrix<int>{2, 3, 4, 5, 6, 7, 8, 9};//Why would you do this? (yet it compiles...)
}
And if the number and sizes of the dimensions is only known at runtime, this code will work around that:
int main() {
std::cout << "Input the sizes of each of the dimensions.\n";
std::string line;
std::getline(std::cin, line);
std::stringstream ss(line);
size_t dim;
std::vector<size_t> dimensions;
while(ss >> dim)
dimensions.emplace_back(dim);
Matrix<int> mat{dimensions};//Voila.
}
Then, we want to be able to access arbitrary indexes of this matrix. This code offers two ways to do so: either statically using templates, or variably at runtime.
//public:
T & operator()(std::vector<size_t> const& indexes) {
if(indexes.size() != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
return _data[get_flat_index(indexes)];
}
T const& operator()(std::vector<size_t> const& indexes) const {
if (indexes.size() != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
return _data[get_flat_index(indexes)];
}
template<typename ... Indexes>
T & operator()(size_t idx, Indexes&& ... indexes) {
if (sizeof...(indexes)+1 != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
return at(flat_index);
}
template<typename ... Indexes>
T const& operator()(size_t idx, Indexes&& ... indexes) const {
if (sizeof...(indexes)+1 != dimensions.size())
throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
return at(flat_index);
}
And then, in practice:
Matrix<int> mat{6, 5};
mat(5, 2) = 17;
//mat(5, 1, 7) = 24; //throws exception at runtime because of wrong number of dimensions.
mat = Matrix<int>{9, 2, 8};
mat(5, 1, 7) = 24;
//mat(5, 2) = 17; //throws exception at runtime because of wrong number of dimensions.
And this works fine with runtime-dynamic indexing:
std::vector<size_t> indexes;
/*...*/
mat(indexes) = 54; //Will throw if index count is wrong, will succeed otherwise
There are a number of other functions that this kind of object might want, like a resize method, but choosing how to implement that is a high-level design decision. I've also left out tons of other potentially valuable implementation details (like an optimizing move-constructor, a comparison operator, a copy constructor) but this should give you a pretty good idea of how to start.
EDIT:
If you want to avoid use of templates entirely, you can cut like half of the code provided here, and just use the methods/constructor that uses std::vector<size_t> to provide dimensions/index data. If you don't need the ability to dynamically adapt at runtime to the number of dimensions, you can remove the std::vector<size_t> overloads, and possibly even make the number of dimensions a template argument for the class itself (which would enable you to use size_t[] or std::array[size_t, N] to store dimensional data).
Well, assuming you care about efficiency at all, you probably want to store all of the elements in a contiguous manner regardless. So you probably want to do something like:
template <std::size_t N, class T>
class MultiArray {
MultiArray(const std::array<std::size_t, N> sizes)
: m_sizes(sizes)
, m_data.resize(product(m_sizes)) {}
std::array<std::size_t, N> m_sizes;
std::vector<T> m_data;
};
The indexing part is where it gets kind of fun. Basically, if you want a[1][2][3] etc to work, you have to have a return some kind of proxy object, that has its own operator[]. Each one would have to be aware of its own rank. Each time you do [] it returns a proxy letting you specify the next index.
template <std::size_t N, class T>
class MultiArray {
// as before
template <std::size_t rank>
class Indexor {
Indexor(MultiArray& parent, const std::array<std::size_t, N>& indices = {})
: m_parent(parent), m_indices(indices) {}
auto operator[](std::size_t index) {
m_indices[rank] = index;
return Indexor<rank+1>(m_indices, m_parent);
}
std::array<std::size_t, N> m_indices;
MultiArray& m_parent;
};
auto operator[](std::size_t index) {
return Indexor<0>(*this)[index];
}
}
Finally, you have a specialization for when you're done with the last index:
template <>
class Indexor<N-1> { // with obvious constructor
auto operator[](std::size_t index) {
m_indices[N-1] = index;
return m_parent.m_data[indexed_product(m_indices, m_parent.m_sizes)];
}
std::array<std::size_t, N> m_indices;
MultiArray& m_parent;
};
Obviously this is a sketch but at this point its just filling out details and getting it to compile. There are other approaches like instead having the indexor object have two iterators and narrowing but that seemed a bit more complex. You also don't need to template the Indexor class and could use a runtime integer instead but that would make it very easy to misuse, having one too many or too few [] would be a runtime error, not compile time.
Edit: you would also be able to initialize this in the way you describe in 17, but not in 14. But in 14 you can just use a function:
template <class ... Ts>
auto make_double_array(Ts ts) {
return MultiArray<sizeof ... Ts, double>(ts...);
}
Edit2: I use product and indexed_product in the implementation. The first is obvious, the second is less so, but hopefully they should be clear. The latter is a function that given an array of dimensions, and an array of indices, would return the position of that element in the array.
I want to create an operator [] in the case of two dimensional vector. After searching, I have found that it's impossible to pass two arguments. How I can obtain the value of m_matrix[i][j] in the main?
relevant code:
class MyMatrix
{
public: // Methods
MyMatrix();
~MyMatrix();
int operator[] (int n);
private: // Attributes
int m_n;
int m_m;
std:: vector <std:: vector <int> > m_matrix;
};
int MyMatrix::operator[](int n, int m) // in the cpp
{
if (n>=0 && m>=0 && n<=m_n && m<=m_m)
{
return m_matrix[n-1][m-1];
}
else
{ cout<<"******************"<<endl;
cout<<"No valid index"<<endl;
cout<<"******************"<<endl;
return 0;
}
}
...
mat_test1[2][2]; // for example in the main
What's wrong with this?
To resume the comment, you may do:
class MyMatrix
{
public:
// Other methods
const std::vector<int>& operator[] (int m) const { return m_matrix.at(m); }
std::vector<int>& operator[] (int m) { return m_matrix.at(m); }
int operator () (int m, int n) const { return m_matrix.at(m).at(n); }
int& operator () (int m, int n) { return m_matrix.at(m).at(n); }
private:
std::vector<std::vector<int>> m_matrix;
};
Note: I used at instead of [] to use the check from vector and so it throws exception for out of bound access.
And then use it:
MyMatrix matrix(5, 4); // size of the matrix from constructor
matrix[0][1] = 42; // MyMatrix::operator [] followed by std::vector<int>::operator[]
matrix(2, 2) = 42; // MyMatrix::operator () (int, int);
Live example.
There are several ways, but in the end, they all come down to using a
proxy.
For various reasons, the usual implementation of a Matrix isn't
std::vector<std::vector<T>>, but simply std::vector<T>, with an
appropriate calculation of the index. In this case:
class Matrix
{
int myRows;
int myColumns;
std::vector<int> myData;
class Proxy
{
Matrix* myOwner;
int myRow;
public:
Proxy( Matrix* owner, int row )
: myOwner( owner )
, myRow( row )
{
}
int& operator[]( int column )
{
return myOwner->get( myRow, column );
}
};
public:
Matrix( int rows, int columns )
: myRows( rows )
, myColumns( columns )
, myData( rows * columns )
{
}
int& get( int row, int column )
{
assert( 0 <= row && row < myRows
&& 0 <= column && column < myColumns );
return myData[ row * myColumns + column ];
}
Proxy operator[]( int row ) { return Proxy( this, row ); }
};
In fact, you'll want a few more overloads (and possibly two proxies) to
handle const and non-const overloads. But this is the general pattern
for such things.
Be aware, too, that there are two conventions concerning such overloads.
One is to say that the [] returns a projection, and that the correct
solution for handling indexing into multi dimensional structures is to
overload operator() (which can be made to take an arbitrary number of
parameters). I personally prefer the [][] solution, but this
preference is not universal; a lot of people with a more mathematical
background prefer using ().
I'm using Expression Templates in a vector-like class for transformations such as moving averages. Here, different to standard arithmetic operations, the operator[](size_t i) does not make a single single acces to element i, but rather there is a whole loop which needs to be evaluated, e.g. for the moving average of a vector v
double operator[](size_t i) const
{
double ad=0.0;
for(int j=i-period+1; j<=i; ++j)
ad+=v[j];
return ad/period;
}
(thats not the real function because one must care for non-negative indices, but that doesn't matter now).
In using such a moving average construct, I have the fear that the code becomes rather in-performant, especially if one takes a double- or triple-moving average. Then one obtains nested loops and therefore quadratic or cubic scaling with the period-size.
My question is, whether compilers are so smart to optimize such redundant loops somehow away? Or is that not the case and one must manually take care for intermediate storage (--which is what I guess)? How could one do this reasonably in the example code below?
Example code, adapted from Wikipedia, compiles with Visual Studio 2013:
CRTP-base class and actual vector:
#include <vector>
template <typename E>
struct VecExpression
{
double operator[](int i) const { return static_cast<E const&>(*this)[i]; }
};
struct Vec : public VecExpression<Vec>
{
Vec(size_t N) : data(N) {}
double operator[](int i) const { return data[i]; }
double& operator[](int i) { return data[i]; }
std::vector<double> data;
};
Moving Average class:
template <typename VectorType>
struct VecMovingAverage : public VecExpression<VecMovingAverage<VectorType> >
{
VecMovingAverage(VectorType const& _vector, int _period) : vector(_vector), period(_period) {}
double operator[](int i) const
{
int s = std::max(i - period + 1, 0);
double ad = 0.0;
for (int j = s; j <= i; ++j)
ad += vector[j];
return ad / (i - s + 1);
}
VectorType const& vector;
int period;
};
template<typename VectorType>
auto MovingAverage(VectorType const& vector, int period = 10) -> VecMovingAverage<VectorType>
{
return VecMovingAverage<VectorType>(vector, period);
}
Now my above mentioned fears arise with expressions like this,
Vec vec(100);
auto tripleMA= MovingAverage(MovingAverage(MovingAverage(vec,20),20),20);
std::cout << tripleMA[40] << std::endl;
which I suppose require 20^3 evaluations for the single operator[] ... ?
EDIT: One obvious solution is to store the result. Move std::vector<double> data into the base class, then change the Moving Average class to something like (untested)
template <typename VectorType, bool Store>
struct VecMovingAverage : public VecExpression<VecMovingAverage<VectorType, Store> >
{
VecMovingAverage(VectorType const& _vector, int _period) : vector(_vector), period(_period) {}
double operator[](int i) const
{
if(Store && i<data.size())
{
return data[i];
}
else
{
int s = std::max(i - period + 1, 0);
double ad = 0.0;
for (int j = s; j <= i; ++j)
ad += vector[j];
ad /= (i - s + 1)
if(Store)
{
data.resize(i+1);
data[i]=ad;
}
return ad;
}
}
VectorType const& vector;
int period;
};
In the function one can then choose to store the result:
template<typename VectorType>
auto MovingAverage(VectorType const& vector, int period = 10) -> VecMovingAverage<VectorType>
{
static const bool Store=true;
return VecMovingAverage<VectorType, Store>(vector, period);
}
This could be extended such that storage is applied only for multiple applications, etc.
I coded a small simple program that compares the performances of several ways to fill a simple 8x8 matrix with different kind of containers. Here's the following code :
#define MATRIX_DIM 8
#define OCCUR_MAX 100000
static void genHeapAllocatedMatrix(void)
{
int **pPixels = new Pixel *[MATRIX_DIM];
for (type::uint32 idy = 0; idy < MATRIX_DIM; idy++) {
pPixels[idy] = new Pixel[MATRIX_DIM];
for (type::uint32 idx = 0; idx < MATRIX_DIM; idx++)
pPixels[idy][idx] = 42;
}
}
static void genStackAllocatedMatrix(void)
{
std::array<std::array<int, 8>, 8> matrix;
for (type::uint32 idy = 0; idy < MATRIX_DIM; idy++) {
for (type::uint32 idx = 0; idx < MATRIX_DIM; idx++) {
matrix[idy][idx] = 42;
}
}
}
static void genStackAllocatedMatrixBasic(void)
{
int matrix[MATRIX_DIM][MATRIX_DIM];
for (type::uint32 idy = 0; idy < MATRIX_DIM; idy++) {
for (type::uint32 idx = 0; idx < MATRIX_DIM; idx++) {
matrix[idy][idx] = 42;
}
}
}
int main(void)
{
clock_t begin, end;
double time_spent;
begin = clock();
for (type::uint32 idx = 0; idx < OCCUR_MAX; idx++)
{
//genHeapAllocatedMatrix();
genStackAllocatedMatrix();
//genStackAllocatedMatrixBasic();
}
end = clock();
time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
std::cout << "Elapsed time = " << time_spent << std::endl;
return (0);
}
As you can guess the more efficient way is the last one with a simple two-dimentional C array (hard-coded). Of course the worse choice is the number one using heap allocations.
My problem is I want to stock this 2-dimensional array as an attribute in a class. Here's a definition of a custom class that handle a matrix :
template <typename T>
class Matrix
{
public:
Matrix(void);
Matrix(type::uint32 column, type::uint32 row);
Matrix(Matrix const &other);
virtual ~Matrix(void);
public:
Matrix &operator=(Matrix const &other);
bool operator!=(Matrix const &other);
bool operator==(Matrix const &other);
type::uint32 rowCount(void) const;
type::uint32 columnCount(void) const;
void printData(void) const;
T **getData(void) const;
void setData(T **matrix);
private:
type::uint32 m_ColumnCount;
type::uint32 m_RowCount;
T **m_pMatrix;
};
To do the job done I tried the following thing using a cast :
Matrix<int> matrix;
int tab[MATRIX_DIM][MATRIX_DIM];
for (type::uint32 idy = 0; idy < MATRIX_DIM; idy++) {
for (type::uint32 idx = 0; idx < MATRIX_DIM; idx++) {
tab[idy][idx] = 42;
}
}
matrix.setData((int**)&tab[0][0]);
This code compiles correctly but if I want to print it there's a segmentation fault.
int tab[MATRIX_DIM][MATRIX_DIM];
for (type::uint32 idy = 0; idy < MATRIX_DIM; idy++) {
for (type::uint32 idx = 0; idx < MATRIX_DIM; idx++) {
tab[idy][idx] = 42;
}
}
int **matrix = (int**)&tab[0][0];
std::cout << matrix[0][0] << std::endl; //Segmentation fault
Is there a possible way to stock this kind of two dimentional array as an attribute without heap allocation?
That's because a two-dimensional array is not an array of pointers.
So, you should use int * for your matrix type, but then of course you will not be able to index it by two dimensions.
Another option is to store a pointer to the array:
int (*matrix)[MATRIX_DIM][MATRIX_DIM];
matrix = &tab;
std::cout << (*matrix)[0][0] << std::endl;
But that doesn't suit well an idea of incapsulating matrix in a class. F better idea would be for the class to allocate the storage itself (possibly in a single heap allocation) and to provide an access to the matrix through methods only (e.g. GetCell(row, col) etc.), without exposing raw pointers.
Measuring the speed of operations on an 8 x 8 array is largely pointless. For a data set as small as that, the cost of the operation will be close to zero and you are mostly measuring setup time, etc.
Timings become important for larger data sets, but you cannot sensibly extrapolate the small set results to the larger set. With larger data sets you will often find that the data exists on multiple memory pages. There is a danger that paging costs will dominate other costs. Very large improvements in efficiency are possible by ensuring that your algorithm processes all (or most) of the data on one page before moving to the next page, rather than constantly swapping pages.
In general, you are best to use the simplest data structures with the least liklihood of programming error and optimising processing algorithms. I say "in general" as extreme cases do exist where small differences in access time matter, but they are rare.
Use a single array to represent the matrix instead of allocating for each index.
I've written a class for this already. Feel free to use it:
#include <vector>
template<typename T, typename Allocator = std::allocator<T>>
class DimArray
{
private:
int Width, Height;
std::vector<T, Allocator> Data;
public:
DimArray(int Width, int Height);
DimArray(T* Data, int Width, int Height);
DimArray(T** Data, int Width, int Height);
virtual ~DimArray() {}
DimArray(const DimArray &da);
DimArray(DimArray &&da);
inline std::size_t size() {return Data.size();}
inline std::size_t size() const {return Data.size();}
inline int width() {return Width;}
inline int width() const {return Width;}
inline int height() {return Height;}
inline int height() const {return Height;}
inline T* operator [](const int Index) {return Data.data() + Height * Index;}
inline const T* operator [](const int Index) const {return Data.data() + Height * Index;}
inline DimArray& operator = (DimArray da);
};
template<typename T, typename Allocator>
DimArray<T, Allocator>::DimArray(int Width, int Height) : Width(Width), Height(Height), Data(Width * Height, 0) {}
template<typename T, typename Allocator>
DimArray<T, Allocator>::DimArray(T* Data, int Width, int Height) : Width(Width), Height(Height), Data(Width * Height, 0) {std::copy(&Data[0], &Data[0] + Width * Height, const_cast<T*>(this->Data.data()));}
template<typename T, typename Allocator>
DimArray<T, Allocator>::DimArray(T** Data, int Width, int Height) : Width(Width), Height(Height), Data(Width * Height, 0) {std::copy(Data[0], Data[0] + Width * Height, const_cast<T*>(this->Data.data()));}
template<typename T, typename Allocator>
DimArray<T, Allocator>::DimArray(const DimArray &da) : Width(da.Width), Height(da.Height), Data(da.Data) {}
template<typename T, typename Allocator>
DimArray<T, Allocator>::DimArray(DimArray &&da) : Width(std::move(da.Width)), Height(std::move(da.Height)), Data(std::move(da.Data)) {}
template<typename T, typename Allocator>
DimArray<T, Allocator>& DimArray<T, Allocator>::operator = (DimArray<T, Allocator> da)
{
this->Width = da.Width;
this->Height = da.Height;
this->Data.swap(da.Data);
return *this;
}
Usage:
int main()
{
DimArray<int> Matrix(1000, 1000); //creates a 1000 * 1000 matrix.
Matrix[0][0] = 100; //ability to index it like a multi-dimensional array.
}
More usage:
template<typename T, std::size_t size>
class uninitialised_stack_allocator : public std::allocator<T>
{
private:
alignas(16) T data[size];
public:
typedef typename std::allocator<T>::pointer pointer;
typedef typename std::allocator<T>::size_type size_type;
typedef typename std::allocator<T>::value_type value_type;
template<typename U>
struct rebind {typedef uninitialised_stack_allocator<U, size> other;};
pointer allocate(size_type n, const void* hint = 0) {return static_cast<pointer>(&data[0]);}
void deallocate(void* ptr, size_type n) {}
size_type max_size() const {return size;}
};
int main()
{
DimArray<int, uninitialised_stack_allocator<int, 1000 * 1000>> Matrix(1000, 1000);
}