Running Sum (Example 1)
The first example of RSUM is very simple. Here is a simple "Reference Manual" style description of it:
RSUM1
Compute a running sum on the array input. The result is a floating point array of the same dimensions.
Calling Sequence
Result = RSUM1(Array)
Arguments
Array
Array for which a running sum will be computed.
This is a minimal design that lacks some important characteristics that IDL system routines usually embody:
- It does not handle scalar input.
- The type of the input is inflexible. IDL routines usually try to handle any input type and do whatever type conversions are necessary.
- The result type is always single precision floating point. IDL routines usually perform computations in the type of the input arguments and return a value of the same type.
We will improve on this design in our subsequent attempts. The code to implement RSUM1 is shown in the following figure. The line numbers are not part of the code and are present to make the discussion easier to follow. Each line of this routine is discussed below:
1
The standard signature for an IDL system function that does not accept keywords.
3
This variable is used to access the input argument in a convenient way.
4
This IDL_VPTR will be used to return the result.
5–6
As the running sum is computed, f_src will point at the input data and f_dst will point at the output data.
7
The number of elements in the input.
10
Obtain the input variable pointer from argv[0].
11
If the input is not single precision floating point, throw an error and quit. This is overly rigid. Real IDL routines would attempt to either type convert the input or do the computation in the input type.
14
This version can only handle arrays. If the user passes a scalar, it throws an error.
15
This routine cannot handle ASSOC file variables. Most IDL routines exclude such variables as they do not contain any data to work with. ASSOC variable data usually comes into a routine as the result of an expression that yields a temporary variable (e.g. TMP = RSUM(MY_ASSOC_VAR(2))).
17
Create a single precision floating point temporary variable of the same size as the input variable and get a pointer to its data area.
20
Get a pointer to the data area of the input variable. At this point we know this variable is always a floating point array.
21
The number of data elements in the input.
22-23
The running sum computation.
25
Return the result.
Running Sum (Example 2)
In our second example of RSUM, we improve on version 1 in several ways:
- RSUM2 accepts scalar input.
- If the input is not of floating type, we type convert it instead of throwing an error.
- If the input is a temporary variable of the correct type, we will do the running sum computation in place and return the input as our result variable rather than creating an extra temporary. This optimization reduces memory use, and can have positive effects on dynamic memory fragmentation.
As always, the first step in writing a system routine is to write a simple description of its interface and intended behavior:
RSUM2
Compute a running sum on the input. The result is a floating point variable with the same structure.
Calling Sequence
Result = RSUM2(Input)
Arguments
Input
Scalar or array data of any numeric type for which a running sum will be computed.
The following is the code for RSUM2:
Discussion of the code for the improvements introduced in this version follow:
10
If the input has the wrong type, obtain one of the right type. If it was already of the correct type, IDL_BasicTypeConversion() will simply return the input value without allocating a temporary variable. Hence, no explicit check for that is required. Also, if the input argument cannot be converted to the desired type (e.g. it is a structure or file variable) IDL_BasicTypeConversion() will throw an error. Hence, we know that the result from this function will be what we want without requiring any further checking.
13
IDL_VarGetData() is a more elegant way to obtain a pointer to variable data along with a count of elements. A further benefit is that it automatically handles scalar variables which removes the restriction from RSUM1.
15–23
If the input variable is a temporary, we will do the computation in place and return the input. Otherwise, we create a temporary variable of the desired type to be the result.
Note that if IDL_BasicTypeConversion() returned a pointer to anything other than the passed in value of argv[0], that value will be a temporary variable which will then be turned into the function result by this code. Hence, we never free the value from IDL_BasicTypeConversion().
Running Sum (Example 3)
RSUM2 is a big improvement over RSUM1, but it still suffers from the fact that all computation is done in a single data type. A real IDL system routine always tries to perform computations in the most significant type presented by its arguments. In a single argument case like RSUM, that would mean doing computations in the input data type whatever that might be. Our final version, RSUM3, resolves this shortcoming.
RSUM3
Compute a running sum on the input. The result is a variable with the same type and structure as the input.
Calling Sequence
Result = RSUM3(Input)
Arguments
Input
Scalar or array data of any numeric type for which a running sum will be computed.
The code for RSUM3 is given in the following figure. Discussion of the code for the improvements introduced in this version follow:
17
f_src and f_dst are no longer pointers to float. They are now the IDL_ALLPTR type, which can point to data of any IDL type. To reflect this change in scope, the leading f_ prefix has been dropped.
22-23
Strings are the only input type that now require conversion. The other types can either support the computation, or are not convertable to a type that can.
36-38
The code for the running sum computation is logically the same for all non-complex data types, differing only in the IDL_ALLPTR field that is used for each type. Using a macro for this means that the expression is only typed in once, and the C compiler automatically fills in the different parts for each data type. This is less error prone than entering the expression manually for each type, and leads to more readable code. This is one of the rare cases where a macro makes things more reliable and readable.
39-44
A macro for the 2 complex types.
46-60
A switch statement that uses the macros defined above to perform the running sum on all possible types. Note the default case, which traps attempts to compute a running sum on structures.
61-62
Don't allow the macros used in the above switch statement to remain defined beyond the scope of this function.