How do I delete a dynamically created matrix? This is likely a duplicate, for which I apologize, but I really can't find a clear answer on here so far. I initialize a matrix as follows:
float ** createMatrix(unsigned int Nrows, unsigned int Ncols) {
float ** result = NULL;
if (Nrows != 0 && Ncols != 0) {
// create the matrix on the heap
result = new float * [Nrows];
result[0] = new float [Nrows*Ncols]();
// link the rows
for (int i = 1; i < Nrows; i++) {
result[i] = result[i-1] + Ncols;
}
}
Now, I wish to create a function to delete it. Do I need two separate statements to delete M[0] and M, or just one for M? i.e. do I need:
void deleteMatrix(float **M){
delete[] M[0];
delete[] M;
}
OR SIMPLY:
void deleteMatrix(float **M){
delete[] M;
}
Any help/explanation would be massively appreciated. Both versions "work" and don't show any errors to me in the console when deleteMatrix(M) is run, so I'm confused. Thanks!
As many other's have stated every time you use new[] you have to have a matching [] delete. However as it currently stands with your functions and how they are declared/defined I believe you are running into an X/Y problem.
Here is why!
You propose to declare your delete function as such: Also you stated that both versions work and don't show any errors... well in your posted question both versions are exactly the same...
void deleteMatrix( float** M ) {
// delete[] rows
// delete[] cols
}
The problem that I'm seeing here is that as you pass in a pointer to a pointer of floats, the function does not know the dimensions of the matrix. It could be a 2x2, 2x3, 3x2, 3x3, MxN etc. Without knowing the dimensions of the matrix how are you able to write the for loop to traverse the inner or nested arrays? You would have to pass those dimensions into the delete function:
void deleteMatrix( float** M, int rowSize, int colSize ) {
for ( int row = 0; row < rowSize; row++ ) {
delete [] M[i];
}
delete [] M;
}
Here is an example similar to what you are trying to implement: thispointer.com
Outside of your actual problem; this tends to be more of a C approach or an outdated C++ approach. It ill advised to use raw pointers and new & delete freely. Using smart pointers is better. However for such a construct such as a matrix class, there are a plethora of freely usable libraries out there that have already defined such classes - interfaces to use and some of the most popular ones are:
GLM
Eigen
Boost
Armadillo
MTL4
Edit - User PaulMcKenzie cleared up a valid point that I have long forgotten since I've been mostly using vector<object> or vector<shared_ptr<object>>. It's been over 15 years since I've first learned about pointers - multi-dimensional arrays and I had forgotten about the concept of about contiguous arrays. The answer to a question that he posted to me in the comment section gives a clear explanation of what I've forgotten; found here. If the arrays are not contiguous in memory then yes it would be an X/Y problem without knowing their dimensions, but since they are; the sizes are not really needed to be known. And what you've already proposed should then work:
void deleteMatrix(float **M){
delete[] M[0];
delete[] M;
}
Edit - I was going back through some of my classes in my libraries and here is a template matrix class that I've written with any dimensional size matrix MxNx...ith It is very versatile in its ease and use. You can expand upon it if you want: I have not done any type checking or assertions but that can easily be added.
Using it is as simple as:
#include <iostream>
#include "Matrix.h"
int main() {
Matrix<int, 2, 2> imat3x3( 1, 2, 3, 4, 5, 6, 7, 8, 9 );
// calling elements() and using vector's [] operator
for ( int i = 0; i < 9; i++ )
std::cout << imat3x3.elements()[i] << ' ';
std::cout << '\n';
// Using class's [] operator
for ( int i = 0; i < 9; i++ )
std::cout << imat3x3[i] << ' ';
std::cout << '\n';
// Using class's () operator
for ( int i = 0; i < 9; i++ )
std::cout << imat3x3(i) << ' ';
std::cout << '\n';
// Okay that was a 3x3 matrix of ints, lets do a 2x2x2 matrix of floats
Matrix<float,2,2,2> fmat2x2x2( 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f );
// now the operators
for ( int i = 0; i < 8; i++ ) {
std::cout << fmat2x2x2[i] << "f ";
std::cout << '\n';
for ( int i = 0; i < 8; i++ ) {
std::cout << fmat2x2x2(i) << "f ";
std::cout << '\n';
std::cout << "\nPress any key and enter to quit.\n";
std::cin.get();
return 0;
}
Matrix.h
#ifndef MATRIX_H
#define MATRIX_H
#include <vector>
#include <algorithm>
#include <numeric>
template<typename Type, size_t... Dims>
class Matrix {
public:
static const size_t _numDims = sizeof...(Dims);
private:
size_t _numElements;
std::vector<Type> _elements;
std::vector<size_t> _strides;
public:
Matrix() noexcept;
template<typename... Args>
Matrix( Args&&... args ) noexcept;
const Type& operator[]( size_t idx );
const Type operator[]( size_t idx ) const;
const Type& operator() ( size_t idx );
const Type operator() ( size_t idx ) const;
size_t numElements() const {
return _elements.size();
}
const std::vector<size_t>& strides() const {
return _strides;
}
const std::vector<Type>& elements() const {
return _elements;
}
};
#include "Matrix.inl"
#endif // !MATRIX_H
Matrix.inl
template<typename Type, size_t... Dims>
Matrix<Type, Dims...>::Matrix() noexcept :
_strides( { Dims... } ) {
using std::begin;
using std::end;
auto mult = std::accumulate( begin( _strides ), end( strides ), 1, std::multiplies<>() );
_numElements = mult;
_elements.resize( _numElements );
}
template<typename Type, size_t... Dims>
template<typename... Args>
Matrix<Type, Dims...>::Matrix( Args&&... args ) noexcept :
_elements( { args... } ),
_strides( { Dims... } ) {
_numElements = _elements.size();
}
template<typename Type, size_t... Dims>
const Type Matrix<Type, Dims...>::operator[]( size_t idx ) const {
return _elements[idx];
}
template<typename Type, size_t... Dims>
const Type& Matrix<Type, Dims...>::operator[]( size_t idx ) {
return _elements[idx];
}
template<typename Type, size_t... Dims>
const Type Matrix<Type, Dims...>::operator()( size_t idx ) const {
return _elements[idx];
}
template<typename Type, size_t... Dims>
const Type& Matrix<Type, Dims...>::operator()( size_t idx ) {
return _elements[idx];
}
Matrix.cpp - This cpp file is not necessary I only have it to easily compile it while debugging the class for basic compiler errors
#include "Matrix.h"
I did not demonstrate the use of the numElements() or stride() functions but they should be fairly self explanatory. The strides function is a very nice feature since if a user calls the template as such <type, 1,3,5> giving you a 1x3x5 Matrix; these are stored in the _strides member vector. This way you always have the indexes needed for the size of each dimension.
Now if you want your matrix on the heap; instead of trying to do a double pointer or [][] and putting each element on the heap, with this class you have two options.
You can either put the instantiated object on the heap directly or you can have this class hold heap objects as well.
std::shared_ptr<Matrix<int,2,2>> ptrMat2x2; // A heap pointer of the matrix
Matrix<shared_ptr<int>,3,3> mat3x3ptrs; // A matrix of heap objects.
The code might seem a bit strange at first glance but this shows that both cases can be done:
#include <iostream>
#include "Matrix.h"
int main() {
// A Matrix<> on the heap via shared_ptr`
std::shared_ptr<Matrix<int, 2, 2>> ptrMat2x2 =
std::make_shared<Matrix<int, 2, 2>>( Matrix<int,2,2>( 1, 2, 3, 4 ) );
// accessing the elements from the shared pointer and printing
for( int i = 0; i < 4; i++ )
std::cout << (*ptrMat2x2.get())(i) << ' ';
std::cout << '\n';
// creating some basic shared_ptrs
auto a = std::make_shared<int>( 1 );
auto b = std::make_shared<int>( 2 );
auto c = std::make_shared<int>( 3 );
auto d = std::make_shared<int>( 4 );
// Matrix that holds shared_ptrs
Matrix<std::shared_ptr<int>, 2, 2> mat2x2ptrs( a, b, c, d );
// print the elements from the matrix (don't forget to dereference).
for( int i = 0; i < 4; i++ )
std::cout << *mat2x2ptrs[i].get() << ' ';
std::cout << '\n';
std::cout << "\nPress any key and enter to quit.\n";
std::cin.get();
return 0;
}
These "2D array" questions come up constantly. I think I'll answer one.
Don't use arrays[]. Don't use new[] and delete[]. Just don't. Use std::vector<std::vector<int>> and let the miracle of C++ do all the newing and deleting for you. Or for something serious, use a well-designed open source library, like boost::matrix. C++ is way cool.
The following is a starter-kit. It can be improved, "privatized", and abstracted in lots of ways.
#include <vector>
using std::size_t;
template<class T>
struct Matrix {
using matrix_type = std::vector<std::vector<T>>;
matrix_type matrix;
Matrix(size_t rows, size_t cols)
: matrix(rows, matrix_type::value_type(cols))
{}
};
int main() {
size_t Nrows = 5u;
size_t Ncols = 2u;
Matrix<int> mx(Nrows, Ncols);
auto& matrix = mx.matrix; // Now use matrix[i][j] or whatever.
// Here you can do anything with matrix that your could do with
// an array or arrays ... and more. And it cleans up after iself.
}
You have allocated two separate arrays, you need to delete two separate arrays, in reverse order.
Related
I want to make a function to define an external multi-dimension array, like this:
'''
const int SIZE=5; // SIZE will be dynamical
int n1=2,n2=3,n3=4,n4=5,n5=6;
int n_loop(SIZE)={n1,n2,n3,n4,n5};
void Mul_array(int n_loop(SIZE))
{
//here will define a multi-array
// I don't know how to do this part
external mul_array[n_loop[0]]....[n_loop[SIZE]]
}
So I can define a multi-dimension array, by only setting n_loop[SIZE], which will be convenient for me.
You cannot get an array of arbitrary length or dimensions. All must be known at compile time.
However, you can get a 1 dimensional dynamic array large enough to contain the multidimensional array and perform the math by hand to compute the mapping from N dimensions to 1 dimension.
The following has been written for easy reading and error checking, not speed.
#include <vector>
#include <iostream>
#include <cstddef>
#include <stdexcept>
// stupid helper function to get the poroduct of all the values in a vector
size_t multiply(const std::vector<size_t> & dims)
{
size_t product = 1;
for (size_t dim:dims)
{
product *= dim;
}
return product;
}
// spun off into own function for easier reading
// computes the offset for one dimension based on previous dimensions
size_t compute_dimension(size_t offset,
size_t dim,
size_t loc)
{
if (loc < dim)
{
offset *= dim; // push existing offset into correct place for higher
// dimension
offset += loc; // add on where to look in this dimension
return offset;
}
throw std::out_of_range("useful error message goes here");
}
// map a multi dimensional corrodinate into a single dimension
size_t compute_offset(const std::vector<size_t> & dims,
const std::initializer_list<size_t> & loc)
{
if (dims.size() != loc.size())
{
throw std::runtime_error("Dimension mis-match");
}
auto locit = loc.begin();
auto dimit = dims.begin();
size_t offset = 0;
while (locit < loc.end()) // compute the 1D offset dimension by dimension
{
offset = compute_dimension(offset, *dimit++, *locit++);
}
return offset;
}
class sdm_1 // Super Dimensional Matrix rev 1
{
public:
sdm_1(const std::initializer_list<size_t> & dims) :
mDims(dims), // Store dimensions
mData(multiply(mDims)) // allocate storage for all dimensions
{
}
// takes dimensions as a brace enclosed initializer list
double& operator()(const std::initializer_list<size_t> & loc)
{
return mData[compute_offset(mDims, loc)];
}
double operator()(const std::initializer_list<size_t> & loc) const
{
return mData[compute_offset(mDims, loc)];
}
// silly little helper to display the array so we can see what's going on
friend std::ostream & operator<<(std::ostream & out,
const sdm_1 & sdm)
{
for (const auto & val: sdm.mData)
{
out << val << ' ';
}
return out;
}
private:
std::vector<size_t> mDims;
std::vector<double> mData;
};
int main()
{
sdm_1 test{2,3,4}; // make a 3D array
std:: cout << test << std::endl; // print it
for (size_t one = 0; one < 2; one++)
{
for (size_t two = 0; two < 3; two++)
{
for (size_t three = 0; three < 4; three++)
{
test({one,two,three}) = 1; // set a value in the array
std:: cout << test << std::endl;
test({one,two,three}) = 0; // and clear it so we get a nice
// number marching across the grid
}
}
}
}
Note that it's very rare to require an unknown number of dimensions. There will be some groovy variadic template voodoo that will allow the number of dimensions to be set at compile time. This will almost certainly be a faster solution.
I'm trying to make light-weight layer on top of continuous array of arbitrary structs (lets call it DataItem), which will handle common operations like file-IO, rendering on screen/GUI (like excel table), searching and sorting by difernet properties etc.
But I want to make my class Table and user-defined struct/class DataItem to be completely independent of each other (i.e. both can compile without knowing each others header-file .h ). I think it cannot be like template<class T> class Table{ std::vectro<T> data;}; because then user would be obligated to implement functionality like DataItem::toString(int icolumn) and I don't want to put that constrain on DataItem struct.
My current implementation rely on pointer arithmetics, switch, and can handle only few types of data members (bool,int,float,double). I wonder if e.g. using templates this could be improved (to make it more generic, safe etc...) without considerably increasing complexity and performance cost.
I want to use it like this:
#include "Table.h"
#include "GUI.h"
#include "Vec3d.h"
// example of user defined DataItem struct
struct TestStruct{
int inum = 115;
double dnum = 11.1154546;
double fvoid= 0.0;
float fnum = 11.115;
Vec3d dvec = (Vec3d){ 1.1545, 2.166, 3.1545};
};
int main(){
// ==== Initialize test data
Table* tab1 = new Table();
tab1->n = 120;
TestStruct* tab_data = new TestStruct[tab1->n];
for(int i=0; i<tab1->n; i++){
tab_data[i].inum = i;
tab_data[i].fnum = i*0.1;
tab_data[i].dnum = i*0.01;
}
// ==== Bind selected properties/members of TestStruct as columns int the table
tab1->bind(tab_data, sizeof(*tab_data) );
// This is actually quite complicated =>
// I would be happy if it could be automatized by some template magic ;-)
tab1->addColum( &(tab_data->inum), 1, DataType::Int );
tab1->addColum( &(tab_data->fnum), 1, DataType::Float );
tab1->addColum( &(tab_data->dnum), 1, DataType::Double );
tab1->addColum( &(tab_data->dvec), 3, DataType::Double );
// ==== Visualize the table Table in GUI
gui.addPanel( new TableView( tab1, "tab1", 150.0, 250.0, 0, 0, 5, 3 ) );
gui.run();
}
My current implementation looks like this:
enum class DataType{ Bool, Int, Float, Double, String };
struct Atribute{
int offset; // offset of data member from address of struct instance [bytes]
int nsub; // number of sub units. e.g. 3 for Vec3
DataType type; // type for conversion
Atribute() = default;
Atribute(int offset_,int nsub_,DataType type_):offset(offset_),nsub(nsub_),type(type_){};
};
class Table{ public:
int n; // number of items/lines in table
int itemsize = 0; // number of bytes per item
char* data = 0; // pointer to data buffer with structs; type is erased to make it generic
std::unordered_map<std::string,int> name2column;
std::vector <Atribute> columns;
void bind(void* data_, int itemsize_){
data=(char*)data_;
itemsize=itemsize_;
}
int addColum(void* ptr, int nsub, DataType type){
// determine offset of address of given data-member with respect to address of enclosing struct
int offset = ((char*)ptr)-((char*)data);
columns.push_back( Atribute( offset, nsub, type ) );
return columns.size()-1;
}
char* toStr(int i, int j, char* s){
const Atribute& kind = columns[j];
void* off = data+itemsize*i+kind.offset; // address of j-th member of i-th instance in data array
// I don't like this switch,
// but still it seems simpler and more efficient that alternative solutions using
// templates/lambda function or function pointers
switch(kind.type){
case DataType::Bool :{ bool* arr=(bool *)off; for(int i=0; i<kind.nsub; i++){ s+=sprintf(s,"%c ", arr[i]?'T':'F' ); }} break;
case DataType::Int :{ int* arr=(int *)off; for(int i=0; i<kind.nsub; i++){ s+=sprintf(s,"%i ", arr[i] ); }} break;
case DataType::Float :{ float* arr=(float *)off; for(int i=0; i<kind.nsub; i++){ s+=sprintf(s,"%g ", arr[i] ); }} break;
case DataType::Double :{ double* arr=(double*)off; for(int i=0; i<kind.nsub; i++){ s+=sprintf(s,"%g ", arr[i] ); }} break;
case DataType::String :{ char* arr=(char *)off; for(int i=0; i<kind.nsub; i++){ s+=sprintf(s,"%s ", arr[i] ); }} break;
}
return s;
}
};
// .... Ommited most of TableView GUI ....
void TableView::render(){
Draw ::setRGB( textColor );
char stmp[1024];
for(int i=i0; i<imax;i++){
int ch0 = 0;
for(int j=j0; j<jmax;j++){
int nch = table->toStr(i,j,stmp)-stmp; // HERE!!! I call Table::toStr()
Draw2D::drawText( stmp, nch, {xmin+ch0*fontSizeDef, ymax-(i-i0+1)*fontSizeDef*2}, 0.0, GUI_fontTex, fontSizeDef );
ch0+=nchs[j];
}
}
}
One way of solving this type of problem is by providing a "traits" class which tells one class how to deal with another class without having to modify the second class. This pattern is used extensively in the standard library.
Your code could be written as:
#include <iostream>
#include <string>
#include <vector>
#include <array>
template <typename T>
struct TableTraits;
template <typename T>
class Table
{
public:
void setData( const std::vector<T>& value )
{
data = value;
}
std::string toString( size_t row, size_t column )
{
return TableTraits<T>::toString( data[ row ], column );
}
void print()
{
for ( size_t row = 0; row < data.size(); row++ )
{
for ( size_t column = 0; column < TableTraits<T>::columns; column++ )
{
std::cout << toString( row, column ) << ", ";
}
std::cout << "\n";
}
}
private:
std::vector<T> data;
};
struct TestStruct
{
int inum = 115;
double dnum = 11.1154546;
double fvoid = 0.0;
float fnum = 11.115f;
std::array<double, 3> dvec = { 1.1545, 2.166, 3.1545 };
};
template <typename T>
std::string stringConvert( const T& value )
{
return std::to_string( value );
}
template <typename T, size_t N>
std::string stringConvert( const std::array<T, N>& value )
{
std::string result;
for ( auto& v : value )
{
result += stringConvert( v ) + "; ";
}
return result;
}
template <>
struct TableTraits<TestStruct>
{
static const size_t columns = 5;
static std::string toString( const TestStruct& row, size_t column )
{
switch ( column )
{
case 0:
return stringConvert( row.inum );
case 1:
return stringConvert( row.dnum );
case 2:
return stringConvert( row.fvoid );
case 3:
return stringConvert( row.fnum );
case 4:
return stringConvert( row.dvec );
default:
throw std::invalid_argument( "column out of range" );
}
}
};
int main()
{
std::vector<TestStruct> data( 10 );
Table<TestStruct> table;
table.setData( data );
table.print();
}
The exact details of the traits class could be different if this example doesn't exactly meet your needs.
You might also find it useful to have the traits methods and constants non-static so that you can pass a traits object into your table to allow customisation per table instance.
You might also want to allow use of a custom traits class in your table, something like this:
template <typename T, typename Traits = TableTraits<T>>
class Table
{
...
std::string toString( size_t row, size_t column )
{
return Traits::toString( data[ row ], column );
}
It looks to me you are trying to use dynamic polymorphism (at run-time) using C-like structures and C-like polymorphism in C++. Templates are useful for static polymorphism. The proper direction is to use OOP (especially class polymorphism), defining concepts as classes: table, cell, column, row, cell-value, etc. As examples, you can check on Github:
OpenXLSX
Spreadsheets-Lower
Current Source
At this point in my code I have this variadic template class:
template<typename ClassType, std::size_t... Args>
class Matrix {
private:
DimensionPack<Args...> dp;
public:
Matrix<ClassType, Args...>(){} // Default
// Public Access Members To Get Information From the DimensionPack which is based
// Not On The Type But Based On The Amount & Values Of This Template's Variadic Parameters
std::vector<unsigned int>& getDimensions() { return dp.dimensions; }
std::vector<unsigned int>& getEvenOrOdd() { return dp.even_or_odd; }
const unsigned int getNumberOfDimensions() const { return dp.total_dimensions; }
const unsigned int getTotalNumElements() const { return dp.total_elements; }
};
It uses this class and structure to do the needed calculations based on its variadic parameter list of values passed in (std::size_t...).
const unsigned int EVEN = 0;
const unsigned int ODD = 1;
struct MatrixDimensionOddOrEven {
const unsigned int even_or_odd;
explicit MatrixDimensionOddOrEven( unsigned int odd_or_even ) : even_or_odd( test( odd_or_even ) ) {}
private:
const unsigned int test( unsigned int value ) const {
if ( value == 0 ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << "invalid number: " << value << " must be >= 1.";
Logger::log( strStream, Logger::TYPE_ERROR );
throw ExceptionHandler( strStream );
}
return ( ((value % 2) == 0) ? EVEN : ODD );
}
}; typedef MatrixDimensionOddOrEven MatDimOddEven;
template <std::size_t... Dims>
class DimensionPack {
public:
std::vector<std::size_t> dimensions;
std::vector<unsigned int> even_or_odd;
const std::size_t total_dimensions = sizeof...(Dims);
const std::size_t total_elements = countElements();
public:
DimensionPack() : dimensions{Dims...},
even_or_odd{ MatrixDimensionOddOrEven{Dims}.even_or_odd...} {
}
private:
std::size_t countElements() {
std::size_t val = 1; // Don't Init to 0 otherwise multiplication won't work here!
for ( std::size_t n = 0; n < dimensions.size(); n++ ) {
val *= dimensions.at( n );
}
return val;
}
};
At this point in my Matrix class I am able to successfully compile and get the results that are expected such as in these cases:
Examples Of Use:
Matrix<float, 1> mat1; // Single Element Matrix - Considered A Scalar Type
Matrix<float, 1,1...> mat1...; // Again - Single Element Matrix - Considered A Scalar Type
Matrix<float, n, m, 1, p> matNM1P; // Where n,m,p are > 1 makes that field of dimensionality flat such as a linear array or vector.
Matrix<float, 2,2> mat2x2; // creates a 2x2 Matrix with 4 elements of type float
Matrix<int, 3,3,3> mat3x3x3; // creates a 3x3x3 Volumetric Matrix with 27 elements of type int.
Matrix<type,n...> mat_multidimensional; // creates any higher order dimension of type.
I can pass them to this function to get the correct results and values:
template<typename type, std::size_t... dims>
void testMatrix( Matrix<type, dims...> matrix ) {
std::cout << "The Matrix Has " << matrix.getNumberOfDimensions() << " Total Dimensions:\n";
std::cout << "The Dimensions Are:\n";
for ( unsigned u = 0; u < matrix.getDimensions().size(); u++ ) {
std::cout << matrix.getDimensions()[u] << " ";
}
std::cout << std::endl;
std::cout << "The even and odd of each dimension are:\n";
for ( unsigned u = 0; u < matrix.getEvenOrOdd().size(); u++ ) {
std::cout << matrix.getEvenOrOdd()[u] << " ";
}
std::cout << std::endl;
std::cout << "The Matrix Has " << matrix.getTotalNumElements() << " total elements.\n\n";
}
Making my main.cpp look like this:
#include <Matrix.h>
int main() {
Matrix<float, 2, 3, 4> mat;
testMatrix( mat );
Matrix<int, 7, 9, 13, 15, 17> mat2;
testMatrix( mat2 );
Matrix<double, 255,255,255,255,255,255,255,255,255> mat9;
testMatrix( mat9 );
return 0;
}
Everything up to this point works fine and the results are expected.
The Goal
As you can see my Matrix class has a default constructor and now it is time to add contents to it. And this is where I'm sort of stuck.
For Example: If a user did the following they would have:
Matrix<float, 4,4,4> mat4x4x4; // A 3D Volumetric Matrix With 256 Elements
Matrix<float, 2,3,5> mat2x3x5; // A 3D Volumetric Matrix With 30 Elements
Matrix<float, 5,7,8,10> mat5x7x8x10; // A 4D Volumetric Matrix With 2800 Elements
Should I just accept a single vector<type> that has a fixed size that
matches the total amount of elements and create an indexing scheme?
Should I have nested <vector<vector<type>> where the amount of nested vectors matches the number of dimensions if the automation of doing such a thing can be done?
Should I create a helper structure that will nest up to 3 nested vectors then repeat that process if the automation of it can be done?
This is where I'm looking for good sound advice as to what my options should be and how it can easily be done.
In my current project I am dealing with a multidimensional datastructure.
The underlying file is stored sequentially (i.e. one huge array, no vector of vectors).
The algorithms that use these datastructures need to know the size of the individual dimensions.
I am wondering if a multidimensional iterator class has been definied somewhere in a generic way and if there are any standards or preferred ways on how to tackle this.
At the moment I am just using a linear iterator with some additional methods that return the size of each dimension and how many dimensions are there in the first part. The reason I don't like it is because I can't use std:: distance in a reasonable way for example (i.e. only returns distance of the whole structure, but not for each dimension separately).
For the most part I will access the datastructure in a linear fashion (first dimension start to finish -> next dimension+...and so on), but it would be good to know when one dimension "ends". I don't know how to do this with just operator*(), operator+() and operator==() in such an approach.
A vector of vectors approach is disfavored, because I don't want to split up the file. Also the algorithms must operate on structure with different dimensionality and are therefore hard to generalize (or maybe there is a way?).
Boost multi_array has the same problems (multiple "levels" of iterators).
I hope this is not too vague or abstract. Any hint in the right direction would be appreciated.
I was looking for a solution myself again and revisited boost:: multi_array. As it turns out it is possible to generate sub views on the data with them, but at the same time also take a direct iterator at the top level and implicitely "flatten" the data structure. The implemented versions of multi_array however do not suit my needs, therefore I probably will implement one myself (that handles the caching of the files in the background) that is compatible with the other multi_arrays.
I will update it again once the implementation is done.
I have just decided to open a public repository on Github : MultiDim Grid which might help for your needs. This is an ongoing project so
I would be glad if you can try it and tell me what you miss / need.
I have started working on this with this topic on codereview.
Put it simply :
MultiDim Grid proposes a flat uni-dimensional array which offer a
generic fast access between multi-dimension coordinates and flatten
index.
You get a container behaviour so you have access to iterators.
That's not that difficult to implement. Just state precisely what functionality your project requires. Here's a dumb sample.
#include <iostream>
#include <array>
#include <vector>
#include <cassert>
template<typename T, int dim>
class DimVector : public std::vector<T> {
public:
DimVector() {
clear();
}
void clear() {
for (auto& i : _sizes)
i = 0;
std::vector<T>::clear();
}
template<class ... Types>
void resize(Types ... args) {
std::array<int, dim> new_sizes = { args ... };
resize(new_sizes);
}
void resize(std::array<int, dim> new_sizes) {
clear();
for (int i = 0; i < dim; ++i)
if (new_sizes[i] == 0)
return;
_sizes = new_sizes;
int realsize = _sizes[0];
for (int i = 1; i < dim; ++i)
realsize *= _sizes[i];
std::vector<T>::resize(static_cast<size_t>(realsize));
}
decltype(auto) operator()(std::array<int, dim> pos) {
// check indexes and compute original index
size_t index;
for (int i = 0; i < dim; ++i) {
assert(0 <= pos[i] && pos[i] < _sizes[i]);
index = (i == 0) ? pos[i] : (index * _sizes[i] + pos[i]);
}
return std::vector<T>::at(index);
}
template<class ... Types>
decltype(auto) at(Types ... args) {
std::array<int, dim> pos = { args ... };
return (*this)(pos);
}
int size(int d) const {
return _sizes[d];
}
class Iterator {
public:
T& operator*() const;
T* operator->() const;
bool operator!=(const Iterator& other) const {
if (&_vec != &other._vec)
return true;
for (int i = 0; i < dim; ++i)
if (_pos[i] != other._pos[i])
return true;
return false;
}
int get_dim(int d) const {
assert(0 <= d && d < dim);
return _pos[d];
}
void add_dim(int d, int value = 1) {
assert(0 <= d && d < dim);
_pos[d] += value;
assert(0 <= _pos[i] && _pos[i] < _vec._sizes[i]);
}
private:
DimVector &_vec;
std::array<int, dim> _pos;
Iterator(DimVector& vec, std::array<int, dim> pos) : _vec(vec), _pos(pos) { }
};
Iterator getIterator(int pos[dim]) {
return Iterator(*this, pos);
}
private:
std::array<int, dim> _sizes;
};
template<typename T, int dim>
inline T& DimVector<T, dim>::Iterator::operator*() const {
return _vec(_pos);
}
template<typename T, int dim>
inline T* DimVector<T, dim>::Iterator::operator->() const {
return &_vec(_pos);
}
using namespace std;
int main() {
DimVector<int, 4> v;
v.resize(1, 2, 3, 4);
v.at(0, 0, 0, 1) = 1;
v.at(0, 1, 0, 0) = 1;
for (int w = 0; w < v.size(0); ++w) {
for (int z = 0; z < v.size(1); ++z) {
for (int y = 0; y < v.size(2); ++y) {
for (int x = 0; x < v.size(3); ++x) {
cout << v.at(w, z, y, x) << ' ';
}
cout << endl;
}
cout << "----------------------------------" << endl;
}
cout << "==================================" << endl;
}
return 0;
}
TODO list:
optimize: use T const& when possible
optimizate iterator: precompute realindex and then just change that realindex
implement const accessors
implement ConstIterator
implement operator>> and operator<< to serialize DimVector to/from file
I want to insert the content of array of integers :
int arr[n] to the vector of QStrings. std::vector<QString> vQString.- I can do it by inserting the array`s elements one by one :
vQString.push_back(QString::number(arr[i]));
By I prefer to do that using one insert operation - any advices?
Thanks
This isn't a 1-line solution. But is an extendable solution. What you basically do is create a template function to do the conversion for you in an exception-safe manner, like below:
namespace yournamespace {
template <typename U>
struct NumberToString {
QString operator()(const U& val) {
return QString::number(val);
}
};
template <typename T, typename U, typename Convertor>
void CopyArrayToVector(QVector<T>& dst, const U* src, const size_t size) {
QVector<T> temp;
temp.reserve(size);
for (int i = 0; i < size; ++i) {
temp.push_back(convert(src[i]));
}
dst.swap(temp);
}
}
Usage:
using yournamespace;
const size_t n = 10;
int *arr = new int[10];
QVector<String> dst;
CopyArrayToVector<QString,int,NumberToString<int> >(dst, arr, n);
DISCLAIMER: I'm not familiar with Qt framework. I whipped this up by looking at their documentation. Please feel free to correct me for any errors.
const int n = 5;
int arr[n] = { 4, 6, 2, 3, 1 };
vector< QString > v( n );
transform( arr, arr + n, v.begin(),
[] ( int i ) { return QString::number( i ); } );
for ( const QString& str : v ) {
cout << qPrintable( str ) << endl;
}
Slightly cheating...! Just use a for loop like everyone else.