Language Syntax

Macro Definitions


An SPP
macro assigns a symbol or identifier to arbitrary text, implementing string substitution. This enables any piece of code to be hidden by using its defined symbol rather than the text itself. Upon precompilation, the macro symbol is replaced by its assigned text. The primary uses of macros are to define symbolic constants such as mathematical constants, whose value will not change at run time, implementing in-line or statement functions, and for creating data structures. Macro definitions allow hiding certain information and can do much to enhance the ease of modifying and maintaining a program. By convention, the names of macros are upper case, to distinguish the names from variables, functions, and other identifiers and to make it clear that a macro is being used. Macros are created by using the define command. If the macro is defined after the procedure statement, it must be defined before the begin statement, and only that procedure may use it. That is, its scope is within a single procedure. If a macro is defined before the procedure statement, it is available to any procedure in the source file. Macros that are shared by several procedures should be defined in an include file, particularly if the source is in different files (see "Include Files" on page 39).

Macros may or may not have arguments. An argument is declared in a macro definition by using a dollar character ($) and a numeral indicating the argument number. In the macro invocation, arguments are passed in parentheses, (). Multiple arguments are separated by commas. Macros without arguments are used primarily to turn explicit constants into symbolic parameters. Examples are shown throughout this text. Macros with arguments are used as statement functions and data structure elements.

Macros incorporating expressions should be enclosed in parentheses to ensure that the expression is executed with the intended precedence. Macro definitions may not include string constants. You may use the string statement to declare string constants. All other types of constants, constant expressions, array and procedure references, are allowed, however. The domain of definition of a macro extends from the line following the macro, to the end of the file (except for include files). Macros may be recursive and may be redefined, resulting in no mention by the compiler.

Macro definitions are frequently shared among procedures in several source files by putting them in an include file. This is another source file, but has the extension .h and is included in any source by using the include statement (see "Include Files" on page 39). There are many examples of macro definitions and structures using them in the IRAF sources, both the system code as well as the applications. Look in the lib$ and hlib$ directories for the include files for the IRAF system. In addition, each applications package usually contains one or more header include files containing numerous examples.

Symbolic Constants

Constants may be declared as variables, initialized with an assignment statement or by using a data statement. Alternately, a symbolic constant may be declared as a macro, using a define statement. Each time the macro is used in the code, its name is replaced by the text specified in the define statement when the code is compiled. There is no data storage allocated nor an assignment executed at run time. It becomes easy to change the values of constants by changing it once in the define statement rather than throughout the code. The meaning of the code frequently becomes clearer by referring to constants by name (PI) rather than by value (3.14159). There are many constants defined automatically as well as several include files available defining many frequently used constants. See Appendix A for a description of these. The following example illustrates the use of macros as symbolic constants:

Data Structures

A data structure allows a set of variables to be treated as a group. These may include variables of different data types, arrays, strings, pointers, etc. See "Data Structures" on page 58 for more details and additional examples.

In this example the macros define a simple structure that permits a different way of using an array. Instead of accessing the array by numeric element numbers, it permits a different name to be defined for each array element that may contain inherently different entities. The array coeff[] is redefined as a simple structure containing the fields I_TYPE, I_NPIX, ..., and I_COEFF. Defining a structure enhances the readability of a program by permitting reference to the fields of the structure by name, rather than the array element (coeff[2]), and furthermore makes it easier to modify the structure. The same code could be written without using macros, referencing coeff as elements of the array or declaring the equivalent elements as separate variables. Note that parentheses are used to refer to elements of the structure, as opposed to square brackets, which refer to array elements. The equivalent implementation without using macros would use an array and reference the elements of the array by their number. This simple example is straightforward. However, for a complicated example, it is usually much clearer to refer to disparate entities by name rather than by an array element.

The same result may be accomplished by using a common block, as is shown in the next example.

Of course, any other procedure using the variables in the common block would have to declare it identically. If you do use common, put it and the associated variable declarations in an include file so there is only one place the declarations needs to be modified. It is possible to define a structure containing any data type. The types int, real, bool, and pointer are guaranteed to be the same length, a single word in memory. A common method of declaring a structure is to use dynamically allocated memory, referring to the structure elements using the Mem[] syntax (see "Memory Allocation -- memio" on page 53). In this case, you need not explicitly specify a different offset for each data type. For types which may differ in size, however, you must be able to refer to the correct offset and size of a particular structure element. This applies to short, long, double, complex, and particularly to char and elements treated as arrays. Note that these should be aligned on long word boundaries*1. The convention is to declare the variables in the order of longest first to shortest last, with character strings declared last. There are system defined macros for aiding in the conversion of pointers to these data types:

Table 1.7: System Macros for Converting Pointers.

The P2T macros permit you to address the next structure element without worrying too much about the word size. These are defined in hlib$iraf.h since they depend on the host architecture. The following example declares a structure containing several different data types and some constants. The difference between this and the previous example is that the memory containing the structure is allocated dynamically instead of using a statically allocated array. This additionally permits multiple instances of the structure to be defined. This is the way many packages handle internal parameters. For example, each time an image is opened using immap(), a structure is allocated containing parameters pertaining to the image. Multiple images may be opened, each having associated parameters organized using the same structure.

Note that even though the P2T macros take care of the offsets into the Mem[] arrays, you still need to keep in mind the size of each structure element to find the offset to the next one. Thus, DVAL is offset by two from XVAL since a complex is two words. However, adjacent fields have consecutive offsets ($1, $1+1, ...) if they occupy a single word. Note also the use of a second argument in IARRAY to specify the array element, the position within the chunk of the allocated memory. The above structure definition would be used by first allocating memory for the structure and accessing each field using the returned structure pointer, as shown in Example 1.10.



Another way to define arrays or character strings in a macro structure is to store only a pointer to dynamically allocated memory in a field of the structure. In this case, the memory for the array has to be allocated explicitly in the code in addition to the memory for the structure.



Macro Functions

Macros with arguments may also be used to define in-line functions. For example, here are a couple of definitions of character classes from the system include lib$ctype.h:

These are used in the following:

Symbolic Constants
Data Structures
Table 1.7: - System Macros for Converting Pointers.
Macro Functions

Generated with CERN WebMaker