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 am looking for an example of how to get pgfkeys to split a list of options so that: (1) some of the options are processed now, and (2) the rest of the options are passed to a second macro for further processing.

It seems that key filtering should be the answer to this, but I'm not sure how to go about it.

Let's start assuming that there is some command \theirmacro{...} whose single argument takes a key-value list of options. I know that it understands options optA and optB, but it may also accept other options (also later updates of theirpackage might even define new options of which I am not aware at this point). Furthermore, theirpackage might or might not be implemented using pgfkeys.

Now I want to define some new \mymacro{...} which additionally also understands optC and optD, and passes all other options to \theirmacro.

That is, calling \mymacro{optA=1,optC=2} should: (1) do something with the value of optC, and internally also call \theirmacro{optA=1} to process the rest of the options.

share|improve this question
2  
If I am not mistaken, then you want to collect all options which are unknown to \mymacro and want to forward them to \theirmacro, right? In particular, you want to do the job within the body of \mymacro? In this case, you might benefit from an /.unknown key handler rather than key filtering (although key filtering might also work). –  Christian Feuersänger Nov 7 '12 at 19:37
    
Yes, that is exactly what I want to do! As you say probably an .unknown handler is a better alternative than filtering, but I'm still a bit lost on how to do the “option collecting” bit. I'm also surprised that pgftikz doesn't seem to already have an option implemented for this. –  Juan A. Navarro Nov 7 '12 at 22:52
    
Perhaps you do not really need to collect a list. You could call \theirmacro{option i=value i} whenever you encounter an unknown option. I'd say the performance overhead is neglectable compared to a bulk approach. And "option collecting" might turn out to be much more expensiv then you thought. –  Christian Feuersänger Nov 8 '12 at 9:55
    
... "an option implemented for this": the key filtering does such a thing. Clearly, its documentation could be improved and perhaps some simple styles would be adequate. I could help you here, but I'd suggest to try the other option first –  Christian Feuersänger Nov 8 '12 at 9:57
    
The \setkeys* and \setrmkeys commands of the xkeyval package are very handy for this type of task, but you're in the world of pgfkeys. \setkeys* will collect unknown ('remaining') keys in the macro \XKV@rm, which you can use directly or pass to another method. \setrmkeys will use the content of \XKV@rm to set the keys that were not recognized in the last run of \setkeys* or \setrmkeys*. –  Ahmed Musa Nov 8 '12 at 12:35

2 Answers 2

Following some of the hints and comments to my question—and also after just finding another quite relevant question on passing unknown keys to other environments—I was able to build the following implementation.

\documentclass{article}
\usepackage{pgfkeys}
\usepackage{etoolbox}

% A mock-up implementation of \theirmacro on `their package`
\pgfkeys{
  /their package/.cd,
  optA/.code = {(A:#1)},
  optB/.code = {(B:#1)}
}

\newcommand\theirmacro[1]{\pgfqkeys{/their package}{#1}}

% my proposed code for unknown option collecting
\pgfkeys{
  /handlers/.collect unknowns/.style = {
    unknown options/.initial = {},
    .unknown/.code = {%
      % Edit by EM:
      \letcs\reserved{pgfk@\pgfkeyscurrentpath/unknown options}%
      \csedef{pgfk@\pgfkeyscurrentpath/unknown options}{%
        \ifx\reserved\empty\else\expandonce\reserved,\fi
        \expandonce\pgfkeyscurrentname
        \ifx\pgfkeysnovalue##1\else=\expandonce\pgfkeyscurrentvalue\fi
      }%
    }
  }
}

% the implementation of \mymacro on `my package`
\pgfkeys{
  /my package/.cd,
  optC/.code = {(C:#1)},
  optD/.code = {(D:#1)},
}

\newcommand\mymacro[1]{%
  I process options:
  \pgfqkeys{/my package}{
    .collect unknowns,%
    #1,
    unknown options/.get = \myunknowns
  }\par
  They process options:
  \expandafter\theirmacro\expandafter{\myunknowns}
}

\begin{document}

\mymacro{optA=3,optC=2,optD=4,optB=4}

\end{document}

This seems to work as I expect it. But I am no whiz on macro expansion hackery (note I had to rely on etoolbox, since otherwise I'm not sure how to achieve these things), so I would like to know if there are any further hints or comments on how to improve this implementation.

On another note I am still puzzled, and curious, on how would one implement a similar thing using pgfkey filters.

share|improve this answer
    
Every call of \mymacro will first emptify pgfk@\pgfkeyscurrentpath/unknown options before repopulating it. Maybe that is what you desire. –  Ahmed Musa Nov 8 '12 at 23:36

Here is another solution. The handler .save unknowns allows us to submit and initialize a store macro for the unknown keys.

\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\def\emexpandonce#1{\unexpanded\expandafter{#1}}

\pgfqkeys{/their package}{
  optA/.code = {(A:#1)}, optB/.code = {(B:#1)},
  optA2/.code = {(A2-#1)}, optB2/.code = {(B2-#1)}
}
\newcommand*\theirmacro{%
  \begingroup
  \edef\x{\endgroup
    \noexpand\pgfqkeys{/their package}{\emexpandonce\unknownkeys}}\x
}

\pgfqkeys{/my package}{
  optC/.code = {(C:#1)}, optD/.code = {(D:#1)},
  optC2/.code = {(C2-#1)}, optD2/.code = {(D2-#1)}
}
\newcommand*\mymacro[1]{%
  I processed options:
  \pgfqkeys{/my package}{.save unknowns=\unknownkeys,#1}\endgraf
  They processed options:
  \theirmacro
}

\pgfkeys{
  /handlers/.save unknowns/.code={
    \edef\reserved{\expandafter\@car\string#1\@nil}%
    \ifx\reserved\@backslashchar
      \def#1{}%
    \else
      \@latexerr{'\detokenize{#1}' is not escaped; maybe not a macro}\@ehd
    \fi
    \pgfkeysalso{
      \pgfkeyscurrentpath/.unknown/.code={%
        \edef#1{%
          \ifx#1\empty\else\emexpandonce#1,\fi
          \emexpandonce\pgfkeyscurrentname
          \ifx\pgfkeysnovalue##1\else=\emexpandonce\pgfkeyscurrentvalue\fi
        }%
      }
    }
  }
}
\makeatother

\begin{document}
\mymacro{optA=3,optC=2,optD=4,optB=4}
\par
\mymacro{optA2=3,optC2=2,optD2=4,optB2=4}
\end{document}
share|improve this answer

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.