6

How can I test in a safe way (i.e. without reserving tokens as explained here) if an expanded macro argument is empty?

SJU
  • 1,721
  • I do not understand what do you mean by “expanded” (i.e., why is it relevant here). In your link the answer says “The safest way is \if\relax\detokenize{#1}\relax”. What “safe way” are you looking for? – Manuel Nov 18 '14 at 12:38
  • @Manuel Basically, I am looking for a test if a macro argument expands to nothing. (\if\relax\detokenize{#1}\relax fails when passed \empty, defined as \def\empty{}.) A safe test is one that is generic enough not to rely on a token not being in the argument. – SJU Nov 18 '14 at 12:52
  • 2
    @AngelTsankov The \if\relax\detokenize{#1}\relax isn't relying on anything: if the argument is \empty then it's not empty. So while 'how do I test if the full expansion of an argument' is a perfectly valid question, it's not due to anything 'unsafe' in the test for an empty argument. – Joseph Wright Nov 18 '14 at 14:18

3 Answers3

6

You can use a \romannumeral trick:

\if\relax\detokenize\expandafter{\romannumeral-`\Q#1}\relax

The missing space after `\Q will trigger expansion of #1 (after macro substitution, of course). Since this expansion ends at the first unexpandable token, this should be what you want.

However a leading space will be ignored.

\def\notempty{\relax}
\def\perhapsempty{\empty}

\if\relax\detokenize\expandafter{\romannumeral-`\Q\empty}\relax
  \message{EMPTY}
\else
  \message{NOT EMPTY}
\fi

\if\relax\detokenize\expandafter{\romannumeral-`\Q\notempty}\relax
  \message{EMPTY}
\else
  \message{NOT EMPTY}
\fi

\if\relax\detokenize\expandafter{\romannumeral-`\Q\perhapsempty}\relax
  \message{EMPTY}
\else
  \message{NOT EMPTY}
\fi

\if\relax\detokenize\expandafter{\romannumeral-`\Q\space}\relax
  \message{EMPTY}
\else
  \message{NOT EMPTY}
\fi

This is pdfTeX, Version 3.14159265-2.6-1.40.15 (TeX Live 2014) (preloaded format=pdftex)
 restricted \write18 enabled.
entering extended mode
(./tsanempty.tex EMPTY NOT EMPTY EMPTY EMPTY)

If you don't need expandability and trust that the tokens you pass are fully expandable, you can use \edef:

\begingroup\edef\x{#1}\expandafter\endgroup\expandafter
\if\expandafter\relax\detokenize\expandafter{\x}\relax

For instance,

\begingroup\edef\x{\empty}\expandafter\endgroup\expandafter
\if\expandafter\relax\detokenize\expandafter{\x}\relax
  \message{EMPTY}
\else
  \message{NOT EMPTY}
\fi

\begingroup\edef\x{\notempty}\expandafter\endgroup\expandafter
\if\expandafter\relax\detokenize\expandafter{\x}\relax
  \message{EMPTY}
\else
  \message{NOT EMPTY}
\fi

\begingroup\edef\x{\perhapsempty}\expandafter\endgroup\expandafter
\if\expandafter\relax\detokenize\expandafter{\x}\relax
  \message{EMPTY}
\else
  \message{NOT EMPTY}
\fi

\begingroup\edef\x{\space}\expandafter\endgroup\expandafter
\if\expandafter\relax\detokenize\expandafter{\x}\relax
  \message{EMPTY}
\else
  \message{NOT EMPTY}
\fi

will return

EMPTY NOT EMPTY EMPTY NOT EMPTY

Actually, an \expandafter is redundant, but clarity is better.

egreg
  • 1,121,712
5

enter image description here

\documentclass{article}
\usepackage[T1]{fontenc}

\def\foo#1{\par \texttt{<\detokenize{#1}>} 
\expandafter\if\relax\detokenize\expandafter{\romannumeral0#1}\relax
\if X#1X%
empty\else
not empty\fi
\else
not empty\fi}

\begin{document}
\foo{}

\foo{ }

\foo{\empty}

\foo{{}}

\foo{\sqrt}

\foo{\csname @gobble\endcsname{hello}}

\end{document}
David Carlisle
  • 757,742
2

Have you tried the ifmtarg package? It provides the macro

\@ifmtarg{<arg>}{<Code for arg empty>}
                {<Code for arg not empty>}

Use texdoc ifmtarg for the documentation.

Peter Wilson
  • 28,066