| |
|
|
Dimension
The ObjexxFCL supports an automatic dynamic array dimensioning system based around Dimension size parameter objects. A Dimension or an expression involving Dimensions can be used wherever an integer size parameter would be used to set an FArray's index range. FArrays that depend on Dimensions will not allocate their data storage until all those Dimensions are initialized and will automatically resize themselves if any of those Dimensions are changed. This eliminates the need to perform explicit sizing of FArrays, which may have complex dependencies on a number of size parameters.
Dimension m; // Uninitialized FArray2D_float A( m, m ); // Unallocated
...
m = 100; // A is allocated to 100 x 100
...
m = 200; // A is reallocated to 200 x 200 |
There are a number of Dimension assignment operators and functions to support different resizing strategies. Assignment operators (= += -= *= /=) will always change the Dimension value and notify all affected arrays to resize and reapply their initializers. There are a number of functions with names of the form assign_if_condition that will conditionally assign the value to the Dimension. For example, assign_if_bigger will only assign the passed value to a Dimension if it is bigger than the current value (reducing the number of reallocations at some cost to the memory footprint). And assign_if_bigger_nic will do the same thing but will only notify the arrays to reinitialize if the Dimension value was changed (nic stands for "notify if changed").
Dimension changes that trigger expensive array resizing operations should be kept out of loops and heavily called functions where possible. If a suitably large size cannot be set outside of those contexts then consider using a Dimension assignment function/strategy that will cause fewer resizing events, such as the assign_if_bigger method.
Notes on Dimension Use
- Dimensions can be default constructed in an uninitialized state or given an initial constant value or Dimension expression.
- Dimension expressions can be any combination of Dimension and integer or floating point constants using the arithmetic operators {+ - * /} and parentheses and the unary functions square and cube and the binary functions min, max, and pow. The expressions are evaluated using normal numeric type promotions.
- Source files that create Dimension expressions will need to include the DimensionExpressions.hh header file in addition to the Dimension.hh header (included by the FArray headers).
- Changes to a Dimension that extend its expression (using += for example) apply to the Dimension's expression not just its current value.
- Copy constructing a Dimension makes it a reference to the passed Dimension: this non-standard semantics is why the copy constructor is declared explicit and why Dimensions cannot be passed by value or held by value in Standard Library template containers.
- Copy assigning a Dimension makes it a reference to the passed Dimension: this is non-standard semantics.
- Assigning a Dimension expression to a Dimension creates a linkage from the Dimension being assigned to the Dimensions it then depends on.
- Assignment to expressions containing the same Dimension (m += m;) use the current expression for that Dimension on the right-hand-side to avoid creating a self-referential cycle. Other cyclic relationships are illegal and cause assertion failures in the debug builds, for example: m = n; n = m;
- Dimensions can be used in most contexts where int and double values are expected and will convert to those types. Since a user-defined conversion is being used there are some contexts where explicit casting will be required (C++ will apply at most one user-defined conversion automatically).
- Function-local FArrays sized by Dimensions that don't change during the function should use the Dimension values in the sizing expressions to avoid the overhead of the automatic sizing notification system and the need to include DimensionExpressions.hh. Use:
FArray2D_int A( 2 * m(), m() );
instead of:
FArray2D_int A( 2 * m, m );
- Use m() or m.value() to get the current integer value of Dimension m. The Dimension must be initialized before you request its value.
- Explicit resizing of arrays via their dimension function calls can still be used when the array sizes depend on Dimensions.
- Don't specify an IndexRange or FArray with a Dimension with a smaller scope/lifetime: you will get assertion failure in a debug build if they are used after the Dimension is destructed.
- To change a set of Dimensions efficiently clear them all using the clear Dimension member function before assigning them to avoid unnecessary resizing of arrays that depend on multiple Dimensions.
- Don't pass Dimension objects by value and expect a separate copy: copy construction creates an association with the passed Dimension, not a copy of its value.
- A const Dimension cannot be directly modified but if it depends on other Dimensions that change its value will change.
- Global Dimensions are subject to the same global initialization order issues that face other C++ global objects. To use a global Dimension in a global array or index range declared in a different header file you can make it a static object wrapped by a global function:
Dimension & m() { static Dimension d; return d; }
and this can be an inline function if used heavily.
|
|
|
|