Next: , Previous: , Up: Answers   [Contents][Index]

17.4 Solution for copy

The macro copy presented above is unable to handle builtin tokens with M4 1.4.x, because it tries to pass the builtin token through the macro curry, where it is silently flattened to an empty string (see Composition). Rather than using the problematic curry to work around the limitation that stack_foreach expects to invoke a macro that takes exactly one argument, we can write a new macro that lets us form the exact two-argument pushdef call sequence needed, so that we are no longer passing a builtin token through a text macro.

Composite: stack_foreach_sep (macro, pre, post, sep)
Composite: stack_foreach_sep_lifo (macro, pre, post, sep)

For each of the pushdef definitions associated with macro, expand the sequence ‘pre`'definition`'post’. Additionally, expand sep between definitions. stack_foreach_sep visits the oldest definition first, while stack_foreach_sep_lifo visits the current definition first. The expansion may dereference macro, but should not modify it. There are a few special macros, such as defn, which cannot be used as the macro parameter.

Note that stack_foreach(`macro', `action') is equivalent to stack_foreach_sep(`macro', `action(', `)'). By supplying explicit parentheses, split among the pre and post arguments to stack_foreach_sep, it is now possible to construct macro calls with more than one argument, without passing builtin tokens through a macro call. It is likewise possible to directly reference the stack definitions without a macro call, by leaving pre and post empty. Thus, in addition to fixing copy on builtin tokens, it also executes with fewer macro invocations.

The new macro also adds a separator that is only output after the first iteration of the helper _stack_reverse_sep, implemented by prepending the original sep to pre and omitting a sep argument in subsequent iterations. Note that the empty string that separates sep from pre is provided as part of the fourth argument when originally calling _stack_reverse_sep, and not by writing $4`'$3 as the third argument in the recursive call; while the other approach would give the same output, it does so at the expense of increasing the argument size on each iteration of _stack_reverse_sep, which results in quadratic instead of linear execution time. The improved stack walking macros are available in m4-1.4.19/examples/stack_sep.m4:

$ m4 -I examples
define(`copy', `ifdef(`$2', `errprint(`$2 already defined
   `stack_foreach_sep(`$1', `pushdef(`$2',', `)')')')dnl
pushdef(`a', `1')pushdef(`a', defn(`divnum'))
copy(`a', `b')
pushdef(`c', `1')pushdef(`c', `2')
stack_foreach_sep_lifo(`c', `', `', `, ')
⇒2, 1
⇒# stack_foreach_sep(macro, pre, post, sep)
⇒# Invoke PRE`'defn`'POST with a single argument of each definition
⇒# from the definition stack of MACRO, starting with the oldest, and
⇒# separated by SEP between definitions.
⇒`_stack_reverse_sep(`$1', `tmp-$1')'dnl
⇒`_stack_reverse_sep(`tmp-$1', `$1', `$2`'defn(`$1')$3', `$4`'')')
⇒# stack_foreach_sep_lifo(macro, pre, post, sep)
⇒# Like stack_foreach_sep, but starting with the newest definition.
⇒`_stack_reverse_sep(`$1', `tmp-$1', `$2`'defn(`$1')$3', `$4`'')'dnl
⇒`_stack_reverse_sep(`tmp-$1', `$1')')
⇒`ifdef(`$1', `pushdef(`$2', defn(`$1'))$3`'popdef(`$1')$0(
⇒  `$1', `$2', `$4$3')')')

Next: , Previous: , Up: Answers   [Contents][Index]