127

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?

Village
  • 13,603
  • 23
  • 116
  • 219

4 Answers4

127

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

egreg
  • 1,121,712
  • 3
    if there is a space between the braces in your second case, the width will be nonzero (although the output will not be visible in many contexts). better to use \ignorespaces and/or \unskip to get rid of such spaces. – barbara beeton Apr 24 '12 at 14:55
  • In your first test, the definition of \temp should not contain that \unskip token—which makes the test always fail. A definition similar to\toks0={#1}\edef\temp{\the\toks0}will also deal with the case where#1contains numerical argument tokens while\def` would croak and fail. (Hey how did you get that token there? Ok, it's a corner case!) – Michaël Le Barbier Nov 05 '13 at 07:12
  • 1
    Why do we need the second \relax in \if\relax\detokenize{#1}\relax? Could it be just a % instead, or does it have another role to play? – n.r. Apr 27 '15 at 16:16
  • @nicolai.rostov It could be (not in all cases, though), but this would cause expansion of the following token in case #1 is empty, because \if wants to see two unexpandable tokens after it. Since \relax is unexpandable, it will cause no problem in case #1 is empty, and it will disappear as part of the “true text” if #1 is not empty. – egreg Apr 27 '15 at 16:22
  • I think I see it. So, what is being compared here is not \relax and \detokenize{#1} but, rather, \relax and \detokenize{#1}\relax, right? – n.r. Apr 27 '15 at 16:26
  • @nicolai.rostov Not really; \if sees \relax and so looks for a second unexpandable token. Since \detokenize is expandable, TeX expands it. The result is a string of “other characters”, so unexpandable and surely different from \relax (if #1 is not empty). So this will trigger following the “false” branch. If #1 is empty, \detokenize{#1} produces nothing, so \if compares the first \relax to the second \relax. – egreg Apr 27 '15 at 16:31
  • 1
    This is really clever. So, \if compares the first \relax with either \detokenize{#1} or the second \relax depending on what #1 turns out to be. – n.r. Apr 27 '15 at 16:35
  • 1
    Your first five lines of your answer doesn't work, because the \temp macro is never empty because it includes \unskip primitive. I don't understand 36 votes with such obvious mistake in the answer. – wipet May 07 '15 at 19:59
  • 5
    @wipet Thanks, it probably was a copy paste problem. Since that's the worst method for checking emptyness, probably nobody noticed. Please, feel free to downvote, if you think an answer is bad (this isn't, of course, apart from the silly mistake that's easily fixed). And you're being rude. – egreg May 07 '15 at 20:02
  • @egreg Let's assume that #1 is empty. I do not understand why \detokenize{#1} does not produce \relax. I thought "nothing"=\relax. For whatever reason it does not in this case, so TeX jumps to the following \relax, but why? – Jonathan Komar May 12 '17 at 12:01
  • @macmadness86 \relax means “do nothing” when executed, but itself is an unexpandable token. As a matter of fact, \detokenize{} produces an empty token list, which is different from \relax. – egreg May 12 '17 at 12:09
  • @egreg I see. So \if ignores token lists —not even recognizing them as anything expandable/expandable— when empty. But \relax is recognized as a token— although also effectively meaning typeset "nothing", it is technically different than an empty token list —therefore fulfilling \if's unexpandable token criterion. – Jonathan Komar May 12 '17 at 12:19
  • @macmadness86 If some expansion produces nothing, how can \if see something? ;-) – egreg May 12 '17 at 12:32
  • @egreg When you put it that way, it makes sense, haha! I think the confusion arises when people say something along the lines of, "\relax produces nothing". It is easy to think of \relax and an empty token list as equivalent (but of course \relax does not produce anything). This is a great example of that. Thanks for your explanations. Very helpful when I can understand them ;) – Jonathan Komar May 12 '17 at 12:42
  • By any chance, would you have the Latex3 version ? – tobiasBora Mar 25 '24 at 10:21
45

The etoolbox package offers \ifdefempty (and its cousin \ifcsempty for control sequence names):

\documentclass{article}

\usepackage{etoolbox}

\def\mymacroA{}
\def\mymacroB{ }
\def\mymacroC{E}
\def\mymacroD{ E }

\begin{document}

\ifdefempty{\mymacroA}{YES}{NO}\par
\ifdefempty{\mymacroB}{YES}{NO}\par
\ifdefempty{\mymacroC}{YES}{NO}\par
\ifdefempty{\mymacroD}{YES}{NO}

\end{document}

Note that like the ifmtarg package commands pointed out by Werner, but unlike Caramdir's \ifx test, \ifdefempty will expand to "true" for macro arguments consisting of at least one space and nothing else.

lockstep
  • 250,273
  • 4
    etoolbox, version 2.5f (2018-08-18), has fixed the issue you mentioned. The revision history says, "(This version) Fix issue with \ifdefempty, \ifcsempty, \ifdefvoid and \ifcsvoid when applied to macros expanding to space tokens". – muzimuzhi Z Aug 22 '18 at 02:08
  • Note that when a macro is defined as \relax it will be nonempty for \ifdefempty. So better use \def\toto{} than \let\toto\relax if you want the emptyness test to be true. – yannis Feb 04 '20 at 18:21
20

Use the ifmtarg package:

enter image description here

\documentclass{article}
\usepackage{ifmtarg}% http://ctan.org/pkg/ifmtarg
\makeatletter
\newcommand{\isempty}[1]{%
  \@ifmtarg{#1}{YES}{NO}}
\newcommand{\isnotempty}[1]{%
  \@ifnotmtarg{#1}{YES}}
\makeatother
\begin{document}
\verb|\isempty{}   |: \isempty{} \par
\verb|\isempty{ }  |: \isempty{ } \par
\verb|\isempty{E}  |: \isempty{E} \par
\verb|\isempty{ E }|: \isempty{ E }

\bigskip

\verb|\isnotempty{}   |: \isnotempty{} \par
\verb|\isnotempty{ }  |: \isnotempty{ } \par
\verb|\isnotempty{E}  |: \isnotempty{E} \par
\verb|\isnotempty{ E }|: \isnotempty{ E }

\end{document}

It defines \@ifmtarg{<stuff>}{<true>}{<false>} and \@ifnotmtarg{<stuff>}{<true>}.

Werner
  • 603,163
13

One way is to do:

\makeatletter
\def\test#1{%
    \def\tmp{#1}%
    \ifx\tmp\@empty%
        Nothing here!%
    \else%
        You passed: \texttt{#1}.%
    \fi%
}
\makeatother

Here \@empty is defined by LaTeX as \def\@empty{}.

Caramdir
  • 89,023
  • 26
  • 255
  • 291