Is it possible to build a macro with an optional parameter enclosed in round brackets ()
rather than square brackets, where one can say for example:
\mymacro(){}{}
or
\mymacro{}{}
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It only takes a minute to sign up.
Sign up to join this communityIs it possible to build a macro with an optional parameter enclosed in round brackets ()
rather than square brackets, where one can say for example:
\mymacro(){}{}
or
\mymacro{}{}
A more traditional solution!
To build a command with any optional parameter, as you find in many of LaTeX's commands, you will need two things:
(a) a macro with delimited parameters
(b) a way to grab the first non-space token that follows the command
The first part is fairly easy using delimited argument macros, for example we can say
\def\test(#1)#2#3{#1, #2, #3}
We can then call this macro as:
\test(a){b}{c}
resulting in a,b,c
To define the ()
as an optional parameter, we effectively need to define the macro as a conditional a sort of a "yes-no" switch. If TeX finds the "(" bracket the "yes-code" will be called and if it finds only the normal arguments the "no-code" will be executed.
For this we can use the \@ifnextchar
macro from the LaTeX kernel.
You can say \@ifnextchar{char}{yes-code}{no-code}
to test for (
. The result then will depend on the token that follows. If this token is the same as the first argument, then the "yes-code" is executed, otherwise the "no-code" is executed. The first argument should be a single token (for instance a character). Spaces are ignored.
As for example we can redefine the LaTeX code for rule
to accept an optional parameter in round brackets, rather than the traditional square brackets.
\def\Rule{\@ifnextchar(\@Rule%
{\@Rule(\z@)}}
and the full example would look like:
\documentclass{article}
\begin{document}
\def\Rule{\@ifnextchar(\@Rule%
{\@Rule(\z@)}}
\def\@Rule(#1)#2#3{%
\leavevmode
\hbox{%
\setlength\@tempdima{#1}%
\setlength\@tempdimb{#2}%
\setlength\@tempdimc{#3}%
\advance\@tempdimc\@tempdima
\vrule\@width\@tempdimb\@height\@tempdimc\@depth-\@tempdima}}
A test \Rule(6.5pt){100pt}{1pt}
Another test \Rule{100pt}{1pt}
\end{document}
(...(...)...)
will only grab to the first parenthesis (except if inner ones are hidden within braces).
– Bruno Le Floch
Mar 7 '11 at 21:09
A simple xparse
-based solution:
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\mymacro{d()mm}{%
\IfNoValueTF{#1}
{}% Case where #1 is not given
{}% Case where #1 is given
}
or if you want an empty #1
when it is not present:
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand\mymacro{D(){}mm}{%
% Code here
}
(This works as d
/D
indicate an optional argument which is delimited.)
\documentclass{article}
\makeatletter
\def\mymacro{\@ifnextchar(\mymacro@i\mymacro@ii}
\def\mymacro@i(#1)#2#3{%
#1:#2:#3}% the definition
\def\mymacro@ii#1#2{%
#1:#2}% the definition
\makeatother
\begin{document}
\mymacro(foo){bar}{baz} -- \mymacro{BAR}{BAZ}
\end{document}