8

Knowing that every command declaration with conditionally optional arguments, as \foo{...} expanding into \foo[default]{...} can be defined by \newcommand\foo[1][default]{...} or the safer version of:

\newcommand\foo{\@ifnextchar[{\@foo}{\@foo[default]}}
\def\@foo[#1]#2{do something with #1 and #2}

this kind of "test" however fails if the optional argument is declared after the mandatory one. Therefore (knowing that the order of argument declaration of \@foo cannot be changed):

\newcommand\foo{\@ifnextchar[{\@foo}{\@foo{}[default]}}
\def\@foo#1[#2]{do something with #1 and #2}

will generate errors.

More generally, if \@bar is defined such that has to contain two (or more, but the general case is left aside) arguments, but only the optional #2 that doesn't appear just after the control sequence name:

\def\@bar#1[#2]{#1 and #2}

then, how is it possible to define \bar such that this condition:

\bar{a}[b] % returns "a and b"
\bar{a}    % returns "a and default"

will be met? In this case an internal command inside the definition of \bar would scan all of the argument tokens of \bar until it finds a [; if it doesn't find any, then it'll just declare \@bar#1[default].

This behaviour should be similar to \@ifnextchar (say \@iftoksexist) with the difference that it will be sufficient for char to just exist as argument token:

\documentclass{article}
\makeatletter
\def\@iftoksexist#1#2#3{...} % 
\def\mybar{%
  \@iftoksexist{[} % or \@iftoksexist[{...}{...}
  {
  %use "\@mybar#1[#2]"
  }
  {
  %use "\@mybar#1[default]"
  }%
} 
\def\@mybar#1[#2]{#1 and #2\par}
\makeatother
%
\begin{document}
%
\mybar{lorem}[ipsum]
\mybar{dolor}
\mybar{sit}[amet]   
%
\end{document}

In this case, \@iftoksexist will search for [ inside every \mybar..., yielding this output:

enter image description here

Yet again, after a sufficient number of trials, and looking this and that, I haven't had much success.

  • 2
    You should have a look at xparse – egreg Sep 14 '14 at 16:30
  • @egreg Right in the weak spot... Unfortunately I haven't put my eye on its documentation for a long time, although it may save some difficulties with the plain TeX understanding of this problem. – alandella Sep 14 '14 at 16:32
7

Just absorb the mandatory argument and “remember” it:

\makeatletter
\newcommand{\foo}[1]{%
  \@ifnextchar[{\foo@aux{#1}}{\foo@aux{#1}[default]}%
}
\def\foo@aux#1[#2]{%
  mandatory is `#1', optional is `#2'%
}
\makeatother

of course this is not very robust and \DeclareRobustCommand should be used.

A syntax like \foo{a}[b]{c} where the optional argument has a default value can be defined in a similar way:

\makeatletter
\newcommand{\foo}[1]{%
  \@ifnextchar[{\foo@aux{#1}}{\foo@aux{#1}[default]}%
}
\def\foo@aux#1[#2]#3{%
  first mandatory is `#1', optional is `#2', second mandatory is `#3'%
}
\makeatother

Alternatively:

\usepackage{xparse}

\NewDocumentCommand{\foo}{mO{default}}{%
  mandatory is `#1', optional is `#2'%
}

which is automatically robust. But as a general advice, avoid trailing optional arguments. The \foo{a}[b]{c} syntax is just

\NewDocumentCommand{\foo}{mO{default}m}{%
  first mandatory is `#1', optional is `#2', second mandatory is `#3'%
}

Note that in the first case spaces will be ignored after \foo{a} if no optional argument is present (the reason is how \@ifnextchar works).

  • The [default] should be in the false branch I guess: \@ifnextchar[{\foo@aux{#1}}{\foo@aux{#1}[default]} – clemens Sep 14 '14 at 16:38
  • @cgnieder You're perfectly right – egreg Sep 14 '14 at 16:40
  • @egreg Needless to say, excellent answer as usual. I'll definitely look up xparse now that I have plenty of time ;) – alandella Sep 14 '14 at 20:40
  • @AndreaL. Exam session ended? – egreg Sep 14 '14 at 20:46
  • @egreg Yeah! But I finished in July; right now I'm preparing some Linear Algebra exercise sheets for my colleagues (with the occasion, I took two birds with one stone) :-) – alandella Sep 14 '14 at 21:02

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.