Next: , Previous: , Up: Arrays   [Contents][Index]


2.3 Constructors

2.3.1 Default constructor

Array();
Array(GeneralArrayStorage<N_rank> storage)

The default constructor creates a C-style array of zero size. Any attempt to access data in the array may result in a run-time error, because there isn’t any data to access!

An optional argument specifies a storage order for the array.

Arrays created using the default constructor can subsequently be given data by the resize(), resizeAndPreserve(), or reference() member functions.

2.3.2 Creating an array from an expression

Array(expression...)

You may create an array from an array expression. For example,

Array<float,2> A(4,3), B(4,3);   // ...
Array<float,2> C(A*2.0+B);

This is an explicit constructor (it will not be used to perform implicit type conversions). The newly constructed array will have the same storage format as the arrays in the expression. If arrays with different storage formats appear in the expression, an error will result. (In this case, you must first construct the array, then assign the expression to it).

2.3.3 Constructors which take extent parameters

Array(int extent1);
Array(int extent1, int extent2);
Array(int extent1, int extent2, int extent3);
...
Array(int extent1, int extent2, int extent3, ..., int extent11)

These constructors take arguments which specify the size of the array to be constructed. You should provide as many arguments as there are dimensions in the array.1

An optional last parameter specifies a storage format:

Array(int extent1, GeneralArrayStorage<N_rank> storage);
Array(int extent1, int extent2, GeneralArrayStorage<N_rank> storage);
...

For high-rank arrays, it may be convenient to use this constructor:

Array(const TinyVector<int, N_rank>& extent);
Array(const TinyVector<int, N_rank>& extent, 
      GeneralArrayStorage<N_rank> storage);

The argument extent is a vector containing the extent (length) of the array in each dimension. The optional second parameter indicates a storage format. Note that you can construct TinyVector<int,N> objects on the fly with the shape(i1,i2,...) global function. For example, Array<int,2> A(shape(3,5)) will create a 3x5 array.

A similar constructor lets you provide both a vector of base index values (lbounds) and extents:

Array(const TinyVector<int, N_rank>& lbound, 
      const TinyVector<int, N_rank>& extent);
Array(const TinyVector<int, N_rank>& lbound,
      const TinyVector<int, N_rank>& extent,
      GeneralArrayStorage<N_rank> storage);

The argument lbound is a vector containing the base index value (or lbound) of the array in each dimension. The argument extent is a vector containing the extent (length) of the array in each dimension. The optional third parameter indicates a storage format. As with the above constructor, you can use the shape(i1,i2,...) global function to create the lbound and extent parameters.

2.3.4 Constructors with Range arguments

These constructors allow arbitrary bases (starting indices) to be set:

Array(Range r1);
Array(Range r1, Range r2);
Array(Range r1, Range r2, Range r3);
...
Array(Range r1, Range r2, Range r3, ..., Range r11);

For example, this code:

Array<int,2> A(Range(10,20), Range(20,30));

will create an 11x11 array whose indices are 10..20 and 20..30. An optional last parameter provides a storage order:

Array(Range r1, GeneralArrayStorage<N_rank> storage);
Array(Range r1, Range r2, GeneralArrayStorage<N_rank> storage);
...

2.3.5 Referencing another array

This constructor makes a shared view of another array’s data:

Array(Array<T_numtype, N_rank>& array);

After this constructor is used, both Array objects refer to the same data. Any changes made to one array will appear in the other array. If you want to make a duplicate copy of an array, use the copy() member function.

2.3.6 Constructing an array from an expression

Arrays may be constructed from expressions, which are described in Array Expressions. The syntax is:

Array(...array expression...);

For example, this code creates an array B which contains the square roots of the elements in A:

Array<float,2> A(N,N);   // ...
Array<float,2> B(sqrt(A));

2.3.7 Creating an array from pre-existing data

When creating an array using a pointer to already existing data, you have three choices for how Blitz++ will handle the data. These choices are enumerated by the enum type preexistingMemoryPolicy:

enum preexistingMemoryPolicy { 
  duplicateData, 
  deleteDataWhenDone, 
  neverDeleteData 
};

If you choose duplicateData, Blitz++ will create an array object using a copy of the data you provide. If you choose deleteDataWhenDone, Blitz++ will not create a copy of the data; and when no array objects refer to the data anymore, it will deallocate the data using delete []. Note that to use deleteDataWhenDone, your array data must have been allocated using the C++ new operator – for example, you cannot allocate array data using Fortran or malloc, then create a Blitz++ array from it using the deleteDataWhenDone flag. The third option is neverDeleteData, which means that Blitz++ will not never deallocate the array data. This means it is your responsibility to determine when the array data is no longer needed, and deallocate it. You should use this option for memory which has not been allocated using the C++ new operator.

These constructors create array objects from pre-existing data:

Array(T_numtype* dataFirst, TinyVector<int, N_rank> shape,
      preexistingMemoryPolicy deletePolicy);
Array(T_numtype* dataFirst, TinyVector<int, N_rank> shape,
      preexistingMemoryPolicy deletePolicy, 
      GeneralArrayStorage<N_rank> storage);

The first argument is a pointer to the array data. It should point to the element of the array which is stored first in memory. The second argument indicates the shape of the array. You can create this argument using the shape() function. For example:

double data[] = { 1, 2, 3, 4 };
Array<double,2> A(data, shape(2,2), neverDeleteData);   // Make a 2x2 array

The shape() function takes N integer arguments and returns a TinyVector<int,N>.

By default, Blitz++ arrays are row-major. If you want to work with data which is stored in column-major order (e.g. a Fortran array), use the second version of the constructor:

Array<double,2> B(data, shape(2,2), neverDeleteData,
                  FortranArray<2>());

This is a tad awkward, so Blitz++ provides the global object fortranArray which will convert to an instance of GeneralArrayStorage<N_rank>:

Array<double,2> B(data, shape(2,2), neverDeleteData, fortranArray);

Another version of this constructor allows you to pass an arbitrary vector of strides:

Array(T_numtype* _bz_restrict dataFirst, TinyVector<int, N_rank> shape,
      TinyVector<int, N_rank> stride, 
      preexistingMemoryPolicy deletePolicy,
      GeneralArrayStorage<N_rank> storage = GeneralArrayStorage<N_rank>())

2.3.8 Interlacing arrays

For some platforms, it can be advantageous to store a set of arrays interlaced together in memory. Blitz++ provides support for this through the routines interlaceArrays() and allocateArrays(). An example:

Array<int,2> A, B;
interlaceArrays(shape(10,10), A, B);

The first parameter of interlaceArrays() is the shape for the arrays (10x10). The subsequent arguments are the set of arrays to be interlaced together. Up to 11 arrays may be interlaced. All arrays must store the same data type and be of the same rank. In the above example, storage is allocated so that A(0,0) is followed immediately by B(0,0) in memory, which is folloed by A(0,1) and B(0,1), and so on.

A related routine is allocateArrays(), which has identical syntax:

Array<int,2> A, B;
allocateArrays(shape(10,10), A, B);

Unlike interlaceArrays(), which always interlaces the arrays, the routine allocateArrays() may or may not interlace them, depending on whether interlacing is considered advantageous for your platform. If the tuning flag BZ_INTERLACE_ARRAYS is defined in <blitz/tuning.h>, then the arrays are interlaced.

Note that the performance effects of interlacing are unpredictable: in some situations it can be a benefit, and in most others it can slow your code down substantially. You should only use interlaceArrays() after running some benchmarks to determine whether interlacing is beneficial for your particular algorithm and architecture.

2.3.9 A note about reference counting

Blitz++ arrays use reference counting. When you create a new array, a memory block is allocated. The Array object acts like a handle for this memory block. A memory block can be shared among multiple Array objects – for example, when you take subarrays and slices. The memory block keeps track of how many Array objects are referring to it. When a memory block is orphaned – when no Array objects are referring to it – it automatically deletes itself and frees the allocated memory.


Footnotes

(1)

If you provide fewer than N_rank arguments, the missing arguments will be filled in using the last provided argument. However, for code clarity, it makes sense to provide all N_rank parameters.


Next: , Previous: , Up: Arrays   [Contents][Index]