0

I'm trying to generate a set of keys using a \foreach loop, but they don't appear to be created properly.

I've tried debugging with a handler that shows the keys, but they look correct to me in the log. There must be something that I'm missing, but I can't figure out what.

See the comments in the MWE below for where the error shows up.

MWE:

\documentclass{article}

\RequirePackage{pgfkeys}
\RequirePackage{pgffor}

\RequirePackage{etoolbox}

\pgfkeys{/handlers/.store in cs/.code=\pgfkeysalso{%
    \pgfkeyscurrentpath/.code=\expandafter\def\csname#1\endcsname{##1}}%
}

\pgfkeysdef{/handlers/.show path}
{%
    \edef\path{\pgfkeyscurrentpath}%
    \pgfkeysgetvalue{\pgfkeyscurrentpath}{\val}%
    \show\path%
}

\makeatletter

\def\meide@keys{\pgfqkeys{/meide}}

\newcommand\meide@setup[3]{%
    % #1 = name
    % #2 = number of levels
    % #3 = further key/value pairs
    \meide@keys{
        #1/.cd,
        levels/.store in cs=meide@#1@levels,
        levels=#2,
    }%
    \foreach \level in {0,...,\csuse{meide@#1@levels}}%
    {%
        \meide@setup@level{#1}{\level}%
    }%
    \meide@keys{#1/.cd,#3}%
}

\newcommand\meide@setup@level[2]{
    % #1 = name
    % #2 = level to create
    \begingroup\edef\x{\endgroup\noexpand%
        \meide@keys{
            #1/level #2/.cd,
            % uncomment the '.show path' handler to see the current keypaths. I see them as expected:
            % /meide/name/level 0/myvals
            % /meide/name/level 1/myvals
            %myvals/.show path,
            myvals/.code 2 args={%
                % #1 = first
                % #2 = second
                \csxdef{meide@#1@myvals@level #2@first}{\unexpanded{##1}}%
                \csxdef{meide@#1@myvals@level #2@second}{\unexpanded{##2}}%
            }
        }%
    }\x%
}

% setting up one of the levels manually to show how it should work
\meide@keys{
    name/level 0/myvals/.code 2 args={%
        % #1 = first
        % #2 = second
        \csxdef{meide@name@myvals@level 0@first}{#1}%
        \csxdef{meide@name@myvals@level 0@second}{#2}%
    },
}

\meide@setup{name}{1}{
    level 0/myvals={a}{b}, % works because it was created manually
    level 1/myvals={c}{d}, % Package pgfkeys Error: I do not know the key '/meide/name/level 1/myvals', to which you passed '{c}{d}'
}

\makeatother

\begin{document}

\csuse{meide@name@myvals@level 0@first} % displays 'a'

\csuse{meide@name@myvals@level 1@first} % displays nothing

\end{document}
1

This doesn't work because your myvals/.code 2 args={...} is a local assignment, but it is executed inside a TeX group. Indeed, the \foreach loop in \meide@setup executes the loop code inside a TeX group. Once this group is finished, all local definitions performed therein automatically vanish. In order to solve this problem, I simply replaced the \foreach loop with expl3 's \int_step_inline:nnn macro, which doesn't create a group around the loop code.

I also removed the \edef and the associated tricks, because I don't see why they are needed here. With these two changes, everything appears to work as expected.

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

\ExplSyntaxOn
% Borrow \int_step_inline:nnn from expl3
\cs_new_eq:NN \intstepinline \int_step_inline:nnn
\ExplSyntaxOff

\pgfkeys{/handlers/.store in cs/.code=\pgfkeysalso{%
    \pgfkeyscurrentpath/.code=\expandafter\def\csname#1\endcsname{##1}}%
}

\pgfkeysdef{/handlers/.show path}
{%
    \edef\path{\pgfkeyscurrentpath}%
    \pgfkeysgetvalue{\pgfkeyscurrentpath}{\val}%
    \show\path
}

\makeatletter

\newcommand\meide@keys{\pgfqkeys{/meide}}

\newcommand\meide@setup[3]{%
    % #1 = name
    % #2 = number of levels
    % #3 = further key/value pairs
    \meide@keys{
        #1/.cd,
        levels/.store in cs=meide@#1@levels,
        levels=#2,
    }%
    \intstepinline{0}{\csuse{meide@#1@levels}}
      {%
        \meide@setup@level{#1}{##1}%
      }%
    \meide@keys{#1/.cd,#3}%
}

\newcommand\meide@setup@level[2]{%
    \meide@keys{
        #1/level #2/.cd,
        % uncomment the '.show path' handler to see the current keypaths. I see them as expected:
        % /meide/name/level 0/myvals
        % /meide/name/level 1/myvals
        %myvals/.show path,
        myvals/.code 2 args={%
            % #1 = first
            % #2 = second
            \csxdef{meide@#1@myvals@level #2@first}{##1}%
            \csxdef{meide@#1@myvals@level #2@second}{##2}%
        },
    }%
}

\meide@setup{name}{1}{
    level 0/myvals={a}{b}, % works
    level 1/myvals={c}{d}, % now also works
}

\makeatother

\begin{document}

\csuse{meide@name@myvals@level 0@first}  % displays 'a'
\csuse{meide@name@myvals@level 0@second} % displays 'b'

\csuse{meide@name@myvals@level 1@first}  % displays 'c'
\csuse{meide@name@myvals@level 1@second} % displays 'd'

\end{document}

enter image description here

Please try to provide a minimal example next time: this takes too much time to read and analyze. You could probably reduce the code, although I understand that maybe you didn't manage to locate the problem very precisely.

2
  • Sorry, I wasn't able to minimize further without showing the problem :) I had missed that setting keys is local to the current group. The edef/begingroup trick I must shamefully admit was a bit of cargo culting I picked up at github.com/T-F-S/genealogytree/blob/master/tex/latex/… – meide Sep 7 '20 at 16:36
  • 1
    Okay, no problem. :-) – frougon Sep 7 '20 at 16:46

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.