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.
  • 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.