5

How may I define a new command that admits starred and non-starred variants, and that also admits an optional argument?

I tried the following:

\documentclass{minimal}
\makeatletter
\newcommand\MyCommand[1][1]{%
  \@ifstar{%
    The starred variant with parameter: #1%
  }{%
    The non-starred variant with parameter: #1%
  }
}
\makeatother
\begin{document}
\MyCommand    \\    
\MyCommand*   \\
\MyCommand[2] \\
\MyCommand*[2]
\end{document}

But this gives:

The non-starred variant with parameter: 1
The starred variant with parameter: 1
The non-starred variant with parameter: 2
The starred variant with parameter: 1[2]

Yet, one can write \MyCommand[2]* to obtain "The starred variant with parameter: 2" but somehow I'd like the above version to work.

  • 2
    Please, avoid using the minimal class; it is not meant for minimal examples. – egreg Mar 15 at 13:52
  • I learned many things with this question, including the fact that the minimal class is not made for minimal examples! – Bruno Mar 15 at 14:27
7

With xparse it's very easy to play around with optional arguments and starred variants:

\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\MyCommand
  {
    s % optional *
    O{1} % first optional argument (default = 1)
  }
  {%
    \IfBooleanTF{#1}
      {The starred variant with parameter: #2}
      {The non-starred variant with parameter: #2}
  }
\begin{document}
\noindent
\MyCommand   \\
\MyCommand*  \\
\MyCommand[2]\\
\MyCommand*[2]
\end{document}

With LaTeX's \newcommand it a little trickier. The \@ifstar macro looks at the next token after the macro is expanded and has absorbed its arguments, so you need to first check for the * and only then look for the optional argument:

\documentclass{article}
\makeatletter
\newcommand\MyCommand
  {%
    \@ifstar
      {\MyCommand@star}
      {\MyCommand@nostar}%
  }
\newcommand\MyCommand@star[1][1]{%
  The starred variant with parameter: #1%
}
\newcommand\MyCommand@nostar[1][1]{%
  The non-starred variant with parameter: #1%
}
\makeatother
\begin{document}
\noindent
\MyCommand   \\
\MyCommand*  \\
\MyCommand[2]\\
\MyCommand*[2]
\end{document}

Both versions print:

enter image description here

Your code works, but not as you expect it to. The \MyCommand[1][1] looks for an optional argument “while expanding” \MyCommand, which then gives you:

\@ifstar{%
  The starred variant with parameter: <optional argument or default>%
}{%
  The non-starred variant with parameter: <optional argument or default>%
}

and only after that the \@ifstar test will be expanded to look for the optional * and choose the text accordingly, so the actual syntax for the command you defined is:

\MyCommand[optional argument]<optional star>
3

Make \MyCommand take no parameters, but just figure out the star. Then fork from there.

\documentclass{minimal}
\makeatletter
\newcommand\MyCommand{%
  \@ifstar{\mycommandstar}{\mycommandnostar}
}
\newcommand\mycommandstar[1][1]{The starred variant with parameter: #1}
\newcommand\mycommandnostar[1][1]{The non-starred variant with parameter: #1}
\makeatother
\begin{document}
\MyCommand    \\    
\MyCommand*   \\
\MyCommand[2] \\
\MyCommand*[2]
\end{document}

enter image description here

  • 2
    I'd add a % at the end of the definition of \MyCommand. It works without that because the definition of \@ifstar ignores space tokens by design, but... :) – Phelype Oleinik Mar 15 at 14:20

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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