19

I have defined a command using xparse as

\DeclareDocumentCommand\mymacro{o o o}

and I want to call it with various numbers of arguments (up to 3)

\mymacro, \mymacro[test][two], \mymacro[][][test]

etc...

The issue is that [] doesn't seem to return a \NoValue but an empty token as the tests I use \IfNoValueTF never return false on it.

Is there a way to make an "empty" argument like \mymacro[] set #1 to \NoValue when it is an empty string?

Essentially I need "empty" arguments to be set to \NoValue for my code to work.

I could always do

\mymacro[\NoValue][test][\NoValue]

but of course that defeats the purpose of making it simple.

In my code I use \IfNoValueTF{#n} a lot but for it to work on empty arguments I would need something like \IfNoValueOrEmptyTF.

1
  • 3
    Instead of three optional arguments you might want to consider using key-value pairs.
    – egreg
    Jul 13, 2012 at 19:15

3 Answers 3

5

Using a mixture of xparse and \ifx conditionals you can test for empty arguments:

enter image description here

\documentclass{article}
\usepackage{xparse}% http://ctan.org/pkg/xparse
\makeatletter
\DeclareDocumentCommand\mymacro{o o o}{
  \def\@tempa{#1}Arg 1 is \ifx\@tempa\@empty\relax empty\else #1\fi. \par
  \def\@tempa{#2}Arg 2 is \ifx\@tempa\@empty\relax empty\else #2\fi. \par
  \def\@tempa{#3}Arg 3 is \ifx\@tempa\@empty\relax empty\else #3\fi.
}
\makeatother
\begin{document}
\verb|\mymacro|:\par\mymacro

\verb|\mymacro[test][two]|: \par\mymacro[test][two]

\verb|\mymacro[][][test]|: \par\mymacro[][][test]

\verb|\mymacro[][][]|: \par\mymacro[][][]

\verb|\mymacro[one][][three]|: \par\mymacro[one][][three]
\end{document}​
3
  • This worked. Wasn't fun changing the different syntaxes({} to \else\fi) it at least it works. I imagine one could wrap IfNoValue to include empty values easy enough though. Jul 13, 2012 at 19:14
  • \makeatletter \newcommand{\IfNoValueOrEmptyTF}[3] {\IfNoValueTF{#1}{#2}{\def\@tempa{#1}\ifx\@tempa\@empty#2\else#3\fi}} \makeatother Jul 13, 2012 at 19:18
  • 1
    \if\relax\detokenize{#1}\relax is more economic.
    – egreg
    Jul 13, 2012 at 19:19
15

Here is a \IfNoValueOrEmpty macro:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\IfNoValueOrEmptyTF}{mmm}
 {
  \IfNoValueTF{#1}{#2}
   {
    \tl_if_empty:nTF {#1} {#2} {#3}
   }
 }
\ExplSyntaxOff

\NewDocumentCommand{\mymacro}{ooo}{%
  \IfNoValueOrEmptyTF{#1}{\typeout{\#1 is empty}}{\typeout{\string#1 is `#1'}}%
  \IfNoValueOrEmptyTF{#2}{\typeout{\#2 is empty}}{\typeout{\string#2 is `#2'}}%
  \IfNoValueOrEmptyTF{#3}{\typeout{\#3 is empty}}{\typeout{\string#3 is `#3'}}%
}

\mymacro
\mymacro[a]
\mymacro[a][b]
\mymacro[a][b][c]
\mymacro[][b]
\mymacro[][][c]
\mymacro[][][]

However, a command with three optional arguments is difficult to manage, because one needs to remember which is which. A key-value syntax might be easier to employ.

3
  • Should this solution work with the current xparse version (2013/11/19)? I do get an empty document when trying to use this.
    – Jörg
    Dec 9, 2013 at 22:42
  • @Jörg This document is not supposed to produce output, but only messages on the terminal.
    – egreg
    Dec 9, 2013 at 22:49
  • Silly me, I missed the typeout...
    – Jörg
    Dec 9, 2013 at 22:56
8

Empty arguments and NoValue ones are not the same (and indeed using \NoValue directly is not supported as we will soon change the definition). Unlike LaTeX2e's \newcommand, xparse differentiates between an argument which is given but is empty and one which is not given. Thus if you want to be able to have input of the form

\foo[][][baz]

you don't want a NoValue test at all, but rather

\ExplSyntaxOn
\DeclareDocumentCommand{\foo}{ O{} O{} O{} }
  {
    \tl_if_empty:nTF {#1}
    ...
  }

testing for an empty value using the code-level test. (We do not have a design-level empty test at the present time.)

7
  • Ok, that makes sense but I can't seem to get it to work using lualatex. I've tried including expl3 and things but it causes problems later on in some of my macros Jul 13, 2012 at 18:56
  • @AbstractDissonance expl3 should not interfere with any code which does not use it. Could you give more detail?
    – Joseph Wright
    Jul 13, 2012 at 19:00
  • All I know is when I replaced If IfNoValueTF with tl_if_empty:NTF, added \ExplSyntaxOn and \usepackage{expl3} some of my lua code failed(saying some functions did not exist). Going to try werners method Jul 13, 2012 at 19:06
  • @AbstractDissonance Did you use \tl_if_empty:NTF or \tl_if_empty:nTF - the two are distinct. I really would appreciate an example so I can check there is not an issue in epxl3: could you e-mail me what you saw (joseph.wright@morningstar2.co.uk)?
    – Joseph Wright
    Jul 13, 2012 at 19:12
  • 2
    @AbstractDissonance You said you added \ExplSyntaxOn, did you also include the corresponding \ExplSyntaxOff after defining the command?
    – Scott H.
    Jul 13, 2012 at 19:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.