I am trying to define a macro with variable number of arguments. For example I want to define a macro with two arguments that can work with one argument as well. So the same macro works in two ways.
\textcolor{red}{sample text}
This would limit the red color to the sample text and whereas just calling \textcolor{red}
would turn on the red color until the end of the document. So the second way of using the macro would be
\textcolor{red}
I was trying to implement the macro using \@ifnextchar
. I am not having much success. Could you please give me some hints?
Based on @Joseph's suggestion below I modified my toy code and it works. I still have problem understanding the actual execution flow in the code. Here is my code.
\documentclass[12pt]{article}
\makeatletter
\def\oneortwoargs#1{%
\@ifnextchar\bgroup%
{\twoargs@aux{#1}}
{\onearg@only{#1}}
}
\def\onearg@only#1{%
Only argument provided: #1 %
}
\def\twoargs@aux#1#2{%
Two arguments are provided. \\%
First argument: #1 \\ %
Second argument: #2%
}
\makeatother
\begin{document}
\oneortwoargs{gg}{fff}
\oneortwoargs{qq}
\end{document}
As @Joseph explained below one token is left in the input stream. What is confusing me is that \onearg@only
and \twoargs@aux
are invoked the same way with only one argument.
Best Answer
I'd use the
xparse
package to do this, as everything is then 'pre-packaged':The same can of course be done using
\@ifnextchar
The key point is that you need to look for
{
using\bgroup
rather than trying to use it directly (as it is the begin-group character).To explain how the
\@ifnextchar
part deals with the need for either 1 or 2 arguments, what happens in my second approach is thatdefines a macro which absorbs one argument. So:
absorbs
red
as#1
and leaves{stuff}
in the input stream, whilealso absorbs
red
as#1
and leavesother stuff
in the input stream. We now apply\@ifnextchar
, which 'peeks' at the next token without absorbing it. The test readsand so depending on the result, either
\textcolor{red}
or\color{red}
is inserted into the input stream. Thus we end up with eitheror
which means that
\textcolor
will get the required two arguments, while\color
only gets one. The key is that{red}
is reinserted into the input after either\textcolor
or\color
.Although it is not directly related to the question, a few notes on the
xparse
approach may be useful. When using thexparse
package, arguments are described by letter, withm
representing a mandatory argument andg
representing an optional argument in braces (i.e. an optional TeX group). A+
before the letter allows that argument to accept paragraph tokens, so in my definition the first argument cannot (it's supposed to be the name of a colour), while the second one can (it is arbitrary text).The
g
argument returns a special token (\NoValue
) if the optional argument was not present at all. If a default value was wanted, I'd have usedG{<default>}
. The same approach applies to standard LaTeX optional arguments, which are represented aso
orO
, depending on whether a default value is required. I have tested for\NoValue
with the test\IfNoValueTF
;\IfNoValueT
and\IfNoValueT
are also available.