10

I am trying to use the xkeyval package to include key arguments in a new environment. It seems to work except for default values. Here's my best try so far:

\documentclass{article}

\usepackage{xkeyval}


\makeatletter
\define@key{Mystuff}{foo}[what]{\def\foo{#1}}
\define@key{Mystuff}{bar}[whatever]{\def\bar{#1}}
\savekeys{Mystuff}{foo,bar}
\makeatother

\newenvironment{myenv}[1][]%
{\setkeys{Mystuff}{#1}BEGIN \foo}
{END \bar}

\begin{document}

\begin{myenv}[bar=BAAAR]
Uh?
\end{myenv}

\end{document}

But this will cause LaTeX to complain about \foo being undefined:

! Undefined control sequence.
\\myenv [#1]->\setkeys {Mystuff}{#1}BEGIN \foo 

l.18 \begin{myenv}[bar=BAAAR]

And the output will be something like "BEGIN Uh? END BAAAR" (without the default "what" value to \foo).

What am I doing wrong there, and how can I change this example so the default value for \foo will be used?

Jay
  • 2,895
  • 2
  • 27
  • 38

2 Answers2

8

The default value is used when you specify the key, but don't assign a value: \begin{myenv}[foo] will define \foo to what. Say

\newenvironment{myenv}[1][]%
{\setkeys{Mystuff}{foo,bar,#1}BEGIN \foo}
{END \bar}

so that the default values will be applied.

Or say

\define@key{Mystuff}{foo}{\def\foo{#1}}
\define@key{Mystuff}{bar}{\def\bar{#1}}

and

\newenvironment{myenv}[1][]%
{\setkeys{Mystuff}{foo=what,bar=whatever,#1}BEGIN \foo}
{END \bar}

A key is never evaluated if it's not specified in \setkeys.


Actually, as pointed out by Ulrike Fischer and Ahmed Musa, one can "preset" some keys, and this will be equivalent to specify them in the \setkeys list:

\define@key{Mystuff}[what]{foo}{\def\foo{#1}}
\define@key{Mystuff}[whatever]{bar}{\def\bar{#1}}
\presetkeys{Mystuff}{foo,bar}{}

What strategy to employ depends mostly on the applications. If you always need that foo and bar are set, then \presetkeys is more economical, as the assignment wouldn't be performed twice when the key is specified.

The \presetkeys, however, can go also in the definition of the environment, so the definition of your environment can be

\newenvironment{myenv}[1][]%
  {\presetkeys{foo,bar}{}%
   \setkeys{Mystuff}{#1}BEGIN \foo}
  {END \bar}

and this wouldn't influence other environments using the Mystuff keys.

egreg
  • 1,121,712
  • Thanks a lot! It works (and I understnad the mechanism better now). – Jay Jun 09 '12 at 12:28
  • 2
    @egreg: In xkeyvals keys are evaluated by \setkeys even if they are not specified explicitly in the key list if you preset them with \presetkeys. – Ulrike Fischer Jun 09 '12 at 12:49
  • @UlrikeFischer Good to know. I think that the two methods both have pros and cons: it all depends on the actual application. – egreg Jun 09 '12 at 12:51
  • So, if I have a key family for which I'd like the defaults to be always the same, I can use \presetkeys, and if I want to be able to specify the defaults in each \newcommand or \newenvironment, I would use \setkeys with them in the new defined command, as @egreg showed. Sounds really good (very flexible)! :-) – Jay Jun 09 '12 at 15:06
  • 2
    \setkeys{Mystuff}{foo=what,bar=whatever,#1} will most likely result in setting the same keys twice: once by the hardwired foo=what,bar=whatever and again by #1. \presetkeys will not set from the preset list the keys whose values have been supplied by the user (in #1 in the above example). – Ahmed Musa Jun 09 '12 at 20:43
  • @Ahmed Musa: but using \setkeys{Mystuff}{foo=what,bar=whatever,#1} still works if I want these defaults for only one command or environment (and not for all that use the Mystuff family), right? – Jay Jun 10 '12 at 00:39
  • @Jay: Your best bet is to use \presetkeys, since your keys may eventually grow in number. There will be no point setting them twice needlessly. There are facilities in ltxkeys package that will automatically set up the defaults for you and the syntaxes there look like those of xkeyval package. For example, look at \ltxkeys@declarekeys. – Ahmed Musa Jun 10 '12 at 17:25
3

\savekeys is the wrong command. What you want is

\presetkeys{Mystuff}{foo,bar}{}

The key foo will then be inserted at the start of the \setkeys list.

Ulrike Fischer
  • 327,261