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?

2 Answers 2

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.

7
  • Thanks a lot! It works (and I understnad the mechanism better now).
    – Jay
    Commented Jun 9, 2012 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. Commented Jun 9, 2012 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
    Commented Jun 9, 2012 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
    Commented Jun 9, 2012 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
    Commented Jun 9, 2012 at 20:43
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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .