3.2. - Some Tips
The syntax rules for defining macros are given in the
Reference Manual
2.4.4.1
The suggestions that follow offer some additional guidance
for writing macros
that will fit in well with the standard library.
When a macro requires arguments, it is a good idea to check that the right number of arguments have been given, and if not, print a usage message and exit to command level. The symbol
If an argument is supposed to be a motor number or mnemonic, use the
A mistyped mnemonic might otherwise become a variable with an arbitrary value (zero for a new variable) resulting in an operation on the wrong motor (usually motor zero).
It is good practice to refer to arguments just once when writing macros to avoid side effects that occur, for example, if the macro is invoked as
When a macro changes a parameter or mode that affects later data, it is a good idea to note that change in the data file and on the printer. Macros such as
If possible, declare local variables
Watch out for name conflicts when naming new macros and using variables. You can prevent most conflicts by using the
Note that several one-letter names such as
Command files that define macros often assign default values to related global variables. You should always check if these global variables have already had a value assigned before assigning default values. If the user had assigned a new value to a variable, you do not want that assignment undone if the macro file is reread. The built-in
When writing macros that move motors, be careful with the
When obtaining input from the user, the functions
When using
Use existing UNIX utilities if they can be of help. For example, if you manipulate UNIX file names in your macros you can use the return value of the test utility to check for existence of a file. For example, the function
When a macro requires arguments, it is a good idea to check that the right number of arguments have been given, and if not, print a usage message and exit to command level. The symbol
$#
will be set to the number of arguments when the macro is run.
For example,
def ascan ' if ($# != 5) { print "Usage: ascan motor start finish intervals time" exit } ... '
If an argument is supposed to be a motor number or mnemonic, use the
_check0
macro
before operating on the motor.
The
_check0
macro
exits to command level if the argument is not a
valid mnemonic.
For instance, to check the first argument of a macro, use
_check0 "$1"
A mistyped mnemonic might otherwise become a variable with an arbitrary value (zero for a new variable) resulting in an operation on the wrong motor (usually motor zero).
It is good practice to refer to arguments just once when writing macros to avoid side effects that occur, for example, if the macro is invoked as
mymac i++
.
Here the variable
i
would be incremented each time
$1
is used in the macro.
In the scan macros, the arguments are assigned to
global variables just after the usage check:
def ascan ' ... { _m1 = $1; _s1 = $2; _f1 = $3; _n1 = int($4); _ctime = $5 } ... '
When a macro changes a parameter or mode that affects later data, it is a good idea to note that change in the data file and on the printer. Macros such as
comment
,
qcomment
and
gpset
are available for that purpose.
If possible, declare local variables
local
to avoid conflicts with other variables, especially when
macros are nested or parsed together.
Watch out for name conflicts when naming new macros and using variables. You can prevent most conflicts by using the
local
keyword to explicitly declare local names within a statement block.
Names declared that way can be used as symbols within the statement block
even if they are already in use as macros.
Otherwise, if you construct commands
using a variable name that is really a macro name,
when that intended variable is encountered,
it will be replaced by the macro, making a mess of things.
Note that several one-letter names such as
d
,
h
,
p
and
l
are already in use as macro names.
Don't use these names as variables, unless they are declared
local
inside a statement block.
Typing
lsdef ?
will list all one letter macro names.
Typing
lsdef _?
will list all two letter macro names that begin with an underscore.
Command files that define macros often assign default values to related global variables. You should always check if these global variables have already had a value assigned before assigning default values. If the user had assigned a new value to a variable, you do not want that assignment undone if the macro file is reread. The built-in
whatis()
function can be used to see if a variable has been assigned a value
(see
2.4.1.2
for an explanation of the
whatis()
return values),
if ((whatis("DATAFILE")>>16)&0x0800) { print "Warning: No open data file. Using \"/dev/null\".\n" open(DATAFILE = "/dev/null") }
When writing macros that move motors, be careful with the
move_all
command.
When moving motors, always do a
waitmove
and
getangles
first.
Then assign new values to
A[]
,
and finally call
move_all
(or
move_em
).
When obtaining input from the user, the functions
getval()
and
yesno()
are useful.
For example,
_update = yesno("Show updated moving and counting", _update) g_mode = getval("Geometry mode", g_mode)results in the following screen output:
Show updated moving and counting (NO)? Geometry mode (3)?You can also use the
input()
built-in function to obtain user input.
Remember, though, that
input()
returns
a string.
If the string contains a valid number, the automatic string-to-number
conversion will take place, if context requires it.
However, no expression simplification is done on the string, so
a response of
2+2
will not have a number value of 4 when returned by
input()
. When using
on()
and
off()
to control output, do the operations on
"tty"
last.
Since
"tty"
is always turned back on if everything else is turned off, the commands
off("tty");on(PRINTER);print "hello world";on("tty");off(PRINTER)will not have the desired effect. The first
off()
turns off everything, so
"tty"
is automatically turned back on, and the message goes to both
PRINTER
and
"tty"
. Use existing UNIX utilities if they can be of help. For example, if you manipulate UNIX file names in your macros you can use the return value of the test utility to check for existence of a file. For example, the function
unix("test -r $1")
will return zero if the file specified by the argument exists
and is readable.