0

I am trying to create my own template package.

In this package, there are several custom environments defined. One of those environments starts and ends a table, i.e:

\newenvironment{customenvtab}{\begin{tabular{c|c}}{\end{tabular}}

In this environment, I have defined a function to fill the table. This function uses macros defined with the help of the pgfkeys package to use key-value arguments. The problem is, only the first key value argument is mapped correctly when used. When I remove the tabular environment, both arguments are mapped correctly when used.

What am I doing wrong?

FYI: I am not very proficient in LaTeX.

Thank you very much in advance for your replys (and your patience, if there is something missing).

Here is my minimal example:

\documentclass{article}
\usepackage{pgfkeys}

\def\customvarset{\pgfqkeys{/rpath}}

\newenvironment{customenvnotab}{% \newcommand*{\resetvarset}{% \customvarset{% spath/.cd,% var1/.initial=var1 tbd,% var2/.initial=var2 tbd } } \resetvarset \def\notabval##1{\pgfkeysvalueof{/rpath/spath/##1}} \newcommand{\cmdnotab}[1]{% \customvarset{spath/.cd, ##1} \notabval{var1}, \notabval{var2} \resetvarset } }{}

\newenvironment{customenvtab}{% \newcommand*{\resetvarsettab}{% \customvarset{% tpath/.cd,% var3/.initial=var3 tbd,% var4/.initial=var4 tbd } } \resetvarsettab \def\tabval##1{\pgfkeysvalueof{/rpath/tpath/##1}} \newcommand{\cmdtab}[1]{% \customvarset{tpath/.cd, ##1} \tabval{var3} & \tabval{var4} \resetvarsettab } \begin{tabular}{c|c} }{% \end{tabular} }

\begin{document} \section{Working} \begin{customenvnotab} \cmdnotab{}\ \cmdnotab{var1=test var1}\ \cmdnotab{var2=test var2}\ \cmdnotab{var1=bla, var2=blu} \end{customenvnotab} \section{Not Working} \begin{customenvtab} \cmdtab{}\ \cmdtab{var3=test var3}\ \cmdtab{var4=test var4}\ \cmdtab{var3=blo, var4=bli} \end{customenvtab}

\end{document}

  • The problem is that the \pgfqkeys command performs local assignments, and each tabular cell implicitly forms a group. So, the assignment works in the first column (where it is done), then is forgotten when the next cell starts. – frougon Jun 28 '22 at 10:03
  • Thank you for your response, @frougon. So calling \customvarset{tpath/.cd, ##1} before each column actually works. What would be a clean way to implement input of multiple key value arguments with pgfkeys in such a setting (if there even is any)? (e.g. usage of another table package, or other types of solutions?) – frankieee Jun 28 '22 at 13:27
  • Yes, this can work. And if the cell (alignment entry) is about to be finished, then the \resetvarsettab is useless. In the spirit of your first idea, I've written a solution that automatically makes global assignments from /path/to/var3=... and /path/to/var4=.... Since these become macro assignments, I modified the definition of \tabval like so: \newcommand*{\tabval}[1]{\pgfkeys{/rpath/tpath/#1/.my getvalue}}. There is automation to allow nice setup with \customvarset{tpath/.my setup keys={var3, var4}}, so it's nice to use but there is some machinery code. Can post if you want. – frougon Jun 28 '22 at 13:39
  • Please do, that would be great :-) – frankieee Jun 28 '22 at 13:58
  • Done. And welcome to TeX.SE :-) – frougon Jun 28 '22 at 14:40

3 Answers3

0

The problem is that the \pgfqkeys command performs local assignments, and each tabular cell (an “alignment entry” in the TeXbook's terminology) implicitly forms a group. Therefore, when your \cmdtab macro outputs &, the local assignments performed in the previous column are forgotten, which explains why you don't have the expected results in the second column.

So, the simplest remedy is to perform the assignments in each cell, for instance by modifying the definition of your \cmdtab macro like so:

\newcommand*{\cmdtab}[1]{%
    \customvarset{tpath/.cd, ##1}\tabval{var3}%
      &\customvarset{tpath/.cd, ##1}\tabval{var4}%
    \resetvarsettab
}

This is however a bit wasteful and redundant (all keys in ##1 are repeatedly assigned in each entry in the exact same way).

Another possibility is to make it so that (inside \pgfkeys{...}) /path/to/key=value performs a global assignment once /path/to/key has been set up in a special way. In order to do so:

  • I define a handler called .my gset so that /path/to/key/.my gset=value globally stores value in the same macro that pgfkeys uses for keys that directly contain a value (in order to avoid “save stack buildup”, you should not perform any local assignment to keys on which you use the .my gset handler—as per the usual rule: don't mix local and global assignments to the same macro or TeX register).

  • For your convenience, I provide an additional handler: .my set up keys for global assignment. After /some/path/.my set up keys for global assignment={key1, ..., keyn}, doing /some/path/keyi=value for some i in {1, ..., n} executes /some/path/keyi/.my gset=value (all this inside \pgfkeys{...}, of course). In other words, this sets up the named keys so that direct assignments to them are global.

Regarding your code: I avoid nesting macro definitions from within the environment definition, as this is wasteful and makes the code less readable (environment logic cluttered, need to double the hash signs, etc.).

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

\makeatletter \pgfkeysdef{/handlers/.my gset} {% \csgdef{pgfk@\pgfkeyscurrentpath}{#1}% }

% Set up key \pgfkeyscurrentpath/#1 (let's call it 'foo') so that % \pgfkeys{foo=value} performs \pgfkeys{foo/.my gset=value}. \pgfkeysdef{/handlers/.my set up key for global assignment} {% \begingroup \edef\tmp{\endgroup \noexpand\pgfkeys{% \pgfkeyscurrentpath/#1/.code={\noexpand\pgfkeys{% \pgfkeyscurrentpath/#1/.my gset=####1}}% }% }\tmp }

% Call the '.my set up key for global assignment' handler for every key in comma % list #1. \pgfkeysdef{/handlers/.my set up keys for global assignment} {% \pgfkeys{% \pgfkeyscurrentpath/.my set up key for global assignment/.list={#1}}% }

\newcommand*{\customvarset}{\pgfqkeys{/rpath}}

\newcommand*{\customenv@resetvarsettab}{% \customvarset{ tpath/.cd, .my set up keys for global assignment={var3, var4}, var3=var3 tbd, var4=var4 tbd, }% }

\newcommand*{\tabval}[1]{\pgfkeysvalueof{/rpath/tpath/#1}}

\newcommand*{\customenv@cmdtab}[1]{% \customvarset{tpath/.cd, #1}% \tabval{var3}&\tabval{var4}% \customenv@resetvarsettab }%

\newenvironment{customenvtab}{% \customenv@resetvarsettab \let\cmdtab\customenv@cmdtab \begin{tabular}{c|c} }{% \end{tabular}% } \makeatother

\begin{document}

\begin{customenvtab} \cmdtab{}\ \cmdtab{var3=test var3}\ \cmdtab{var4=test var4}\ \cmdtab{var3=blo, var4=bli} \end{customenvtab}

\end{document}

enter image description here

frougon
  • 24,283
  • 1
  • 32
  • 55
  • Why hack the handlers in this way instead of using something like \pgfkeysgetvalue{<path/to/key>}{\mytmp}\expandafter&\mytmp? – Skillmon Jun 28 '22 at 16:35
  • The handlers are all mine. 2) How do you reach column 10 in an elegant way?..
  • – frougon Jun 28 '22 at 16:39
  • \expanded :) 1. Ok, hacking isn't the correct term.
  • – Skillmon Jun 28 '22 at 16:39
  • @Skillmon The OP wrote “FYI: I am not very proficient in LaTeX” and I wanted to support the input syntax from the question! :) – frougon Jun 28 '22 at 16:40
  • But \expanded would support the input syntax from the question (just not the internal definitions of it). – Skillmon Jun 28 '22 at 16:41
  • Mmm, yes, with this addition. But this is much less entertaining. Feel free to write your own answer using \expanded (I sort of stole you one tick recently, so...). :-) – frougon Jun 28 '22 at 16:45
  • @Skillmon Using \expanded as in \expanded{\tabval{var3} & \tabval{var4}} is a good idea for simple cases, however there is the risk of expanding something too early. For instance, if cell 2 contains expandable tests like \ifnum, \ifcase or \ifmmode (which could be inside the value of var4!), without specific protection, the \expanded trick will expand them even before cell 1 has been typeset. With my approach, there is no out-of-order expansion, therefore no such risk. – frougon Jun 28 '22 at 18:22
  • agreed, hence my answer doesn't expand things too much. – Skillmon Jun 28 '22 at 19:28