passing multidimentional arrays as parameters in c++

Started by
7 comments, last by johnstanp 14 years, 3 months ago
hey there. i am currently trying to create an A* class for my engine,and this class needs to take a multidimentional array in the constractor. the problem i cant seem to make it happen with a multidimentional array. the array's size is also not known inside the class,since it could be of any size. now i did find information regarding multidimentional arrays as parameters to functions,but it was only if i already knew the size and had to set it in the constractor's declearation. is it even possible to pass a multidimentional array as a parameter to to a function without the size of the array being constant?
Advertisement
If you mean multidimensional arrays as in int foo[DIM1][DIM2], then no, you cannot pass them to a function without knowing all by the last dimension. But on the other hand, you cannot even create the array without knowing all but the last dimension, so you already have other problems in that you actually cannot pass something that you cannot create.

If you mean multidimensional arrays as in int **foo, then you can pass it to a function as an int ** parameter. But since pointer doesn't have a size along with it, you have to keep track of the size manually.

My recommendation for multidimensional arrays is the multidimensional array package in boost.
ohh that's not good. i am actually trying to avoid third party libraries as much as possible.
but i got an idea,how about putting the array inside a struct,and passing the struct as a parameter?
The struct will do nothing to handle the first problem, and the second problem is what boost already solves for you (not exactly, but in principle). But if you choose to implement your own instead of using a perfectly fine and existing solution is, of course, your own decision.
Maybe pass a multidimensional vector instead? That would be resizable, easy to pass to functions, and does not involve external libraries.
Quote:Original post by EJH
Maybe pass a multidimensional vector instead? That would be resizable, easy to pass to functions, and does not involve external libraries.

that could work,but wouldn't putting a multidimentional array in a struct and passing the struct as a parameter be cheaper,and more comftable?
I've almost always done multi-dimensional arrays with a single array indexed, eg, array[x_pos + y_pos*array_width]. After a little while, "x + y*width" becomes second-nature, and in certain circumstances you can wrap it in an inline function.

You will probably want to wrap your arrays in a small class, with operator() overloaded to take the place of subscripting (array(x, y) instead of array[x][y], it's just better). But there are still the issues of copying, resizing and what size it is to begin with to be covered, and undoubtedly others that I can't think of at the moment.

I found Boost.MultiArray to be unintuitive, but that may be because the weird things I was trying to make it do led me too far into the weird places of its architecture. It goes far beyond a simple multi-array wrapper to be a very general library, and pays for it in complexity. But you probably won't need to mess with much more than the subscript operators and its slightly weird types.

If you have simple requirements, you might be able to implement your own quickly and solidly enough to not need to use Boost. But you should really consider bringing it in. There's just so much other useful stuff in there that it might be worth it, even for you.
I agree with others with using boost. And not just for multi array. Most of the stuff in boost is in headers only, so no added linking or compiling necesseraly required.

Wrapping the array in separate class could be good idea.
template<typename T>class array_2d{public:    array_2d(size_t width, size_t height)    : m_data(height, std::vector<T>(width))    {    }    T const& operator()(size_t row, size_t column)    {        assert(row < m_data.size());        assert(column < m_data[row].size());        return m_data[row][column];    }private:    std::vector<std::vector<T> > m_data;};

This way you can start with easy std::vectors, and if this takes too much performance (most likely it wont), you can change the underlying implementation easily.
An example of implementation:
#include <cassert>#include <cmath>#include <iomanip>#include <iostream>template< typename T >class Multi_array{	public:		Multi_array()			:elements_(0),			dimensions_(0),			n_dimensions_(0)		{		}				Multi_array(			int unsigned * dimensions,			int unsigned n_dimensions		)			:n_dimensions_( n_dimensions )		{			assert( n_dimensions_ );			assert( dimensions );						dimensions_ = new int unsigned[n_dimensions_];						length_ = 1;						for( int unsigned i = 0; i < n_dimensions_; ++i )			{				assert( dimensions );								dimensions_ = dimensions;				length_ *= dimensions_;			}						elements_ = new T[length_];		}				Multi_array( Multi_array const & Multi_array )			:n_dimensions_( Multi_array.n_dimensions_ )		{			if( n_dimensions_ )			{				dimensions_ = new int unsigned[n_dimensions_];								length_ = 1;								for( int unsigned i = 0; i < n_dimensions_; ++i )				{					dimensions_ = Multi_array.dimensions_;					length_ *= dimensions_;				}								elements_ = new T[length_];								for( int unsigned i = 0; i < length_; ++i )				{					elements_ = Multi_array.elements_;					}			}			else			{				dimensions_ = 0;				elements_ = 0;			}		}				Multi_array & operator=( Multi_array const & Multi_array )		{			n_dimensions_ = Multi_array.n_dimensions_;						if( n_dimensions_ )			{				if( dimensions_ )				{					delete [] dimensions_;				}								dimensions_ = new int unsigned[n_dimensions_];								length_ = 1;								for( int unsigned i = 0; i < n_dimensions_; ++i )				{					dimensions_ = Multi_array.dimensions_;					length_ *= dimensions_;				}								if( elements_ )				{					delete [] elements_;				}								elements_ = new T[length_];								for( int unsigned i = 0; i < length_; ++i )				{					elements_ = Multi_array.elements_;					}			}			else			{				if( dimensions_ )				{					delete [] dimensions_;					dimensions_ = 0;				}								if( elements_ )				{					delete [] elements_;					elements_ = 0;				}			}		}				~Multi_array()		{			if( dimensions_ )			{				delete [] dimensions_;			}						if( elements_ )			{				delete [] elements_;			}		}				T const & operator()( int unsigned * indexes )const		{			return elements_[ compute_offset( indexes ) ];		}				T & operator()( int unsigned * indexes )		{			return elements_[ compute_offset( indexes ) ];		}				void print( std::ostream & out )const		{			if( elements_ && dimensions_ && n_dimensions_ )			{				for( unsigned int i = 0; i < length_; ++i )				{					out << std::setw( 6 ) << i << std::setw( 12 ) << elements_ << "\n";				}								out << "\n";			}		}		private:		T * elements_;		int unsigned * dimensions_;		int unsigned n_dimensions_;		int unsigned length_;				int unsigned compute_offset( int unsigned * indexes )const		{			assert( elements_ );			assert( dimensions_ );			assert( n_dimensions_ );						int unsigned offset = 0;			int unsigned temp = 1;						for( int unsigned i = 0; i < n_dimensions_; ++i )			{				assert( indexes < dimensions_ );								temp = indexes;								for( int j = i + 1; j < n_dimensions_; ++j )				{					temp *= dimensions_[j];				}								offset += temp;			}						return offset;		}};int main( int argc, char** argv ){		int unsigned dims[] = { 2, 3, 4 };		Multi_array< int > array( dims, 3 );	int unsigned indexes[] = { 1, 2, 1 };		array( indexes ) = 1;	indexes[2] = 3;	array( indexes ) = 3;	array.print( std::cout );	std::cout << "\n";		Multi_array< int > array1 = array;	array1.print( std::cout );	std::cout << "\n";		Multi_array< int > array2;	array2 =array1;	array2.print( std::cout );	array2 = Multi_array< int >();	array2.print( std::cout );	return 0;}


Well, it would be better to implement a template class

template< unsigned int N > class Array

where N stands for N-dimensional array. With specializations, you would be able to write better operator()( ... ) overloads: you can, for a determined N, overload operator()( ... ) with the correct number of parameters, instead of passing as a parameter, an array of indexes. You could also pre-compute a mapping of possible input parameters.

[Edited by - johnstanp on December 29, 2009 1:05:08 PM]

This topic is closed to new replies.

Advertisement