[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 17. Correct version of some examples

Some of the examples in this manuals are buggy or not very robust, for demonstration purposes. Improved versions of these composite macros are presented here.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 17.1 Solution for `exch`

The `exch` macro (see section Arguments to macros) as presented requires clients to double quote their arguments. A nicer definition, which lets clients follow the rule of thumb of one level of quoting per level of parentheses, involves adding quotes in the definition of `exch`, as follows:

    define(`exch', ``$2', `$1'')
    ⇒
    define(exch(`expansion text', `macro'))
    ⇒
    macro
    ⇒expansion text

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 17.2 Solution for `forloop`

The `forloop` macro (see section Iteration by counting) as presented earlier can go into an infinite loop if given an iterator that is not parsed as a macro name. It does not do any sanity checking on its numeric bounds, and only permits decimal numbers for bounds. Here is an improved version, shipped as ``m4-1.4.8/examples/forloop2.m4'`; this version also optimizes based on the fact that the starting bound does not need to be passed to the helper `_forloop`.

    undivert(`forloop2.m4')dnl
    ⇒divert(`-1')
    ⇒# forloop(var, from, to, stmt) - improved version:
    ⇒#   works even if VAR is not a strict macro name
    ⇒#   performs sanity check that FROM is larger than TO
    ⇒#   allows complex numerical expressions in TO and FROM
    ⇒define(`forloop', `ifelse(eval(`($3) >= ($2)'), `1',
    ⇒  `pushdef(`$1', eval(`$2'))_forloop(`$1',
    ⇒    eval(`$3'), `$4')popdef(`$1')')')
    ⇒define(`_forloop',
    ⇒  `$3`'ifelse(indir(`$1'), `$2', `',
    ⇒    `define(`$1', incr(indir(`$1')))$0($@)')')
    ⇒divert`'dnl
    include(`forloop2.m4')
    ⇒
    forloop(`i', `2', `1', `no iteration occurs')
    ⇒
    forloop(`', `1', `2', ` odd iterator name')
    ⇒ odd iterator name odd iterator name
    forloop(`i', `5 + 5', `0xc', ` 0x`'eval(i, `16')')
    ⇒ 0xa 0xb 0xc
    forloop(`i', `a', `b', `non-numeric bounds')
    error-->m4:stdin:6: bad expression in eval (bad input): (b) >= (a)
    ⇒

Of course, it is possible to make even more improvements, such as adding an optional step argument, or allowing iteration through descending sequences. GNU Autoconf provides some of these additional bells and whistles in its `m4_for` macro.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 17.3 Solution for `foreach`

The `foreach` and `foreachq` macros (see section Iteration by list contents) as presented earlier each have flaws. First, we will examine and fix the quadratic behavior of `foreachq`:

    include(`foreachq.m4')
    ⇒
    traceon(`shift')debugmode(`aq')
    ⇒
    foreachq(`x', ``1', `2', `3', `4'', `x
    ')dnl
    ⇒1
    error-->m4trace: -3- shift(`1', `2', `3', `4')
    error-->m4trace: -2- shift(`1', `2', `3', `4')
    ⇒2
    error-->m4trace: -4- shift(`1', `2', `3', `4')
    error-->m4trace: -3- shift(`2', `3', `4')
    error-->m4trace: -3- shift(`1', `2', `3', `4')
    error-->m4trace: -2- shift(`2', `3', `4')
    ⇒3
    error-->m4trace: -5- shift(`1', `2', `3', `4')
    error-->m4trace: -4- shift(`2', `3', `4')
    error-->m4trace: -3- shift(`3', `4')
    error-->m4trace: -4- shift(`1', `2', `3', `4')
    error-->m4trace: -3- shift(`2', `3', `4')
    error-->m4trace: -2- shift(`3', `4')
    ⇒4
    error-->m4trace: -6- shift(`1', `2', `3', `4')
    error-->m4trace: -5- shift(`2', `3', `4')
    error-->m4trace: -4- shift(`3', `4')
    error-->m4trace: -3- shift(`4')

Each successive iteration was adding more quoted `shift` invocations, and the entire list contents were passing through every iteration. In general, when recursing, it is a good idea to make the recursion use fewer arguments, rather than adding additional quoted uses of `shift`. By doing so, `m4` uses less memory, invokes fewer macros, is less likely to run into machine limits, and most importantly, performs faster. The fixed version of `foreachq` can be found in ``m4-1.4.8/examples/foreachq2.m4'`:

    include(`foreachq2.m4')
    ⇒
    undivert(`foreachq2.m4')dnl
    ⇒include(`quote.m4')dnl
    ⇒divert(`-1')
    ⇒# foreachq(x, `item_1, item_2, ..., item_n', stmt)
    ⇒#   quoted list, improved version
    ⇒define(`foreachq', `pushdef(`$1')_foreachq($@)popdef(`$1')')
    ⇒define(`_arg1q', ``$1'')
    ⇒define(`_rest', `ifelse(`$#', `1', `', `dquote(shift($@))')')
    ⇒define(`_foreachq', `ifelse(`$2', `', `',
    ⇒  `define(`$1', _arg1q($2))$3`'$0(`$1', _rest($2), `$3')')')
    ⇒divert`'dnl
    traceon(`shift')debugmode(`aq')
    ⇒
    foreachq(`x', ``1', `2', `3', `4'', `x
    ')dnl
    ⇒1
    error-->m4trace: -3- shift(`1', `2', `3', `4')
    ⇒2
    error-->m4trace: -3- shift(`2', `3', `4')
    ⇒3
    error-->m4trace: -3- shift(`3', `4')
    ⇒4

Note that the fixed version calls unquoted helper macros in `_foreachq` to trim elements immediately; those helper macros in turn must re-supply the layer of quotes lost in the macro invocation. Contrast the use of `_arg1q`, which quotes the first list element, with `_arg1` of the earlier implementation that returned the first list element directly.

For a different approach, the improved version of `foreach`, available in ``m4-1.4.8/examples/foreach2.m4'`, simply overquotes the arguments to `_foreach` to begin with, using `dquote_elt`. Then `_foreach` can just use `_arg1` to remove the extra layer of quoting that was added up front:

    include(`foreach2.m4')
    ⇒
    undivert(`foreach2.m4')dnl
    ⇒include(`quote.m4')dnl
    ⇒divert(`-1')
    ⇒# foreach(x, (item_1, item_2, ..., item_n), stmt)
    ⇒#   parenthesized list, improved version
    ⇒define(`foreach', `pushdef(`$1')_foreach(`$1',
    ⇒  (dquote(dquote_elt$2)), `$3')popdef(`$1')')
    ⇒define(`_arg1', `$1')
    ⇒define(`_foreach', `ifelse(`$2', `(`')', `',
    ⇒  `define(`$1', _arg1$2)$3`'$0(`$1', (dquote(shift$2)), `$3')')')
    ⇒divert`'dnl
    traceon(`shift')debugmode(`aq')
    ⇒
    foreach(`x', `(`1', `2', `3', `4')', `x
    ')dnl
    error-->m4trace: -4- shift(`1', `2', `3', `4')
    error-->m4trace: -4- shift(`2', `3', `4')
    error-->m4trace: -4- shift(`3', `4')
    ⇒1
    error-->m4trace: -3- shift(``1'', ``2'', ``3'', ``4'')
    ⇒2
    error-->m4trace: -3- shift(``2'', ``3'', ``4'')
    ⇒3
    error-->m4trace: -3- shift(``3'', ``4'')
    ⇒4
    error-->m4trace: -3- shift(``4'')

In summary, recursion over list elements is trickier than it appeared at first glance, but provides a powerful idiom within `m4` processing. As a final demonstration, both list styles are now able to handle several scenarios that would wreak havoc on the original implementations. This points out one other difference between the two list styles. `foreach` evaluates unquoted list elements only once, in preparation for calling `_foreach`. But `foreachq` evaluates unquoted list elements twice while visiting the first list element, once in `_arg1q` and once in `_rest`. When deciding which list style to use, one must take into account whether repeating the side effects of unquoted list elements will have any detrimental effects.

    include(`foreach2.m4')
    ⇒
    include(`foreachq2.m4')
    ⇒
    dnl 0-element list:
    foreach(`x', `', `<x>') / foreachq(`x', `', `<x>')
    ⇒ /
    dnl 1-element list of empty element
    foreach(`x', `()', `<x>') / foreachq(`x', ``'', `<x>')
    ⇒<> / <>
    dnl 2-element list of empty elements
    foreach(`x', `(`',`')', `<x>') / foreachq(`x', ``',`'', `<x>')
    ⇒<><> / <><>
    dnl 1-element list of a comma
    foreach(`x', `(`,')', `<x>') / foreachq(`x', ``,'', `<x>')
    ⇒<,> / <,>
    dnl 2-element list of unbalanced parentheses
    foreach(`x', `(`(', `)')', `<x>') / foreachq(`x', ``(', `)'', `<x>')
    ⇒<(><)> / <(><)>
    define(`active', `ACT, IVE')
    ⇒
    traceon(`active')
    ⇒
    dnl list of unquoted macros; expansion occurs before recursion
    foreach(`x', `(active, active)', `<x>
    ')dnl
    error-->m4trace: -4- active -> `ACT, IVE'
    error-->m4trace: -4- active -> `ACT, IVE'
    ⇒<ACT>
    ⇒<IVE>
    ⇒<ACT>
    ⇒<IVE>
    foreachq(`x', `active, active', `<x>
    ')dnl
    error-->m4trace: -3- active -> `ACT, IVE'
    error-->m4trace: -3- active -> `ACT, IVE'
    ⇒<ACT>
    error-->m4trace: -3- active -> `ACT, IVE'
    error-->m4trace: -3- active -> `ACT, IVE'
    ⇒<IVE>
    ⇒<ACT>
    ⇒<IVE>
    dnl list of quoted macros; expansion occurs during recursion
    foreach(`x', `(`active', `active')', `<x>
    ')dnl
    error-->m4trace: -1- active -> `ACT, IVE'
    ⇒<ACT, IVE>
    error-->m4trace: -1- active -> `ACT, IVE'
    ⇒<ACT, IVE>
    foreachq(`x', ``active', `active'', `<x>
    ')dnl
    error-->m4trace: -1- active -> `ACT, IVE'
    ⇒<ACT, IVE>
    error-->m4trace: -1- active -> `ACT, IVE'
    ⇒<ACT, IVE>
    dnl list of double-quoted macro names; no expansion
    foreach(`x', `(``active'', ``active'')', `<x>
    ')dnl
    ⇒<active>
    ⇒<active>
    foreachq(`x', ```active'', ``active''', `<x>
    ')dnl
    ⇒<active>
    ⇒<active>

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 17.4 Solution for `cleardivert`

The `cleardivert` macro (see section Discarding diverted text) cannot, as it stands, be called without arguments to clear all pending diversions. That is because using undivert with an empty string for an argument is different than using it with no arguments at all. Compare the earlier definition with one that takes the number of arguments into account:

    define(`cleardivert',
      `pushdef(`_n', divnum)divert(`-1')undivert($@)divert(_n)popdef(`_n')')
    ⇒
    divert(`1')one
    divert
    ⇒
    cleardivert
    ⇒
    undivert
    ⇒one
    ⇒
    define(`cleardivert',
      `pushdef(`_num', divnum)divert(`-1')ifelse(`$#', `0',
        `undivert`'', `undivert($@)')divert(_num)popdef(`_num')')
    ⇒
    divert(`2')two
    divert
    ⇒
    cleardivert
    ⇒
    undivert
    ⇒

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 17.5 Solution for `fatal_error`

The `fatal_error` macro (see section Exiting from `m4`) is not robust to versions of GNU M4 earlier than 1.4.8, where invoking `__file__` (see section Printing current location) inside `m4wrap` would result in an empty string, and `__line__` resulted in ``0'` even though all files start at line 1. Furthermore, versions earlier than 1.4.6 did not support the `__program__` macro. If you want `fatal_error` to work across the entire 1.4.x release series, a better implementation would be:

    define(`fatal_error',
      `errprint(ifdef(`__program__', `__program__', ``m4'')'dnl
    `:ifelse(__line__, `0', `',
        `__file__:__line__:')` fatal error: $*
    ')m4exit(`1')')
    ⇒
    m4wrap(`divnum(`demo of internal message')
    fatal_error(`inside wrapped text')')
    ⇒
    ^D
    error-->m4:stdin:6: Warning: excess arguments to builtin `divnum' ignored
    ⇒0
    error-->m4:stdin:6: fatal error: inside wrapped text

[ << ] | [ >> ] |  |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

This document was generated by root on April, 16 2008 using texi2html 1.76.