[Tex/LaTex] Optional arguments in pgfkeys

optional argumentspgfkeys

I need an optional argument in a pgfkey? Something like the following:

\pgfkeys{example/.code = (Argument: #2, optional argument: #1)}
\pgfkeys{example=[other]{some}}

should result in

(Argument: some, optional argument: other)

and

\pgfkeys{example=[]{some}}

in

(Argument: some, optional argument: )

Best Answer

Here are all the ways I know of to do what you want:

  • First, you could use \pgfkeyslet together with a macro, defined through \newcommand, that takes an optional argument (Marc's approach, simplified a bit but somewhat less functional). The downside: first, you have to remember to take care of the \pgfkeyseov that is inserted, and also, if you only pass one argument, the braces are removed so you have to double them. Marc's method doesn't have this problem (and is therefore perfect for what you ask, but see my last paragraph) but requires you to get your hands fairly dirty with delimited macros, kernel functions, and internal workings of the pgfkeys system (the manual even tells you never to set .@cmd directly).

  • Second, you could use the .code handler directly together with a \newcommand macro. This makes it unnecessary to deal with \pgfkeyseov but, because the processing passes through an internally-defined macro that takes a single argument delimited by that control sequence, the key's value is handled twice and so you need three levels of braces. This is, obviously, not desirable.

  • Third, you could use .code 2 args (Andrew's approach), which works if you have learned about the double-processing and remember to add two levels of braces (the third level is unnecessary because the result is not going to be passed to a macro that expects braces).

  • Fourth, you could give up on having one key with two arguments and do it in what I consider to be the right way: define two keys and some defaults that enforce the mandatory/optional semantics, and have everything go through a macro that sets things up so the usage is transparent. In this one, you just have to write \mykeyfour{mandatory = ?, optional = ?}; if you skip mandatory, then rather than TeX crashing, you simply get an (invalid) text (though you could have it raise an error if you like).

I prefer option four because, in my opinion, what you are asking for literally is in violation of the key-value paradigm. You seem to want to define a key that acts like a LaTeX macro, but keys don't behave like macros: they behave like pgfkeys keys! That has its own rules, its own facilities for setting defaults and handling optional values, and correspondingly, its own syntax conventions. I would also like to observe that it only makes sense to have optional arguments in a top-level key that a user might call: in any internal key, you have enough control to make all arguments explicit. However, in a user-visible key there is a good reason not to do this either.

Among the many technical reasons not to do it the way you want is one practical one: it obscures the intent of the code. Having multiple arguments means that the meaning of the argument must be inferred from the position; but in a key-value system, the whole point is that the meaning can be inferred from the name of the key! If your key is /draw a box and takes an optional argument [height] and mandatory argument {width}, then you have not done better than \makebox. If I were defining a key-value version of makebox I would have it work like \makebox[width = w, height = h]{text} (in fact, I did this in How to create a command with key values?), so that you knew what the random numbers meant. Using keys means you have the freedom to be verbose, to separate the ingredients of your function and have them processed in any order at any time and be handled independently. Use that freedom!

Here are my examples:

\documentclass{article}
\usepackage{pgfkeys}

\makeatletter
% arg 3 = \pgfeov
\newcommand\macro[3][]{(mandatory arg: #2; optional arg: #1)}
\pgfkeyslet{/key/.@cmd}{\macro}
\makeatother

\newcommand\macrotwo[2][]{(mandatory arg: #2; optional arg: #1)}
\pgfkeys{
 /key two/.code = \macrotwo#1
}

\pgfkeys{
 /key three/.code 2 args = {(mandatory arg: #1; optional arg: #2)}
}

\newcommand\mykeyfour[1]{%
 \pgfkeys{/key four,optional,mandatory,#1,print}%
}
\pgfkeys{
 /key four/.is family, /key four,
 optional/.default = {},
 optional/.store in = \keyfouroptional,
 mandatory/.default = {(invalid)},
 mandatory/.store in = \keyfourmandatory,
 print/.code = {(mandatory arg: \keyfourmandatory, optional arg: \keyfouroptional)},
}

\begin{document}
\noindent /key:

 \pgfkeys{/key = [optional]{mandatory}}

 % The braces are removed when the argument is scanned...
 \pgfkeys{/key = {{mandatory}}}

\noindent /key two:

 \pgfkeys{/key two = [optional]{mandatory}}

 % Would you believe that the braces are unwrapped TWICE?
 \pgfkeys{/key two = {{{mandatory}}}}

\noindent /key three:

 \pgfkeys{/key three = {mandatory}{optional}}

 % Braces removed twice again...
 \pgfkeys{/key three = {{mandatory}}}

\noindent /key four:

 \mykeyfour{mandatory = mandatory, optional = optional}

 \mykeyfour{optional = optional, mandatory = mandatory}

 \mykeyfour{mandatory = mandatory}

 \mykeyfour{optional = optional}
\end{document}
Related Question