4

In one of my macros, I wanted to set some width to either two-column or single column width. In other macros I do use key width, so I wanted to store the requested width in that key. So my first attempt was

    \setkeys{MEMacros}{wide=false,#1}% 
    \par 1: wide=\ME@wide\par % Test point 1
\expandafter\ifstrequal\expandafter{\ME@wide}{true}
    {\setkeys{MEMacros}{width=\textwidth}}%
    {\setkeys{MEMacros}{width=\columnwidth}}%
    \par 2: wide=\ME@wide\par % Test point 2

However, in the second test point \ME@wide has a value different from that in the first test point. I guessed that the issue is caused by \setkeys after the first test point. And really, if I replace them with

\expandafter\ifstrequal\expandafter{\ME@wide}{true}
    {\def\mywidth{\textwidth}}
    {\def\mywidth{\columnwidth}}

then everything works fine. So, it looks like using \setkeys...{width...} changes the value of the wide key.

What do I wrong, or why using \setkeys in such a situation has the side-effect of changing the value of another key? Or, setting key not from the 1st argument is illegal? Below is a complete MWE.

\documentclass{book}
\usepackage{xkeyval}
\usepackage{etoolbox}
\usepackage{ifthen}

    \makeatletter
    \define@key{MEMacros}{wide}[true]{\def\ME@wide{#1}}
    \define@key{MEMacros}{width}[3cm]{\def\ME@width{#1}}
    \presetkeys{MEMacros}{wide=false}{}%
    \presetkeys{MEMacros}{width=3cm}{}%

\newcommand\Table[2][]{%
    \setkeys{MEMacros}{wide=false,#1}% 
    \par 1: wide=\ME@wide, \par
    \expandafter\ifstrequal\expandafter{\ME@wide}{true}
        {\setkeys{MEMacros}{width=\textwidth}}%
        {\setkeys{MEMacros}{width=\columnwidth}}%
    \par 2: wide=\ME@wide, 
    }
\makeatother

\begin{document}
This is my table, wide:

\Table[wide]{A wide table}

\end{document}

And the result is

This is my table, wide:
1: wide=true,
2: wide=false,
5
  • 3
    Please, make a compilable example.
    – egreg
    Jan 18 '15 at 14:20
  • 2
    There must be some more context here: for example, you have #1 so presumably there is a wrapper macro. Can you show us this structure? Also, can you show us what you actually see for your two tests?
    – Joseph Wright
    Jan 18 '15 at 14:22
  • 2
    You've got \presetkeys{MEMacros}{wide=false} in your source, which deliberately sets wide=false every time you do \setkeys{MEMacros} if the wide key isn't used. Presumably that isn't what you want to achieve, so perhaps you might explain the intention.
    – Joseph Wright
    Jan 18 '15 at 15:38
  • Thanks a lot. My idea was that \presetkeys is executed only when \setkeys is invoked with argument #1. Also, since multiple \setkeys allowed, and in case of several arguments it should also be used, I did not expect that \setkeys{width} will also reset what I expected to be reset only with \setkeys{wide}.
    – katang
    Jan 18 '15 at 15:50
  • @user11708 As far as \setkeys is concerned, there is no difference between something passed from 'higher up' as #1 and something explicit. The whole point of presetting is to deal with keys that are not given.
    – Joseph Wright
    Jan 18 '15 at 15:52
2

You're not doing anything wrong. What you've done is use \presetkeys which is the cause of your apparent problem. The following is mentioned about \presetkeys in the xkeyval documentation (section 6 Presetting keys, p 15):

\presetkeys[<prefix>]{<family>}{<head keys>}{<tail keys>}
\gpresetkeys[<prefix>]{<family>}{<head keys>}{<tail keys>}

\presetkeys will save <head keys> to \XKV<prefix><family>preseth and <tail keys> to \XKV<prefix><family>presett. Savings are done locally by \presetkeys and globally by \gpresetkeys (compare \savekeys and \gsavekeys, [...]). The saved macros will be used by \setkeys, when they are defined, whenever <family> is used in the <families> argument of \setkeys. Head keys will be set before setting user keys, tail keys will be set afterwards. However, if a key appears in the user input, this particular key will not be set by any of the preset keys.

So, since you have

\presetkeys{MEMacros}{wide=false}{}

as part of the <head keys>, wide=false will be set with every call to \setkeys{MEMacros}{<keys>} before anything else.

More specifically,

\setkeys{MEMacros}{wide=false,#1}

will set wide to false (as a result of the \presetkeys{MEMacros}{wide=false}{} clause), then it will set wide to false again (due to it being supplied inside the \setkeys clause. Finally, wide is set to true as a result of #1 being set. In the subsequent conditional, another call to \setkeys makes wide=false (due to \presetkeys clause again).

If you want the default value of wide to be false to start with, issuing

\gsetkeys{MEMacros}{wide=false}

would do the trick, rather than using \presetkeys. Alternatively, set the default keys inside your \Table macro with every call to \setkeys.

Of course, the above discussion also holds for other \presetkeys (width in this case).

1

Not sure how this fits into your bigger scheme, but you could do:

\documentclass{book}
\usepackage{xkeyval}
\usepackage{etoolbox}
\usepackage{ifthen}

\makeatletter
\define@key{MEMacros}{wide}[true]{\def\ME@wide{#1}}
\define@key{MEMacros}{width}[3cm]{\def\ME@width{#1}}
\presetkeys{MEMacros}{wide=false}{}%
\presetkeys{MEMacros}{width=3cm}{}%

\newcommand\Table[2][]{%
  \setkeys{MEMacros}{#1}%
  \par 1: wide=\ME@wide, \par
  \expandafter\ifstrequal\expandafter{\ME@wide}{true}
  {\setkeys{MEMacros}{#1,width=\textwidth}}%
  {\setkeys{MEMacros}{#1,width=\columnwidth}}%
  \par 2: wide=\ME@wide,
}
\makeatother

\begin{document}
  This is my table, wide:

  \Table[wide]{A wide table}

\end{document}

two true

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.