TransWikia.com

Generating pgfkeys paths using foreach loop

TeX - LaTeX Asked on February 5, 2021

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=expandafterdefcsname#1endcsname{##1}}%
}

pgfkeysdef{/handlers/.show path}
{%
    edefpath{pgfkeyscurrentpath}%
    pgfkeysgetvalue{pgfkeyscurrentpath}{val}%
    showpath%
}

makeatletter

defmeide@keys{pgfqkeys{/meide}}

newcommandmeide@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}%
}

newcommandmeide@setup@level[2]{
    % #1 = name
    % #2 = level to create
    begingroupedefx{endgroupnoexpand%
        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}

One Answer

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=expandafterdefcsname#1endcsname{##1}}%
}

pgfkeysdef{/handlers/.show path}
{%
    edefpath{pgfkeyscurrentpath}%
    pgfkeysgetvalue{pgfkeyscurrentpath}{val}%
    showpath
}

makeatletter

newcommandmeide@keys{pgfqkeys{/meide}}

newcommandmeide@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}%
}

newcommandmeide@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.

Answered by frougon on February 5, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP