Wired2Code

Just another WordPress.com site

N-dimension array initialization

In the previous article Container assignment list I show how to assign items to STL containers using a list of items separated by a comma.

This example will show how to do the same with a N-dimensional array, with debug build bounds checking. First an example which shows how to declare and initialize a N-dimensional array of type int or type double with a comma separated list of items.

  1. // define a 3 dimensional array of int's
  2. // and initialize.
  3. MyArray<int, 3> intArray3D;
  4. intArray3D = 1, 2, 3;
  5.  
  6. // define a 2 diemsional array of double's
  7. // and initialize.
  8. MyArray<double, 2> doubleArray2D;
  9. doubleArray2D = 10.0, 20.0;
  10.  
  11. // define a 3 x 3 matrix of double and initialize
  12. MyArray<double, 9> doubleMatrix3x3;
  13. doubleMatrix3x3 = 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0;

The N-Dimensional array class implementation

The array class code below only shows enough code to get the assignment and comma operator overloaded. Other useful code and overloads that this class would have is not shown, in order to keep the discussion simple and on topic.

MyArray Class
  1. template<class T, int ArraySize>
  2. class MyArray
  3. {
  4. public:
  5.     MyArray()
  6.     {
  7.         assert(ArraySize != 0);
  8.         // Set items in array to 0
  9.         for(size_t i = 0; i < ArraySize; ++i)
  10.             m_array[i] = 0;
  11.     }
  12.  
  13.     Initializer<T, ArraySize> operator=(T val)
  14.     {
  15.         // store RHS of = to first array item location
  16.         m_array[0] = val;
  17.  
  18.         // Call Initializer construct with address of m_array[1],
  19.         // return Initializer object.
  20.         // Allows the overloaded comma operator for Initializer
  21.         // to be called repeatedly.
  22.         return Initializer<T, ArraySize>(&m_array[1]);
  23.     }
  24. private:
  25.     T m_array[ArraySize];
  26. };

MyArray is a template class that needs 2 template parameters, the first is the type of array to create, and the second is the number of elements the array can store.

The default constructor assigns 0 to each element in the array, if this does not make sense for the type object being stored in the array then modify as needed.

The overloaded assignment operator takes the first item in the list, right of the = sign (RHS), and assigns it to first position in the array. It then calls Initializer’s constructor with the address of the second element in the array, and returns the Initializer object.

This sets up Initializer, which overloads the comma operator, to handle the rest of the assignment list. Overloading the assignment and comma operators is discussed in the article Container assignment list.

The Initializer class is simple and documented below.

Initializer Class for MyArray
  1. // Initializer class. Helper class for MyArray,
  2. // keeping track of the current element being
  3. // assigned, with the overloaded comma operator,
  4. template<class T, int ArraySize>
  5. class Initializer
  6. {
  7. public:
  8.     Initializer() : m_ptr(0)
  9.     {
  10.         #ifndef NDEBUG
  11.             m_nCount = 0;
  12.         #endif
  13.     }
  14.  
  15.     // Constructor called by MyArray operator=
  16.     // ptr will equal MyArray::m_array[1]. Since
  17.     // For debug builds set m_nCount to 1 since
  18.     // MyArray already assigned element [0].
  19.     Initializer(T * ptr) : m_ptr(ptr)
  20.     {
  21.         #ifndef NDEBUG
  22.             m_nCount = 1;
  23.         #endif
  24.     }
  25.  
  26.     // Overloaded comma operator
  27.     Initializer & operator,(T val)
  28.     {
  29.         #ifndef NDEBUG
  30.             // Debug assert for array overrun
  31.             assert(m_nCount < ArraySize);
  32.             ++m_nCount;
  33.         #endif
  34.  
  35.         // store value in pointer to array, then
  36.         // increment pointer to array to next location
  37.         *m_ptr++ = val;
  38.         return *this;
  39.     }
  40. private:
  41.     // Pointer to array location
  42.     T * m_ptr;
  43.     #ifndef NDEBUG
  44.         int m_nCount;
  45.     #endif
  46. };

July 12, 2010 Posted by | Arrays, C++ Programming, Templates | , , , , , | Leave a comment

Container assignment lists

A few weeks ago while reading the Blitz++ Documentation I noticed Blitz++ was initializing Matrices and other containers using commas.

  1. Container<int> bucket;
  2. bucket = 1, 2, 3, 4, 5;

Finding this both useful and cool, had to figure out how it was done.

But first…

If the container has a std::vector member then a constructor taking both the start and end of the array can be used to initialize vector. First an array is initialized, and the beginning and end of the array is passed to the container’s constructor.

  1. const int elements = 5;
  2. int a[elements] = {1, 2, 3, 4, 5, };
  3. Container<int> vec(a, &a[elements]);

Although this is doable it isn’t flexible and you might be incline to code a bunch of push_back calls

  1. Container<int> vec;
  2. vec.push_back(1);
  3. vec.push_back(2);
  4. vec.push_back(3);
  5. vec.push_back(4);
  6. vec.push_back(5);

If we have to go that route it would be much nicer to write code as shown at the beginning of the article.

Overloading a couple operators

The goal again, is to be able to write code such as

  1. Container<int> bucket;
  2. bucket = 1, 2, 3, 4, 5;

which assigns values to a container using a list.

Below is code for a template container class that has std::vector as a member. Only the two operators to be overloaded are shown in the example.

Container class
  1. template<class T>
  2. class Container
  3. {
  4. public:
  5. std::vector<T> & operator=(const T & nVal) {
  6. m_vector.clear();
  7. m_vector.push_back(nVal);
  8. return m_vector;
  9. }
  10. friend std::vector<T> & operator,(std::vector<T> & lst, T val)
  11. {
  12. lst.push_back(val);
  13. return lst;
  14. }
  15. std::vector<T> m_vector;
  16. };

Assignment operator

The assignment operator is overloaded to take a const reference T as the first parameter and returns a reference to m_vector. In the previous examples T is an int.  When the compiler sees

  1. bucket = 1

it grabs the value to the right of = and if the correct type, produces code for the assignment operator. The overloaded operator= first clears the contents of m_vector, then pushes the value onto m_vector.

The assignment operator returns a reference of m_vector which allows the overloaded comma operator to be called from the assignment list.

Comma operator

The second operator overloaded for the container class is the comma operator, which evaluates the object type to the left, evaluates the object type to the right and operates on it.

For the purposes of the example, the assignment operator returned a reference to std::vector<int> (m_vector) from the first item in the list.

  1. bucket = 1, 2, 3, 4, 5;

The compiler uses the returned type on the first seen comma (LHS), looks to the right of the comma and sees it is an int type (RHS). This matches the overloaded comma operator’s definition of

  1. friend std::vector<int> & operator,(std::vector<int> & lst, int val)

Since the comma operator is a binary operator, it needs have global scope and declaring the function as a friend provides that.

For the container class the overloaded comma operator simply pushes the RHS hand value onto the LHS object, and returns a reference to the std::vector<T> (m_vector), and repeats until no items in the list are left.

Conclusion

This type of operator overloading can eliminate ugly lists of repeated push_back calls and can easily be used with the other STL containers.

It is important that your container class has a member of the STL container you want to use. Do not inherit from one of the STL containers. They do not have a virtual destructor, which is needed for a derived class.

July 9, 2010 Posted by | C++ Programming, STL Containers, Templates | , , , , , | 1 Comment