21

I tried several thing to test if a parameter is empty or equal to a specific carater. How should one proceed ?

By the way, what is the best way to have conditionnal code on a parameter who can have three values : empty, v, h ?

\documentclass{standalone}
\newcommand{\Test}[3]{%
    \ifnum #1=1 C'est \'egal à 1 \fi
    \if #2v C'est \'egal à v \fi
    \ifx #2v C'est \'egal à v \fi
    \if #3\empty C'est vide \fi
    \ifx #3\empty C'est vide \fi
    }

\begin{document}
\Test{1}{v}{}
\end{document}

Never says vide...

4
  • 10
    Have you seen \ifblank or \ifstrempty in etoolbox?
    – egreg
    May 23, 2014 at 13:04
  • 10
    I use the ifthen-package and use it like: \ifthenelse{\equal{#1}{}}{it's empty}{it's not empty}
    – musicman
    May 23, 2014 at 13:10
  • probably a duplicate of tex.stackexchange.com/questions/53068/… May 23, 2014 at 13:15
  • @musicman Yes of course, I just forget about it.
    – Tarass
    May 23, 2014 at 13:44

4 Answers 4

16

If using the primitive TeX if syntax you should always take care to put the test token first before user supplied token, otherwise you have no control over what is being tested:

\documentclass{standalone}
\newcommand{\Test}[3]{%
    \ifnum #1=1 C'est \'egal à 1 \fi
    \if #2v C'est \'egal à v \fi
    \ifx #2v C'est \'egal à v \fi
    \if #3\empty C'est vide \fi
    \ifx #3\empty C'est vide \fi
    }

\begin{document}
\Test{2=2}{aa}{!!}
\end{document}

The 4th test will never test that #3 is empty, if given a single character it tests if it is C.

The exact test you should use depends on your definition of empty. which of these is "empty" where the outer {} are the delimiters of the argument: {} { } {\empty} {\mbox{}}

I normally do something like

 \ifx\som@internal@macro#1\som@internal@macro

where \som@internal@macro is some defined macro in the file that is unlikely to be used in the argument. Then if #1 is empty the test is

 \ifx\som@internal@macro\som@internal@macro

so tests true, If #1 is non empty (and does not start with \som@internal@macro) then it tests false.

4
  • 2
    while true, this doesn't show an example of what would work. that would be a kindness. May 23, 2014 at 14:20
  • @barbarabeeton perhaps but the "how to do it part" is a duplicate question (and I gave a link to answered in comment on the question) the best way depends on what you want to d really:-) so here I just addressed the code sample. To make you happy I'll add one working example:-) May 23, 2014 at 14:23
  • Thank you for your answer, explainations and link. Why \som@internal@macro should the be defined, as in the linked example it is not : \ifx\hfuzz#3\hfuzz ?
    – Tarass
    May 23, 2014 at 17:05
  • 1
    @Tarass\hfuzz is defined (it is a TeX primitive) you want something that is unlikely to appear in #1 but any undefined command is \ifx equal so if you use \my@undefined#1\my@undefined but the user mistypes #1 as \sectiom then it would test as true and try to execute the second guard token. May 23, 2014 at 18:41
15

Here's David Carlisle's solution in a nice macro called \ifempty. Use it in place of an \if as shown. Make sure to wrap your argument to \ifempty in curly brackets.

\def \ifempty#1{\def\temp{#1} \ifx\temp\empty }

\def \mymacro#1{ \ifempty{#1} empty \else not empty \fi }

\mymacro{something} % prints "not empty"
\mymacro{}          % prints "empty"
2
  • \mymacro is having difficulty accepting/recognising newcommand strings. For example \newcommand{\name{}} appears to be always "not empty" irrespective if \newcommand{\name{}{John} or `\newcommand{\name{}{}
    – 3kstc
    Jul 29, 2019 at 3:27
  • Thanks, @Daniel for the macro. I also had problems in referencing contexts such that I found this variation of the first line working: \def\ifempty#1{\ifx\empty#1\empty}
    – mfg
    Aug 25 at 18:23
8

As shown by egreg on this thread, using the \ifstrempty command from the etoolbox package, it's possible to test if the given string is empty or not, with the added bonus that linebreaks in the given string won't cause problems.

Here is an example using this command in a macro to test if string #1 is empty :

\usepackage{etoolbox}
\newcommand{\Test}[1]{%
    \ifstrempty{#1}%
        {%
            empty%
        }{%
            not empty%
        }%
}

This macro can also be used to print #2 if #1 is empty and #3 if it's non empty.

\newcommand{\Test}[3]{%
    \ifstrempty{#1}%
    {%
        #2% If #1 is empty
    }{%
        #3% If #1 is not empty
    }%
}
1

You say there are three possibilities for the value which a parameter can have.

Parameters and ⟨parameter text⟩ of a macro definition are means for denoting how at the time of expanding the macro in question macro arguments are to be gathered from the token stream and processed.

Macro arguments in turn are combinations of tokens.

The term "value" is sort of ambiguous:

  1. Do you mean that there are exactly three different combinations of tokens that could be gathered as macro-argument?

  2. Or does expansion/evaluation as a TeX-⟨number⟩ play a rôle, also?

  3. Do two arguments have the same "value" also in case they do not consist of the same combination of tokens but of different combinations of tokens with an equal amount of tokens and for every possible k the k-th tokens having the same \meaning, i.e., something like:

    \let\One=1
    \let\Two=2
    argument 1: 1\Two
    argument 2: \One2
    

In case it is just about different combinations of tokens, and expansion/evaluation and equality of meanings of different tokens do not play a rôle, and none of the possible combinations contains special tokens like curly braces or hashes (explicit character tokens of category code 1 or 2 or 6), forking can be implemented by means of delimited arguments where parts of the argument-delimiters are formed by the combinations of tokens that are possible. No curly braces and no hashes because these kinds of tokens cannot be components of delimiters of delimited arguments.

\documentclass{article}

\makeatletter
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%% If e-TeX-extensions are available, you can do:
%\newcommand\UD@CheckWhetherNull[1]{%
%  \romannumeral0\if\relax\detokenize{#1}\relax
%    \@firstoftwo{\expandafter\expandafter\expandafter}{} \expandafter\@firstoftwo
%  \else
%    \@firstoftwo{\expandafter\expandafter\expandafter}{} \expandafter\@secondoftwo
%  \fi
%}%
%%-----------------------------------------------------------------------------
%% Check whether argument contains ! not nested in braces:
%%.............................................................................
\@ifdefinable\UD@gobbletoExclam{\long\def\UD@gobbletoExclam#1!{}}%
\newcommand\UD@CheckWhetherNoExclam[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletoExclam#1!}%
}%
\newcommand\Test[3]{%
   \UD@CheckWhetherNoExclam{#1}{%
     \ForkParameterOne!#1!1!{Argument 1 est vide.}%
                      !!#1!{Argument 1 est \'egal à 1.}%
                      !!1!{Argument 1 n'est pas vide, n'est pas egal à 1, et est sans ``!''.}%
                      !!!!%
   }{Argument 1 n'est pas vide, n'est pas egal à 1, et est avec ``!''.} %
   \UD@CheckWhetherNoExclam{#2}{%
     \ForkParameterTwo!#2!v!h!{Argument 2 est vide.}%
                      !!#2!h!{Argument 2 est \'egal à v.}%
                      !!v!#2!{Argument 2 est \'egal à h.}%
                      !!v!h!{Argument 2 n'est pas vide, ni est egal à v, ni est egal à h, et est sans ``!''.}%
                      !!!!%
   }{Argument 2 n'est pas vide, ni est egal à v, ni est egal à h, et est avec ``!''.} %
   \UD@CheckWhetherNoExclam{#3}{%
     \ForkParameterThree!#3!{Argument 3 est vide.}%
                      !!{Argument 3 n'est pas vide et est sans ``!''.}%
                      !!!!%
   }{Argument 3 n'est pas vide et est avec ``!''.}%
}%
\@ifdefinable\ForkParameterOne{%
  \long\def\ForkParameterOne#1!!1!#2#3!!!!{#2}%
}%
\@ifdefinable\ForkParameterTwo{%
  \long\def\ForkParameterTwo#1!!v!h!#2#3!!!!{#2}%
}%
\@ifdefinable\ForkParameterThree{%
  \long\def\ForkParameterThree#1!!#2#3!!!!{#2}%
}%
\makeatother

\parindent=0ex
\parskip=\bigskipamount

\begin{document}
\verb|\Test{1}{v}{}| $\to$\\ \Test{1}{v}{}

\verb|\Test{1}{h}{}| $\to$\\ \Test{1}{h}{}

\verb|\Test{}{}{}| $\to$\\ \Test{}{}{}

\verb|\Test{X}{Y}{Z}| $\to$\\ \Test{X}{Y}{Z}

\verb|\Test{!}{!}{!}| $\to$\\ \Test{!}{!}{!}

\verb|\Test{1a}{vb}{}| $\to$\\ \Test{1a}{vb}{}
\end{document}

enter image description here

If you need the result of the evaluation as a token-sequence, e.g., within expansion-contexts, you can add a bit of exchanging arguments and \romannumeral0-expansion:

\documentclass{article}

\makeatletter
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%% If e-TeX-extensions are available, you can do:
%\newcommand\UD@CheckWhetherNull[1]{%
%  \romannumeral0\ifx\relax\detokenize{#1}\relax
%    \@firstoftwo{\expandafter\expandafter\expandafter}{} \expandafter\@firstoftwo
%  \else
%    \@firstoftwo{\expandafter\expandafter\expandafter}{} \expandafter\@secondoftwo
%  \fi
%}%
%%-----------------------------------------------------------------------------
%% Exchanging two arguments:
%%.............................................................................
\newcommand\UD@Exchange[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% Check whether argument contains ! not nested in braces:
%%.............................................................................
\@ifdefinable\UD@gobbletoExclam{\long\def\UD@gobbletoExclam#1!{}}%
\newcommand\UD@CheckWhetherNoExclam[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@gobbletoExclam#1!}%
}%
%%-----------------------------------------------------------------------------
%% \Test. The result is delivered after two expansion-steps/after two "hits"
%%        with \expandafter.
%%.............................................................................
\newcommand\Test[3]{%
   \romannumeral0%
   \expandafter\UD@Exchange\expandafter{%
     \romannumeral0%
     \UD@CheckWhetherNoExclam{#3}{%
       \ForkParameterThree!#3!{ Argument 3 est vide.}%
                        !!{ Argument 3 n'est pas vide et est sans ``!''.}%
                        !!!!%
     }{ Argument 3 n'est pas vide et est avec ``!''.} %
   }{%
     \expandafter\UD@Exchange\expandafter{%
       \romannumeral0%
       \UD@CheckWhetherNoExclam{#2}{%
         \ForkParameterTwo!#2!v!h!{ Argument 2 est vide.}%
                          !!#2!h!{ Argument 2 est \'egal à v.}%
                          !!v!#2!{ Argument 2 est \'egal à h.}%
                          !!v!h!{ Argument 2 n'est pas vide, ni est egal à v, ni est egal à h, et est sans ``!''.}%
                          !!!!%
       }{ Argument 2 n'est pas vide, ni est egal à v, ni est egal à h, et est avec ``!''.} %
     }{%
       \expandafter\UD@Exchange\expandafter{%
         \romannumeral0%
         \UD@CheckWhetherNoExclam{#1}{%
           \ForkParameterOne!#1!1!{ Argument 1 est vide.}%
                            !!#1!{ Argument 1 est \'egal à 1.}%
                            !!1!{ Argument 1 n'est pas vide, n'est pas egal à 1, et est sans ``!''.}%
                            !!!!%
         }{ Argument 1 n'est pas vide, n'est pas egal à 1, et est avec ``!''.} %
       }{ }%
     }%
   }%
}%
\@ifdefinable\ForkParameterOne{%
  \long\def\ForkParameterOne#1!!1!#2#3!!!!{#2}%
}%
\@ifdefinable\ForkParameterTwo{%
  \long\def\ForkParameterTwo#1!!v!h!#2#3!!!!{#2}%
}%
\@ifdefinable\ForkParameterThree{%
  \long\def\ForkParameterThree#1!!#2#3!!!!{#2}%
}%
\makeatother

\parindent=0ex
\parskip=0ex
\topsep=0ex
\partopsep=0ex

\begin{document}
% adjust gap between top-margin and 1st line of text
\hbox{}%
\kern-\headheight
\kern-\headsep
\kern-1in
\kern-\topmargin
\kern-\baselineskip
\kern-\topskip
\kern\ht\strutbox
\kern.5cm
\enlargethispage{.5cm}%
%--------------------------

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{1}{v}{}%
}
\end{verbatim}
$\to$\\
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{1}{v}{}%
}%
{\tt\frenchspacing\sloppy\string\temp=\meaning\temp\par}%

\kern-\ht\strutbox\kern\dp\strutbox\hrulefill\null

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{1}{h}{}%
}
\end{verbatim}
$\to$\\
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{1}{h}{}%
}%
{\tt\frenchspacing\sloppy\string\temp=\meaning\temp\par}%

\kern-\ht\strutbox\kern\dp\strutbox\hrulefill\null

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{}{}{}%
}
\end{verbatim}
$\to$\\
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{}{}{}%
}%
{\tt\frenchspacing\sloppy\string\temp=\meaning\temp\par}%

\kern-\ht\strutbox\kern\dp\strutbox\hrulefill\null

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{X}{Y}{Z}%
}
\end{verbatim}
$\to$\\
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{X}{Y}{Z}%
}%
{\tt\frenchspacing\sloppy\string\temp=\meaning\temp\par}%

\kern-\ht\strutbox\kern\dp\strutbox\hrulefill\null

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{!}{!}{!}%
}
\end{verbatim}
$\to$\\
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{!}{!}{!}%
}%
{\tt\frenchspacing\sloppy\string\temp=\meaning\temp\par}%

\kern-\ht\strutbox\kern\dp\strutbox\hrulefill\null

\begin{verbatim}
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{1a}{vb}{}%
}
\end{verbatim}
$\to$\\
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\temp
\expandafter\expandafter\expandafter{%
  \Test{1a}{vb}{}%
}%
{\tt\frenchspacing\sloppy\string\temp=\meaning\temp\par}%

\end{document}

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .