This example shows how a simple routine can be developed in stages. RSUM is a function that returns the running sum of the values in its single input argument. We will present three versions of this routine, each one of which represents an improvement in functionality and flexibility.

All three versions use the function IDL_MakeTempFromTemplate(), described in “Creating A Temporary Variable Using Another Variable As A Template”. The result of RSUM always has the same general shape and dimensions as the input argument. IDL_MakeTempFromTemplate() encapsulates the task of creating a temporary variable of the desired type and shape using the input argument as a template.

Running Sum (Example 1)


The first example of RSUM is very simple. It computes 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 examples. 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*/ IDL_VPTR IDL_rsum1(int argc, IDL_VPTR argv[])
/*2*/ {
/*3*/ IDL_VPTR v; 
/*4*/ IDL_VPTR r; 
/*5*/ float *f_src; 
/*6*/ float *f_dst; 
/*7*/ IDL_MEMINT n;
/*8*/ 
/*9*/ 
/*10*/ v = argv[0];
/*11*/ if (v->type != IDL_TYP_FLOAT) 
/*12*/    IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_LONGJMP,
/*13*/    "argument must be float"); 
/*14*/ IDL_ENSURE_ARRAY(v);
/*15*/ IDL_EXCLUDE_FILE(v);
/*16*/ 
/*17*/ f_dst = (float *)
/*18*/    IDL_VarMakeTempFromTemplate(v, IDL_TYP_FLOAT, 
/*19*/       (IDL_StructDefPtr) 0, &r, FALSE);
/*20*/ f_src = (float *) v->value.arr->data;
/*21*/ n = v->value.arr->n_elts - 1;
/*22*/ *f_dst++ = *f_src++;/* First element */
/*23*/ for (; n--; f_dst++) *f_dst = *(f_dst - 1) + *f_src++;
/*24*/ 
/*25*/ return r;
/*26*/ }

 

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.

Running Sum (Example 2)


In our second example of RSUM, we improve on the first 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.

This example computes 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.

Sample Code

 

/*1*/ IDL_VPTR IDL_rsum2(int argc, IDL_VPTR argv[])
/*2*/ {
/*3*/ IDL_VPTR v; 
/*4*/ IDL_VPTR r; 
/*5*/ float *f_src; 
/*6*/ float *f_dst; 
/*7*/ IDL_MEMINT n;
/*8*/ 
/*9*/ 
/*10*/ v = IDL_BasicTypeConversion(1, argv, IDL_TYP_FLOAT);
/*11*/ /* IDL_BasicTypeConversion calls IDL_ENSURE_SIMPLE, so skip /*12*/ it here. */
/*13*/ IDL_VarGetData(v, &n, (char **) &f_src, FALSE);
/*14*/ 
/*15*/ /* Get a result var, reusing the input if possible */
/*16*/ if (v->flags & V_TEMP) {
/*17*/ r = v;
/*18*/ f_dst = f_src;
/*19*/ } else {
/*20*/ f_dst = (float *)
/*21*/ IDL_VarMakeTempFromTemplate(v, IDL_TYP_FLOAT, 
/*22*/ (IDL_StructDefPtr) 0, &r, FALSE);
/*23*/ }
/*24*/
/*25*/ *f_dst++ = *f_src++;/* First element */
/*26*/ n--;
/*27*/ for (; n--; f_dst++) *f_dst = *(f_dst - 1) + *f_src++;
/*28*/ 
/*29*/ return r;
/*30*/ }

 

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 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.

This example computes 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.

 

Sample Code

 

/*1*/ cx_public IDL_VPTR IDL_rsum3(int argc, IDL_VPTR argv[])
/*2*/ {
/*3*/ IDL_VPTR v, r;
/*4*/ union {
/*5*/ char *sc;	/* Standard char */ 
/*6*/ UCHAR *c;	/* IDL_TYP_BYTE */ 
/*7*/ IDL_INT *i;	/* IDL_TYP_INT */ 
/*8*/ IDL_UINT *ui;	/* IDL_TYP_UINT */ 
/*9*/ IDL_LONG *l;	/* IDL_TYP_LONG */ 
/*10*/ IDL_ULONG *ul;	/* IDL_TYP_ULONG */ 
/*11*/ IDL_LONG64 *l64;	/* IDL_TYP_LONG64 */ 
/*12*/ IDL_ULONG64 *ul64;	/* IDL_TYP_ULONG64 */ 
/*13*/ float *f;	/* IDL_TYP_FLOAT */ 
/*14*/ double *d;	/* IDL_TYP_DOUBLE */ 
/*15*/ IDL_COMPLEX *cmp;	/* IDL_TYP_COMPLEX */ 
/*16*/ IDL_DCOMPLEX *dcmp;	/* IDL_TYP_DCOMPLEX */
/*17*/ } src, dst; 
/*18*/ IDL_LONG n;
/*19*/ 
/*20*/ 
/*21*/ v = argv[0];
/*22*/ if (v->type == IDL_TYP_STRING)
/*23*/ v = IDL_BasicTypeConversion(1, argv, IDL_TYP_FLOAT); 
/*24*/ IDL_VarGetData(v, &n, &(src.sc), TRUE);
/*25*/ n--;	/* First is a special case */
/*26*/ 
/*27*/ /* Get a result var, reusing the input if possible */
/*28*/ if (v->flags & IDL_V_TEMP) {
/*29*/ r = v;
/*30*/ dst = src;
/*31*/ } else {
/*32*/ dst.sc = IDL_VarMakeTempFromTemplate(v, v->type, 
/*33*/    (IDL_StructDefPre) 0, &r, FALSE);
/*34*/ }
/*35*/ 
/*36*/ #define DOCASE(type, field) \
/*37*/ case type: for (*dst.field++ = *src.field++; n--;\
/*38*/ dst.field++)\
/*39*/ *dst.field = *(dst.field - 1) + *src.field++; break
/*40*/ 
/*41*/ #define DOCASE_CMP(type, field) case type: \
/*42*/ for (*dst.field++ = *src.field++; n--; \
/*43*/ dst.field++, src.field++) { \
/*44*/ dst.field->r = (dst.field - 1)->r + src.field->r; \
/*45*/ dst.field->i = (dst.field - 1)->i + src.field->i; } \
/*46*/ break
/*47*/ 
/*48*/ switch (v->type) { 
/*49*/ DOCASE(IDL_TYP_BYTE, c); 
/*50*/ DOCASE(IDL_TYP_INT, i); 
/*51*/ DOCASE(IDL_TYP_LONG, l); 
/*52*/ DOCASE(IDL_TYP_FLOAT, f); 
/*53*/ DOCASE(IDL_TYP_DOUBLE, d); 
/*54*/ DOCASE_CMP(IDL_TYP_COMPLEX, cmp); 
/*55*/ DOCASE_CMP(IDL_TYP_DCOMPLEX, dcmp); 
/*56*/ DOCASE(IDL_TYP_UINT, ui); 
/*57*/ DOCASE(IDL_TYP_ULONG, ul); 
/*58*/ DOCASE(IDL_TYP_LONG64, l64); 
/*59*/ DOCASE(IDL_TYP_ULONG64, ul64);
/*60*/ default: IDL_Message(IDL_M_NAMED_GENERIC, IDL_MSG_LONGJMP, 
/*61*/    "unexpected type");
/*62*/ }
/*63*/ #undef DOCASE
/*64*/ #undef DOCASE_CMP
/*65*/ 
/*66*/ return r;
/*67*/ }

 

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-39 - 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.

41-46 - A macro for the 2 complex types.

46-59 - 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.

60-61 - Don’t allow the macros used in the above switch statement to remain defined beyond the scope of this function.