What's the best practice way to test whether parameter is empty?
The method of the question
\bgroup
\setbox0=\hbox{#1}
\ifdim\wd0=0pt
it is empty
\else
it is not empty
\fi
\egroup
has several problems, if used for a general test:
\setbox0\hbox{#1}
leak color specials, when#1
contains top level\color
commands, because the color macros use\aftergroup
to reset the color after the current group (\hbox
). This can be fixed for both plain TeX and LaTeX by using an additional group:\setbox0=\hbox{\begingroup#1\endgroup}
In LaTeX
\sbox0{#1}
can be used.#1
can contain material, but the overall width is zero, examples:\hbox{$\not$}% the width of the glyph `\not` is zero so that `\not=` forms the "not equals" symbols \hbox{\rlap{Text}}
#1
can contain so much material, that the width overflows. TeX does not throw an error, but the width can be zero accidentally again.The width of the
\hbox
depends on the font. For example, TikZ sets\nullfont
in its environments, thus the width would always be zero.\bgroup
and\egroup
are the macro form for{
and}
. They have a serious side effect in math, that they form a math subformula, which behaves as math ordinary atom and influences the horizontal spacing. This can be fixed by using\begingroup
and\endgroup
.
Test based on macro definition
#1
can be put into a macro and then the macro can be tested for emptiness:
\def\param{#1}%
\ifx\param\empty
The parameter is empty.%
\else
The parameter is not empty.%
\fi
This is not perfect yet, because #1
might contain #
, which is problematic in macro definitions, because they need to be doubled and numbered. This can be avoided by the use of a token register:
\toks0={#1}%
\edef\param{\the\toks0}% No full expansion, the token register is unpacked only
\ifx\param\empty
...
In LaTeX \@empty
can be used, but it also provides plain TeX's \empty
.
The side effects of setting \toks0
and defining \param
can be removed by using a group:
\begingroup \toks0={#1}% \edef\param{\the\toks0}% \expandafter\endgroup \ifx\param\empty ...
This solution works in both plain TeX and LaTeX; e-TeX is not used. Because of the assignment and definition the code is not expandable.
Expandable tests
If e-TeX is available, \detokenize
allows a safe expandable method,
see also the answer of PhilipPirrip:
\if\relax\detokenize{#1}\relax The parameter is empty.% \else The parameter is not empty.% \fi
Because \detokenize
converts is argument to simple characters with category code other (same as digits) and the space (in case of the space), it does not contain any command tokens and other problematic stuff, which could break
\if
.
Without e-TeX the test should not use \if
, but \ifx
:
\ifx\relax#1\relax
...
However, a macro with meaning \relax
might be present at the start of parameter #1
. Therefore \relax
should be replaced by something, which is unlikely to be used in #1
, examples:
\def\TestEmptyFence{TestEmptyFence}
\ifx\TestEmptyFence#1\TestEmptyFence
...
Or Donald Arseneau (url.sty
, ...) often uses a character with unusual catcode:
\begingroup
\catcode`Q=3 %
\gdef\MyEmptyTestMacro#1{%
\ifxQ#1Q%
...
}%
\endgroup % restore catcode of Q
However, these expandable tests without e-TeX can be broken, e.g., if #1
contains unmatched \if
commands. To reduce these problems, see the much more elaborate answer of Ulrich Diez.
I have marked the best solutions, non-expandable without e-TeX and expandable with e-TeX, by adding a quote environment to the code block.
Improvement of the if branches
\def\foobar#1#2#3{%
\if...
#2% if true
\else
#3% otherwise
\fi
can be improved, because the code has the limitation, that #2
is followed by \else
and #3
is followed by \fi
. Thus both #2
and #3
cannot contain macros at the end, which expect following parameters, e.g.:
\foobar{...}{\textit}{\textbf}{Hello}
Instead of Hello
, \textit
gets \else
and \textbf
gets\fi` as parameter, breaking the code.
The standard way is finishing the \if
construct first and selecting the argument via \@firstoftwo
and \@secondoftwo
:
\def\foobar#1{%
\if...
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
The \expandafter
closes the current if branch first. The macros \@firstoftwo
and \@secondoftwo
are defined in LaTeX:
\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}
Plain TeX does not have an equivalent, thus they need to be defined there.
Here is an alternative expandable solution:
- it uses
- no
e-TeX
, - no TeX conditionals,
- no
\expandafter
's, - no
\string
's.
- no
- it expands in three steps in all cases (with some usual
\romannumeral0
wrapper one could get it to expand in two steps), which presumably makes it reasonably fast, - it is entirely based on expansion of suitably delimited macros, but
the tested argument should not contain at top level
\IfNoTokB
token (as it appears in the parameter texts of the auxiliary macros).
As with Ulrich Diez's answer, the macro only examines whether the argument has tokens or no token, with #1=\empty
the argument is declared non-empty, although its expansion would be empty.
Code (for Plain
or LaTeX
):
% auxiliary macros
\long\def\IfNoTokA #1\IfNoTokB \IfNoTokB {}
\long\def\IfNoTokB \IfNoTokC #1#2{#1}
\long\def\IfNoTokC #1#2{#2}% this is \@secondoftwo in LaTeX
\long\def\IfNoTokens #1{\IfNoTokA\IfNoTokB #1\IfNoTokB\IfNoTokB\IfNoTokC }
%% The definition could be simplified if it was not asked to
%% distinguish #1 = space tokens from #1 = truly no token
Alternative where the only forbidden token in #1
will now be the active backslash (a quite unlikely token, but \IfNoTokB
already was):
% active \ replaces \IfNoTokB
\begingroup
\catcode`\|=0
\catcode`\\=13
|long|gdef|IfNoTokA #1\\{}
|long|gdef\|IfNoTokC #1#2{#1}
|long|gdef|IfNoTokC #1#2{#2}% = \@secondoftwo
|long|gdef|IfNoTokens #1{|IfNoTokA\#1\\|IfNoTokC }
|endgroup
Usage:
%% \IfNoTokens
%% {<The macro argument which is to be checked>}%
%% {<Tokens to be delivered in case of empty argument>}%
%% {<Tokens to be delivered in case of non emty argument>}%
%%
Tests:
\tracingmacros1
empty? (yes): \IfNoTokens {}{Yes}{No}
empty? (no): \IfNoTokens { }{Yes}{No}
empty? (no): \IfNoTokens {{}}{Yes}{No}
empty? (no): \IfNoTokens {\if}{Yes}{No}
empty? (no): \IfNoTokens {#}{Yes}{No}
empty? (no): \IfNoTokens {\A}{Yes}{No}
\bye
Here is the log showing the expansions:
#1=empty
:
\IfNoTokens #1->\IfNoTokA \IfNoTokB #1\IfNoTokB \IfNoTokB \IfNoTokC
#1<-
\IfNoTokA #1\IfNoTokB \IfNoTokB ->
#1<-
\IfNoTokB \IfNoTokC #1#2->#1
#1<-Yes
#2<-No
#1=space token
:
\IfNoTokens #1->\IfNoTokA \IfNoTokB #1\IfNoTokB \IfNoTokB \IfNoTokC
#1<-
\IfNoTokA #1\IfNoTokB \IfNoTokB ->
#1<-\IfNoTokB
\IfNoTokC #1#2->#2
#1<-Yes
#2<-No
#1 = {}
:
\IfNoTokens #1->\IfNoTokA \IfNoTokB #1\IfNoTokB \IfNoTokB \IfNoTokC
#1<-{}
\IfNoTokA #1\IfNoTokB \IfNoTokB ->
#1<-\IfNoTokB {}
\IfNoTokC #1#2->#2
#1<-Yes
#2<-No
#1=\if
:
\IfNoTokens #1->\IfNoTokA \IfNoTokB #1\IfNoTokB \IfNoTokB \IfNoTokC
#1<-\if
\IfNoTokA #1\IfNoTokB \IfNoTokB ->
#1<-\IfNoTokB \if
\IfNoTokC #1#2->#2
#1<-Yes
#2<-No
#1=#
:
\IfNoTokens #1->\IfNoTokA \IfNoTokB #1\IfNoTokB \IfNoTokB \IfNoTokC
#1<-##
\IfNoTokA #1\IfNoTokB \IfNoTokB ->
#1<-\IfNoTokB ##
\IfNoTokC #1#2->#2
#1<-Yes
#2<-No
#1=\A
:
\IfNoTokens #1->\IfNoTokA \IfNoTokB #1\IfNoTokB \IfNoTokB \IfNoTokC
#1<-\A
\IfNoTokA #1\IfNoTokB \IfNoTokB ->
#1<-\IfNoTokB \A
\IfNoTokC #1#2->#2
#1<-Yes
#2<-No
EGreg had a nice answer on that, check https://tex.stackexchange.com/a/127506/65222 Here's the relevant part:
A clever example of \if
is for testing whether an argument is empty:
\def\cs#1{%
\if\relax\detokenize{#1}\relax
The argument is empty%
\else
The argument #1 is non empty%
\fi
}
It uses \detokenize
which is an e-TeX feature. If the argument is empty, the comparison would be between \relax
and \relax
, which are equal as far as \if
is concerned; otherwise, \detokenize
would return a string of characters (of category code 12) and \relax
is never equal to a character for \if
.