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 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: )
share|improve this question

3 Answers 3

up vote 16 down vote accepted

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}
share|improve this answer
    
I know you're a bit obsessed with pgfkeys, but in this case I think you're right. If one is going to use keys, use them as keys and not as sort-of-macros. –  Loop Space Jan 2 '12 at 22:06
    
@Andrew: Even a stopped clock tells the right time twice a day :) –  Ryan Reich Jan 2 '12 at 22:11
    
Started using an extra key. Makes sense indeed. I'm also starting to really love pgfkeys. Everytime I need something it is there for me. I love the way you can go very "object oriented-ish" with the directories. –  romeovs Jan 6 '12 at 22:39
1  
@romeovs: It's not a package, it's a way of life. –  Ryan Reich Jan 6 '12 at 22:42

Implementing this may be done using the pattern matching technique to deal with multiple arguments. It is explained in Section 55.3.2 (Keys that Execute Commands) of my pgf manual.

\documentclass{minimal}
\usepackage{tikz}

\makeatletter
\def\mycommand{%
    \@ifnextchar[%
        \mycommand@%
        {\mycommand@[]}%
}
\def\mycommand@[#1]#2\pgfeov{%
    Argument=#2, Option=#1.%
}
\makeatother
\pgfkeyslet{/example/.@cmd}{\mycommand}

\begin{document}
\pgfkeys{/example=[other]{hello}}
\pgfkeys{/example=hello}
\end{document}

I'm also providing the following solution, which is a slightly nicer API since it only requires one symbol (a + in this case) to provide the option.

\documentclass{minimal}
\usepackage{tikz}

\makeatletter
\def\mycommand#1\pgfeov{%
    \mycommand@#1++\pgfeov%
}
\def\mycommand@#1+#2+#3\pgfeov{%
    Argument=#1, Option=#2.%
}
\pgfkeyslet{/example/.@cmd}{\mycommand}
\makeatother

\begin{document}
    \pgfkeys{/example=hello}
    \pgfkeys{/example=hello+world}
\end{document}
share|improve this answer

The key handler <key>/.code 2 args=<code> says that the second argument is optional and will be set to the empty string if not provided. This is in Section 55.4.3 on p492 of the PGF manual (for version 2.10). The same works for styles (as detailed in Section 55.4.4 on p493).

With the handler <key>/.code=<code> then the argument is also optional. However, with more than 2 arguments then they are mandatory (that is, with <key>/.code n args={<argument count>}{<code>}).

share|improve this answer
    
this doesn't work for me, when no second argument is given I get following behavior: the first argument is the first letter of the given argument and the second argument is taken as all the other letters. –  romeovs Jan 2 '12 at 14:47
    
@romeovs Same story here. I tried \pgfkeys{/example/.code 2 args={Default=#2. Required=#1.}} \pgfkeys{/example={hello}} \pgfkeys{/example={hello}{world}} and the second \pgfkeys command gave me h for the required argument and ello for the option. The third \pgfkeys command worked. –  Marc van Dongen Jan 2 '12 at 15:08

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.