[Tex/LaTex] How to check if a macro value is empty or will not create text with plain TeX conditionals

conditionalstex-core

I have a macro which holds some text value, e.g. \macro{some text}. If it is empty, I would like for nothing to appear in the document, but it has some content of any kind which would produce text displayed in the document, I would like a message to appear, "Text: some text".

  • \macro{} would be considered empty.
  • \def\somedata{}, \macro{\somedata} would be considered empty.
  • \macro{0} would be considered not empty.

I have tried making a TeX conditional which can check if the value is not empty, but nothing I tried works, e.g.:

\ifx #1 {}
\else
    Text: #1
\fi

\ifx#1{}
\else
    Text: #1
\fi

\ifx #1 \nil
\else
    Text: #1
\fi

\ifx#1!=""
\else
    Text: #1
\fi

What is the correct syntax for creating a conditional in plain TeX with checks if a value is not empty?

Best Answer

(1) Tests for empty token list as input

You can try

\def\temp{#1}\ifx\temp\empty
  <EMPTY>%
\else
  <NON EMPTY>%
\fi

If you know that a token, say \hfuzz, will not appear in #1, then

\ifx\hfuzz#1\hfuzz
  <EMPTY>%
\else
  <NON EMPTY>%
\fi

This, differently from the previous test, is expandable.

The safest test uses e-TeX:

\if\relax\detokenize{#1}\relax
  <EMPTY>%
\else
  <NON EMPTY>%
\fi

Using this last one shouldn't be a problem, but be warned that it doesn't work with "Knuth TeX".

Comments

You should know also that \ifx compares the two tokens that follow it and has no = sign. Similarly, \if compares the two tokens that follow it, but after having done complete expansion.

So, how does the last test work? With \detokenize{#1} the argument is transformed into a sequence of characters of category code 12, none of which is \if-equivalent to \relax. Thus with empty #1 the test would compare \relax with \relax and so return True; with #1 non empty, say abc, the code would be

\if\relax abc\relax<EMPTY>\else<NOT EMPTY>\fi

and the comparison between \relax and a returns False, so only the <NOT EMPTY> code would remain.

(2) Test for no printed output

In order to test for no printed output you can't have an expandable test:

\setbox0=\hbox{#1\unskip}\ifdim\wd0=0pt
  <EMPTY>%
\else
  <NOT EMPTY>%
\fi

This assumes that #1 doesn't contain vertical material, such as \vfill. Remove the \unskip if also "space output" should be considered. However, this can go wrong when \mymacro{\hskip1pt\hskip1pt} is called; it mostly depends on what you expect to go in the argument how to cope with limit cases.

If e-TeX is used, one can define a \foreverunspace macro that will remove all trailing spaces (assuming no whatsit appears):

\def\foreverunspace{%
  \ifnum\lastnodetype=11
    \unskip\foreverunspace
  \else
    \ifnum\lastnodetype=12
      \unkern\foreverunspace
    \else
      \ifnum\lastnodetype=13
        \unpenalty\foreverunspace
      \fi
    \fi
  \fi
}

and so the test can be

\setbox0=\hbox{#1\foreverunspace}\ifdim\wd0=0pt
  <EMPTY>%
\else
  <NOT EMPTY>%
\fi

that would remove any trailing combination of glues, kerns and penalties.

Related Question