Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I'm writing my first package, and I'm trying to work out how to create a new command with an optional final argument. In this instance, if an argument is omitted, it defaults to blank. This is problematic because it assumes that the two arguments passed in this case are #2 and #3.

\newcommand{\sfsx}[3][]{\wrapper{#1}{#2}{#3}}

Unfortunately, I would like it the other way around, where if only two arguments are given, they are assumed to be #1 and #2. Is there a simple way to do this that doesn't invoke other packages?

(\wrapper in this case is just another command that takes on various forms depending on package options).

share|improve this question
add comment

2 Answers 2

up vote 10 down vote accepted

Try the new xparse package, which is built on top of LaTeX3. With this package you can specify arbitrary combinations of mandatory and optional arguments. This case would be as easy as

\DeclareDocumentCommand \sfsx { m m o } { \wrapper{#1}{#2}{#3} }

If you want to have your own default value, use O{value} instead of o.

share|improve this answer
1  
That works pretty well, with the breaking change that the optional argument loses its formatting. Consider the following: \newcommand{\wrapper}[3]{\ensuremath{\mathrm{\uppercase{#1}}_{#2#3}}}: running \sfsx{c}{2}{v} will produce the expected behaviour for the first two characters but the third will be set in normal sized roman rather than subscript italic. –  Richard Terrett Oct 5 '11 at 14:44
1  
@RichardTerrett: [v] :-) –  Andrey Vihrov Oct 5 '11 at 14:50
    
Problem solved, thankyou. –  Richard Terrett Oct 5 '11 at 14:57
1  
If you want the final argument in a TeX group ({...}), you could use the G or g argument specification. –  Joseph Wright Oct 5 '11 at 15:07
add comment

This can't be done using \newcommand directly. One way is to read the first mandatory arguments, store them in a temporary macro and then call another macro which has only one optional argument and reads the other ones from the temporary macro. An easier way would be to use the xparse package which allows for several optional arguments at any position (see Andrey's answer).

\documentclass{article}

\makeatletter
\newcommand{\sfsx}[2]{%
    \def\sfsx@args{{#1}{#2}}%
    \sfsx@
}
\newcommand{\sfsx@}[1][]{%
    \expandafter\wrapper\sfsx@args{#1}%
}
\makeatletter

\newcommand{\wrapper}[3]{%
    I got (#1), (#2) and (#3).
}

\begin{document}

\sfsx{A}{B}

\sfsx{A}{B}[C]

\end{document}

This can be made a little more efficient if you use the internal \@testopt macro directly which is used by LaTeX to test for the optional argument:

\documentclass{article}

\makeatletter
\newcommand{\sfsx}[2]{%
    \@testopt{\sfsx@{#1}{#2}}{}%
}
\def\sfsx@#1#2[#3]{%
    \wrapper{#1}{#2}{#3}%
}
\makeatletter

\newcommand{\wrapper}[3]{%
    I got (#1), (#2) and (#3).
}

\begin{document}

\sfsx{A}{B}

\sfsx{A}{B}[C]

\end{document}
share|improve this answer
    
Instead of the empty brace group after \@testopt you can write {xyz}, and xyz will be the default value for the optional argument. –  egreg Oct 5 '11 at 14:48
    
I appreciate the detailed response. As I'm not well versed in the answer syntax, I'm further investigating xparse, as whilst it does introduce a dependency, it also seems the most parsimonious method. (:P) –  Richard Terrett Oct 5 '11 at 14:49
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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