147

I want to define a command that expands to two completely different things depending on whether I supply, or not, an optional argument. For example,

\mycmd{normal}            ->  something(normal)
\mycmd[optional]{normal}  ->  anotherthing(normal, optional)

How can I do this?

2

5 Answers 5

104

If you read deep in the LaTeX code you can find lots of examples where this is done. Any command that has optional arguments actually does this already: there are actually two different commands and which one is called depends on whether or not it is called with an optional command. The TeX trick is to use \@ifnextchar[. For example,

\def\mycmd{\@ifnextchar[{\@with}{\@without}}
\def\@with[#1]#2{hello #1, have you met #2?}
\def\@without#1{goodbye #1}

What happens there is that the TeX interpreter meets the \mycmd macro and expands it (swallowing following white-space, of course). The first thing it then encounters is a test on the next character (which, incidentally, is non-destructive - the next character is simply observed, not processed). If it is a square bracket, then it puts \@with in the stream and starts again. \@with is defined to take two arguments, the first of which is surrounded by square brackets. It therefore matches the first optional argument and the next thingy in the TeX stream. If the next character wasn't a square bracket then \@without is put into the stream. This takes one (normal) parameter. But since \@with and \@without are two completely separate commands, they can do whatever they like with the input.

(Note: For commands defined with \newcommand which take an optional parameter then sort-of what happens is that the two commands \@with and \@without expand to the same command, but the optional parameter of \@with gets passed to it as the first parameter whereas in \@without the default value is passed. It's not quite like that, but the difference is more in conciseness of programming than anything else.)

Here's a fully compilable example, including the mysterious and mystical duo \makeatletter and \makeatother:

\documentclass{article}
%\url{https://tex.stackexchange.com/a/314/86}

\makeatletter
\def\mycmd{\@ifnextchar[{\@with}{\@without}}
\def\@with[#1]#2{Hello #1, have you met #2?}
\def\@without#1{Goodbye #1.}
\makeatother

\begin{document}
\mycmd[Polyphemus]{Odysseus}
\mycmd{Circe}
\end{document}

Produces:

Hello Polyphemus, have you met Odysseus? Goodbye Circe.

12
  • 3
    @juan: but it uses \def instead of \newcommand which isn’t recommended in LaTeX. To note just one advantage, \newcommand tests whether the command already exists and provides meaningful diagnostics, while \def will just override the command, potentially breaking other code. So, in summary, don’t use \def when \newcommand will do. Jul 27, 2010 at 8:29
  • 2
    @Konrad: absolutely! Never use raw TeX unless you know what you are doing! But surrounding the whole thing in an \@ifundefined would satisfy your requirement, I deem. Jul 27, 2010 at 8:35
  • 1
    A weakness of this solution is that \mycmd will be fragile, so it should be preceded by \protect when used in moving arguments.
    – egreg
    Jun 5, 2012 at 12:26
  • 2
    @user69453 In this case the [ is not part of a pair of brackets enclosing an argument. The \@ifnextchar[ looks at the next character in the stream and tests to see if it is [ to see if there is an optional argument or not. Thus in this case [ is playing the role of just a simple single character. Jul 4, 2016 at 17:54
  • 1
    @MartinDrozdik Of course, this is a demonstration of how it can be done, not a template for a general system. There would be ways of adapting this so that the auxiliary commands have a systematic naming scheme, but it would depend on how you specified the two separate actions. But that's a different question. Anyway, nowadays, I'd probably just use xparse with a few conditionals. Aug 31, 2018 at 15:51
71

The LaTeX3 xparse package is designed to aid the construction of macros that take complex optional arguments. In this case, you'd write

\usepackage{xparse}
....
\DeclareDocumentCommand \foo { o m } {%
  \IfNoValueTF {#1} {%
    \something {#2}%
  }{%
    \someotherthing {#1} {#2}%
  }%
}

Internally, this does the same thing as Andrew's answer, but the coding is a little more straightforward/readable and it is also more easily extended. Want another optional argument? Just add another o in the { o m } argument. (o is ‘optional argument’ and m is ‘mandatory argument’; see the xparse documentation for more.)

4
  • Does \IfNoValue check if the second argument was supplied or does it check if the first argument was empty. That is does it distinguish between \foo[something] and \foo[][something] and what if I want the opposite behaviour?
    – Aditya
    Sep 9, 2010 at 16:38
  • 3
    It checks if the first argument was not supplied. It is also possible to specify a default with O{default} which I think might be what you're thinking of. There isn't an \IfEmpty conditional in xparse but there is an equivalent in expl3 or ifmtarg. Sep 9, 2010 at 22:50
  • 1
    Better use \IfNoValueTF {#1} {%. If the optional argument takes more then one digit, this will be important.
    – LaRiFaRi
    Mar 27, 2015 at 13:56
  • Note that foo is then used as \foo{One argument}, \foo[First argument]{Second argument}, if I am not mistaken.
    – Clément
    Oct 23, 2023 at 17:45
57

I want to thank for all of the other answers received. But lately I've turned into a huge fan of the etoolbox package, which provides a great abstraction to do exactly what I wanted at the LaTeX level (with no low level TeX trickery):

\usepackage{etoolbox}

\newcommand\mycmd[2][]{%
  \ifstrempty{#1}{%
    % something with #2
  }{%
    % some other thing with #1 and #2
  }%
}
0
26

I'd prefer Will Robertson's solution, that's more manageable. For a "pure" LaTeX solution:

\makeatletter
\def\ifemptyarg#1{%
  \if\relax\detokenize{#1}\relax % H. Oberdiek
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}
\makeatother

\newcommand{\mycmd}[1][]{%
   \ifemptyarg{#1}
     {<code for empty argument>}
     {<code for non empty argument>}%
}
3
  • For some reason calling \mycmd in both cases leaves the mandatory argument in the stream, i.e. \mycmd{a} generates <code for non empty argument>a
    – ScumCoder
    Jan 14, 2020 at 18:21
  • @ScumCoder \mycmd has no mandatory argument.
    – egreg
    Jan 14, 2020 at 18:24
  • Oh... I see. So the usage is either \mycmd, or \mycmd[a].
    – ScumCoder
    Jan 14, 2020 at 18:28
13

I usually just use the ifthen package to solve this, by testing whether the optional argument is empty.

\newcommand\mycmd@noargs{foo}
\newcommand\mycmd@withargs[1]{bar #1}
\newcommand\mycmd[1][]{
  \ifthenelse{\equal{#1}{}}{
    \mycmd@noargs{}}{
    \mycmd@withargs{#1}}}

(Notice that since it uses @ as a letter, you need to use \makeatletter before this definition if it’s in your main LaTeX file (instead of a package), and \makeatother after it.)

3
  • 9
    This is not really a solution, I'm afraid: test it with \mycmd[aa] where \ifx returns true because it compares a with a.
    – egreg
    Jun 5, 2012 at 11:07
  • 3
    @egreg Whoa. Surprising that nobody has noticed this for two years. Jun 5, 2012 at 12:23
  • 6
    (Note that egreg and Konrad are discussing a pure-Tex alternative that has now been edited out.)
    – dubiousjim
    Jun 30, 2014 at 9:18

You must log in to answer this question.

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