smilax:: Tcl Hacks : cmacro.tcl [Changes]   [Calendar]   [Search]   [Index]   [PhotoTags]   

[Bedstraw] *smilax*

Tcl Hacks: cmacro.tcl

A C-preprocessor-like macro substitution mechanism for Tcl source code.


The purpose is that macro expansions will be inlined in a Tcl proc, for better performance than if another proc were called. (This is also a common reason why macros are used instead of functions in C.)


#  C-like Macro system for Tcl.
#    cmacro::reset ?beginChar? ?midChar? ?endChar?
#       Delete any existing macro definitions,
#       and set the special substitution delimiters.
#       Defaults are  "<" "," ">"
#    cmacro::define name formal_parameter_list body
#       Define a macro named "name" to expand to "body",
#       substituting occurrances of the formal parameters
#       with values provided when the macro is used.
#       Tcl "string map" is used to make the substitutions.
#    cmacro::substitute string
#       Preform macro substitutions on string
#       and return the new string.
#    cmacro::mproc name params body
#       Define a procedure just like "proc" does,
#       but first preform macro substitutions on the body.
#  For examples, see the tests at the end of this file.
#  To test, run with ::argv set to "--test-cmacro"


Here we define three macros, "hd", "tl", and "tlN". The first two take one parameter, but tlN takes two.

        cmacro::define hd X {[lindex X 0]}
        cmacro::define tl X {[lrange X 1 end]}
        cmacro::define tlN {N X} {[lrange X N end]}

(* "hd" returns the head of a list, its first element. "tl" returns the tail, the rest of the list. "tlN" returns the tail starting with the Nth element, rather than starting with the second. *)

Now we use them in two proc definitions, but we use "cmacro::mproc" rather than "proc" so macro substitutions will occur.

        cmacro::mproc second list { return hd< tl<$list> > }
        cmacro::mproc tailN {n list} { return tlN<$n, $list> }

(* "second" returns the head of the tail of the list, which is its second item. "tailN" is a simple wrapper around "tlN", demonstrating how a "," is used to separate the arguments to the macro "tlN". *)

Although the C preprocessor uses parentheses "(" ")" to mark macro arguments, we prefer angle brackets "<" ">" so that parentheses may be used for array references. (You may change the special characters with the cmacro::reset command if you don't like angle brackets.)

The "<" must occur immediately after the macro name, followed by one or more arguments separated by ",", and finally the ">" ends the macro arguments. Inner macros substitute before outer ones. There may be no extra "<", ",", or ">" characters inside the macro arguments.

Macro arguments are "string trim"med, so spaces may be used after "<", before ">", and around "," to improve readability, without changing the substitution. This matters when the formal parameters of the macro are used in strings in the macro definition.

Whereas C allows macros with no arguments and no parens, we require the angle brackets to be used.

Here is "second_proc", which is the same as "second" except that it is written using procs instead of macros:

proc hd_proc X {lindex $X 0}
proc tl_proc X {lrange $X 1 end}
proc second_proc list {hd_proc [tl_proc $list]}
And here is a preformance test:
set list {one two three four five}

puts fast=[time {second $list} 2000]
puts slow=[time {second_proc $list} 2000]
And the results:
$ tclsh8.4 perform.tcl
fast=7 microseconds per iteration
slow=37 microseconds per iteration


(last modified 2006-05-29)       [Login]
(No back references.)